Merge "Allow to target heaps registered during running profile."
diff --git a/.clang-tidy b/.clang-tidy
index 5f168ce..07073df 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,4 +1,4 @@
-Checks: android-cloexec-*,bugprone-*,google-explicit-constructor,android-comparison-in-temp-failure-retry
+Checks: android-cloexec-*,bugprone-*,google-explicit-constructor,android-comparison-in-temp-failure-retry,modernize-use-nullptr,performance-for-range-copy,performance-noexcept-move-constructor,readability-container-size-empty,readability-else-after-return
 CheckOptions:
   - key:             bugprone-assert-side-effect.AssertMacros
     value:           'PERFETTO_DCHECK'
diff --git a/Android.bp b/Android.bp
index 0da74e3..d089dff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1558,11 +1558,6 @@
   name: "perfetto_include_perfetto_ext_tracing_ipc_ipc",
 }
 
-// GN: //include/perfetto/profiling:deobfuscator
-filegroup {
-  name: "perfetto_include_perfetto_profiling_deobfuscator",
-}
-
 // GN: //include/perfetto/profiling:normalize
 filegroup {
   name: "perfetto_include_perfetto_profiling_normalize",
@@ -6822,6 +6817,7 @@
     "src/profiling/memory/bookkeeping_dump.cc",
     "src/profiling/memory/heapprofd_producer.cc",
     "src/profiling/memory/java_hprof_producer.cc",
+    "src/profiling/memory/log_histogram.cc",
     "src/profiling/memory/system_property.cc",
     "src/profiling/memory/unwinding.cc",
   ],
@@ -8473,7 +8469,6 @@
     ":perfetto_include_perfetto_ext_traced_traced",
     ":perfetto_include_perfetto_ext_tracing_core_core",
     ":perfetto_include_perfetto_ext_tracing_ipc_ipc",
-    ":perfetto_include_perfetto_profiling_deobfuscator",
     ":perfetto_include_perfetto_profiling_normalize",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
@@ -8814,7 +8809,6 @@
     ":perfetto_include_perfetto_ext_trace_processor_export_json",
     ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
-    ":perfetto_include_perfetto_profiling_deobfuscator",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
     ":perfetto_include_perfetto_trace_processor_storage",
@@ -8960,7 +8954,6 @@
     ":perfetto_include_perfetto_ext_trace_processor_export_json",
     ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
     ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
-    ":perfetto_include_perfetto_profiling_deobfuscator",
     ":perfetto_include_perfetto_profiling_pprof_builder",
     ":perfetto_include_perfetto_protozero_protozero",
     ":perfetto_include_perfetto_trace_processor_basic_types",
diff --git a/BUILD b/BUILD
index e4e4cdb..752d469 100644
--- a/BUILD
+++ b/BUILD
@@ -412,14 +412,6 @@
     ],
 )
 
-# GN target: //include/perfetto/profiling:deobfuscator
-filegroup(
-    name = "include_perfetto_profiling_deobfuscator",
-    srcs = [
-        "include/perfetto/profiling/deobfuscator.h",
-    ],
-)
-
 # GN target: //include/perfetto/profiling:pprof_builder
 filegroup(
     name = "include_perfetto_profiling_pprof_builder",
@@ -743,6 +735,7 @@
     name = "src_profiling_deobfuscator",
     srcs = [
         "src/profiling/deobfuscator.cc",
+        "src/profiling/deobfuscator.h",
     ],
 )
 
@@ -3265,7 +3258,6 @@
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
-        ":include_perfetto_profiling_deobfuscator",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
         ":include_perfetto_trace_processor_storage",
@@ -3388,7 +3380,6 @@
     hdrs = [
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
-        ":include_perfetto_profiling_deobfuscator",
         ":include_perfetto_profiling_pprof_builder",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
@@ -3441,7 +3432,6 @@
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
-        ":include_perfetto_profiling_deobfuscator",
         ":include_perfetto_profiling_pprof_builder",
         ":include_perfetto_protozero_protozero",
         ":include_perfetto_trace_processor_basic_types",
diff --git a/CHANGELOG b/CHANGELOG
index 65d16b4..6f3dd02 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,14 +1,27 @@
 Unreleased:
   Tracing service and probes:
+    *
+  Trace Processor:
+    *
+  UI:
+    *
+
+
+v11.0 - 2021-01-01:
+  Tracing service and probes:
     * Added trace packet interceptor API for rerouting trace data into
       non-Perfetto systems.
     * Added support for printing track events to the console.
     * Added a way to observe track event tracing sessions starting and
       stopping.
   Trace Processor:
-    *
+    * Added "ancestor_slice" and "experimental_ancestor_stack_profile_callsite"
+      table functions to look up ancestors of CPU stack samples in profiler
+      tables.
+    * Added power metric reporting suspend/resume time periods.
   UI:
-    *
+    * Fixed CPU time calculation in example queries.
+    * Added tracks to debug Android SystemUI jank.
 
 
 v10.0 - 2020-12-01:
diff --git a/docs/design-docs/heapprofd-design.md b/docs/design-docs/heapprofd-design.md
index c4bbd29..fe5a8ef 100644
--- a/docs/design-docs/heapprofd-design.md
+++ b/docs/design-docs/heapprofd-design.md
@@ -216,7 +216,7 @@
 ### Stack Unwinding
 Stack unwinding is the process of determining the chain of return addresses from the raw bytes of the stack. These are the addresses we want to attribute the allocated memory to.
 
-The most efficient way of stack unwinding is using frame pointers. This is unreliable on Android as we do not control build parameters for vendor libraries or OEM builds and due to issues on ARM32. Thus, our stack unwinding relies on libunwindstack which uses DWARF information from the library ELF files to determine return addresses. This can significantly slower, with unwinding of a stack taking between 100μs and ~100 ms ([data from simpleperf](https://gist.github.com/segfaulthunter/a3a5a352196f9037f34241f8fb09004d)).
+The most efficient way of stack unwinding is using frame pointers. This is unreliable on Android as we do not control build parameters for vendor libraries or OEM builds and due to issues on ARM32. Thus, our stack unwinding relies on libunwindstack which uses DWARF information from the library ELF files to determine return addresses. This can significantly slower, with unwinding of a stack taking between 100μs and ~100 ms ([data from simpleperf](https://gist.github.com/fmayer/a3a5a352196f9037f34241f8fb09004d)).
 
 [libunwindstack](https://cs.android.com/android/platform/superproject/+/master:system/unwinding/libunwindstack/) is Android's replacement for [libunwind](https://www.nongnu.org/libunwind/). It has a modern C++ object-oriented API surface and support for Android specific features allowing it to unwind mixed native and Java applications using information emitted by ART depending on execution mode. It also supports symbolization for native code and all three execution modes or ART.
 
diff --git a/include/perfetto/ext/base/string_writer.h b/include/perfetto/ext/base/string_writer.h
index 07ba24a..cee3198 100644
--- a/include/perfetto/ext/base/string_writer.h
+++ b/include/perfetto/ext/base/string_writer.h
@@ -71,7 +71,13 @@
   template <char padchar, uint64_t padding>
   void AppendPaddedInt(int64_t sign_value) {
     const bool negate = std::signbit(static_cast<double>(sign_value));
-    uint64_t absolute_value = static_cast<uint64_t>(std::abs(sign_value));
+    uint64_t absolute_value;
+    if (sign_value == std::numeric_limits<int64_t>::min()) {
+      absolute_value =
+          static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
+    } else {
+      absolute_value = static_cast<uint64_t>(std::abs(sign_value));
+    }
     AppendPaddedInt<padchar, padding>(absolute_value, negate);
   }
 
diff --git a/include/perfetto/profiling/BUILD.gn b/include/perfetto/profiling/BUILD.gn
index 17dc5db..e7f9ddf 100644
--- a/include/perfetto/profiling/BUILD.gn
+++ b/include/perfetto/profiling/BUILD.gn
@@ -19,7 +19,3 @@
 source_set("normalize") {
   sources = [ "normalize.h" ]
 }
-
-source_set("deobfuscator") {
-  sources = [ "deobfuscator.h" ]
-}
diff --git a/include/perfetto/profiling/memory/heap_profile.h b/include/perfetto/profiling/memory/heap_profile.h
index 6bd337c..1a780d1 100644
--- a/include/perfetto/profiling/memory/heap_profile.h
+++ b/include/perfetto/profiling/memory/heap_profile.h
@@ -18,7 +18,7 @@
 // callstacks causing these allocations in heap profiles.
 //
 // In the context of this API, a "heap" is memory associated with an allocator.
-// An example for allocator is the malloc-family of libc functions (malloc /
+// An example of an allocator is the malloc-family of libc functions (malloc /
 // calloc / posix_memalign).
 //
 // A very simple custom allocator would look like this:
@@ -41,7 +41,8 @@
 // void* my_malloc(size_t size) {
 //   void* ptr = [code to somehow allocate get size bytes];
 //   AHeapProfile_reportAllocation(g_heap_id, static_cast<uintptr_t>(ptr),
-//   size); return ptr;
+//                                 size);
+//   return ptr;
 // }
 //
 // void my_free(void* ptr) {
@@ -133,7 +134,7 @@
 // The returned heap_id can be used in AHeapProfile_reportAllocation and
 // AHeapProfile_reportFree.
 //
-// Takes ownership of info.
+// Takes ownership of |info|.
 uint32_t AHeapProfile_registerHeap(AHeapInfo* _Nullable info);
 
 // Called by libc upon receipt of the profiling signal.
diff --git a/src/profiling/BUILD.gn b/src/profiling/BUILD.gn
index 64cbbd2..d542f96 100644
--- a/src/profiling/BUILD.gn
+++ b/src/profiling/BUILD.gn
@@ -16,7 +16,10 @@
 import("../../gn/test.gni")
 
 source_set("deobfuscator") {
-  sources = [ "deobfuscator.cc" ]
+  sources = [
+    "deobfuscator.cc",
+    "deobfuscator.h",
+  ]
   deps = [
     "../../gn:default_deps",
     "../../include/perfetto/ext/base:base",
@@ -24,7 +27,6 @@
     "../../protos/perfetto/trace/profiling:zero",
     "../../src/protozero:protozero",
   ]
-  public_deps = [ "../../include/perfetto/profiling:deobfuscator" ]
 }
 
 perfetto_unittest_source_set("unittests") {
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
index 862e4a2..193681f 100644
--- a/src/profiling/deobfuscator.cc
+++ b/src/profiling/deobfuscator.cc
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include "perfetto/profiling/deobfuscator.h"
+#include "src/profiling/deobfuscator.h"
+
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
diff --git a/include/perfetto/profiling/deobfuscator.h b/src/profiling/deobfuscator.h
similarity index 95%
rename from include/perfetto/profiling/deobfuscator.h
rename to src/profiling/deobfuscator.h
index 1a285a9..061b5da 100644
--- a/include/perfetto/profiling/deobfuscator.h
+++ b/src/profiling/deobfuscator.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
-#define INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
+#ifndef SRC_PROFILING_DEOBFUSCATOR_H_
+#define SRC_PROFILING_DEOBFUSCATOR_H_
 
 #include <functional>
 #include <map>
@@ -121,4 +121,4 @@
 }  // namespace profiling
 }  // namespace perfetto
 
-#endif  // INCLUDE_PERFETTO_PROFILING_DEOBFUSCATOR_H_
+#endif  // SRC_PROFILING_DEOBFUSCATOR_H_
diff --git a/src/profiling/deobfuscator_unittest.cc b/src/profiling/deobfuscator_unittest.cc
index be35060..66001dd 100644
--- a/src/profiling/deobfuscator_unittest.cc
+++ b/src/profiling/deobfuscator_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "perfetto/profiling/deobfuscator.h"
+#include "src/profiling/deobfuscator.h"
 
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 3933798..ddc960f 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -78,6 +78,7 @@
 
 source_set("client_api_standalone") {
   deps = [
+    ":client",
     ":client_api",
     ":daemon",
     "../../../gn:default_deps",
@@ -265,6 +266,8 @@
     "heapprofd_producer.h",
     "java_hprof_producer.cc",
     "java_hprof_producer.h",
+    "log_histogram.cc",
+    "log_histogram.h",
     "system_property.cc",
     "system_property.h",
     "unwinding.cc",
@@ -297,6 +300,7 @@
   deps = [
     ":client",
     ":daemon",
+    ":ring_buffer",
     ":wire_protocol",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
@@ -317,6 +321,13 @@
     "unwinding_unittest.cc",
     "wire_protocol_unittest.cc",
   ]
+
+  # Do not build with Android to avoid applying PERFETTO_ANDROID_ASYNC_SAFE_LOG
+  # to the whole perfetto_unittests target.
+  if (!perfetto_build_with_android) {
+    sources += [ "client_api_unittest.cc" ]
+    deps += [ ":client_api" ]
+  }
 }
 
 source_set("end_to_end_tests") {
diff --git a/src/profiling/memory/client_api.cc b/src/profiling/memory/client_api.cc
index e833577..66997a4 100644
--- a/src/profiling/memory/client_api.cc
+++ b/src/profiling/memory/client_api.cc
@@ -43,9 +43,6 @@
 #include "src/profiling/memory/unhooked_allocator.h"
 #include "src/profiling/memory/wire_protocol.h"
 
-using perfetto::profiling::ScopedSpinlock;
-using perfetto::profiling::UnhookedAllocator;
-
 struct AHeapInfo {
   // Fields set by user.
   char heap_name[HEAPPROFD_HEAP_NAME_SZ];
@@ -67,6 +64,10 @@
 struct AHeapProfileDisableCallbackInfo {};
 
 namespace {
+
+using perfetto::profiling::ScopedSpinlock;
+using perfetto::profiling::UnhookedAllocator;
+
 #if defined(__GLIBC__)
 const char* getprogname() {
   return program_invocation_short_name;
@@ -141,9 +142,6 @@
   abort();
 }
 
-// Note: g_client can be reset by heapprofd_initialize without calling this
-// function.
-
 void DisableAllHeaps() {
   for (uint32_t i = kMinHeapId; i < g_next_heap_id.load(); ++i) {
     AHeapInfo& info = GetHeap(i);
@@ -159,6 +157,8 @@
   }
 }
 
+// Note: g_client can be reset by AHeapProfile_initSession without calling this
+// function.
 void ShutdownLazy(const std::shared_ptr<perfetto::profiling::Client>& client) {
   ScopedSpinlock s(&g_client_lock, ScopedSpinlock::Mode::Try);
   if (PERFETTO_UNLIKELY(!s.locked()))
@@ -360,6 +360,7 @@
 
   if (!client->RecordMalloc(heap_id, sampled_alloc_sz, size, id)) {
     ShutdownLazy(client);
+    return false;
   }
   return true;
 }
@@ -389,6 +390,7 @@
 
   if (!client->RecordMalloc(heap_id, size, size, id)) {
     ShutdownLazy(client);
+    return false;
   }
   return true;
 }
diff --git a/src/profiling/memory/client_api_factory.h b/src/profiling/memory/client_api_factory.h
index ea607a6..f6b983f 100644
--- a/src/profiling/memory/client_api_factory.h
+++ b/src/profiling/memory/client_api_factory.h
@@ -19,12 +19,13 @@
 
 #include <memory>
 
-#include "src/profiling/memory/client.h"
 #include "src/profiling/memory/unhooked_allocator.h"
 
 namespace perfetto {
 namespace profiling {
 
+class Client;
+
 void StartHeapprofdIfStatic();
 
 std::shared_ptr<Client> ConstructClient(
diff --git a/src/profiling/memory/client_api_standalone.cc b/src/profiling/memory/client_api_standalone.cc
index b9562bf..fa7c0c9 100644
--- a/src/profiling/memory/client_api_standalone.cc
+++ b/src/profiling/memory/client_api_standalone.cc
@@ -24,6 +24,7 @@
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 #include "perfetto/profiling/memory/heap_profile.h"
 #include "src/profiling/common/proc_utils.h"
+#include "src/profiling/memory/client.h"
 #include "src/profiling/memory/heapprofd_producer.h"
 
 #include <string>
diff --git a/src/profiling/memory/client_api_unittest.cc b/src/profiling/memory/client_api_unittest.cc
new file mode 100644
index 0000000..7301d03
--- /dev/null
+++ b/src/profiling/memory/client_api_unittest.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 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/unix_socket.h"
+#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/profiling/memory/heap_profile.h"
+
+#include "src/profiling/memory/client.h"
+#include "src/profiling/memory/client_api_factory.h"
+#include "src/profiling/memory/shared_ring_buffer.h"
+#include "src/profiling/memory/wire_protocol.h"
+#include "test/gtest_and_gmock.h"
+
+#include <memory>
+
+namespace perfetto {
+namespace profiling {
+
+namespace {
+
+ClientConfiguration g_client_config;
+int g_shmem_fd;
+
+base::UnixSocketRaw& GlobalServerSocket() {
+  static base::UnixSocketRaw* srv_sock = new base::UnixSocketRaw;
+  return *srv_sock;
+}
+
+void DisconnectGlobalServerSocket() {
+  base::UnixSocketRaw destroy;
+  std::swap(destroy, GlobalServerSocket());
+}
+
+}  // namespace
+
+// This is called by AHeapProfile_initSession (client_api.cc) to construct a
+// client. The Client API requires to be linked against another compliation
+// unit that provides this function. This way, it can be used in different
+// circumstances (central heapprofd, fork heapprofd) and be agnostic about the
+// details. This is is used to create a test Client here.
+void StartHeapprofdIfStatic() {}
+std::shared_ptr<Client> ConstructClient(
+    UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
+  base::UnixSocketRaw cli_sock;
+  base::UnixSocketRaw& srv_sock = GlobalServerSocket();
+  std::tie(cli_sock, srv_sock) = base::UnixSocketRaw::CreatePair(
+      base::SockFamily::kUnix, base::SockType::kStream);
+  auto ringbuf = SharedRingBuffer::Create(8 * 1048576);
+  PERFETTO_CHECK(ringbuf);
+  PERFETTO_CHECK(cli_sock);
+  PERFETTO_CHECK(srv_sock);
+  g_shmem_fd = ringbuf->fd();
+  return std::allocate_shared<Client>(unhooked_allocator, std::move(cli_sock),
+                                      g_client_config, std::move(*ringbuf),
+                                      getpid(), GetMainThreadStackRange());
+}
+
+namespace {
+
+TEST(ClientApiTest, NoClient) {
+  uint32_t heap_id = AHeapProfile_registerHeap(AHeapInfo_create("NoClient"));
+  EXPECT_FALSE(AHeapProfile_reportAllocation(heap_id, 1, 1));
+}
+
+TEST(ClientApiTest, ClientEnabledHeap) {
+  uint32_t heap_id =
+      AHeapProfile_registerHeap(AHeapInfo_create("ClientEnabledHeap"));
+  ClientConfiguration client_config{};
+  client_config.default_interval = 1;
+  strcpy(&client_config.heaps[0].name[0], "ClientEnabledHeap");
+  client_config.heaps[0].interval = 1;
+  client_config.num_heaps = 1;
+
+  g_client_config = client_config;
+
+  AHeapProfile_initSession(malloc, free);
+  PERFETTO_CHECK(g_shmem_fd);
+  auto ringbuf = SharedRingBuffer::Attach(base::ScopedFile(dup(g_shmem_fd)));
+  g_shmem_fd = 0;
+  PERFETTO_CHECK(ringbuf);
+  EXPECT_TRUE(AHeapProfile_reportAllocation(heap_id, 1, 1));
+  // Check that the service received something on the shmem.
+  EXPECT_TRUE(ringbuf->BeginRead());
+  DisconnectGlobalServerSocket();
+  ringbuf->SetShuttingDown();
+  EXPECT_FALSE(AHeapProfile_reportAllocation(heap_id, 1, 1));
+}
+
+TEST(ClientApiTest, ClientAllHeaps) {
+  uint32_t heap_id =
+      AHeapProfile_registerHeap(AHeapInfo_create("ClientAllHeaps"));
+  ClientConfiguration client_config{};
+  client_config.default_interval = 1;
+  client_config.all_heaps = true;
+
+  g_client_config = client_config;
+
+  AHeapProfile_initSession(malloc, free);
+  PERFETTO_CHECK(g_shmem_fd);
+  auto ringbuf = SharedRingBuffer::Attach(base::ScopedFile(dup(g_shmem_fd)));
+  g_shmem_fd = 0;
+  PERFETTO_CHECK(ringbuf);
+  EXPECT_TRUE(AHeapProfile_reportAllocation(heap_id, 1, 1));
+  // Check that the service received something on the shmem.
+  EXPECT_TRUE(ringbuf->BeginRead());
+  DisconnectGlobalServerSocket();
+  ringbuf->SetShuttingDown();
+  EXPECT_FALSE(AHeapProfile_reportAllocation(heap_id, 1, 1));
+}
+
+}  // namespace
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index b73740f..60208ac 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -445,7 +445,7 @@
     switch (test_mode()) {
       case TestMode::kCentral:
         fork_prop_ = DisableFork();
-        PERFETTO_CHECK(ReadProperty(kHeapprofdModeProperty, "") == "");
+        PERFETTO_CHECK(ReadProperty(kHeapprofdModeProperty, "").empty());
         break;
       case TestMode::kFork:
         fork_prop_ = EnableFork();
@@ -911,7 +911,7 @@
   uint64_t total_freed = 0;
   for (const protos::gen::TracePacket& packet : packets) {
     if (packet.has_profile_packet() &&
-        packet.profile_packet().process_dumps().size() > 0) {
+        !packet.profile_packet().process_dumps().empty()) {
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
@@ -981,7 +981,7 @@
   uint64_t total_freed = 0;
   for (const protos::gen::TracePacket& packet : packets) {
     if (packet.has_profile_packet() &&
-        packet.profile_packet().process_dumps().size() > 0) {
+        !packet.profile_packet().process_dumps().empty()) {
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
@@ -1044,7 +1044,7 @@
   uint64_t total_freed = 0;
   for (const protos::gen::TracePacket& packet : packets) {
     if (packet.has_profile_packet() &&
-        packet.profile_packet().process_dumps().size() > 0) {
+        !packet.profile_packet().process_dumps().empty()) {
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
@@ -1108,7 +1108,7 @@
   uint64_t total_freed = 0;
   for (const protos::gen::TracePacket& packet : packets) {
     if (packet.has_profile_packet() &&
-        packet.profile_packet().process_dumps().size() > 0) {
+        !packet.profile_packet().process_dumps().empty()) {
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
@@ -1409,7 +1409,7 @@
   uint64_t total_allocated = 0;
   for (const protos::gen::TracePacket& packet : packets) {
     if (packet.has_profile_packet() &&
-        packet.profile_packet().process_dumps().size() > 0) {
+        !packet.profile_packet().process_dumps().empty()) {
       const auto& dumps = packet.profile_packet().process_dumps();
       ASSERT_EQ(dumps.size(), 1u);
       const protos::gen::ProfilePacket_ProcessHeapSamples& dump = dumps[0];
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 4b1d02f..81a14fe 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -97,15 +97,6 @@
   return false;
 }
 
-// Return largest n such that pow(2, n) < value.
-size_t Log2LessThan(uint64_t value) {
-  size_t i = 0;
-  while (value) {
-    i++;
-    value >>= 1;
-  }
-  return i;
-}
 
 bool IsFile(int fd, const char* fn) {
   struct stat fdstat;
@@ -178,30 +169,6 @@
   return true;
 }
 
-const uint64_t LogHistogram::kMaxBucket = 0;
-
-std::vector<std::pair<uint64_t, uint64_t>> LogHistogram::GetData() {
-  std::vector<std::pair<uint64_t, uint64_t>> data;
-  data.reserve(kBuckets);
-  for (size_t i = 0; i < kBuckets; ++i) {
-    if (i == kBuckets - 1)
-      data.emplace_back(kMaxBucket, values_[i]);
-    else
-      data.emplace_back(1 << i, values_[i]);
-  }
-  return data;
-}
-
-size_t LogHistogram::GetBucket(uint64_t value) {
-  if (value == 0)
-    return 0;
-
-  size_t hibit = Log2LessThan(value);
-  if (hibit >= kBuckets)
-    return kBuckets - 1;
-  return hibit;
-}
-
 // We create kUnwinderThreads unwinding threads. Bookkeeping is done on the main
 // thread.
 HeapprofdProducer::HeapprofdProducer(HeapprofdMode mode,
@@ -681,6 +648,27 @@
       dump_interval);
 }
 
+// static
+void HeapprofdProducer::SetStats(
+    protos::pbzero::ProfilePacket::ProcessStats* stats,
+    const ProcessState& process_state) {
+  stats->set_unwinding_errors(process_state.unwinding_errors);
+  stats->set_heap_samples(process_state.heap_samples);
+  stats->set_map_reparses(process_state.map_reparses);
+  stats->set_total_unwinding_time_us(process_state.total_unwinding_time_us);
+  stats->set_client_spinlock_blocked_us(
+      process_state.client_spinlock_blocked_us);
+  auto* unwinding_hist = stats->set_unwinding_time_us();
+  for (const auto& p : process_state.unwinding_time_us.GetData()) {
+    auto* bucket = unwinding_hist->add_buckets();
+    if (p.first == LogHistogram::kMaxBucket)
+      bucket->set_max_bucket(true);
+    else
+      bucket->set_upper_limit(p.first);
+    bucket->set_count(p.second);
+  }
+}
+
 void HeapprofdProducer::DumpProcessState(DataSource* data_source,
                                          pid_t pid,
                                          ProcessState* process_state) {
@@ -716,22 +704,7 @@
           if (heap_name)
             proto->set_heap_name(heap_name);
           auto* stats = proto->set_stats();
-          stats->set_unwinding_errors(process_state->unwinding_errors);
-          stats->set_heap_samples(process_state->heap_samples);
-          stats->set_map_reparses(process_state->map_reparses);
-          stats->set_total_unwinding_time_us(
-              process_state->total_unwinding_time_us);
-          stats->set_client_spinlock_blocked_us(
-              process_state->client_spinlock_blocked_us);
-          auto* unwinding_hist = stats->set_unwinding_time_us();
-          for (const auto& p : process_state->unwinding_time_us.GetData()) {
-            auto* bucket = unwinding_hist->add_buckets();
-            if (p.first == LogHistogram::kMaxBucket)
-              bucket->set_max_bucket(true);
-            else
-              bucket->set_upper_limit(p.first);
-            bucket->set_count(p.second);
-          }
+          SetStats(stats, *process_state);
         };
 
     DumpState dump_state(data_source->trace_writer.get(),
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index aa11ab7..58ec238 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -41,6 +41,7 @@
 #include "src/profiling/common/profiler_guardrails.h"
 #include "src/profiling/memory/bookkeeping.h"
 #include "src/profiling/memory/bookkeeping_dump.h"
+#include "src/profiling/memory/log_histogram.h"
 #include "src/profiling/memory/system_property.h"
 #include "src/profiling/memory/unwinding.h"
 #include "src/profiling/memory/unwound_messages.h"
@@ -57,20 +58,6 @@
   std::string cmdline;
 };
 
-class LogHistogram {
- public:
-  static const uint64_t kMaxBucket;
-  static constexpr size_t kBuckets = 20;
-
-  void Add(uint64_t value) { values_[GetBucket(value)]++; }
-  std::vector<std::pair<uint64_t, uint64_t>> GetData();
-
- private:
-  size_t GetBucket(uint64_t value);
-
-  std::array<uint64_t, kBuckets> values_ = {};
-};
-
 // TODO(rsavitski): central daemon can do less work if it knows that the global
 // operating mode is fork-based, as it then will not be interacting with the
 // clients. This can be implemented as an additional mode here.
@@ -274,6 +261,8 @@
   void FinishDataSourceFlush(FlushRequestID flush_id);
   void DumpProcessesInDataSource(DataSource* ds);
   void DumpProcessState(DataSource* ds, pid_t pid, ProcessState* process);
+  static void SetStats(protos::pbzero::ProfilePacket::ProcessStats* stats,
+                       const ProcessState& process_state);
 
   void DoContinuousDump(DataSourceInstanceID id, uint32_t dump_interval);
 
diff --git a/src/profiling/memory/log_histogram.cc b/src/profiling/memory/log_histogram.cc
new file mode 100644
index 0000000..3db734f
--- /dev/null
+++ b/src/profiling/memory/log_histogram.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 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 "src/profiling/memory/log_histogram.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include <utility>
+#include <vector>
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+// Return largest n such that pow(2, n) < value.
+size_t Log2LessThan(uint64_t value) {
+  size_t i = 0;
+  while (value) {
+    i++;
+    value >>= 1;
+  }
+  return i;
+}
+
+}  // namespace
+
+const uint64_t LogHistogram::kMaxBucket = 0;
+
+std::vector<std::pair<uint64_t, uint64_t>> LogHistogram::GetData() const {
+  std::vector<std::pair<uint64_t, uint64_t>> data;
+  data.reserve(kBuckets);
+  for (size_t i = 0; i < kBuckets; ++i) {
+    if (i == kBuckets - 1)
+      data.emplace_back(kMaxBucket, values_[i]);
+    else
+      data.emplace_back(1 << i, values_[i]);
+  }
+  return data;
+}
+
+size_t LogHistogram::GetBucket(uint64_t value) {
+  if (value == 0)
+    return 0;
+
+  size_t hibit = Log2LessThan(value);
+  if (hibit >= kBuckets)
+    return kBuckets - 1;
+  return hibit;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/log_histogram.h b/src/profiling/memory/log_histogram.h
new file mode 100644
index 0000000..9b16b86
--- /dev/null
+++ b/src/profiling/memory/log_histogram.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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 SRC_PROFILING_MEMORY_LOG_HISTOGRAM_H_
+#define SRC_PROFILING_MEMORY_LOG_HISTOGRAM_H_
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include <array>
+#include <utility>
+#include <vector>
+
+namespace perfetto {
+namespace profiling {
+
+class LogHistogram {
+ public:
+  static const uint64_t kMaxBucket;
+  static constexpr size_t kBuckets = 20;
+
+  void Add(uint64_t value) { values_[GetBucket(value)]++; }
+  std::vector<std::pair<uint64_t, uint64_t>> GetData() const;
+
+ private:
+  size_t GetBucket(uint64_t value);
+
+  std::array<uint64_t, kBuckets> values_ = {};
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_MEMORY_LOG_HISTOGRAM_H_
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index b4c01f1..8e1b810 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -311,7 +311,8 @@
   *this = std::move(other);
 }
 
-SharedRingBuffer& SharedRingBuffer::operator=(SharedRingBuffer&& other) {
+SharedRingBuffer& SharedRingBuffer::operator=(
+    SharedRingBuffer&& other) noexcept {
   mem_fd_ = std::move(other.mem_fd_);
   std::tie(meta_, mem_, size_) = std::tie(other.meta_, other.mem_, other.size_);
   std::tie(other.meta_, other.mem_, other.size_) =
diff --git a/src/profiling/memory/shared_ring_buffer.h b/src/profiling/memory/shared_ring_buffer.h
index 78575df..47ecfcd 100644
--- a/src/profiling/memory/shared_ring_buffer.h
+++ b/src/profiling/memory/shared_ring_buffer.h
@@ -95,7 +95,7 @@
   SharedRingBuffer() = default;
 
   SharedRingBuffer(SharedRingBuffer&&) noexcept;
-  SharedRingBuffer& operator=(SharedRingBuffer&&);
+  SharedRingBuffer& operator=(SharedRingBuffer&&) noexcept;
 
   bool is_valid() const { return !!mem_; }
   size_t size() const { return size_; }
diff --git a/src/profiling/memory/shared_ring_buffer_unittest.cc b/src/profiling/memory/shared_ring_buffer_unittest.cc
index df1bc5c..7f58b90 100644
--- a/src/profiling/memory/shared_ring_buffer_unittest.cc
+++ b/src/profiling/memory/shared_ring_buffer_unittest.cc
@@ -225,10 +225,9 @@
           // Failing to read after the writers are done means that there is no
           // data left in the ring buffer.
           return;
-        } else {
-          std::this_thread::yield();
-          continue;
         }
+        std::this_thread::yield();
+        continue;
       }
       ASSERT_GT(buf_and_size.size, 0u);
       std::string data = ToString(buf_and_size);
diff --git a/src/profiling/memory/system_property.cc b/src/profiling/memory/system_property.cc
index a2fda64..6272cf8 100644
--- a/src/profiling/memory/system_property.cc
+++ b/src/profiling/memory/system_property.cc
@@ -26,14 +26,15 @@
 namespace perfetto {
 namespace profiling {
 
-SystemProperties::Handle::Handle(Handle&& other) {
+SystemProperties::Handle::Handle(Handle&& other) noexcept {
   system_properties_ = other.system_properties_;
   property_ = std::move(other.property_);
   all_ = other.all_;
   other.system_properties_ = nullptr;
 }
 
-SystemProperties::Handle& SystemProperties::Handle::operator=(Handle&& other) {
+SystemProperties::Handle& SystemProperties::Handle::operator=(
+    Handle&& other) noexcept {
   // Construct this temporary because the RHS could be an lvalue cast to an
   // rvalue reference whose lifetime we do not know.
   Handle tmp(std::move(other));
diff --git a/src/profiling/memory/system_property.h b/src/profiling/memory/system_property.h
index f713940..2b1e32f 100644
--- a/src/profiling/memory/system_property.h
+++ b/src/profiling/memory/system_property.h
@@ -45,8 +45,8 @@
     Handle(const Handle&) = delete;
     Handle& operator=(const Handle&) = delete;
 
-    Handle(Handle&&);
-    Handle& operator=(Handle&&);
+    Handle(Handle&&) noexcept;
+    Handle& operator=(Handle&&) noexcept;
 
     friend class SystemProperties;
     ~Handle();
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 2b9f101..26a3380 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -116,7 +116,7 @@
   if (filter.pids.count(pid)) {
     return false;
   }
-  if (!filter.cmdlines.size() && !filter.pids.size() &&
+  if (filter.cmdlines.empty() && filter.pids.empty() &&
       !filter.additional_cmdline_count) {
     // If no filters are set allow everything.
     return false;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index fcf53bb..cb6a17e 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -36,7 +36,7 @@
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/version.h"
-#include "perfetto/profiling/deobfuscator.h"
+
 #include "perfetto/trace_processor/read_trace.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "src/trace_processor/metrics/chrome/all_chrome_metrics.descriptor.h"
@@ -47,7 +47,7 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
 #include "src/trace_processor/rpc/httpd.h"
 #endif
-
+#include "src/profiling/deobfuscator.h"
 #include "src/profiling/symbolizer/local_symbolizer.h"
 #include "src/profiling/symbolizer/symbolize_database.h"
 #include "src/profiling/symbolizer/symbolizer.h"
diff --git a/test/cts/AndroidTest.xml b/test/cts/AndroidTest.xml
index 81d39c3..800a6d4 100644
--- a/test/cts/AndroidTest.xml
+++ b/test/cts/AndroidTest.xml
@@ -33,7 +33,6 @@
         <option name="append-bitness" value="true" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="setprop persist.heapprofd.enable 1" />
         <option name="run-command" value="setprop persist.traced_perf.enable 1" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.GTest" >
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index a89db03..c1674d8 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -39,7 +39,6 @@
 source_set("utils") {
   deps = [
     "../../gn:default_deps",
-    "../../include/perfetto/profiling:deobfuscator",
     "../../include/perfetto/protozero",
     "../../include/perfetto/trace_processor",
     "../../protos/perfetto/trace:zero",
@@ -49,10 +48,7 @@
     "../../src/profiling/symbolizer",
     "../../src/profiling/symbolizer:symbolize_database",
   ]
-  public_deps = [
-    "../../include/perfetto/ext/base",
-    "../../include/perfetto/profiling:deobfuscator",
-  ]
+  public_deps = [ "../../include/perfetto/ext/base" ]
   if (enable_perfetto_zlib) {
     public_deps += [ "../../gn:zlib" ]
   }
@@ -94,9 +90,9 @@
     "../../gn:default_deps",
     "../../include/perfetto/base",
     "../../include/perfetto/ext/traced:sys_stats_counters",
-    "../../include/perfetto/profiling:deobfuscator",
     "../../include/perfetto/protozero",
     "../../protos/perfetto/trace:zero",
+    "../../src/profiling:deobfuscator",
     "../../src/profiling/symbolizer",
     "../../src/profiling/symbolizer:symbolize_database",
     "../../src/trace_processor:lib",
diff --git a/tools/trace_to_text/deobfuscate_profile.cc b/tools/trace_to_text/deobfuscate_profile.cc
index dfdb67b..bc11730 100644
--- a/tools/trace_to_text/deobfuscate_profile.cc
+++ b/tools/trace_to_text/deobfuscate_profile.cc
@@ -21,8 +21,8 @@
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/utils.h"
-#include "perfetto/profiling/deobfuscator.h"
 #include "perfetto/trace_processor/trace_processor.h"
+#include "src/profiling/deobfuscator.h"
 #include "tools/trace_to_text/deobfuscate_profile.h"
 #include "tools/trace_to_text/utils.h"
 
diff --git a/tools/trace_to_text/utils.h b/tools/trace_to_text/utils.h
index 1ac1d6d..f95c3d8 100644
--- a/tools/trace_to_text/utils.h
+++ b/tools/trace_to_text/utils.h
@@ -27,7 +27,7 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
-#include "perfetto/profiling/deobfuscator.h"
+#include "src/profiling/deobfuscator.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 #include <zlib.h>
diff --git a/ui/index.html b/ui/index.html
index a47de38..ff1ad37 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -10,10 +10,6 @@
   <link rel="icon" type="image/png" href="assets/favicon.png">
   <!-- Global site tag (gtag.js) - Google Analytics -->
   <script async src="https://www.googletagmanager.com/gtag/js?id=UA-137828855-1"></script>
-  <script>
-    window.dataLayer = window.dataLayer || [];
-    function gtag(){dataLayer.push(arguments);}
-  </script>
 </head>
 <body>
   <main>
diff --git a/ui/src/frontend/analytics.ts b/ui/src/frontend/analytics.ts
index 8a0ba21..dc6f051 100644
--- a/ui/src/frontend/analytics.ts
+++ b/ui/src/frontend/analytics.ts
@@ -34,6 +34,20 @@
 
 export class Analytics {
   constructor() {
+    // The code below is taken from the official Google Analytics docs [1] and
+    // adapted to TypeScript. We have it here rather than as an inline script
+    // in index.html (as suggested by GA's docs) because inline scripts don't
+    // play nicely with the CSP policy, at least in Firefox (Firefox doesn't
+    // support all CSP 3 features we use).
+    // [1] https://developers.google.com/analytics/devguides/collection/gtagjs .
+    const gtagGlobals = window as {} as {
+      dataLayer: IArguments[],
+      gtag: () => void,
+    };
+    gtagGlobals.dataLayer = gtagGlobals.dataLayer || [];
+    if (gtagGlobals.gtag === undefined) {
+      gtagGlobals.gtag = () => gtagGlobals.dataLayer.push(arguments);
+    }
     gtag('js', new Date());
   }