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'],
}