Merge "tp: allow span outer join on unpartitioned tables"
diff --git a/Android.bp b/Android.bp
index 3b4bbdd..563cfa8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6838,6 +6838,7 @@
 filegroup {
     name: "perfetto_src_base_base",
     srcs: [
+        "src/base/crash_keys.cc",
         "src/base/ctrl_c_handler.cc",
         "src/base/event_fd.cc",
         "src/base/file_utils.cc",
diff --git a/BUILD b/BUILD
index edf1172..9e1fcd0 100644
--- a/BUILD
+++ b/BUILD
@@ -333,6 +333,7 @@
     srcs = [
         "include/perfetto/ext/base/circular_queue.h",
         "include/perfetto/ext/base/container_annotations.h",
+        "include/perfetto/ext/base/crash_keys.h",
         "include/perfetto/ext/base/ctrl_c_handler.h",
         "include/perfetto/ext/base/endian.h",
         "include/perfetto/ext/base/event_fd.h",
@@ -634,6 +635,7 @@
 perfetto_cc_library(
     name = "src_base_base",
     srcs = [
+        "src/base/crash_keys.cc",
         "src/base/ctrl_c_handler.cc",
         "src/base/event_fd.cc",
         "src/base/file_utils.cc",
diff --git a/gn/gen_perfetto_version_header.gni b/gn/gen_perfetto_version_header.gni
index 17c879f..59cd681 100644
--- a/gn/gen_perfetto_version_header.gni
+++ b/gn/gen_perfetto_version_header.gni
@@ -21,15 +21,20 @@
 
 import("perfetto.gni")
 
+_ver_script = "${perfetto_root_path}tools/write_version_header.py"
+_has_git = false
+if (perfetto_enable_git_rev_version_header) {
+  _has_git = "1" == exec_script(_ver_script, [ "--check_git" ], "trim string")
+}
+
 template("gen_perfetto_version_header") {
   action(target_name) {
-    script = "${perfetto_root_path}tools/write_version_header.py"
+    script = _ver_script
     changelog = "${perfetto_root_path}CHANGELOG"
     inputs = [ changelog ]
     outputs = []
     args = []
-    if (perfetto_build_standalone && !is_perfetto_build_generator &&
-        perfetto_enable_git_rev_version_header) {
+    if (_has_git) {
       inputs += [ "${perfetto_root_path}.git/HEAD" ]
     }
 
@@ -49,7 +54,7 @@
       ]
       outputs += [ invoker.ts_out ]
     }
-    if (!perfetto_enable_git_rev_version_header) {
+    if (!_has_git) {
       args += [ "--no_git" ]
     }
   }
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index c6817aa..4520087 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -235,7 +235,9 @@
 }
 
 declare_args() {
-  perfetto_enable_git_rev_version_header = enable_perfetto_version_gen
+  perfetto_enable_git_rev_version_header =
+      enable_perfetto_version_gen && perfetto_build_standalone &&
+      !is_perfetto_build_generator
 
   # The traced_probes daemon is very Linux-specific, as it depends on ftrace and
   # various /proc interfaces. There is no point making its code platform-neutral
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index ec8cca8..e86ca99 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -18,6 +18,7 @@
   sources = [
     "circular_queue.h",
     "container_annotations.h",
+    "crash_keys.h",
     "ctrl_c_handler.h",
     "endian.h",
     "event_fd.h",
diff --git a/include/perfetto/ext/base/crash_keys.h b/include/perfetto/ext/base/crash_keys.h
new file mode 100644
index 0000000..921265f
--- /dev/null
+++ b/include/perfetto/ext/base/crash_keys.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
+#define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
+
+#include <algorithm>
+#include <atomic>
+
+#include <stdint.h>
+#include <string.h>
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/string_view.h"
+
+// Crash keys are very simple global variables with static-storage that
+// are reported on crash time for managed crashes (CHECK/FATAL/Watchdog).
+// - Translation units can define a CrashKey and register it at some point
+//   during initialization.
+// - CrashKey instances must be long-lived. They should really be just global
+//   static variable in the anonymous namespace.
+// Example:
+// subsystem_1.cc
+//   CrashKey g_client_id("ipc_client_id");
+//   ...
+//   OnIpcReceived(client_id) {
+//      g_client_id.Set(client_id);
+//      ... // Process the IPC
+//      g_client_id.Clear();
+//   }
+//   Or equivalently:
+//   OnIpcReceived(client_id) {
+//      auto scoped_key = g_client_id.SetScoped(client_id);
+//      ... // Process the IPC
+//   }
+//
+// If a crash happens while processing the IPC, the crash report will
+// have a line "ipc_client_id: 42".
+//
+// Thread safety considerations:
+// CrashKeys can be registered and set/cleared from any thread.
+// There is no compelling use-case to have full acquire/release consistency when
+// setting a key. This means that if a thread crashes immediately after a
+// crash key has been set on another thread, the value printed on the crash
+// report could be incomplete. The code guarantees defined behavior and does
+// not rely on null-terminated string (in the worst case 32 bytes of random
+// garbage will be printed out).
+
+// The tests live in logging_unittest.cc.
+
+namespace perfetto {
+namespace base {
+
+constexpr size_t kCrashKeyMaxStrSize = 32;
+
+// CrashKey instances must be long lived
+class CrashKey {
+ public:
+  class ScopedClear {
+   public:
+    explicit ScopedClear(CrashKey* k) : key_(k) {}
+    ~ScopedClear() {
+      if (key_)
+        key_->Clear();
+    }
+    ScopedClear(const ScopedClear&) = delete;
+    ScopedClear& operator=(const ScopedClear&) = delete;
+    ScopedClear& operator=(ScopedClear&&) = delete;
+    ScopedClear(ScopedClear&& other) : key_(other.key_) {
+      other.key_ = nullptr;
+    }
+
+   private:
+    CrashKey* key_;
+  };
+
+  // constexpr so it can be used in the anon namespace without requiring a
+  // global constructor.
+  // |name| must be a long-lived string.
+  constexpr explicit CrashKey(const char* name)
+      : registered_{}, type_(Type::kUnset), name_(name), str_value_{} {}
+  CrashKey(const CrashKey&) = delete;
+  CrashKey& operator=(const CrashKey&) = delete;
+  CrashKey(CrashKey&&) = delete;
+  CrashKey& operator=(CrashKey&&) = delete;
+
+  enum class Type : uint8_t { kUnset = 0, kInt, kStr };
+
+  void Clear() { type_ = Type::kUnset; }
+
+  void Set(int64_t value) {
+    int_value_ = value;
+    type_ = Type::kInt;
+    if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed)))
+      Register();
+  }
+
+  void Set(StringView sv) {
+    size_t len = std::min(sv.size(), sizeof(str_value_) - 1);
+    memcpy(str_value_, sv.data(), len);
+    str_value_[len] = '\0';
+    type_ = Type::kStr;
+    if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed)))
+      Register();
+  }
+
+  ScopedClear SetScoped(int64_t value) PERFETTO_WARN_UNUSED_RESULT {
+    Set(value);
+    return ScopedClear(this);
+  }
+
+  ScopedClear SetScoped(StringView sv) PERFETTO_WARN_UNUSED_RESULT {
+    Set(sv);
+    return ScopedClear(this);
+  }
+
+  int64_t int_value() const { return int_value_; }
+  size_t ToString(char* dst, size_t len);
+
+ private:
+  void Register();
+
+  std::atomic<bool> registered_;
+  Type type_;
+  const char* const name_;
+  union {
+    char str_value_[kCrashKeyMaxStrSize];
+    int64_t int_value_;
+  };
+};
+
+// Fills |dst| with a string containing one line for each crash key
+// (excluding the unset ones).
+// Returns number of chars written, without counting the NUL terminator.
+// This is used in logging.cc when emitting the crash report abort message.
+size_t SerializeCrashKeys(char* dst, size_t len);
+
+void UnregisterAllCrashKeysForTesting();
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index dc8eff5..5d5d645 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -31,6 +31,7 @@
     "../../include/perfetto/ext/base",
   ]
   sources = [
+    "crash_keys.cc",
     "ctrl_c_handler.cc",
     "event_fd.cc",
     "file_utils.cc",
diff --git a/src/base/crash_keys.cc b/src/base/crash_keys.cc
new file mode 100644
index 0000000..5c975df
--- /dev/null
+++ b/src/base/crash_keys.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "perfetto/ext/base/crash_keys.h"
+
+#include <string.h>
+
+#include <atomic>
+#include <cinttypes>
+
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace base {
+
+namespace {
+
+constexpr size_t kMaxKeys = 32;
+
+std::atomic<CrashKey*> g_keys[kMaxKeys]{};
+std::atomic<uint32_t> g_num_keys{};
+}  // namespace
+
+void CrashKey::Register() {
+  // If doesn't matter if we fail below. If there are no slots left, don't
+  // keep trying re-registering on every Set(), the outcome won't change.
+
+  // If two threads raced on the Register(), avoid registering the key twice.
+  if (registered_.exchange(true))
+    return;
+
+  uint32_t slot = g_num_keys.fetch_add(1);
+  if (slot >= kMaxKeys) {
+    PERFETTO_LOG("Too many crash keys registered");
+    return;
+  }
+  g_keys[slot].store(this);
+}
+
+// Returns the number of chars written, without counting the \0.
+size_t CrashKey::ToString(char* dst, size_t len) {
+  if (len > 0)
+    *dst = '\0';
+  switch (type_) {
+    case Type::kUnset:
+      break;
+    case Type::kInt:
+      return SprintfTrunc(dst, len, "%s: %" PRId64 "\n", name_, int_value_);
+    case Type::kStr:
+      // Don't assume |str_value_| is properly null-terminated.
+      return SprintfTrunc(dst, len, "%s: %.*s\n", name_,
+                          int(sizeof(str_value_)), str_value_);
+  }
+  return 0;
+}
+
+void UnregisterAllCrashKeysForTesting() {
+  g_num_keys.store(0);
+  for (auto& key : g_keys)
+    key.store(nullptr);
+}
+
+size_t SerializeCrashKeys(char* dst, size_t len) {
+  size_t written = 0;
+  uint32_t num_keys = g_num_keys.load();
+  if (len > 0)
+    *dst = '\0';
+  for (uint32_t i = 0; i < num_keys && written < len; i++) {
+    CrashKey* key = g_keys[i].load();
+    if (!key)
+      continue;  // Can happen if we hit this between the add and the store.
+    written += key->ToString(dst + written, len - written);
+  }
+  PERFETTO_DCHECK(written <= len);
+  PERFETTO_DCHECK(len == 0 || dst[written] == '\0');
+  return written;
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/logging.cc b/src/base/logging.cc
index aaf6c69..9318ecd 100644
--- a/src/base/logging.cc
+++ b/src/base/logging.cc
@@ -28,6 +28,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/time.h"
+#include "perfetto/ext/base/crash_keys.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/base/log_ring_buffer.h"
@@ -201,7 +202,9 @@
   // But if that happens we have bigger problems, not worth designing around it.
   // The behaviour is still defined in the race case (the string attached to
   // the crash report will contain a mixture of log strings).
-  g_log_ring_buffer.Read(g_crash_buf, sizeof(g_crash_buf));
+  size_t wr = 0;
+  wr += SerializeCrashKeys(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
+  wr += g_log_ring_buffer.Read(&g_crash_buf[wr], sizeof(g_crash_buf) - wr);
 
   // Read() null-terminates the string properly. This is just to avoid UB when
   // two threads race on each other (T1 writes a shorter string, T2
diff --git a/src/base/logging_unittest.cc b/src/base/logging_unittest.cc
index ce400f9..ee042bd 100644
--- a/src/base/logging_unittest.cc
+++ b/src/base/logging_unittest.cc
@@ -23,6 +23,7 @@
 #include <thread>
 #include <vector>
 
+#include "perfetto/ext/base/crash_keys.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/base/log_ring_buffer.h"
 #include "test/gtest_and_gmock.h"
@@ -174,6 +175,83 @@
               testing::UnorderedElementsAreArray(expected_events));
 }
 
+TEST(CrashKeysTest, SetClearAndLongKeys) {
+  UnregisterAllCrashKeysForTesting();
+
+  char buf[1024];
+  memset(buf, 'x', sizeof(buf));
+  EXPECT_EQ(0u, SerializeCrashKeys(buf, sizeof(buf)));
+  EXPECT_STREQ(buf, "");
+
+  CrashKey k1("key1");
+  CrashKey k2("key2");
+  CrashKey k3("key3");
+  CrashKey k4("key4");
+
+  k1.Set(0);
+  k1.Clear();
+
+  k2.Set(42);
+
+  k3.Set("xx");
+  k3.Clear();
+
+  k4.Set("value");
+
+  EXPECT_EQ(21u, SerializeCrashKeys(buf, sizeof(buf)));
+  EXPECT_STREQ(buf, "key2: 42\nkey4: value\n");
+
+  EXPECT_EQ(0u, SerializeCrashKeys(buf, 0));
+
+  EXPECT_EQ(0u, SerializeCrashKeys(buf, 1));
+  EXPECT_STREQ(buf, "");
+
+  // Test truncated output.
+  EXPECT_EQ(5u, SerializeCrashKeys(buf, 5 + 1));
+  EXPECT_STREQ(buf, "key2:");
+
+  k2.Clear();
+
+  std::string long_str(1024, 'x');
+  k4.Set(StringView(long_str));
+
+  EXPECT_EQ(6 + kCrashKeyMaxStrSize, SerializeCrashKeys(buf, sizeof(buf)));
+  std::string expected =
+      "key4: " + long_str.substr(0, kCrashKeyMaxStrSize - 1) + "\n";
+  EXPECT_EQ(buf, expected);
+
+  UnregisterAllCrashKeysForTesting();
+}
+
+TEST(CrashKeysTest, ScopedSet) {
+  UnregisterAllCrashKeysForTesting();
+
+  char buf[1024];
+  memset(buf, 'x', sizeof(buf));
+
+  CrashKey k1("key1");
+  CrashKey k2("key2");
+
+  auto scoped_key = k1.SetScoped(42);
+  EXPECT_GT(SerializeCrashKeys(buf, sizeof(buf)), 0u);
+  EXPECT_STREQ(buf, "key1: 42\n");
+
+  {
+    auto scoped_key2 = k2.SetScoped("foo");
+    EXPECT_GT(SerializeCrashKeys(buf, sizeof(buf)), 0u);
+    EXPECT_STREQ(buf, "key1: 42\nkey2: foo\n");
+  }
+
+  EXPECT_GT(SerializeCrashKeys(buf, sizeof(buf)), 0u);
+  EXPECT_STREQ(buf, "key1: 42\n");
+
+  k1.Clear();
+  EXPECT_EQ(0u, SerializeCrashKeys(buf, sizeof(buf)));
+  EXPECT_STREQ(buf, "");
+
+  UnregisterAllCrashKeysForTesting();
+}
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 9467ca9..da1ce4c 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -21,6 +21,7 @@
 #include <utility>
 
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/crash_keys.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/ipc/service.h"
 #include "perfetto/ext/ipc/service_descriptor.h"
@@ -37,6 +38,8 @@
 constexpr base::SockFamily kHostSockFamily =
     kUseTCPSocket ? base::SockFamily::kInet : base::SockFamily::kUnix;
 
+base::CrashKey g_crash_key_uid("ipc_uid");
+
 uid_t GetPosixPeerUid(base::UnixSocket* sock) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   base::ignore_result(sock);
@@ -123,6 +126,9 @@
   ClientConnection* client = it->second;
   BufferedFrameDeserializer& frame_deserializer = client->frame_deserializer;
 
+  auto peer_uid = GetPosixPeerUid(client->sock.get());
+  auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
+
   size_t rsize;
   do {
     auto buf = frame_deserializer.BeginReceive();
@@ -217,8 +223,9 @@
     });
   }
 
-  service->client_info_ =
-      ClientInfo(client->id, GetPosixPeerUid(client->sock.get()));
+  auto peer_uid = GetPosixPeerUid(client->sock.get());
+  auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
+  service->client_info_ = ClientInfo(client->id, peer_uid);
   service->received_fd_ = &client->received_fd;
   method.invoker(service, *decoded_req_args, std::move(deferred_reply));
   service->received_fd_ = nullptr;
@@ -251,6 +258,9 @@
 
 // static
 void HostImpl::SendFrame(ClientConnection* client, const Frame& frame, int fd) {
+  auto peer_uid = GetPosixPeerUid(client->sock.get());
+  auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
+
   std::string buf = BufferedFrameDeserializer::Serialize(frame);
 
   // When a new Client connects in OnNewClientConnection we set a timeout on
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index c84b90d..f0c706b 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -781,12 +781,13 @@
       return base::ErrStatus("Unknown metric %s", name.c_str());
 
     const auto& sql_metric = *metric_it;
-    auto queries = base::SplitString(sql_metric.sql, ";\n");
-    for (const auto& query : queries) {
-      PERFETTO_DLOG("Executing query: %s", query.c_str());
-      auto prep_it = tp->ExecuteQuery(query);
-      prep_it.Next();
-      RETURN_IF_ERROR(prep_it.Status());
+    for (const auto& outer : base::SplitString(sql_metric.sql, ";\n")) {
+      for (const auto& query : base::SplitString(outer, ";\r\n")) {
+        PERFETTO_DLOG("Executing query: %s", query.c_str());
+        auto prep_it = tp->ExecuteQuery(query);
+        prep_it.Next();
+        RETURN_IF_ERROR(prep_it.Status());
+      }
     }
 
     auto output_query =
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index c5f9168..21139c6 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -25,6 +25,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/crash_keys.h"
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
@@ -61,6 +62,8 @@
 constexpr uint32_t kTypeTimeExtend = 30;
 constexpr uint32_t kTypeTimeStamp = 31;
 
+base::CrashKey g_crash_key_cpu("ftrace_cpu");
+
 struct EventHeader {
   uint32_t type_or_length : 5;
   uint32_t time_delta : 27;
@@ -160,6 +163,7 @@
     size_t max_pages,
     const std::set<FtraceDataSource*>& started_data_sources) {
   PERFETTO_DCHECK(max_pages > 0 && parsing_buf_size_pages > 0);
+  auto scoped_key = g_crash_key_cpu.SetScoped(static_cast<int>(cpu_));
   metatrace::ScopedEvent evt(metatrace::TAG_FTRACE,
                              metatrace::FTRACE_CPU_READ_CYCLE);
 
diff --git a/tools/write_version_header.py b/tools/write_version_header.py
index e8ddcb9..7dc8783 100755
--- a/tools/write_version_header.py
+++ b/tools/write_version_header.py
@@ -81,6 +81,7 @@
 
 def main():
   parser = argparse.ArgumentParser()
+  parser.add_argument('--check_git', action='store_true')
   parser.add_argument(
       '--no_git',
       action='store_true',
@@ -91,6 +92,11 @@
   parser.add_argument('--changelog', help='Path to CHANGELOG.')
   args = parser.parse_args()
 
+  if args.check_git:
+    has_git = os.path.exists(os.path.join(PROJECT_ROOT, '.git', 'HEAD'))
+    print('1' if has_git else '0')
+    return 0
+
   release = get_latest_release(args.changelog)
 
   if args.no_git: