Merge "cleanup: CHECK(false) -> FATAL("blah")"
diff --git a/Android.bp b/Android.bp
index 3968ef0..1d782d4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,6 +57,7 @@
     "src/profiling/memory/bookkeeping.cc",
     "src/profiling/memory/heapprofd_producer.cc",
     "src/profiling/memory/main.cc",
+    "src/profiling/memory/proc_utils.cc",
     "src/profiling/memory/process_matcher.cc",
     "src/profiling/memory/record_reader.cc",
     "src/profiling/memory/socket_listener.cc",
@@ -531,6 +532,17 @@
     "src/ipc/host_impl.cc",
     "src/ipc/service_proxy.cc",
     "src/ipc/virtual_destructors.cc",
+    "src/profiling/memory/bookkeeping.cc",
+    "src/profiling/memory/client.cc",
+    "src/profiling/memory/heapprofd_end_to_end_test.cc",
+    "src/profiling/memory/heapprofd_producer.cc",
+    "src/profiling/memory/process_matcher.cc",
+    "src/profiling/memory/record_reader.cc",
+    "src/profiling/memory/sampler.cc",
+    "src/profiling/memory/socket_listener.cc",
+    "src/profiling/memory/system_property.cc",
+    "src/profiling/memory/unwinding.cc",
+    "src/profiling/memory/wire_protocol.cc",
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/proto_decoder.cc",
@@ -596,8 +608,11 @@
   ],
   shared_libs: [
     "libandroid",
+    "libbase",
     "liblog",
+    "libprocinfo",
     "libprotobuf-cpp-lite",
+    "libunwindstack",
   ],
   static_libs: [
     "libgmock",
@@ -2424,6 +2439,7 @@
     "src/profiling/memory/heapprofd_integrationtest.cc",
     "src/profiling/memory/heapprofd_producer.cc",
     "src/profiling/memory/interner_unittest.cc",
+    "src/profiling/memory/proc_utils.cc",
     "src/profiling/memory/process_matcher.cc",
     "src/profiling/memory/process_matcher_unittest.cc",
     "src/profiling/memory/record_reader.cc",
diff --git a/BUILD.gn b/BUILD.gn
index 25f28f8..3ec10f8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -151,6 +151,11 @@
     if (perfetto_build_with_android) {
       cflags = [ "-DPERFETTO_BUILD_WITH_ANDROID" ]
     }
+
+    # TODO(fmayer): Fix in process daemons.
+    if (should_build_heapprofd && is_android && !start_daemons_for_testing) {
+      deps += [ "src/profiling/memory:end_to_end_tests" ]
+    }
   }
 
   if (monolithic_binaries) {
diff --git a/include/perfetto/tracing/core/shared_memory_abi.h b/include/perfetto/tracing/core/shared_memory_abi.h
index a76a1e0..0d584e5 100644
--- a/include/perfetto/tracing/core/shared_memory_abi.h
+++ b/include/perfetto/tracing/core/shared_memory_abi.h
@@ -302,9 +302,12 @@
     struct Packets {
       // Number of valid TracePacket protobuf messages contained in the chunk.
       // Each TracePacket is prefixed by its own size. This field is
-      // monotonically updated by the Producer with release store semantic after
-      // the packet has been written into the chunk.
+      // monotonically updated by the Producer with release store semantic when
+      // the packet at position |count| is started. This last packet may not be
+      // considered complete until |count| is incremented for the subsequent
+      // packet or the chunk is completed.
       uint16_t count : 10;
+      static constexpr size_t kMaxCount = (1 << 10) - 1;
 
       // See Flags above.
       uint16_t flags : 6;
@@ -374,17 +377,19 @@
     }
 
     // Increases |packets.count| with release semantics (note, however, that the
-    // packet count is incremented *before* starting writing a packet).
-    // The increment is atomic but NOT race-free (i.e. no CAS). Only the
-    // Producer is supposed to perform this increment, and it's supposed to do
-    // that in a thread-safe way (holding a lock). A Chunk cannot be shared by
-    // multiple Producer threads without locking. The packet count is cleared by
-    // TryAcquireChunk(), when passing the new header for the chunk.
-    void IncrementPacketCount() {
+    // packet count is incremented *before* starting writing a packet). Returns
+    // the new packet count. The increment is atomic but NOT race-free (i.e. no
+    // CAS). Only the Producer is supposed to perform this increment, and it's
+    // supposed to do that in a thread-safe way (holding a lock). A Chunk cannot
+    // be shared by multiple Producer threads without locking. The packet count
+    // is cleared by TryAcquireChunk(), when passing the new header for the
+    // chunk.
+    uint16_t IncrementPacketCount() {
       ChunkHeader* chunk_header = header();
       auto packets = chunk_header->packets.load(std::memory_order_relaxed);
       packets.count++;
       chunk_header->packets.store(packets, std::memory_order_release);
+      return packets.count;
     }
 
     // Flags are cleared by TryAcquireChunk(), by passing the new header for
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index d651c42..2ddf368 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -58,6 +58,8 @@
     "heapprofd_producer.cc",
     "heapprofd_producer.h",
     "interner.h",
+    "proc_utils.cc",
+    "proc_utils.h",
     "process_matcher.cc",
     "process_matcher.h",
     "queue_messages.h",
@@ -117,6 +119,27 @@
   ]
 }
 
