Merge "Reland: perfetto_cmd: add ability to compress trace packets"
diff --git a/Android.bp b/Android.bp
index 71646b3..b9dd77f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -218,20 +218,6 @@
],
}
-// GN target: //:idle_alloc
-cc_binary {
- name: "idle_alloc",
- srcs: [
- "tools/idle_alloc.cc",
- ],
- shared_libs: [
- "liblog",
- ],
- defaults: [
- "perfetto_defaults",
- ],
-}
-
// GN target: //:libperfetto
cc_library_shared {
name: "libperfetto",
diff --git a/BUILD.gn b/BUILD.gn
index e28ab12..89c95be 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -378,8 +378,10 @@
}
}
-executable("idle_alloc") {
- sources = [
- "tools/idle_alloc.cc",
- ]
+if (perfetto_build_standalone) {
+ executable("idle_alloc") {
+ sources = [
+ "tools/idle_alloc.cc",
+ ]
+ }
}
diff --git a/docs/heapprofd.md b/docs/heapprofd.md
index c5ea5ed..4234686 100644
--- a/docs/heapprofd.md
+++ b/docs/heapprofd.md
@@ -189,7 +189,7 @@
idle.
4. Interact with your program.
-Once you are done interacting, `Ctrl-C` the invokation of
+Once you are done interacting, `Ctrl-C` the invocation of
`tools/heap_profile`, and upload the `heap_dump.2.*.pb.gz` file to pprof.
You can then see the memory that was idle in the `idle_space` tab.
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index c1b1cdc..bb9ee37 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -20,6 +20,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+
#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
@@ -33,17 +34,6 @@
#include <sys/system_properties.h>
#endif
-// This test only works when run on Android using an Android Q version of
-// Bionic.
-// TODO(b/118428762): look into unwinding issues on x86.
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
- PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS) || defined(__i386__) || \
- defined(__x86_64__)
-#define MAYBE_SKIP(x) DISABLED_##x
-#else
-#define MAYBE_SKIP(x) x
-#endif
-
namespace perfetto {
namespace profiling {
namespace {
@@ -51,6 +41,7 @@
constexpr useconds_t kMsToUs = 1000;
using ::testing::AnyOf;
+using ::testing::Bool;
using ::testing::Eq;
class HeapprofdDelegate : public ThreadDelegate {
@@ -199,7 +190,7 @@
"unwinding_time_us: " + FormatHistogram(stats.unwinding_time_us());
}
-class HeapprofdEndToEnd : public ::testing::Test {
+class HeapprofdEndToEnd : public ::testing::TestWithParam<bool> {
public:
HeapprofdEndToEnd() {
// This is not needed for correctness, but works around a init behavior that
@@ -207,10 +198,20 @@
// and then set to 1 again too quickly, init decides that the service is
// "restarting" and waits before restarting it.
usleep(50000);
+ bool should_fork = GetParam();
+ if (should_fork) {
+ fork_prop_ = EnableFork();
+ PERFETTO_CHECK(ReadProperty(kHeapprofdModeProperty, "") == "fork");
+ } else {
+ fork_prop_ = DisableFork();
+ PERFETTO_CHECK(ReadProperty(kHeapprofdModeProperty, "") == "");
+ }
}
protected:
base::TestTaskRunner task_runner;
+ base::ScopedResource<std::string*, SetModeProperty, nullptr> fork_prop_{
+ nullptr};
std::unique_ptr<TestHelper> Trace(const TraceConfig& trace_config) {
auto helper = GetHelper(&task_runner);
@@ -314,776 +315,655 @@
}
EXPECT_GT(dumps, 0);
}
+};
- void Smoke() {
- constexpr size_t kAllocSize = 1024;
+TEST_P(HeapprofdEndToEnd, Smoke) {
+ constexpr size_t kAllocSize = 1024;
- pid_t pid = ForkContinuousMalloc(kAllocSize);
+ pid_t pid = ForkContinuousMalloc(kAllocSize);
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(2000);
- trace_config.set_flush_timeout_ms(10000);
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(2000);
+ trace_config.set_flush_timeout_ms(10000);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
- ds_config->set_target_buffer(0);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- heapprofd_config->set_all(false);
- auto* cont_config = heapprofd_config->set_continuous_dump_config();
- cont_config->set_dump_phase_ms(0);
- cont_config->set_dump_interval_ms(100);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ heapprofd_config->set_all(false);
+ auto* cont_config = heapprofd_config->set_continuous_dump_config();
+ cont_config->set_dump_phase_ms(0);
+ cont_config->set_dump_interval_ms(100);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
- auto helper = Trace(trace_config);
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
+ auto helper = Trace(trace_config);
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+}
+
+TEST_P(HeapprofdEndToEnd, TwoProcesses) {
+ constexpr size_t kAllocSize = 1024;
+ constexpr size_t kAllocSize2 = 7;
+
+ pid_t pid = ForkContinuousMalloc(kAllocSize);
+ pid_t pid2 = ForkContinuousMalloc(kAllocSize2);
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(2000);
+ trace_config.set_flush_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid2));
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ auto helper = Trace(trace_config);
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid2));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid2), kAllocSize2);
+
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+ PERFETTO_CHECK(kill(pid2, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid2, nullptr, 0)) == pid2);
+}
+
+TEST_P(HeapprofdEndToEnd, FinalFlush) {
+ constexpr size_t kAllocSize = 1024;
+
+ pid_t pid = ForkContinuousMalloc(kAllocSize);
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(2000);
+ trace_config.set_flush_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ auto helper = Trace(trace_config);
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
+
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+}
+
+TEST_P(HeapprofdEndToEnd, NativeStartup) {
+ auto helper = GetHelper(&task_runner);
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc");
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ helper->StartTracing(trace_config);
+
+ // Wait to guarantee that the process forked below is hooked by the profiler
+ // by virtue of the startup check, and not by virtue of being seen as a
+ // running process. This sleep is here to prevent that, accidentally, the
+ // test gets to the fork()+exec() too soon, before the heap profiling daemon
+ // has received the trace config.
+ sleep(1);
+
+ // Make sure the forked process does not get reparented to init.
+ setsid();
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ PERFETTO_FATAL("Failed to fork.");
+ case 0: {
+ const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDIN_FILENO);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
+ nullptr, envp) == 0);
+ break;
+ }
+ default:
+ break;
}
- void TwoProcesses() {
- constexpr size_t kAllocSize = 1024;
- constexpr size_t kAllocSize2 = 7;
+ helper->WaitForTracingDisabled(20000);
- pid_t pid = ForkContinuousMalloc(kAllocSize);
- pid_t pid2 = ForkContinuousMalloc(kAllocSize2);
+ helper->ReadData();
+ helper->WaitForReadData();
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(2000);
- trace_config.set_flush_timeout_ms(10000);
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
- ds_config->set_target_buffer(0);
+ const auto& packets = helper->trace();
+ ASSERT_GT(packets.size(), 0u);
+ size_t profile_packets = 0;
+ size_t samples = 0;
+ uint64_t total_allocated = 0;
+ uint64_t total_freed = 0;
+ for (const protos::TracePacket& packet : packets) {
+ if (packet.has_profile_packet() &&
+ packet.profile_packet().process_dumps().size() > 0) {
+ const auto& dumps = packet.profile_packet().process_dumps();
+ ASSERT_EQ(dumps.size(), 1);
+ const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
+ EXPECT_EQ(dump.pid(), pid);
+ profile_packets++;
+ for (const auto& sample : dump.samples()) {
+ samples++;
+ total_allocated += sample.self_allocated();
+ total_freed += sample.self_freed();
+ }
+ }
+ }
+ EXPECT_EQ(profile_packets, 1);
+ EXPECT_GT(samples, 0);
+ EXPECT_GT(total_allocated, 0);
+ EXPECT_GT(total_freed, 0);
+}
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- heapprofd_config->add_pid(static_cast<uint64_t>(pid2));
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+TEST_P(HeapprofdEndToEnd, NativeStartupDenormalizedCmdline) {
+ auto helper = GetHelper(&task_runner);
- auto helper = Trace(trace_config);
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid2));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid2), kAllocSize2);
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- PERFETTO_CHECK(kill(pid2, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid2, nullptr, 0)) == pid2);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc@1.2.3");
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ helper->StartTracing(trace_config);
+
+ // Wait to guarantee that the process forked below is hooked by the profiler
+ // by virtue of the startup check, and not by virtue of being seen as a
+ // running process. This sleep is here to prevent that, accidentally, the
+ // test gets to the fork()+exec() too soon, before the heap profiling daemon
+ // has received the trace config.
+ sleep(1);
+
+ // Make sure the forked process does not get reparented to init.
+ setsid();
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ PERFETTO_FATAL("Failed to fork.");
+ case 0: {
+ const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDIN_FILENO);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
+ nullptr, envp) == 0);
+ break;
+ }
+ default:
+ break;
}
- void FinalFlush() {
- constexpr size_t kAllocSize = 1024;
+ helper->WaitForTracingDisabled(20000);
- pid_t pid = ForkContinuousMalloc(kAllocSize);
+ helper->ReadData();
+ helper->WaitForReadData();
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(2000);
- trace_config.set_flush_timeout_ms(10000);
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
- ds_config->set_target_buffer(0);
+ const auto& packets = helper->trace();
+ ASSERT_GT(packets.size(), 0u);
+ size_t profile_packets = 0;
+ size_t samples = 0;
+ uint64_t total_allocated = 0;
+ uint64_t total_freed = 0;
+ for (const protos::TracePacket& packet : packets) {
+ if (packet.has_profile_packet() &&
+ packet.profile_packet().process_dumps().size() > 0) {
+ const auto& dumps = packet.profile_packet().process_dumps();
+ ASSERT_EQ(dumps.size(), 1);
+ const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
+ EXPECT_EQ(dump.pid(), pid);
+ profile_packets++;
+ for (const auto& sample : dump.samples()) {
+ samples++;
+ total_allocated += sample.self_allocated();
+ total_freed += sample.self_freed();
+ }
+ }
+ }
+ EXPECT_EQ(profile_packets, 1);
+ EXPECT_GT(samples, 0);
+ EXPECT_GT(total_allocated, 0);
+ EXPECT_GT(total_freed, 0);
+}
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+TEST_P(HeapprofdEndToEnd, DiscoverByName) {
+ auto helper = GetHelper(&task_runner);
- auto helper = Trace(trace_config);
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc");
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ // Make sure the forked process does not get reparented to init.
+ setsid();
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ PERFETTO_FATAL("Failed to fork.");
+ case 0: {
+ const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDIN_FILENO);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
+ nullptr, envp) == 0);
+ break;
+ }
+ default:
+ break;
}
- void NativeStartup() {
- auto helper = GetHelper(&task_runner);
+ // Wait to make sure process is fully initialized, so we do not accidentally
+ // match it by the startup logic.
+ sleep(1);
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
+ helper->StartTracing(trace_config);
+ helper->WaitForTracingDisabled(20000);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
+ helper->ReadData();
+ helper->WaitForReadData();
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc");
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- helper->StartTracing(trace_config);
-
- // Wait to guarantee that the process forked below is hooked by the profiler
- // by virtue of the startup check, and not by virtue of being seen as a
- // running process. This sleep is here to prevent that, accidentally, the
- // test gets to the fork()+exec() too soon, before the heap profiling daemon
- // has received the trace config.
- sleep(1);
-
- // Make sure the forked process does not get reparented to init.
- setsid();
- pid_t pid = fork();
- switch (pid) {
- case -1:
- PERFETTO_FATAL("Failed to fork.");
- case 0: {
- const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
- int null = open("/dev/null", O_RDWR);
- dup2(null, STDIN_FILENO);
- dup2(null, STDOUT_FILENO);
- dup2(null, STDERR_FILENO);
- PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
- nullptr, envp) == 0);
- break;
- }
- default:
- break;
- }
-
- helper->WaitForTracingDisabled(20000);
-
- helper->ReadData();
- helper->WaitForReadData();
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
-
- const auto& packets = helper->trace();
- ASSERT_GT(packets.size(), 0u);
- size_t profile_packets = 0;
- size_t samples = 0;
- uint64_t total_allocated = 0;
- uint64_t total_freed = 0;
- for (const protos::TracePacket& packet : packets) {
- if (packet.has_profile_packet() &&
- packet.profile_packet().process_dumps().size() > 0) {
- const auto& dumps = packet.profile_packet().process_dumps();
- ASSERT_EQ(dumps.size(), 1);
- const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
- EXPECT_EQ(dump.pid(), pid);
- profile_packets++;
- for (const auto& sample : dump.samples()) {
- samples++;
- total_allocated += sample.self_allocated();
- total_freed += sample.self_freed();
- }
+ const auto& packets = helper->trace();
+ ASSERT_GT(packets.size(), 0u);
+ size_t profile_packets = 0;
+ size_t samples = 0;
+ uint64_t total_allocated = 0;
+ uint64_t total_freed = 0;
+ for (const protos::TracePacket& packet : packets) {
+ if (packet.has_profile_packet() &&
+ packet.profile_packet().process_dumps().size() > 0) {
+ const auto& dumps = packet.profile_packet().process_dumps();
+ ASSERT_EQ(dumps.size(), 1);
+ const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
+ EXPECT_EQ(dump.pid(), pid);
+ profile_packets++;
+ for (const auto& sample : dump.samples()) {
+ samples++;
+ total_allocated += sample.self_allocated();
+ total_freed += sample.self_freed();
}
}
- EXPECT_EQ(profile_packets, 1);
- EXPECT_GT(samples, 0);
- EXPECT_GT(total_allocated, 0);
- EXPECT_GT(total_freed, 0);
+ }
+ EXPECT_EQ(profile_packets, 1);
+ EXPECT_GT(samples, 0);
+ EXPECT_GT(total_allocated, 0);
+ EXPECT_GT(total_freed, 0);
+}
+
+TEST_P(HeapprofdEndToEnd, DiscoverByNameDenormalizedCmdline) {
+ auto helper = GetHelper(&task_runner);
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc@1.2.3");
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ // Make sure the forked process does not get reparented to init.
+ setsid();
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ PERFETTO_FATAL("Failed to fork.");
+ case 0: {
+ const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
+ int null = open("/dev/null", O_RDWR);
+ dup2(null, STDIN_FILENO);
+ dup2(null, STDOUT_FILENO);
+ dup2(null, STDERR_FILENO);
+ PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
+ nullptr, envp) == 0);
+ break;
+ }
+ default:
+ break;
}
- void NativeStartupDenormalizedCmdline() {
- auto helper = GetHelper(&task_runner);
+ // Wait to make sure process is fully initialized, so we do not accidentally
+ // match it by the startup logic.
+ sleep(1);
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
+ helper->StartTracing(trace_config);
+ helper->WaitForTracingDisabled(20000);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
+ helper->ReadData();
+ helper->WaitForReadData();
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc@1.2.3");
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- helper->StartTracing(trace_config);
-
- // Wait to guarantee that the process forked below is hooked by the profiler
- // by virtue of the startup check, and not by virtue of being seen as a
- // running process. This sleep is here to prevent that, accidentally, the
- // test gets to the fork()+exec() too soon, before the heap profiling daemon
- // has received the trace config.
- sleep(1);
-
- // Make sure the forked process does not get reparented to init.
- setsid();
- pid_t pid = fork();
- switch (pid) {
- case -1:
- PERFETTO_FATAL("Failed to fork.");
- case 0: {
- const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
- int null = open("/dev/null", O_RDWR);
- dup2(null, STDIN_FILENO);
- dup2(null, STDOUT_FILENO);
- dup2(null, STDERR_FILENO);
- PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
- nullptr, envp) == 0);
- break;
- }
- default:
- break;
- }
-
- helper->WaitForTracingDisabled(20000);
-
- helper->ReadData();
- helper->WaitForReadData();
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
-
- const auto& packets = helper->trace();
- ASSERT_GT(packets.size(), 0u);
- size_t profile_packets = 0;
- size_t samples = 0;
- uint64_t total_allocated = 0;
- uint64_t total_freed = 0;
- for (const protos::TracePacket& packet : packets) {
- if (packet.has_profile_packet() &&
- packet.profile_packet().process_dumps().size() > 0) {
- const auto& dumps = packet.profile_packet().process_dumps();
- ASSERT_EQ(dumps.size(), 1);
- const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
- EXPECT_EQ(dump.pid(), pid);
- profile_packets++;
- for (const auto& sample : dump.samples()) {
- samples++;
- total_allocated += sample.self_allocated();
- total_freed += sample.self_freed();
- }
+ const auto& packets = helper->trace();
+ ASSERT_GT(packets.size(), 0u);
+ size_t profile_packets = 0;
+ size_t samples = 0;
+ uint64_t total_allocated = 0;
+ uint64_t total_freed = 0;
+ for (const protos::TracePacket& packet : packets) {
+ if (packet.has_profile_packet() &&
+ packet.profile_packet().process_dumps().size() > 0) {
+ const auto& dumps = packet.profile_packet().process_dumps();
+ ASSERT_EQ(dumps.size(), 1);
+ const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
+ EXPECT_EQ(dump.pid(), pid);
+ profile_packets++;
+ for (const auto& sample : dump.samples()) {
+ samples++;
+ total_allocated += sample.self_allocated();
+ total_freed += sample.self_freed();
}
}
- EXPECT_EQ(profile_packets, 1);
- EXPECT_GT(samples, 0);
- EXPECT_GT(total_allocated, 0);
- EXPECT_GT(total_freed, 0);
}
+ EXPECT_EQ(profile_packets, 1);
+ EXPECT_GT(samples, 0);
+ EXPECT_GT(total_allocated, 0);
+ EXPECT_GT(total_freed, 0);
+}
- void DiscoverByName() {
- auto helper = GetHelper(&task_runner);
+TEST_P(HeapprofdEndToEnd, ReInit) {
+ constexpr uint64_t kFirstIterationBytes = 5;
+ constexpr uint64_t kSecondIterationBytes = 7;
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
+ base::Pipe signal_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
+ base::Pipe ack_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
-
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc");
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
-
- // Make sure the forked process does not get reparented to init.
- setsid();
- pid_t pid = fork();
- switch (pid) {
- case -1:
- PERFETTO_FATAL("Failed to fork.");
- case 0: {
- const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
- int null = open("/dev/null", O_RDWR);
- dup2(null, STDIN_FILENO);
- dup2(null, STDOUT_FILENO);
- dup2(null, STDERR_FILENO);
- PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
- nullptr, envp) == 0);
- break;
- }
- default:
- break;
- }
-
- // Wait to make sure process is fully initialized, so we do not accidentally
- // match it by the startup logic.
- sleep(1);
-
- helper->StartTracing(trace_config);
- helper->WaitForTracingDisabled(20000);
-
- helper->ReadData();
- helper->WaitForReadData();
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
-
- const auto& packets = helper->trace();
- ASSERT_GT(packets.size(), 0u);
- size_t profile_packets = 0;
- size_t samples = 0;
- uint64_t total_allocated = 0;
- uint64_t total_freed = 0;
- for (const protos::TracePacket& packet : packets) {
- if (packet.has_profile_packet() &&
- packet.profile_packet().process_dumps().size() > 0) {
- const auto& dumps = packet.profile_packet().process_dumps();
- ASSERT_EQ(dumps.size(), 1);
- const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
- EXPECT_EQ(dump.pid(), pid);
- profile_packets++;
- for (const auto& sample : dump.samples()) {
- samples++;
- total_allocated += sample.self_allocated();
- total_freed += sample.self_freed();
- }
- }
- }
- EXPECT_EQ(profile_packets, 1);
- EXPECT_GT(samples, 0);
- EXPECT_GT(total_allocated, 0);
- EXPECT_GT(total_freed, 0);
- }
-
- void DiscoverByNameDenormalizedCmdline() {
- auto helper = GetHelper(&task_runner);
-
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
-
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
-
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_process_cmdline("heapprofd_continuous_malloc@1.2.3");
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
-
- // Make sure the forked process does not get reparented to init.
- setsid();
- pid_t pid = fork();
- switch (pid) {
- case -1:
- PERFETTO_FATAL("Failed to fork.");
- case 0: {
- const char* envp[] = {"HEAPPROFD_TESTING_RUN_MALLOC=1", nullptr};
- int null = open("/dev/null", O_RDWR);
- dup2(null, STDIN_FILENO);
- dup2(null, STDOUT_FILENO);
- dup2(null, STDERR_FILENO);
- PERFETTO_CHECK(execle("/proc/self/exe", "heapprofd_continuous_malloc",
- nullptr, envp) == 0);
- break;
- }
- default:
- break;
- }
-
- // Wait to make sure process is fully initialized, so we do not accidentally
- // match it by the startup logic.
- sleep(1);
-
- helper->StartTracing(trace_config);
- helper->WaitForTracingDisabled(20000);
-
- helper->ReadData();
- helper->WaitForReadData();
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
-
- const auto& packets = helper->trace();
- ASSERT_GT(packets.size(), 0u);
- size_t profile_packets = 0;
- size_t samples = 0;
- uint64_t total_allocated = 0;
- uint64_t total_freed = 0;
- for (const protos::TracePacket& packet : packets) {
- if (packet.has_profile_packet() &&
- packet.profile_packet().process_dumps().size() > 0) {
- const auto& dumps = packet.profile_packet().process_dumps();
- ASSERT_EQ(dumps.size(), 1);
- const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
- EXPECT_EQ(dump.pid(), pid);
- profile_packets++;
- for (const auto& sample : dump.samples()) {
- samples++;
- total_allocated += sample.self_allocated();
- total_freed += sample.self_freed();
- }
- }
- }
- EXPECT_EQ(profile_packets, 1);
- EXPECT_GT(samples, 0);
- EXPECT_GT(total_allocated, 0);
- EXPECT_GT(total_freed, 0);
- }
-
- void ReInit() {
- constexpr uint64_t kFirstIterationBytes = 5;
- constexpr uint64_t kSecondIterationBytes = 7;
-
- base::Pipe signal_pipe = base::Pipe::Create(base::Pipe::kBothNonBlock);
- base::Pipe ack_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
-
- setsid();
- pid_t pid = fork();
- switch (pid) {
- case -1:
- PERFETTO_FATAL("Failed to fork.");
- case 0: {
- uint64_t bytes = kFirstIterationBytes;
- signal_pipe.wr.reset();
- ack_pipe.rd.reset();
- for (;;) {
+ setsid();
+ pid_t pid = fork();
+ switch (pid) {
+ case -1:
+ PERFETTO_FATAL("Failed to fork.");
+ case 0: {
+ uint64_t bytes = kFirstIterationBytes;
+ signal_pipe.wr.reset();
+ ack_pipe.rd.reset();
+ for (;;) {
+ AllocateAndFree(bytes);
+ char buf[1];
+ if (bool(signal_pipe.rd) &&
+ read(*signal_pipe.rd, buf, sizeof(buf)) == 0) {
+ // make sure the client has noticed that the session has stopped
AllocateAndFree(bytes);
- char buf[1];
- if (bool(signal_pipe.rd) &&
- read(*signal_pipe.rd, buf, sizeof(buf)) == 0) {
- // make sure the client has noticed that the session has stopped
- AllocateAndFree(bytes);
- bytes = kSecondIterationBytes;
- signal_pipe.rd.reset();
- ack_pipe.wr.reset();
- }
- usleep(10 * kMsToUs);
- }
- PERFETTO_FATAL("Should be unreachable");
- }
- default:
- break;
- }
-
- signal_pipe.rd.reset();
- ack_pipe.wr.reset();
-
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(2000);
- trace_config.set_flush_timeout_ms(10000);
-
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
- ds_config->set_target_buffer(0);
-
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- heapprofd_config->set_all(false);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
-
- auto helper = Trace(trace_config);
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
- kFirstIterationBytes);
-
- signal_pipe.wr.reset();
- char buf[1];
- ASSERT_EQ(read(*ack_pipe.rd, buf, sizeof(buf)), 0);
- ack_pipe.rd.reset();
-
- // A brief sleep to allow the client to notice that the profiling session is
- // to be torn down (as it rejects concurrent sessions).
- usleep(500 * kMsToUs);
-
- PERFETTO_LOG("HeapprofdEndToEnd::Reinit: Starting second");
- helper = Trace(trace_config);
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
- kSecondIterationBytes);
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- }
-
- void ConcurrentSession() {
- constexpr size_t kAllocSize = 1024;
-
- pid_t pid = ForkContinuousMalloc(kAllocSize);
-
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
-
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
- ds_config->set_target_buffer(0);
-
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- heapprofd_config->set_all(false);
- auto* cont_config = heapprofd_config->set_continuous_dump_config();
- cont_config->set_dump_phase_ms(0);
- cont_config->set_dump_interval_ms(100);
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
-
- auto helper = GetHelper(&task_runner);
- helper->StartTracing(trace_config);
- sleep(1);
- auto helper_concurrent = GetHelper(&task_runner);
- helper_concurrent->StartTracing(trace_config);
-
- helper->WaitForTracingDisabled(20000);
- helper->ReadData();
- helper->WaitForReadData();
- PrintStats(helper.get());
- ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
- ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
- ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
- ValidateRejectedConcurrent(helper_concurrent.get(),
- static_cast<uint64_t>(pid), false);
-
- helper_concurrent->WaitForTracingDisabled(20000);
- helper_concurrent->ReadData();
- helper_concurrent->WaitForReadData();
- PrintStats(helper.get());
- ValidateOnlyPID(helper_concurrent.get(), static_cast<uint64_t>(pid));
- ValidateRejectedConcurrent(helper_concurrent.get(),
- static_cast<uint64_t>(pid), true);
-
- PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
- PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
- }
-
- // TODO(rsavitski): fold exit status assertions into existing tests where
- // possible.
- void NativeProfilingActiveAtProcessExit() {
- constexpr uint64_t kTestAllocSize = 128;
- base::Pipe start_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
-
- setsid();
- pid_t pid = fork();
- if (pid == 0) { // child
- start_pipe.rd.reset();
- start_pipe.wr.reset();
- for (int i = 0; i < 200; i++) {
- // malloc and leak, otherwise the free batching will cause us to filter
- // out the allocations (as we don't see the interleaved frees).
- volatile char* x = static_cast<char*>(malloc(kTestAllocSize));
- if (x) {
- x[0] = 'x';
+ bytes = kSecondIterationBytes;
+ signal_pipe.rd.reset();
+ ack_pipe.wr.reset();
}
usleep(10 * kMsToUs);
}
- exit(0);
+ PERFETTO_FATAL("Should be unreachable");
}
+ default:
+ break;
+ }
- ASSERT_NE(pid, -1) << "Failed to fork.";
- start_pipe.wr.reset();
+ signal_pipe.rd.reset();
+ ack_pipe.wr.reset();
- // Construct tracing config (without starting profiling).
- auto helper = GetHelper(&task_runner);
- TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(10 * 1024);
- trace_config.set_duration_ms(5000);
- trace_config.set_flush_timeout_ms(10000);
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(2000);
+ trace_config.set_flush_timeout_ms(10000);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.heapprofd");
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
- protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
- heapprofd_config->set_sampling_interval_bytes(1);
- heapprofd_config->add_pid(static_cast<uint64_t>(pid));
- ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ heapprofd_config->set_all(false);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
- // Wait for child to have been scheduled at least once.
- char buf[1] = {};
- ASSERT_EQ(PERFETTO_EINTR(read(*start_pipe.rd, buf, sizeof(buf))), 0);
+ auto helper = Trace(trace_config);
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
+ kFirstIterationBytes);
+
+ signal_pipe.wr.reset();
+ char buf[1];
+ ASSERT_EQ(read(*ack_pipe.rd, buf, sizeof(buf)), 0);
+ ack_pipe.rd.reset();
+
+ // A brief sleep to allow the client to notice that the profiling session is
+ // to be torn down (as it rejects concurrent sessions).
+ usleep(500 * kMsToUs);
+
+ PERFETTO_LOG("HeapprofdEndToEnd::Reinit: Starting second");
+ helper = Trace(trace_config);
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid),
+ kSecondIterationBytes);
+
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+}
+
+TEST_P(HeapprofdEndToEnd, ConcurrentSession) {
+ constexpr size_t kAllocSize = 1024;
+
+ pid_t pid = ForkContinuousMalloc(kAllocSize);
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
+
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
+ ds_config->set_target_buffer(0);
+
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ heapprofd_config->set_all(false);
+ auto* cont_config = heapprofd_config->set_continuous_dump_config();
+ cont_config->set_dump_phase_ms(0);
+ cont_config->set_dump_interval_ms(100);
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+
+ auto helper = GetHelper(&task_runner);
+ helper->StartTracing(trace_config);
+ sleep(1);
+ auto helper_concurrent = GetHelper(&task_runner);
+ helper_concurrent->StartTracing(trace_config);
+
+ helper->WaitForTracingDisabled(20000);
+ helper->ReadData();
+ helper->WaitForReadData();
+ PrintStats(helper.get());
+ ValidateHasSamples(helper.get(), static_cast<uint64_t>(pid));
+ ValidateOnlyPID(helper.get(), static_cast<uint64_t>(pid));
+ ValidateSampleSizes(helper.get(), static_cast<uint64_t>(pid), kAllocSize);
+ ValidateRejectedConcurrent(helper_concurrent.get(),
+ static_cast<uint64_t>(pid), false);
+
+ helper_concurrent->WaitForTracingDisabled(20000);
+ helper_concurrent->ReadData();
+ helper_concurrent->WaitForReadData();
+ PrintStats(helper.get());
+ ValidateOnlyPID(helper_concurrent.get(), static_cast<uint64_t>(pid));
+ ValidateRejectedConcurrent(helper_concurrent.get(),
+ static_cast<uint64_t>(pid), true);
+
+ PERFETTO_CHECK(kill(pid, SIGKILL) == 0);
+ PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid, nullptr, 0)) == pid);
+}
+
+// TODO(rsavitski): fold exit status assertions into existing tests where
+// possible.
+TEST_P(HeapprofdEndToEnd, NativeProfilingActiveAtProcessExit) {
+ constexpr uint64_t kTestAllocSize = 128;
+ base::Pipe start_pipe = base::Pipe::Create(base::Pipe::kBothBlock);
+
+ setsid();
+ pid_t pid = fork();
+ if (pid == 0) { // child
start_pipe.rd.reset();
+ start_pipe.wr.reset();
+ for (int i = 0; i < 200; i++) {
+ // malloc and leak, otherwise the free batching will cause us to filter
+ // out the allocations (as we don't see the interleaved frees).
+ volatile char* x = static_cast<char*>(malloc(kTestAllocSize));
+ if (x) {
+ x[0] = 'x';
+ }
+ usleep(10 * kMsToUs);
+ }
+ exit(0);
+ }
- // Trace until child exits.
- helper->StartTracing(trace_config);
+ ASSERT_NE(pid, -1) << "Failed to fork.";
+ start_pipe.wr.reset();
- siginfo_t siginfo = {};
- int wait_ret = PERFETTO_EINTR(
- waitid(P_PID, static_cast<id_t>(pid), &siginfo, WEXITED));
- ASSERT_FALSE(wait_ret) << "Failed to waitid.";
+ // Construct tracing config (without starting profiling).
+ auto helper = GetHelper(&task_runner);
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(10 * 1024);
+ trace_config.set_duration_ms(5000);
+ trace_config.set_flush_timeout_ms(10000);
- // Assert that the child exited successfully.
- EXPECT_EQ(siginfo.si_code, CLD_EXITED) << "Child did not exit by itself.";
- EXPECT_EQ(siginfo.si_status, 0) << "Child's exit status not successful.";
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.heapprofd");
- // Assert that we did profile the process.
- helper->FlushAndWait(2000);
- helper->DisableTracing();
- helper->WaitForTracingDisabled(10000);
- helper->ReadData();
- helper->WaitForReadData();
+ protozero::HeapBuffered<protos::pbzero::HeapprofdConfig> heapprofd_config;
+ heapprofd_config->set_sampling_interval_bytes(1);
+ heapprofd_config->add_pid(static_cast<uint64_t>(pid));
+ ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
- const auto& packets = helper->trace();
- ASSERT_GT(packets.size(), 0u);
- size_t profile_packets = 0;
- size_t samples = 0;
- uint64_t total_allocated = 0;
- for (const protos::TracePacket& packet : packets) {
- if (packet.has_profile_packet() &&
- packet.profile_packet().process_dumps().size() > 0) {
- const auto& dumps = packet.profile_packet().process_dumps();
- ASSERT_EQ(dumps.size(), 1);
- const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
- EXPECT_EQ(dump.pid(), pid);
- profile_packets++;
- for (const auto& sample : dump.samples()) {
- samples++;
- total_allocated += sample.self_allocated();
- }
+ // Wait for child to have been scheduled at least once.
+ char buf[1] = {};
+ ASSERT_EQ(PERFETTO_EINTR(read(*start_pipe.rd, buf, sizeof(buf))), 0);
+ start_pipe.rd.reset();
+
+ // Trace until child exits.
+ helper->StartTracing(trace_config);
+
+ siginfo_t siginfo = {};
+ int wait_ret =
+ PERFETTO_EINTR(waitid(P_PID, static_cast<id_t>(pid), &siginfo, WEXITED));
+ ASSERT_FALSE(wait_ret) << "Failed to waitid.";
+
+ // Assert that the child exited successfully.
+ EXPECT_EQ(siginfo.si_code, CLD_EXITED) << "Child did not exit by itself.";
+ EXPECT_EQ(siginfo.si_status, 0) << "Child's exit status not successful.";
+
+ // Assert that we did profile the process.
+ helper->FlushAndWait(2000);
+ helper->DisableTracing();
+ helper->WaitForTracingDisabled(10000);
+ helper->ReadData();
+ helper->WaitForReadData();
+
+ const auto& packets = helper->trace();
+ ASSERT_GT(packets.size(), 0u);
+ size_t profile_packets = 0;
+ size_t samples = 0;
+ uint64_t total_allocated = 0;
+ for (const protos::TracePacket& packet : packets) {
+ if (packet.has_profile_packet() &&
+ packet.profile_packet().process_dumps().size() > 0) {
+ const auto& dumps = packet.profile_packet().process_dumps();
+ ASSERT_EQ(dumps.size(), 1);
+ const protos::ProfilePacket_ProcessHeapSamples& dump = dumps.Get(0);
+ EXPECT_EQ(dump.pid(), pid);
+ profile_packets++;
+ for (const auto& sample : dump.samples()) {
+ samples++;
+ total_allocated += sample.self_allocated();
}
}
- EXPECT_EQ(profile_packets, 1);
- EXPECT_GT(samples, 0);
- EXPECT_GT(total_allocated, 0);
}
-};
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(Smoke_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- Smoke();
+ EXPECT_EQ(profile_packets, 1);
+ EXPECT_GT(samples, 0);
+ EXPECT_GT(total_allocated, 0);
}
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(TwoProcesses_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- TwoProcesses();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(TwoProcesses_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- TwoProcesses();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(Smoke_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- Smoke();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(FinalFlush_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- FinalFlush();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(FinalFlush_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- FinalFlush();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(NativeStartup_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- NativeStartup();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(NativeStartup_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- NativeStartup();
-}
-
-TEST_F(HeapprofdEndToEnd,
- MAYBE_SKIP(NativeStartupDenormalizedCmdline_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- NativeStartupDenormalizedCmdline();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(NativeStartupDenormalizedCmdline_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- NativeStartupDenormalizedCmdline();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(DiscoverByName_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- DiscoverByName();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(DiscoverByName_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- DiscoverByName();
-}
-
-TEST_F(HeapprofdEndToEnd,
- MAYBE_SKIP(DiscoverByNameDenormalizedCmdline_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- DiscoverByNameDenormalizedCmdline();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(DiscoverByNameDenormalizedCmdline_Fork)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- DiscoverByNameDenormalizedCmdline();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(ReInit_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- ReInit();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(ReInit_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- ReInit();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(ConcurrentSession_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- ConcurrentSession();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(ConcurrentSession_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- ConcurrentSession();
-}
-
-TEST_F(HeapprofdEndToEnd,
- MAYBE_SKIP(NativeProfilingActiveAtProcessExit_Central)) {
- auto prop = DisableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "");
- NativeProfilingActiveAtProcessExit();
-}
-
-TEST_F(HeapprofdEndToEnd, MAYBE_SKIP(NativeProfilingActiveAtProcessExit_Fork)) {
- // RAII handle that resets to central mode when out of scope.
- auto prop = EnableFork();
- ASSERT_EQ(ReadProperty(kHeapprofdModeProperty, ""), "fork");
- NativeProfilingActiveAtProcessExit();
-}
+// This test only works when run on Android using an Android Q version of
+// Bionic.
+// TODO(b/118428762): look into unwinding issues on x86.
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS) || defined(__i386__) || \
+ defined(__x86_64__)
+INSTANTIATE_TEST_CASE_P(DISABLED_ForkMode, HeapprofdEndToEnd, Bool());
+#else
+INSTANTIATE_TEST_CASE_P(ForkMode, HeapprofdEndToEnd, Bool());
+#endif
} // namespace
} // namespace profiling
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 4024a51..7885567 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -48,7 +48,6 @@
'//:heapprofd_client',
'//:heapprofd',
'//:trigger_perfetto',
- '//:idle_alloc',
]
# Defines a custom init_rc argument to be applied to the corresponding output