Merge "trace_processor: initial implementation of the args table"
diff --git a/include/perfetto/base/metatrace.h b/include/perfetto/base/metatrace.h
index b30b910..95313fc 100644
--- a/include/perfetto/base/metatrace.h
+++ b/include/perfetto/base/metatrace.h
@@ -19,6 +19,8 @@
#include <string.h>
+#include <string>
+
#include "perfetto/base/logging.h"
#include "perfetto/base/utils.h"
@@ -31,6 +33,11 @@
WriteEvent('B', evt_name, cpu);
}
+ MetaTrace(const std::string& str, size_t cpu)
+ : str_copy_(str), evt_name_(str_copy_.c_str()), cpu_(cpu) {
+ WriteEvent('B', evt_name_, cpu);
+ }
+
~MetaTrace() { WriteEvent('E', evt_name_, cpu_); }
private:
@@ -39,6 +46,7 @@
void WriteEvent(char type, const char* evt_name, size_t cpu);
+ std::string str_copy_;
const char* const evt_name_;
const size_t cpu_;
};
diff --git a/include/perfetto/base/paged_memory.h b/include/perfetto/base/paged_memory.h
index 615a226..ea2aaae 100644
--- a/include/perfetto/base/paged_memory.h
+++ b/include/perfetto/base/paged_memory.h
@@ -76,8 +76,8 @@
void EnsureCommitted(size_t /*committed_size*/) {}
#endif // TRACK_COMMITTED_SIZE()
- void* Get() const noexcept;
- bool IsValid() const noexcept;
+ inline void* Get() const noexcept { return p_; }
+ inline bool IsValid() const noexcept { return !!p_; }
private:
PagedMemory(char* p, size_t size);
diff --git a/include/perfetto/tracing/core/tracing_service.h b/include/perfetto/tracing/core/tracing_service.h
index cdeb575..dda7193 100644
--- a/include/perfetto/tracing/core/tracing_service.h
+++ b/include/perfetto/tracing/core/tracing_service.h
@@ -72,6 +72,18 @@
virtual void RegisterDataSource(const DataSourceDescriptor&) = 0;
virtual void UnregisterDataSource(const std::string& name) = 0;
+ // Associate the trace writer with the given |writer_id| with
+ // |target_buffer|. The service may use this information to retrieve and
+ // copy uncommitted chunks written by the trace writer into its associated
+ // buffer, e.g. when a producer process crashes or when a flush is
+ // necessary.
+ virtual void RegisterTraceWriter(uint32_t writer_id,
+ uint32_t target_buffer) = 0;
+
+ // Remove the association of the trace writer previously created via
+ // RegisterTraceWriter.
+ virtual void UnregisterTraceWriter(uint32_t writer_id) = 0;
+
// Called by the Producer to signal that some pages in the shared memory
// buffer (shared between Service and Producer) have changed.
using CommitDataCallback = std::function<void()>;
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index 0a27f82..8217795 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -38,6 +38,15 @@
rpc UnregisterDataSource(UnregisterDataSourceRequest)
returns (UnregisterDataSourceResponse) {}
+ // Associates a trace writer with its target buffer.
+ rpc RegisterTraceWriter(RegisterTraceWriterRequest)
+ returns (RegisterTraceWriterResponse) {}
+
+ // Removes a trace writer association previously added by
+ // RegisterTraceWriter.
+ rpc UnregisterTraceWriter(UnregisterTraceWriterRequest)
+ returns (UnregisterTraceWriterResponse) {}
+
// Sent by the client to request the service to:
// 1) Move some chunks from the shmem buffer into the logging buffer.
// 2) Patch the content of some chunks previously moved.
@@ -97,6 +106,27 @@
message UnregisterDataSourceResponse {}
+// Arguments for rpc RegisterTraceWriter().
+
+message RegisterTraceWriterRequest {
+ // The ID of a producer's trace writer.
+ optional uint32 trace_writer_id = 1;
+
+ // The ID of the target buffer that the trace writer commits its chunks to.
+ optional uint32 target_buffer = 2;
+}
+
+message RegisterTraceWriterResponse {}
+
+// Arguments for rpc UnregisterTraceWriter().
+
+message UnregisterTraceWriterRequest {
+ // The ID of a producer's trace writer.
+ optional uint32 trace_writer_id = 1;
+}
+
+message UnregisterTraceWriterResponse {}
+
// Arguments for rpc CommitData().
// See commit_data_request.proto for CommitDataRequest. That has its own file
// because it is used also as input to generate the C++ classes in tracing/core
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
index 19e703b..baae164 100644
--- a/src/base/metatrace.cc
+++ b/src/base/metatrace.cc
@@ -45,11 +45,15 @@
if (fd == -1)
return;
+ // The JSON event format expects both "pid" and "tid" fields to create
+ // per-process tracks. Here what we really want to achieve is having one track
+ // per cpu. So we just pretend that each CPU is its own process with
+ // pid == tid == cpu.
char json[256];
int len = sprintf(json,
"{\"ts\": %f, \"cat\": \"PERF\", \"ph\": \"%c\", \"name\": "
- "\"%s\", \"pid\": %zu},\n",
- GetWallTimeNs().count() / 1000.0, type, evt_name, cpu);
+ "\"%s\", \"pid\": %zu, \"tid\": %zu},\n",
+ GetWallTimeNs().count() / 1000.0, type, evt_name, cpu, cpu);
ignore_result(WriteAll(fd, json, static_cast<size_t>(len)));
}
diff --git a/src/base/paged_memory.cc b/src/base/paged_memory.cc
index 79b7b2e..b5433d5 100644
--- a/src/base/paged_memory.cc
+++ b/src/base/paged_memory.cc
@@ -152,13 +152,5 @@
}
#endif // TRACK_COMMITTED_SIZE()
-void* PagedMemory::Get() const noexcept {
- return p_;
-}
-
-bool PagedMemory::IsValid() const noexcept {
- return p_;
-}
-
} // namespace base
} // namespace perfetto
diff --git a/src/profiling/memory/README.md b/src/profiling/memory/README.md
index e3b4669..619dba8 100644
--- a/src/profiling/memory/README.md
+++ b/src/profiling/memory/README.md
@@ -4,7 +4,22 @@
subject to frequent change and will be obsoleted once heapprofd is integrated
into Perfetto._
-Currently heapprofd only works with SELinux disabled and when run as root.
+
+## Using convenience script
+
+Use the `tools/heap_profile` script to heap profile a process. See all the
+arguments using `tools/heap_profile -h`, or use the defaults and just profile a
+process (e.g. `system_server`):
+
+```
+tools/heap_profile --name system_server
+```
+
+This will create a heap dump every second for a default of 1 minute.
+Head to http://pprof/ and upload the gzipped protos to get a visualization.
+
+## Manual
+Currently heapprofd only works with SELinux disabled.
To start profiling the process `${PID}`, run the following sequence of commands.
Adjust the `INTERVAL` to trade-off runtime impact for higher accuracy of the
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 602bcc2..1cffd75 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -27,6 +27,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/metatrace.h"
+#include "perfetto/base/optional.h"
#include "perfetto/base/utils.h"
#include "src/traced/probes/ftrace/ftrace_data_source.h"
#include "src/traced/probes/ftrace/proto_translation_table.h"
@@ -40,6 +41,33 @@
namespace {
+// For further documentation of these constants see the kernel source:
+// linux/include/linux/ring_buffer.h
+// Some information about the values of these constants are exposed to user
+// space at: /sys/kernel/debug/tracing/events/header_event
+constexpr uint32_t kTypeDataTypeLengthMax = 28;
+constexpr uint32_t kTypePadding = 29;
+constexpr uint32_t kTypeTimeExtend = 30;
+constexpr uint32_t kTypeTimeStamp = 31;
+
+constexpr uint32_t kMainThread = 255; // for METATRACE
+
+struct PageHeader {
+ uint64_t timestamp;
+ uint64_t size;
+ uint64_t overwrite;
+};
+
+struct EventHeader {
+ uint32_t type_or_length : 5;
+ uint32_t time_delta : 27;
+};
+
+struct TimeStamp {
+ uint64_t tv_nsec;
+ uint64_t tv_sec;
+};
+
bool ReadIntoString(const uint8_t* start,
const uint8_t* end,
uint32_t field_id,
@@ -81,36 +109,40 @@
return true;
}
-void SetBlocking(int fd, bool is_blocking) {
+bool SetBlocking(int fd, bool is_blocking) {
int flags = fcntl(fd, F_GETFL, 0);
flags = (is_blocking) ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
- PERFETTO_CHECK(fcntl(fd, F_SETFL, flags) == 0);
+ return fcntl(fd, F_SETFL, flags) == 0;
}
-// For further documentation of these constants see the kernel source:
-// linux/include/linux/ring_buffer.h
-// Some information about the values of these constants are exposed to user
-// space at: /sys/kernel/debug/tracing/events/header_event
-constexpr uint32_t kTypeDataTypeLengthMax = 28;
-constexpr uint32_t kTypePadding = 29;
-constexpr uint32_t kTypeTimeExtend = 30;
-constexpr uint32_t kTypeTimeStamp = 31;
+base::Optional<PageHeader> ParsePageHeader(const uint8_t** ptr,
+ uint16_t page_header_size_len) {
+ const uint8_t* end_of_page = *ptr + base::kPageSize;
+ PageHeader page_header;
+ if (!CpuReader::ReadAndAdvance<uint64_t>(ptr, end_of_page,
+ &page_header.timestamp))
+ return base::nullopt;
-struct PageHeader {
- uint64_t timestamp;
- uint64_t size;
- uint64_t overwrite;
-};
+ uint32_t overwrite_and_size;
-struct EventHeader {
- uint32_t type_or_length : 5;
- uint32_t time_delta : 27;
-};
+ // On little endian, we can just read a uint32_t and reject the rest of the
+ // number later.
+ if (!CpuReader::ReadAndAdvance<uint32_t>(
+ ptr, end_of_page, base::AssumeLittleEndian(&overwrite_and_size)))
+ return base::nullopt;
-struct TimeStamp {
- uint64_t tv_nsec;
- uint64_t tv_sec;
-};
+ page_header.size = (overwrite_and_size & 0x000000000000ffffull) >> 0;
+ page_header.overwrite = (overwrite_and_size & 0x00000000ff000000ull) >> 24;
+ PERFETTO_DCHECK(page_header.size <= base::kPageSize);
+
+ // Reject rest of the number, if applicable. On 32-bit, size_bytes - 4 will
+ // evaluate to 0 and this will be a no-op. On 64-bit, this will advance by 4
+ // bytes.
+ PERFETTO_DCHECK(page_header_size_len >= 4);
+ *ptr += page_header_size_len - 4;
+
+ return base::make_optional(page_header);
+}
} // namespace
@@ -129,7 +161,7 @@
// Make reads from the raw pipe blocking so that splice() can sleep.
PERFETTO_CHECK(trace_fd_);
- SetBlocking(*trace_fd_, true);
+ PERFETTO_CHECK(SetBlocking(*trace_fd_, true));
// We need a non-default SIGPIPE handler to make it so that the blocking
// splice() is woken up when the ~CpuReader() dtor destroys the pipes.
@@ -162,10 +194,14 @@
// and only then close the staging pipe.
cmd_ = ThreadCtl::kExit;
trace_fd_.reset();
- pthread_kill(worker_thread_.native_handle(), SIGPIPE);
+ InterruptWorkerThreadWithSignal();
worker_thread_.join();
}
+void CpuReader::InterruptWorkerThreadWithSignal() {
+ pthread_kill(worker_thread_.native_handle(), SIGPIPE);
+}
+
// static
void CpuReader::RunWorkerThread(size_t cpu,
int trace_fd,
@@ -244,8 +280,11 @@
#endif
}
-bool CpuReader::Drain(const std::set<FtraceDataSource*>& data_sources) {
+// Invoked on the main thread by FtraceController, |drain_rate_ms| after the
+// first CPU wakes up from the blocking read()/splice().
+void CpuReader::Drain(const std::set<FtraceDataSource*>& data_sources) {
PERFETTO_DCHECK_THREAD(thread_checker_);
+ PERFETTO_METATRACE("Drain(" + std::to_string(cpu_) + ")", kMainThread);
for (;;) {
uint8_t* buffer = GetBuffer();
long bytes =
@@ -271,8 +310,6 @@
bundle->set_overwrite_count(metadata->overwrite_count);
}
}
-
- return true;
}
uint8_t* CpuReader::GetBuffer() {
@@ -298,36 +335,18 @@
const uint8_t* const start_of_page = ptr;
const uint8_t* const end_of_page = ptr + base::kPageSize;
- PageHeader page_header;
- if (!ReadAndAdvance<uint64_t>(&ptr, end_of_page, &page_header.timestamp))
+ auto page_header = ParsePageHeader(&ptr, table->page_header_size_len());
+ if (!page_header.has_value())
return 0;
- // TODO(fmayer): Do kernel deepdive to double check this.
- uint16_t size_bytes = table->ftrace_page_header_spec().size.size;
- PERFETTO_CHECK(size_bytes >= 4);
- uint32_t overwrite_and_size;
- // On little endian, we can just read a uint32_t and reject the rest of the
- // number later.
- if (!ReadAndAdvance<uint32_t>(&ptr, end_of_page,
- base::AssumeLittleEndian(&overwrite_and_size)))
- return 0;
+ // ParsePageHeader advances |ptr| to point past the end of the header.
- page_header.size = (overwrite_and_size & 0x000000000000ffffull) >> 0;
- page_header.overwrite = (overwrite_and_size & 0x00000000ff000000ull) >> 24;
- metadata->overwrite_count = static_cast<uint32_t>(page_header.overwrite);
-
- PERFETTO_DCHECK(page_header.size <= base::kPageSize);
-
- // Reject rest of the number, if applicable. On 32-bit, size_bytes - 4 will
- // evaluate to 0 and this will be a no-op. On 64-bit, this will advance by 4
- // bytes.
- ptr += size_bytes - 4;
-
- const uint8_t* const end = ptr + page_header.size;
+ metadata->overwrite_count = static_cast<uint32_t>(page_header->overwrite);
+ const uint8_t* const end = ptr + page_header->size;
if (end > end_of_page)
return 0;
- uint64_t timestamp = page_header.timestamp;
+ uint64_t timestamp = page_header->timestamp;
while (ptr < end) {
EventHeader event_header;
@@ -371,10 +390,10 @@
// Data record:
default: {
PERFETTO_CHECK(event_header.type_or_length <= kTypeDataTypeLengthMax);
- // type_or_length is <=28 so it represents the length of a data record.
- // if == 0, this is an extended record and the size of the record is
- // stored in the first uint32_t word in the payload.
- // See Kernel's include/linux/ring_buffer.h
+ // type_or_length is <=28 so it represents the length of a data
+ // record. if == 0, this is an extended record and the size of the
+ // record is stored in the first uint32_t word in the payload. See
+ // Kernel's include/linux/ring_buffer.h
uint32_t event_size;
if (event_header.type_or_length == 0) {
if (!ReadAndAdvance<uint32_t>(&ptr, end, &event_size))
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index e1ff39b..3d3112f 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -67,7 +67,8 @@
// Drains all available data from the staging pipe into the buffer of the
// passed data sources.
// Should be called in response to the |on_data_available| callback.
- bool Drain(const std::set<FtraceDataSource*>&);
+ void Drain(const std::set<FtraceDataSource*>&);
+ void InterruptWorkerThreadWithSignal();
template <typename T>
static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) {
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 707482f..189066f 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -46,16 +46,6 @@
namespace perfetto {
namespace {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-constexpr const char* kTracingPaths[] = {
- "/sys/kernel/tracing/", "/sys/kernel/debug/tracing/", nullptr,
-};
-#else
-constexpr const char* kTracingPaths[] = {
- "/sys/kernel/debug/tracing/", nullptr,
-};
-#endif
-
constexpr int kDefaultDrainPeriodMs = 100;
constexpr int kMinDrainPeriodMs = 1;
constexpr int kMaxDrainPeriodMs = 1000 * 60;
@@ -86,6 +76,14 @@
} // namespace
+const char* const FtraceController::kTracingPaths[] = {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ "/sys/kernel/tracing/", "/sys/kernel/debug/tracing/", nullptr,
+#else
+ "/sys/kernel/debug/tracing/", nullptr,
+#endif
+};
+
// Method of last resort to reset ftrace state.
// We don't know what state the rest of the system and process is so as far
// as possible avoid allocations.
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index 15d7dde..b1845ce 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -49,6 +49,8 @@
// Utility class for controlling ftrace.
class FtraceController {
public:
+ static const char* const kTracingPaths[];
+
class Observer {
public:
virtual ~Observer();
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 7e71737..9d8a53f 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -437,47 +437,6 @@
data_source.reset();
}
-// TODO(b/73452932): Fix and reenable this test.
-TEST(FtraceControllerTest, DISABLED_DrainPeriodRespected) {
- auto controller =
- CreateTestController(false /* nice runner */, false /* nice procfs */);
-
- // For this test we don't care about calls to WriteToFile/ClearFile.
- EXPECT_CALL(*controller->procfs(), WriteToFile(_, _)).Times(AnyNumber());
- EXPECT_CALL(*controller->procfs(), ClearFile(_)).Times(AnyNumber());
-
- FtraceConfig config = CreateFtraceConfig({"group/foo"});
- auto data_source = controller->AddFakeDataSource(config);
- ASSERT_TRUE(controller->StartDataSource(data_source.get()));
-
- // Test several cycles of a worker producing data and make sure the drain
- // delay is consistent with the drain period.
- const int kCycles = 50;
- EXPECT_CALL(*controller->runner(),
- PostDelayedTask(_, controller->drain_period_ms()))
- .Times(kCycles);
- EXPECT_CALL(*controller, OnDrainCpuForTesting(_)).Times(kCycles);
- EXPECT_CALL(*controller->runner(), PostTask(_)).Times(kCycles);
-
- // Simulate a worker thread continually reporting pages of available data.
- auto on_data_available = controller->GetDataAvailableCallback(0u);
- std::thread worker([on_data_available] {
- for (int i = 0; i < kCycles; i++)
- on_data_available();
- });
-
- for (int i = 0; i < kCycles; i++) {
- controller->WaitForData(0u);
- // Run two tasks: one to drain each CPU and another to unblock the worker.
- controller->runner()->RunLastTask();
- controller->runner()->RunLastTask();
- controller->now_ms += controller->drain_period_ms();
- }
-
- worker.join();
- data_source.reset();
-}
-
TEST(FtraceControllerTest, BackToBackEnableDisable) {
auto controller =
CreateTestController(false /* nice runner */, false /* nice procfs */);
diff --git a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
index 5667439..b63bd98 100644
--- a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
@@ -32,7 +32,13 @@
namespace perfetto {
namespace {
-constexpr char kTracingPath[] = "/sys/kernel/debug/tracing/";
+std::string GetFtracePath() {
+ size_t i = 0;
+ while (!FtraceProcfs::Create(FtraceController::kTracingPaths[i])) {
+ i++;
+ }
+ return std::string(FtraceController::kTracingPaths[i]);
+}
void ResetFtrace(FtraceProcfs* ftrace) {
ftrace->DisableAllEvents();
@@ -42,7 +48,7 @@
std::string ReadFile(const std::string& name) {
std::string result;
- PERFETTO_CHECK(base::ReadFile(kTracingPath + name, &result));
+ PERFETTO_CHECK(base::ReadFile(GetFtracePath() + name, &result));
return result;
}
@@ -63,7 +69,7 @@
#define MAYBE_CreateWithGoodPath DISABLED_CreateWithGoodPath
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_CreateWithGoodPath) {
- EXPECT_TRUE(FtraceProcfs::Create(kTracingPath));
+ EXPECT_TRUE(FtraceProcfs::Create(GetFtracePath()));
}
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
@@ -72,7 +78,7 @@
#define MAYBE_CreateWithBadPath DISABLED_CreateWithBadath
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_CreateWithBadPath) {
- EXPECT_FALSE(FtraceProcfs::Create(kTracingPath + std::string("bad_path")));
+ EXPECT_FALSE(FtraceProcfs::Create(GetFtracePath() + std::string("bad_path")));
}
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
@@ -81,7 +87,7 @@
#define MAYBE_ClearTrace DISABLED_ClearTrace
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_ClearTrace) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
ResetFtrace(&ftrace);
ftrace.WriteTraceMarker("Hello, World!");
ftrace.ClearTrace();
@@ -94,7 +100,7 @@
#define MAYBE_TraceMarker DISABLED_TraceMarker
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_TraceMarker) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
ResetFtrace(&ftrace);
ftrace.WriteTraceMarker("Hello, World!");
EXPECT_THAT(GetTraceOutput(), HasSubstr("Hello, World!"));
@@ -106,7 +112,7 @@
#define MAYBE_EnableDisableEvent DISABLED_EnableDisableEvent
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_EnableDisableEvent) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
ResetFtrace(&ftrace);
ftrace.EnableEvent("sched", "sched_switch");
sleep(1);
@@ -124,7 +130,7 @@
#define MAYBE_EnableDisableTracing DISABLED_EnableDisableTracing
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_EnableDisableTracing) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
ResetFtrace(&ftrace);
EXPECT_TRUE(ftrace.IsTracingEnabled());
ftrace.WriteTraceMarker("Before");
@@ -145,7 +151,7 @@
#define MAYBE_ReadFormatFile DISABLED_ReadFormatFile
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_ReadFormatFile) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
std::string format = ftrace.ReadEventFormat("ftrace", "print");
EXPECT_THAT(format, HasSubstr("name: print"));
EXPECT_THAT(format, HasSubstr("field:char buf"));
@@ -157,7 +163,7 @@
#define MAYBE_CanOpenTracePipeRaw DISABLED_CanOpenTracePipeRaw
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_CanOpenTracePipeRaw) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
EXPECT_TRUE(ftrace.OpenPipeForCpu(0));
}
@@ -167,7 +173,7 @@
#define MAYBE_Clock DISABLED_Clock
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_Clock) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
std::set<std::string> clocks = ftrace.AvailableClocks();
EXPECT_THAT(clocks, Contains("local"));
EXPECT_THAT(clocks, Contains("global"));
@@ -184,7 +190,7 @@
#define MAYBE_CanSetBufferSize DISABLED_CanSetBufferSize
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_CanSetBufferSize) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
EXPECT_TRUE(ftrace.SetCpuBufferSizeInPages(4ul));
EXPECT_EQ(ReadFile("buffer_size_kb"), "16\n"); // (4096 * 4) / 1024
EXPECT_TRUE(ftrace.SetCpuBufferSizeInPages(5ul));
@@ -197,7 +203,7 @@
#define MAYBE_FtraceControllerHardReset DISABLED_FtraceControllerHardReset
#endif
TEST(FtraceProcfsIntegrationTest, MAYBE_FtraceControllerHardReset) {
- FtraceProcfs ftrace(kTracingPath);
+ FtraceProcfs ftrace(GetFtracePath());
ResetFtrace(&ftrace);
ftrace.SetCpuBufferSizeInPages(4ul);
diff --git a/src/traced/probes/ftrace/proto_translation_table.h b/src/traced/probes/ftrace/proto_translation_table.h
index 592629f..fe4abde 100644
--- a/src/traced/probes/ftrace/proto_translation_table.h
+++ b/src/traced/probes/ftrace/proto_translation_table.h
@@ -130,6 +130,14 @@
return ftrace_page_header_spec_;
}
+ // Returns the size in bytes of the "size" field in the ftrace header. This
+ // usually matches sizeof(void*) in the kernel (which can be != sizeof(void*)
+ // of user space on 32bit-user + 64-bit-kernel configurations).
+ inline uint16_t page_header_size_len() const {
+ // TODO(fmayer): Do kernel deepdive to double check this.
+ return ftrace_page_header_spec_.size.size;
+ }
+
// Retrieves the ftrace event from the proto translation
// table. If it does not exist, reads the format file and creates a
// new event with the proto id set to generic. Virtual for testing.
diff --git a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
index c5497b9..833c60b 100644
--- a/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl_unittest.cc
@@ -37,6 +37,8 @@
public:
void RegisterDataSource(const DataSourceDescriptor&) override {}
void UnregisterDataSource(const std::string&) override {}
+ void RegisterTraceWriter(uint32_t, uint32_t) override {}
+ void UnregisterTraceWriter(uint32_t) override {}
void NotifyFlushComplete(FlushRequestID) override {}
void NotifyDataSourceStopped(DataSourceInstanceID) override {}
SharedMemory* shared_memory() const override { return nullptr; }
diff --git a/src/tracing/core/trace_writer_impl_unittest.cc b/src/tracing/core/trace_writer_impl_unittest.cc
index e80730a..79e79be 100644
--- a/src/tracing/core/trace_writer_impl_unittest.cc
+++ b/src/tracing/core/trace_writer_impl_unittest.cc
@@ -34,6 +34,8 @@
class FakeProducerEndpoint : public TracingService::ProducerEndpoint {
void RegisterDataSource(const DataSourceDescriptor&) override {}
void UnregisterDataSource(const std::string&) override {}
+ void RegisterTraceWriter(uint32_t, uint32_t) override {}
+ void UnregisterTraceWriter(uint32_t) override {}
void CommitData(const CommitDataRequest&, CommitDataCallback) override {}
void NotifyFlushComplete(FlushRequestID) override {}
void NotifyDataSourceStopped(DataSourceInstanceID) override {}
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 3a47e4d..015c52b 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -1492,6 +1492,20 @@
service_->UnregisterDataSource(id_, name);
}
+void TracingServiceImpl::ProducerEndpointImpl::RegisterTraceWriter(
+ uint32_t /*writer_id*/,
+ uint32_t /*target_buffer*/) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ // TODO(eseckler): Store association into a map. Make sure to verify that the
+ // target buffer is "allowed" when using this association later.
+}
+
+void TracingServiceImpl::ProducerEndpointImpl::UnregisterTraceWriter(
+ uint32_t /*writer_id*/) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ // TODO(eseckler): Remove association from the map.
+}
+
void TracingServiceImpl::ProducerEndpointImpl::CommitData(
const CommitDataRequest& req_untrusted,
CommitDataCallback callback) {
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 862cecd..d1a7e35 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -73,6 +73,9 @@
// TracingService::ProducerEndpoint implementation.
void RegisterDataSource(const DataSourceDescriptor&) override;
void UnregisterDataSource(const std::string& name) override;
+ void RegisterTraceWriter(uint32_t writer_id,
+ uint32_t target_buffer) override;
+ void UnregisterTraceWriter(uint32_t writer_id) override;
void CommitData(const CommitDataRequest&, CommitDataCallback) override;
void SetSharedMemory(std::unique_ptr<SharedMemory>);
std::unique_ptr<TraceWriter> CreateTraceWriter(BufferID) override;
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 420f2fa..3bd8fc6 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -207,6 +207,34 @@
req, ipc::Deferred<protos::UnregisterDataSourceResponse>());
}
+void ProducerIPCClientImpl::RegisterTraceWriter(uint32_t writer_id,
+ uint32_t target_buffer) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ if (!connected_) {
+ PERFETTO_DLOG(
+ "Cannot RegisterTraceWriter(), not connected to tracing service");
+ return;
+ }
+ protos::RegisterTraceWriterRequest req;
+ req.set_trace_writer_id(writer_id);
+ req.set_target_buffer(target_buffer);
+ producer_port_.RegisterTraceWriter(
+ req, ipc::Deferred<protos::RegisterTraceWriterResponse>());
+}
+
+void ProducerIPCClientImpl::UnregisterTraceWriter(uint32_t writer_id) {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ if (!connected_) {
+ PERFETTO_DLOG(
+ "Cannot UnregisterTraceWriter(), not connected to tracing service");
+ return;
+ }
+ protos::UnregisterTraceWriterRequest req;
+ req.set_trace_writer_id(writer_id);
+ producer_port_.UnregisterTraceWriter(
+ req, ipc::Deferred<protos::UnregisterTraceWriterResponse>());
+}
+
void ProducerIPCClientImpl::CommitData(const CommitDataRequest& req,
CommitDataCallback callback) {
PERFETTO_DCHECK_THREAD(thread_checker_);
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.h b/src/tracing/ipc/producer/producer_ipc_client_impl.h
index ba749ad..23a1d31 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -63,6 +63,8 @@
// tracing library, which know nothing about the IPC transport.
void RegisterDataSource(const DataSourceDescriptor&) override;
void UnregisterDataSource(const std::string& name) override;
+ void RegisterTraceWriter(uint32_t writer_id, uint32_t target_buffer) override;
+ void UnregisterTraceWriter(uint32_t writer_id) override;
void CommitData(const CommitDataRequest&, CommitDataCallback) override;
void NotifyDataSourceStopped(DataSourceInstanceID) override;
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index 95dcf67..ae28ef7 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -90,7 +90,9 @@
if (!producer) {
PERFETTO_DLOG(
"Producer invoked RegisterDataSource() before InitializeConnection()");
- return response.Reject();
+ if (response.IsBound())
+ response.Reject();
+ return;
}
DataSourceDescriptor dsd;
@@ -98,8 +100,10 @@
GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(dsd);
// RegisterDataSource doesn't expect any meaningful response.
- response.Resolve(
- ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create());
+ if (response.IsBound()) {
+ response.Resolve(
+ ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create());
+ }
}
// Called by the IPC layer.
@@ -122,13 +126,60 @@
PERFETTO_DLOG(
"Producer invoked UnregisterDataSource() before "
"InitializeConnection()");
- return response.Reject();
+ if (response.IsBound())
+ response.Reject();
+ return;
}
producer->service_endpoint->UnregisterDataSource(req.data_source_name());
// UnregisterDataSource doesn't expect any meaningful response.
- response.Resolve(
- ipc::AsyncResult<protos::UnregisterDataSourceResponse>::Create());
+ if (response.IsBound()) {
+ response.Resolve(
+ ipc::AsyncResult<protos::UnregisterDataSourceResponse>::Create());
+ }
+}
+
+void ProducerIPCService::RegisterTraceWriter(
+ const protos::RegisterTraceWriterRequest& req,
+ DeferredRegisterTraceWriterResponse response) {
+ RemoteProducer* producer = GetProducerForCurrentRequest();
+ if (!producer) {
+ PERFETTO_DLOG(
+ "Producer invoked RegisterTraceWriter() before "
+ "InitializeConnection()");
+ if (response.IsBound())
+ response.Reject();
+ return;
+ }
+ producer->service_endpoint->RegisterTraceWriter(req.trace_writer_id(),
+ req.target_buffer());
+
+ // RegisterTraceWriter doesn't expect any meaningful response.
+ if (response.IsBound()) {
+ response.Resolve(
+ ipc::AsyncResult<protos::RegisterTraceWriterResponse>::Create());
+ }
+}
+
+void ProducerIPCService::UnregisterTraceWriter(
+ const protos::UnregisterTraceWriterRequest& req,
+ DeferredUnregisterTraceWriterResponse response) {
+ RemoteProducer* producer = GetProducerForCurrentRequest();
+ if (!producer) {
+ PERFETTO_DLOG(
+ "Producer invoked UnregisterTraceWriter() before "
+ "InitializeConnection()");
+ if (response.IsBound())
+ response.Reject();
+ return;
+ }
+ producer->service_endpoint->UnregisterTraceWriter(req.trace_writer_id());
+
+ // UnregisterTraceWriter doesn't expect any meaningful response.
+ if (response.IsBound()) {
+ response.Resolve(
+ ipc::AsyncResult<protos::UnregisterTraceWriterResponse>::Create());
+ }
}
void ProducerIPCService::CommitData(const protos::CommitDataRequest& proto_req,
@@ -137,6 +188,8 @@
if (!producer) {
PERFETTO_DLOG(
"Producer invoked CommitData() before InitializeConnection()");
+ if (resp.IsBound())
+ resp.Reject();
return;
}
CommitDataRequest req;
@@ -167,6 +220,8 @@
PERFETTO_DLOG(
"Producer invoked NotifyDataSourceStopped() before "
"InitializeConnection()");
+ if (response.IsBound())
+ response.Reject();
return;
}
producer->service_endpoint->NotifyDataSourceStopped(request.data_source_id());
diff --git a/src/tracing/ipc/service/producer_ipc_service.h b/src/tracing/ipc/service/producer_ipc_service.h
index caa1978..d714f3f 100644
--- a/src/tracing/ipc/service/producer_ipc_service.h
+++ b/src/tracing/ipc/service/producer_ipc_service.h
@@ -49,6 +49,10 @@
DeferredRegisterDataSourceResponse) override;
void UnregisterDataSource(const protos::UnregisterDataSourceRequest&,
DeferredUnregisterDataSourceResponse) override;
+ void RegisterTraceWriter(const protos::RegisterTraceWriterRequest&,
+ DeferredRegisterTraceWriterResponse) override;
+ void UnregisterTraceWriter(const protos::UnregisterTraceWriterRequest&,
+ DeferredUnregisterTraceWriterResponse) override;
void CommitData(const protos::CommitDataRequest&,
DeferredCommitDataResponse) override;
void NotifyDataSourceStopped(
diff --git a/src/tracing/test/mock_producer.cc b/src/tracing/test/mock_producer.cc
index 1294c1b..e7dfa7d 100644
--- a/src/tracing/test/mock_producer.cc
+++ b/src/tracing/test/mock_producer.cc
@@ -67,6 +67,15 @@
service_endpoint_->UnregisterDataSource(name);
}
+void MockProducer::RegisterTraceWriter(uint32_t writer_id,
+ uint32_t target_buffer) {
+ service_endpoint_->RegisterTraceWriter(writer_id, target_buffer);
+}
+
+void MockProducer::UnregisterTraceWriter(uint32_t writer_id) {
+ service_endpoint_->UnregisterTraceWriter(writer_id);
+}
+
void MockProducer::WaitForTracingSetup() {
static int i = 0;
auto checkpoint_name =
diff --git a/src/tracing/test/mock_producer.h b/src/tracing/test/mock_producer.h
index be13084..6f5ab0d 100644
--- a/src/tracing/test/mock_producer.h
+++ b/src/tracing/test/mock_producer.h
@@ -49,6 +49,8 @@
size_t shared_memory_size_hint_bytes = 0);
void RegisterDataSource(const std::string& name, bool ack_stop = false);
void UnregisterDataSource(const std::string& name);
+ void RegisterTraceWriter(uint32_t writer_id, uint32_t target_buffer);
+ void UnregisterTraceWriter(uint32_t writer_id);
void WaitForTracingSetup();
void WaitForDataSourceSetup(const std::string& name);
void WaitForDataSourceStart(const std::string& name);
diff --git a/tools/heap_profile b/tools/heap_profile
new file mode 100755
index 0000000..5744477
--- /dev/null
+++ b/tools/heap_profile
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2017 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.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import atexit
+import hashlib
+import os
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import urllib
+
+TRACE_TO_TEXT_SHAS = {
+ 'linux': 'c704cf765b9c5445c16a8e7ec5757cad364a8d92',
+ 'mac': 'aed4ad02da526a3f1e4f9df47d4989ae9305b30e',
+}
+TRACE_TO_TEXT_PATH = tempfile.gettempdir()
+TRACE_TO_TEXT_BASE_URL = (
+ 'https://storage.googleapis.com/perfetto/')
+
+def check_hash(file_name, sha_value):
+ with open(file_name, 'rb') as fd:
+ # TODO(fmayer): Chunking.
+ file_hash = hashlib.sha1(fd.read()).hexdigest()
+ return file_hash == sha_value
+
+
+def load_trace_to_text(platform):
+ sha_value = TRACE_TO_TEXT_SHAS[platform]
+ file_name = 'trace_to_text-' + platform + '-' + sha_value
+ local_file = os.path.join(TRACE_TO_TEXT_PATH, file_name)
+
+ if os.path.exists(local_file):
+ if not check_hash(local_file, sha_value):
+ os.remove(local_file)
+ else:
+ return local_file
+
+ url = TRACE_TO_TEXT_BASE_URL + file_name
+ urllib.urlretrieve(url, local_file)
+ if not check_hash(local_file, sha_value):
+ os.remove(local_file)
+ raise ValueError("Invalid signature.")
+ os.chmod(local_file, 0o755)
+ return local_file
+
+
+NULL = open('/dev/null', 'r')
+
+CFG_IDENT = ' '
+CFG='''buffers {{
+ size_kb: 32768
+}}
+
+data_sources {{
+ config {{
+ name: "android.heapprofd"
+ heapprofd_config {{
+
+ all: {all}
+ sampling_interval_bytes: {interval}
+{target_cfg}
+ continuous_dump_config {{
+ dump_phase_ms: 0
+ dump_interval_ms: 1000
+ }}
+ }}
+ }}
+}}
+
+duration_ms: {duration}
+'''
+
+PERFETTO_CMD=('CFG=\'{}\'; echo ${{CFG}} | '
+ 'perfetto -t -c - -o /data/misc/perfetto-traces/profile -b')
+IS_INTERRUPTED = False
+def sigint_handler(sig, frame):
+ global IS_INTERRUPTED
+ IS_INTERRUPTED = True
+
+
+def on_exit(enforcing):
+ subprocess.check_output(['adb', 'shell', 'su root stop heapprofd'])
+ subprocess.check_output(
+ ['adb', 'shell', 'su root setenforce %s' % enforcing])
+
+
+def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--interval", help="Sampling interval. "
+ "Default 128000 (128kB)", type=int, default=128000)
+ parser.add_argument("-d", "--duration", help="Duration of profile (ms). "
+ "Default 1 minute", type=int, default=60000)
+ parser.add_argument("-a", "--all", help="Profile the whole system",
+ action='store_true')
+ parser.add_argument("-p", "--pid", help="PIDs to profile", nargs='+',
+ type=int)
+ parser.add_argument("-n", "--name", help="Process names to profile",
+ nargs='+')
+ parser.add_argument("-t", "--trace-to-text-binary",
+ help="Path to local trace to text. For debugging.")
+
+ args = parser.parse_args()
+
+ fail = False
+ if args.all is None and args.pid is None and args.name is None:
+ print("FATAL: Neither --all nor PID nor NAME given.", file=sys.stderr)
+ fail = True
+ if args.duration is None:
+ print("FATAL: No duration given.", file=sys.stderr)
+ fail = True
+ if args.interval is None:
+ print("FATAL: No interval given.", file=sys.stderr)
+ fail = True
+ if fail:
+ parser.print_help()
+ return 1
+ target_cfg = ""
+ if args.pid:
+ for pid in args.pid:
+ target_cfg += '{}pid: {}\n'.format(CFG_IDENT, pid)
+ if args.name:
+ for name in args.name:
+ target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_IDENT, name)
+
+ trace_to_text_binary = args.trace_to_text_binary
+ if trace_to_text_binary is None:
+ platform = None
+ if sys.platform.startswith('linux'):
+ platform = 'linux'
+ elif sys.platform.startswith('darwin'):
+ platform = 'mac'
+ else:
+ print("Invalid platform: {}".format(sys.platform), file=sys.stderr)
+ return 1
+
+ trace_to_text_binary = load_trace_to_text(platform)
+
+ cfg = CFG.format(all=str(args.all == True).lower(), interval=args.interval,
+ duration=args.duration, target_cfg=target_cfg)
+
+ enforcing = subprocess.check_output(['adb', 'shell', 'getenforce'])
+ atexit.register(on_exit, enforcing)
+
+ subprocess.check_call(['adb', 'shell', 'su root setenforce 0'])
+ subprocess.check_call(['adb', 'shell', 'su root start heapprofd'])
+
+ perfetto_pid = subprocess.check_output(
+ ['adb', 'exec-out', PERFETTO_CMD.format(cfg)]).strip()
+
+ old_handler = signal.signal(signal.SIGINT, sigint_handler)
+ print("Profiling active. Press Ctrl+C to terminate.")
+ exists = True
+ while exists and not IS_INTERRUPTED:
+ exists = subprocess.call(
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+ time.sleep(1)
+ signal.signal(signal.SIGINT, old_handler)
+ if IS_INTERRUPTED:
+ # Not check_call because it could have existed in the meantime.
+ subprocess.call(['adb', 'shell', 'kill', '-INT', perfetto_pid])
+
+ subprocess.check_call(['adb', 'pull', '/data/misc/perfetto-traces/profile',
+ '/tmp/profile'], stdout=NULL)
+ trace_to_text_output = subprocess.check_output(
+ [trace_to_text_binary, 'profile', '/tmp/profile'],
+ stderr=NULL)
+ profile_path = None
+ for word in trace_to_text_output.split():
+ if 'heap_profile-' in word:
+ profile_path = word
+ if profile_path is None:
+ print("Could not find trace_to_text output path.", file=sys.stderr)
+ return 1
+
+ profile_files = os.listdir(profile_path)
+ if not profile_files:
+ print("No profiles generated", file=sys.stderr)
+ return 1
+
+ subprocess.check_call(['gzip'] + [os.path.join(profile_path, x) for x in
+ os.listdir(profile_path)])
+ print("Wrote profiles to {}".format(profile_path))
+ print("These can be viewed using pprof. Googlers: head to pprof/ and "
+ "upload them.")
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tools/profile b/tools/profile
deleted file mode 100755
index 78f0b19..0000000
--- a/tools/profile
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/bash
-
-# 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.
-
-set -euo pipefail
-
-TRACE_TO_TEXT=""
-
-while getopts ":i:d:p:n:t:" opt; do
- case $opt in
- i) INTERVAL=$OPTARG;;
- d) DURATION=$OPTARG;;
- p) PID=$OPTARG;;
- n) NAME=$OPTARG;;
- t) TRACE_TO_TEXT=$OPTARG;;
- ?) echo "Usage: $0 -i SAMPLING_INTERVAL -d DURATION [-p PID]"\
- "[-n PROCESS_NAME] [-t TRACE_TO_TEXT_PATH]"
- exit;;
- esac
-done
-
-if [[ -z "${TRACE_TO_TEXT}" ]]; then
- SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
- TRACE_TO_TEXT="$SCRIPTDIR/trace_to_text"
-fi
-
-ENFORCE_MODE=$(adb shell getenforce)
-function finish {
- adb shell su root stop heapprofd
- adb shell su root setenforce $ENFORCE_MODE
-}
-trap finish EXIT
-
-adb shell su root setenforce 0
-adb shell su root start heapprofd
-
-CFG='
-buffers {
- size_kb: 32768
-}
-
-data_sources {
- config {
- name: "android.heapprofd"
- heapprofd_config {
- sampling_interval_bytes: '${INTERVAL}'
- '${PID+"pid: $PID"}'
- '${NAME+"process_cmdline: \"${NAME}\""}'
- continuous_dump_config {
- dump_phase_ms: 10000
- dump_interval_ms: 1000
- }
- }
- }
-}
-
-duration_ms: '${DURATION}
-
-PERFETTO_PID=$(adb exec-out 'CFG='"'${CFG}'
-"'echo ${CFG} | perfetto -t -c - -o /data/misc/perfetto-traces/profile -b'\
- | grep -o '^pid: [0-9]*$' | awk '{ print $2 }')
-
-# TODO(fmayer): Allow to interrupt.
-adb exec-out "while [[ -d /proc/$PERFETTO_PID ]]; do sleep 1; done" | cat
-adb pull /data/misc/perfetto-traces/profile /tmp/profile > /dev/null
-
-OUTDIR=$($TRACE_TO_TEXT profile /tmp/profile | grep -o "[^ ]*heap_profile[^ ]*")
-gzip $OUTDIR/*.pb
-
-echo "Wrote profiles to $OUTDIR"