+source_set("end_to_end_tests") {
+  public_configs = [ "../../../buildtools:libunwindstack_config" ]
+  testonly = true
+  deps = [
+    ":client",
+    ":daemon",
+    ":wire_protocol",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_deps",
+    "../../../test:test_helper",
+    "../../base",
+    "../../base:test_support",
+  ]
+  sources = [
+    "heapprofd_end_to_end_test.cc",
+  ]
+  if (start_daemons_for_testing) {
+    defines = [ "PERFETTO_START_DAEMONS_FOR_TESTING" ]
+  }
+}
+
 # This will export publicly visibile symbols for the malloc_hooks.
 source_set("malloc_hooks") {
   deps = [
diff --git a/src/profiling/memory/bookkeeping.cc b/src/profiling/memory/bookkeeping.cc
index 4e4862f..9553f38 100644
--- a/src/profiling/memory/bookkeeping.cc
+++ b/src/profiling/memory/bookkeeping.cc
@@ -330,6 +330,7 @@
         it = bookkeeping_data_.erase(it);
       }
     }
+    trace_packet->Finalize();
     dump_rec.callback();
   } else if (rec->record_type == BookkeepingRecord::Type::Free) {
     FreeRecord& free_rec = rec->free_record;
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
new file mode 100644
index 0000000..cbb04a6
--- /dev/null
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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 "gtest/gtest.h"
+#include "perfetto/base/build_config.h"
+#include "src/base/test/test_task_runner.h"
+#include "test/test_helper.h"
+
+#include "src/profiling/memory/heapprofd_producer.h"
+#include "src/tracing/ipc/default_socket.h"
+
+#include <sys/system_properties.h>
+
+// This test only works when run on Android using an Android Q version of
+// Bionic.
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#error "This test can only be used on Android."
+#endif
+
+// If we're building on Android and starting the daemons ourselves,
+// create the sockets in a world-writable location.
+#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
+#define TEST_PRODUCER_SOCK_NAME "/data/local/tmp/traced_producer"
+#else
+#define TEST_PRODUCER_SOCK_NAME ::perfetto::GetProducerSocket()
+#endif
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+class HeapprofdDelegate : public ThreadDelegate {
+ public:
+  HeapprofdDelegate(const std::string& producer_socket)
+      : producer_socket_(producer_socket) {}
+  ~HeapprofdDelegate() override = default;
+
+  void Initialize(base::TaskRunner* task_runner) override {
+    producer_.reset(new HeapprofdProducer(task_runner));
+    producer_->ConnectWithRetries(producer_socket_.c_str());
+  }
+
+ private:
+  std::string producer_socket_;
+  std::unique_ptr<HeapprofdProducer> producer_;
+};
+
+constexpr const char* kEnableHeapprofdProperty = "persist.heapprofd.enable";
+
+int __attribute__((unused)) SetProperty(const char* value) {
+  __system_property_set(kEnableHeapprofdProperty, value);
+  return 0;
+}
+
+TEST(HeapprofdEndToEnd, Smoke) {
+  base::TestTaskRunner task_runner;
+
+  TestHelper helper(&task_runner);
+  helper.StartServiceIfRequired();
+
+#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
+  TaskRunnerThread producer_thread("perfetto.prd");
+  producer_thread.Start(std::unique_ptr<HeapprofdDelegate>(
+      new HeapprofdDelegate(TEST_PRODUCER_SOCK_NAME)));
+#else
+  base::ignore_result(TEST_PRODUCER_SOCK_NAME);
+  std::string prev_property_value = "0";
+  const prop_info* pi = __system_property_find(kEnableHeapprofdProperty);
+  if (pi) {
+    __system_property_read_callback(
+        pi,
+        [](void* cookie, const char*, const char* value, uint32_t) {
+          *reinterpret_cast<std::string*>(cookie) = value;
+        },
+        &prev_property_value);
+  }
+  __system_property_set(kEnableHeapprofdProperty, "1");
+  base::ScopedResource<const char*, SetProperty, nullptr> unset_property(
+      prev_property_value.c_str());
+#endif
+
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(10 * 1024);
+  trace_config.set_duration_ms(1000);
+
+  pid_t pid = fork();
+  switch (pid) {
+    case -1:
+      PERFETTO_FATAL("Failed to fork.");
+    case 0:
+      for (;;) {
+        // This volatile is needed to prevent the compiler from trying to be
+        // helpful and compiling a "useless" malloc + free into a noop.
+        volatile char* x = static_cast<char*>(malloc(1024));
+        if (x) {
+          x[1] = 'x';
+          free(const_cast<char*>(x));
+        }
+      }
+    default:
+      break;
+  }
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.heapprofd");
+  ds_config->set_target_buffer(0);
+
+  auto* heapprofd_config = ds_config->mutable_heapprofd_config();
+  heapprofd_config->set_sampling_interval_bytes(1);
+  *heapprofd_config->add_pid() = static_cast<uint64_t>(pid);
+  heapprofd_config->set_all(false);
+  heapprofd_config->mutable_continuous_dump_config()->set_dump_phase_ms(0);
+  heapprofd_config->mutable_continuous_dump_config()->set_dump_interval_ms(100);
+
+  helper.StartTracing(trace_config);
+  helper.WaitForTracingDisabled(5000);
+
+  helper.ReadData();
+  helper.WaitForReadData();
+
+  PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+
+  const auto& packets = helper.trace();
+  ASSERT_GT(packets.size(), 0u);
+  size_t profile_packets = 0;
+  for (const protos::TracePacket& packet : packets) {
+    if (packet.has_profile_packet() &&
+        packet.profile_packet().process_dumps().size() > 0) {
+      profile_packets++;
+    }
+  }
+  EXPECT_GT(profile_packets, 0);
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index f86f120..a4feeda 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -46,77 +46,6 @@
   return client_config;
 }
 
-template <typename Fn>
-void ForEachPid(const char* file, Fn callback) {
-  base::ScopedDir proc_dir(opendir("/proc"));
-  if (!proc_dir) {
-    PERFETTO_DFATAL("Failed to open /proc");
-    return;
-  }
-  struct dirent* entry;
-  while ((entry = readdir(*proc_dir))) {
-    char filename_buf[128];
-    ssize_t written = snprintf(filename_buf, sizeof(filename_buf),
-                               "/proc/%s/%s", entry->d_name, file);
-    if (written < 0 || static_cast<size_t>(written) >= sizeof(filename_buf)) {
-      if (written < 0)
-        PERFETTO_DFATAL("Failed to concatenate cmdline file.");
-      else
-        PERFETTO_DFATAL("Overflow when concatenating cmdline file.");
-      continue;
-    }
-    char* end;
-    long int pid = strtol(entry->d_name, &end, 10);
-    if (*end != '\0')
-      continue;
-    callback(static_cast<pid_t>(pid), filename_buf);
-  }
-}
-
-void FindAllProfilablePids(std::set<pid_t>* pids) {
-  ForEachPid("cmdline", [pids](pid_t pid, const char* filename_buf) {
-    if (pid == getpid())
-      return;
-    struct stat statbuf;
-    // Check if we have permission to the process.
-    if (stat(filename_buf, &statbuf) == 0)
-      pids->emplace(pid);
-  });
-}
-
-void FindPidsForCmdlines(const std::vector<std::string>& cmdlines,
-                         std::set<pid_t>* pids) {
-  ForEachPid("cmdline", [&cmdlines, pids](pid_t pid, const char* filename_buf) {
-    if (pid == getpid())
-      return;
-    std::string process_cmdline;
-    process_cmdline.reserve(128);
-    if (!base::ReadFile(filename_buf, &process_cmdline))
-      return;
-    if (process_cmdline.empty())
-      return;
-
-    // Strip everything after @ for Java processes.
-    // Otherwise, strip newline at EOF.
-    size_t endpos = process_cmdline.find('\0');
-    if (endpos == std::string::npos) {
-      PERFETTO_DFATAL("No nullbyte in cmdline.");
-      return;
-    }
-    size_t atpos = process_cmdline.find('@');
-    if (atpos != std::string::npos && atpos < endpos)
-      endpos = atpos;
-    if (endpos < 1)
-      return;
-    process_cmdline.resize(endpos);
-
-    for (const std::string& cmdline : cmdlines) {
-      if (process_cmdline == cmdline)
-        pids->emplace(static_cast<pid_t>(pid));
-    }
-  });
-}
-
 }  // namespace
 
 // We create kUnwinderThreads unwinding threads and one bookeeping thread.
@@ -325,10 +254,16 @@
   dump_record.pids.insert(dump_record.pids.begin(), pids.cbegin(), pids.cend());
   dump_record.trace_writer = data_source.trace_writer;
 
+  std::weak_ptr<TraceWriter> weak_trace_writer = data_source.trace_writer;
+
   auto weak_producer = weak_factory_.GetWeakPtr();
   base::TaskRunner* task_runner = task_runner_;
   if (has_flush_id) {
-    dump_record.callback = [task_runner, weak_producer, flush_id] {
+    dump_record.callback = [task_runner, weak_producer, flush_id,
+                            weak_trace_writer] {
+      auto trace_writer = weak_trace_writer.lock();
+      if (trace_writer)
+        trace_writer->Flush();
       task_runner->PostTask([weak_producer, flush_id] {
         if (weak_producer)
           return weak_producer->FinishDataSourceFlush(flush_id);
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index 4cb2e9c..7ae76e7 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -26,6 +26,7 @@
 #include "perfetto/tracing/core/tracing_service.h"
 
 #include "src/profiling/memory/bounded_queue.h"
+#include "src/profiling/memory/proc_utils.h"
 #include "src/profiling/memory/process_matcher.h"
 #include "src/profiling/memory/socket_listener.h"
 #include "src/profiling/memory/system_property.h"
diff --git a/src/profiling/memory/proc_utils.cc b/src/profiling/memory/proc_utils.cc
new file mode 100644
index 0000000..d2d60cc
--- /dev/null
+++ b/src/profiling/memory/proc_utils.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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/proc_utils.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "perfetto/base/file_utils.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+bool GetProcFile(pid_t pid, const char* file, char* filename_buf, size_t size) {
+  ssize_t written = snprintf(filename_buf, size, "/proc/%d/%s", pid, file);
+  if (written < 0 || static_cast<size_t>(written) >= size) {
+    if (written < 0)
+      PERFETTO_ELOG("Failed to concatenate cmdline file.");
+    else
+      PERFETTO_ELOG("Overflow when concatenating cmdline file.");
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+// This is mostly the same as GetHeapprofdProgramProperty in
+// https://android.googlesource.com/platform/bionic/+/master/libc/bionic/malloc_common.cpp
+// This should give the same result as GetHeapprofdProgramProperty.
+bool GetCmdlineForPID(pid_t pid, std::string* name) {
+  std::string filename = "/proc/" + std::to_string(pid) + "/cmdline";
+  base::ScopedFile fd(base::OpenFile(filename, O_RDONLY | O_CLOEXEC));
+  if (!fd) {
+    PERFETTO_DLOG("Failed to open %s", filename.c_str());
+    return false;
+  }
+  char cmdline[128];
+  ssize_t rd = read(*fd, cmdline, sizeof(cmdline) - 1);
+  if (rd == -1) {
+    PERFETTO_DLOG("Failed to read %s", filename.c_str());
+    return false;
+  }
+  cmdline[rd] = '\0';
+  char* first_arg =
+      static_cast<char*>(memchr(cmdline, '\0', static_cast<size_t>(rd)));
+  if (first_arg == nullptr || first_arg == cmdline + sizeof(cmdline) - 1) {
+    PERFETTO_DLOG("Overflow reading cmdline");
+    return false;
+  }
+  // For consistency with what we do with Java app cmdlines, trim everything
+  // after the @ sign of the first arg.
+  char* first_at =
+      static_cast<char*>(memchr(cmdline, '@', static_cast<size_t>(rd)));
+  if (first_at != nullptr && first_at < first_arg) {
+    *first_at = '\0';
+    first_arg = first_at;
+  }
+  char* start = static_cast<char*>(
+      memrchr(cmdline, '/', static_cast<size_t>(first_arg - cmdline)));
+  if (start == first_arg) {
+    // The first argument ended in a slash.
+    PERFETTO_DLOG("cmdline ends in /");
+    return false;
+  } else if (start == nullptr) {
+    start = cmdline;
+  } else {
+    // Skip the /.
+    start++;
+  }
+  size_t name_size = static_cast<size_t>(first_arg - start);
+  name->assign(start, name_size);
+  return true;
+}
+
+void FindAllProfilablePids(std::set<pid_t>* pids) {
+  ForEachPid([pids](pid_t pid) {
+    if (pid == getpid())
+      return;
+
+    char filename_buf[128];
+    if (!GetProcFile(pid, "cmdline", filename_buf, sizeof(filename_buf)))
+      return;
+    struct stat statbuf;
+    // Check if we have permission to the process.
+    if (stat(filename_buf, &statbuf) == 0)
+      pids->emplace(pid);
+  });
+}
+
+void FindPidsForCmdlines(const std::vector<std::string>& cmdlines,
+                         std::set<pid_t>* pids) {
+  ForEachPid([&cmdlines, pids](pid_t pid) {
+    if (pid == getpid())
+      return;
+    std::string process_cmdline;
+    process_cmdline.reserve(128);
+    GetCmdlineForPID(pid, &process_cmdline);
+    for (const std::string& cmdline : cmdlines) {
+      if (process_cmdline == cmdline)
+        pids->emplace(static_cast<pid_t>(pid));
+    }
+  });
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/proc_utils.h b/src/profiling/memory/proc_utils.h
new file mode 100644
index 0000000..5fa5294
--- /dev/null
+++ b/src/profiling/memory/proc_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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_PROC_UTILS_H_
+#define SRC_PROFILING_MEMORY_PROC_UTILS_H_
+
+#include <sys/types.h>
+#include <set>
+#include <vector>
+
+#include "perfetto/base/scoped_file.h"
+
+namespace perfetto {
+namespace profiling {
+
+template <typename Fn>
+void ForEachPid(Fn callback) {
+  base::ScopedDir proc_dir(opendir("/proc"));
+  if (!proc_dir) {
+    PERFETTO_DFATAL("Failed to open /proc");
+    return;
+  }
+  struct dirent* entry;
+  while ((entry = readdir(*proc_dir))) {
+    char* end;
+    long int pid = strtol(entry->d_name, &end, 10);
+    if (*end != '\0')
+      continue;
+    callback(static_cast<pid_t>(pid));
+  }
+}
+
+void FindAllProfilablePids(std::set<pid_t>* pids);
+void FindPidsForCmdlines(const std::vector<std::string>& cmdlines,
+                         std::set<pid_t>* pids);
+bool GetCmdlineForPID(pid_t pid, std::string* name);
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_MEMORY_PROC_UTILS_H_
diff --git a/src/profiling/memory/socket_listener.cc b/src/profiling/memory/socket_listener.cc
index 8f570f8..eee39c7 100644
--- a/src/profiling/memory/socket_listener.cc
+++ b/src/profiling/memory/socket_listener.cc
@@ -15,61 +15,14 @@
  */
 
 #include "src/profiling/memory/socket_listener.h"
+
 #include "perfetto/base/utils.h"
+#include "src/profiling/memory/proc_utils.h"
 
 namespace perfetto {
 namespace profiling {
 namespace {
 
-// This is mostly the same as GetHeapprofdProgramProperty in
-// https://android.googlesource.com/platform/bionic/+/master/libc/bionic/malloc_common.cpp
-// This should give the same result as GetHeapprofdProgramProperty.
-// TODO(fmayer): Move all the /proc parsing functions to separate file.
-bool GetCmdlineForPID(pid_t pid, std::string* name) {
-  std::string filename = "/proc/" + std::to_string(pid) + "/cmdline";
-  base::ScopedFile fd(base::OpenFile(filename, O_RDONLY | O_CLOEXEC));
-  if (!fd) {
-    PERFETTO_DLOG("Failed to open %s", filename.c_str());
-    return false;
-  }
-  char cmdline[128];
-  ssize_t rd = read(*fd, cmdline, sizeof(cmdline) - 1);
-  if (rd == -1) {
-    PERFETTO_DLOG("Failed to read %s", filename.c_str());
-    return false;
-  }
-  cmdline[rd] = '\0';
-  char* first_arg =
-      static_cast<char*>(memchr(cmdline, '\0', static_cast<size_t>(rd)));
-  if (first_arg == nullptr || first_arg == cmdline + sizeof(cmdline) - 1) {
-    PERFETTO_DLOG("Overflow reading cmdline");
-    return false;
-  }
-  // For consistency with what we do with Java app cmdlines, trim everything
-  // after the @ sign of the first arg.
-  char* first_at =
-      static_cast<char*>(memchr(cmdline, '@', static_cast<size_t>(rd)));
-  if (first_at != nullptr && first_at < first_arg) {
-    *first_at = '\0';
-    first_arg = first_at;
-  }
-  char* start = static_cast<char*>(
-      memrchr(cmdline, '/', static_cast<size_t>(first_arg - cmdline)));
-  if (start == first_arg) {
-    // The first argument ended in a slash.
-    PERFETTO_DLOG("cmdline ends in /");
-    return false;
-  } else if (start == nullptr) {
-    start = cmdline;
-  } else {
-    // Skip the /.
-    start++;
-  }
-  size_t name_size = static_cast<size_t>(first_arg - start);
-  name->assign(start, name_size);
-  return true;
-}
-
 ClientConfiguration MergeProcessSetSpecs(
     const std::vector<const ProcessSetSpec*>& process_sets) {
   ClientConfiguration result{};
diff --git a/src/tracing/core/trace_writer_impl.cc b/src/tracing/core/trace_writer_impl.cc
index 4a5e913..840588d 100644
--- a/src/tracing/core/trace_writer_impl.cc
+++ b/src/tracing/core/trace_writer_impl.cc
@@ -93,7 +93,9 @@
   // It doesn't make sense to begin a packet that is going to fragment
   // immediately after (8 is just an arbitrary estimation on the minimum size of
   // a realistic packet).
-  if (protobuf_stream_writer_.bytes_available() < kPacketHeaderSize + 8) {
+  bool chunk_too_full =
+      protobuf_stream_writer_.bytes_available() < kPacketHeaderSize + 8;
+  if (chunk_too_full || reached_max_packets_per_chunk_) {
     protobuf_stream_writer_.Reset(GetNewBuffer());
   }
 
@@ -109,7 +111,9 @@
   uint8_t* header = protobuf_stream_writer_.ReserveBytes(kPacketHeaderSize);
   memset(header, 0, kPacketHeaderSize);
   cur_packet_->set_size_field(header);
-  cur_chunk_.IncrementPacketCount();
+  uint16_t new_packet_count = cur_chunk_.IncrementPacketCount();
+  reached_max_packets_per_chunk_ =
+      new_packet_count == ChunkHeader::Packets::kMaxCount;
   TracePacketHandle handle(cur_packet_.get());
   cur_fragment_start_ = protobuf_stream_writer_.write_ptr();
   fragmenting_packet_ = true;
@@ -200,6 +204,7 @@
   header.packets.store(packets, std::memory_order_relaxed);
 
   cur_chunk_ = shmem_arbiter_->GetNewChunk(header);
+  reached_max_packets_per_chunk_ = false;
   uint8_t* payload_begin = cur_chunk_.payload_begin();
   if (fragmenting_packet_) {
     cur_packet_->set_size_field(payload_begin);
diff --git a/src/tracing/core/trace_writer_impl.h b/src/tracing/core/trace_writer_impl.h
index 3ba878d..9dd74d9 100644
--- a/src/tracing/core/trace_writer_impl.h
+++ b/src/tracing/core/trace_writer_impl.h
@@ -84,6 +84,11 @@
   // starting the TracePacket header.
   bool fragmenting_packet_ = false;
 
+  // Set to |true| when the current chunk contains the maximum number of packets
+  // a chunk can contain. When this is |true|, the next packet requires starting
+  // a new chunk.
+  bool reached_max_packets_per_chunk_ = false;
+
   // When a packet is fragmented across different chunks, the |size_field| of
   // the outstanding nested protobuf messages is redirected onto Patch entries
   // in this list at the time the Chunk is returned (because at that point we
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index a197df0..d91f0ff 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -193,6 +193,8 @@
   TaskRunnerThread producer_thread("perfetto.prd");
   producer_thread.Start(std::unique_ptr<ProbesProducerDelegate>(
       new ProbesProducerDelegate(TEST_PRODUCER_SOCK_NAME)));
+#else
+  base::ignore_result(TEST_PRODUCER_SOCK_NAME);
 #endif
 
   helper.ConnectConsumer();
diff --git a/tools/build_all_configs.py b/tools/build_all_configs.py
index 6642e71..1ab7331 100755
--- a/tools/build_all_configs.py
+++ b/tools/build_all_configs.py
@@ -29,6 +29,8 @@
 ANDROID_BUILD_CONFIGS = {
   'android_debug': ['target_os="android"', 'is_clang=true', 'is_debug=true'],
   'android_release': ['target_os="android"', 'is_clang=true', 'is_debug=false'],
+  'android_release_incl_heapprofd':
+    ['target_os="android"', 'is_clang=true', 'is_debug=false', 'android_api_level=26'],
   'android_asan': ['target_os="android"', 'is_clang=true', 'is_debug=false', 'is_asan=true'],
   'android_lsan': ['target_os="android"', 'is_clang=true', 'is_debug=false', 'is_lsan=true'],
 }