Merge "Explicitly intern the empty string as 0."
diff --git a/Android.bp b/Android.bp
index b740f3d..cbbed46 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,12 +20,12 @@
"src/trace_processor/metrics/android/android_mem.sql",
"src/trace_processor/metrics/android/android_mem_lmk.sql",
],
- cmd: "$(location tools/gen_merged_sql_metrics) --cpp_out=$(out) $(in)",
+ cmd: "$(location tools/gen_merged_sql_metrics.py) --cpp_out=$(out) $(in)",
out: [
"src/trace_processor/metrics/sql_metrics.h",
],
tool_files: [
- "tools/gen_merged_sql_metrics",
+ "tools/gen_merged_sql_metrics.py",
],
}
@@ -3215,6 +3215,7 @@
"src/trace_processor/fuchsia_trace_parser.cc",
"src/trace_processor/fuchsia_trace_tokenizer.cc",
"src/trace_processor/fuchsia_trace_utils.cc",
+ "src/trace_processor/heap_profile_tracker.cc",
"src/trace_processor/instants_table.cc",
"src/trace_processor/metrics/metrics.cc",
"src/trace_processor/process_table.cc",
diff --git a/BUILD b/BUILD
index 3c33de3..80a7419 100644
--- a/BUILD
+++ b/BUILD
@@ -27,10 +27,10 @@
"src/trace_processor/metrics/android/android_mem.sql",
"src/trace_processor/metrics/android/android_mem_lmk.sql",
],
- cmd = "$(location gen_merged_sql_metrics_py) --cpp_out=$@ $SRCS",
outs = [
"src/trace_processor/metrics/sql_metrics.h",
],
+ cmd = "$(location gen_merged_sql_metrics_py) --cpp_out=$@ $(SRCS)",
tools = [
"gen_merged_sql_metrics_py",
],
@@ -179,6 +179,8 @@
"src/trace_processor/fuchsia_trace_tokenizer.h",
"src/trace_processor/fuchsia_trace_utils.cc",
"src/trace_processor/fuchsia_trace_utils.h",
+ "src/trace_processor/heap_profile_tracker.cc",
+ "src/trace_processor/heap_profile_tracker.h",
"src/trace_processor/instants_table.cc",
"src/trace_processor/instants_table.h",
"src/trace_processor/json_trace_parser.cc",
@@ -189,6 +191,7 @@
"src/trace_processor/json_trace_utils.h",
"src/trace_processor/metrics/metrics.cc",
"src/trace_processor/metrics/metrics.h",
+ "src/trace_processor/metrics/sql_metrics.h",
"src/trace_processor/null_term_string_view.h",
"src/trace_processor/process_table.cc",
"src/trace_processor/process_table.h",
@@ -427,6 +430,8 @@
"src/trace_processor/fuchsia_trace_tokenizer.h",
"src/trace_processor/fuchsia_trace_utils.cc",
"src/trace_processor/fuchsia_trace_utils.h",
+ "src/trace_processor/heap_profile_tracker.cc",
+ "src/trace_processor/heap_profile_tracker.h",
"src/trace_processor/instants_table.cc",
"src/trace_processor/instants_table.h",
"src/trace_processor/json_trace_parser.cc",
@@ -437,6 +442,7 @@
"src/trace_processor/json_trace_utils.h",
"src/trace_processor/metrics/metrics.cc",
"src/trace_processor/metrics/metrics.h",
+ "src/trace_processor/metrics/sql_metrics.h",
"src/trace_processor/null_term_string_view.h",
"src/trace_processor/process_table.cc",
"src/trace_processor/process_table.h",
@@ -631,6 +637,8 @@
"src/trace_processor/fuchsia_trace_tokenizer.h",
"src/trace_processor/fuchsia_trace_utils.cc",
"src/trace_processor/fuchsia_trace_utils.h",
+ "src/trace_processor/heap_profile_tracker.cc",
+ "src/trace_processor/heap_profile_tracker.h",
"src/trace_processor/instants_table.cc",
"src/trace_processor/instants_table.h",
"src/trace_processor/json_trace_parser.cc",
@@ -641,6 +649,7 @@
"src/trace_processor/json_trace_utils.h",
"src/trace_processor/metrics/metrics.cc",
"src/trace_processor/metrics/metrics.h",
+ "src/trace_processor/metrics/sql_metrics.h",
"src/trace_processor/null_term_string_view.h",
"src/trace_processor/process_table.cc",
"src/trace_processor/process_table.h",
@@ -763,22 +772,21 @@
)
gensignature(
- name = "trace_processor_shell_sig",
+ name = "trace_processor_sig",
srcs = [
":trace_processor_shell",
- ],
-)
-
-gensignature(
- name = "trace_to_text_sig",
- srcs = [
":trace_to_text",
],
+ tags = [
+ "__TRACE_PROCESSOR_SIG_TAG1",
+ "__TRACE_PROCESSOR_SIG_TAG2",
+ ],
)
py_binary(
name = "gen_merged_sql_metrics_py",
srcs = [
- "tools/gen_merged_sql_metrics"
- ]
+ "tools/gen_merged_sql_metrics.py",
+ ],
+ main = "tools/gen_merged_sql_metrics.py",
)
diff --git a/BUILD.extras b/BUILD.extras
index 22e47a3..cda25fb 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -1,20 +1,19 @@
gensignature(
- name = "trace_processor_shell_sig",
+ name = "trace_processor_sig",
srcs = [
":trace_processor_shell",
- ],
-)
-
-gensignature(
- name = "trace_to_text_sig",
- srcs = [
":trace_to_text",
],
+ tags = [
+ "__TRACE_PROCESSOR_SIG_TAG1",
+ "__TRACE_PROCESSOR_SIG_TAG2",
+ ],
)
py_binary(
name = "gen_merged_sql_metrics_py",
srcs = [
- "tools/gen_merged_sql_metrics"
- ]
+ "tools/gen_merged_sql_metrics.py",
+ ],
+ main = "tools/gen_merged_sql_metrics.py",
)
diff --git a/include/perfetto/tracing/core/shared_memory_arbiter.h b/include/perfetto/tracing/core/shared_memory_arbiter.h
index 5dc4c03..660014c 100644
--- a/include/perfetto/tracing/core/shared_memory_arbiter.h
+++ b/include/perfetto/tracing/core/shared_memory_arbiter.h
@@ -58,12 +58,12 @@
//
// All StartupTraceWriters created by the registry are bound to the arbiter
// and the given target buffer. The writers may not be bound immediately if
- // they are concurrently being written to. The registry will retry on the
- // arbiter's TaskRunner until all writers were bound successfully.
+ // they are concurrently being written to or if this method isn't called on
+ // the arbiter's TaskRunner. The registry will retry on the arbiter's
+ // TaskRunner until all writers were bound successfully.
//
- // Should only be called on the passed TaskRunner's sequence. By calling this
- // method, the registry's ownership is transferred to the arbiter. The arbiter
- // will delete the registry once all writers were bound.
+ // By calling this method, the registry's ownership is transferred to the
+ // arbiter. The arbiter will delete the registry once all writers were bound.
//
// TODO(eseckler): Make target buffer assignment more flexible (i.e. per
// writer). For now, embedders can use multiple registries instead.
diff --git a/include/perfetto/tracing/core/startup_trace_writer_registry.h b/include/perfetto/tracing/core/startup_trace_writer_registry.h
index db19862..df48c5f 100644
--- a/include/perfetto/tracing/core/startup_trace_writer_registry.h
+++ b/include/perfetto/tracing/core/startup_trace_writer_registry.h
@@ -91,8 +91,8 @@
// concurrently being written to. The registry will retry on the passed
// TaskRunner until all writers were bound successfully.
//
- // Calls |on_bound_callback| asynchronously on |trace_writer| once all writers
- // were bound.
+ // Calls |on_bound_callback| asynchronously on the passed TaskRunner once all
+ // writers were bound.
void BindToArbiter(
SharedMemoryArbiterImpl*,
BufferID target_buffer,
diff --git a/protos/perfetto/trace/interned_data/interned_data.proto b/protos/perfetto/trace/interned_data/interned_data.proto
index 266b061..4120e81 100644
--- a/protos/perfetto/trace/interned_data/interned_data.proto
+++ b/protos/perfetto/trace/interned_data/interned_data.proto
@@ -32,7 +32,7 @@
// The writer will emit new entries when it encounters new internable values
// that aren't yet in the index. Data in current and subsequent TracePackets can
// then refer to the entry by its position (interning ID, abbreviated "iid") in
-// its index.
+// its index. An interning ID with value 0 is considered invalid (not set).
//
// Because of the incremental build-up, the interning index will miss data when
// TracePackets are lost, e.g. because a chunk was overridden in the central
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index fc3c02f..2f333e2 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -2338,7 +2338,7 @@
// The writer will emit new entries when it encounters new internable values
// that aren't yet in the index. Data in current and subsequent TracePackets can
// then refer to the entry by its position (interning ID, abbreviated "iid") in
-// its index.
+// its index. An interning ID with value 0 is considered invalid (not set).
//
// Because of the incremental build-up, the interning index will miss data when
// TracePackets are lost, e.g. because a chunk was overridden in the central
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index 93f075f..f7da664 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -56,7 +56,7 @@
base::ScopedFile fd;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
bool is_memfd = false;
- fd.reset(static_cast<int>(syscall(__NR_memfd_create, "heaprofd_ringbuf",
+ fd.reset(static_cast<int>(syscall(__NR_memfd_create, "heapprofd_ringbuf",
MFD_CLOEXEC | MFD_ALLOW_SEALING)));
is_memfd = !!fd;
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 61b462e..a1be89e 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -72,6 +72,8 @@
"fuchsia_trace_tokenizer.h",
"fuchsia_trace_utils.cc",
"fuchsia_trace_utils.h",
+ "heap_profile_tracker.cc",
+ "heap_profile_tracker.h",
"instants_table.cc",
"instants_table.h",
"null_term_string_view.h",
@@ -219,6 +221,7 @@
"event_tracker_unittest.cc",
"filtered_row_index_unittest.cc",
"ftrace_utils_unittest.cc",
+ "heap_profile_tracker_unittest.cc",
"null_term_string_view_unittest.cc",
"process_table_unittest.cc",
"process_tracker_unittest.cc",
diff --git a/src/trace_processor/heap_profile_tracker.cc b/src/trace_processor/heap_profile_tracker.cc
new file mode 100644
index 0000000..9b9e776
--- /dev/null
+++ b/src/trace_processor/heap_profile_tracker.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/heap_profile_tracker.h"
+
+#include "src/trace_processor/trace_processor_context.h"
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+HeapProfileTracker::HeapProfileTracker(TraceProcessorContext* context)
+ : context_(context), empty_(context_->storage->InternString({"", 0})) {}
+
+HeapProfileTracker::~HeapProfileTracker() = default;
+
+void HeapProfileTracker::AddString(ProfileIndex pidx,
+ SourceStringId id,
+ StringId str) {
+ string_map_.emplace(std::make_pair(pidx, id), str);
+}
+
+void HeapProfileTracker::AddMapping(ProfileIndex pidx,
+ SourceMappingId id,
+ const SourceMapping& mapping) {
+ auto opt_name_id = FindString(pidx, mapping.name_id);
+ if (!opt_name_id)
+ return;
+ const StringId name_id = opt_name_id.value();
+
+ auto opt_build_id = FindString(pidx, mapping.build_id);
+ if (!opt_build_id)
+ return;
+ const StringId build_id = opt_build_id.value();
+
+ int64_t cur_row =
+ context_->storage->mutable_heap_profile_mappings()->FindOrInsert(
+ build_id, static_cast<int64_t>(mapping.offset),
+ static_cast<int64_t>(mapping.start),
+ static_cast<int64_t>(mapping.end),
+ static_cast<int64_t>(mapping.load_bias), name_id);
+ mappings_.emplace(std::make_pair(pidx, id), cur_row);
+}
+
+void HeapProfileTracker::AddFrame(ProfileIndex pidx,
+ SourceFrameId id,
+ const SourceFrame& frame) {
+ auto opt_str_id = FindString(pidx, frame.name_id);
+ if (!opt_str_id)
+ return;
+ const StringId& str_id = opt_str_id.value();
+
+ auto mapping_it = mappings_.find({pidx, frame.mapping_id});
+ if (mapping_it == mappings_.end()) {
+ context_->storage->IncrementStats(stats::heapprofd_invalid_mapping_id);
+ PERFETTO_DFATAL("Invalid mapping.");
+ return;
+ }
+ int64_t mapping_row = mapping_it->second;
+
+ int64_t cur_row =
+ context_->storage->mutable_heap_profile_frames()->FindOrInsert(
+ str_id, mapping_row, static_cast<int64_t>(frame.rel_pc));
+ frames_.emplace(std::make_pair(pidx, id), cur_row);
+}
+
+void HeapProfileTracker::AddCallstack(ProfileIndex pidx,
+ SourceCallstackId id,
+ const SourceCallstack& frame_ids) {
+ int64_t parent_id = 0;
+ for (size_t depth = 0; depth < frame_ids.size(); ++depth) {
+ std::vector<uint64_t> frame_subset = frame_ids;
+ frame_subset.resize(depth + 1);
+ auto self_it = callstacks_from_frames_.find({pidx, frame_subset});
+ if (self_it != callstacks_from_frames_.end()) {
+ parent_id = self_it->second;
+ continue;
+ }
+
+ uint64_t frame_id = frame_ids[depth];
+ auto it = frames_.find({pidx, frame_id});
+ if (it == frames_.end()) {
+ context_->storage->IncrementStats(stats::heapprofd_invalid_frame_id);
+ PERFETTO_DFATAL("Unknown frames.");
+ return;
+ }
+ int64_t frame_row = it->second;
+ int64_t self_id =
+ context_->storage->mutable_heap_profile_callsites()->FindOrInsert(
+ static_cast<int64_t>(depth), parent_id, frame_row);
+ parent_id = self_id;
+ }
+ callstacks_.emplace(std::make_pair(pidx, id), parent_id);
+}
+
+void HeapProfileTracker::AddAllocation(ProfileIndex pidx,
+ const SourceAllocation& alloc) {
+ auto it = callstacks_.find({pidx, alloc.callstack_id});
+ if (it == callstacks_.end()) {
+ context_->storage->IncrementStats(stats::heapprofd_invalid_callstack_id);
+ PERFETTO_DFATAL("Unknown callstack %" PRIu64 " : %zu", alloc.callstack_id,
+ callstacks_.size());
+ return;
+ }
+ context_->storage->mutable_heap_profile_allocations()->Insert(
+ static_cast<int64_t>(alloc.timestamp), static_cast<int64_t>(alloc.pid),
+ static_cast<int64_t>(it->second), static_cast<int64_t>(alloc.alloc_count),
+ static_cast<int64_t>(alloc.self_allocated));
+ context_->storage->mutable_heap_profile_allocations()->Insert(
+ static_cast<int64_t>(alloc.timestamp), static_cast<int64_t>(alloc.pid),
+ static_cast<int64_t>(it->second), -static_cast<int64_t>(alloc.free_count),
+ -static_cast<int64_t>(alloc.self_freed));
+}
+
+void HeapProfileTracker::StoreAllocation(ProfileIndex pidx,
+ SourceAllocation alloc) {
+ pending_allocs_.emplace_back(pidx, std::move(alloc));
+}
+
+void HeapProfileTracker::ApplyAllAllocations() {
+ for (const auto& p : pending_allocs_)
+ AddAllocation(p.first, p.second);
+}
+
+int64_t HeapProfileTracker::GetDatabaseFrameIdForTesting(
+ ProfileIndex pidx,
+ SourceFrameId frame_id) {
+ auto it = frames_.find({pidx, frame_id});
+ if (it == frames_.end()) {
+ PERFETTO_DFATAL("Invalid frame.");
+ return -1;
+ }
+ return it->second;
+}
+
+base::Optional<StringId> HeapProfileTracker::FindString(ProfileIndex pidx,
+ SourceStringId id) {
+ base::Optional<StringId> res;
+ if (id == 0) {
+ res = empty_;
+ return res;
+ }
+
+ auto it = string_map_.find({pidx, id});
+ if (it == string_map_.end()) {
+ context_->storage->IncrementStats(stats::heapprofd_invalid_string_id);
+ PERFETTO_DFATAL("Invalid string.");
+ return res;
+ }
+ res = it->second;
+ return res;
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/heap_profile_tracker.h b/src/trace_processor/heap_profile_tracker.h
new file mode 100644
index 0000000..f1ce455
--- /dev/null
+++ b/src/trace_processor/heap_profile_tracker.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
+
+#include <deque>
+
+#include "perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "src/trace_processor/trace_storage.h"
+
+namespace std {
+
+template <>
+struct hash<std::pair<uint64_t, uint64_t>> {
+ using argument_type = std::pair<uint64_t, uint64_t>;
+ using result_type = size_t;
+
+ result_type operator()(const argument_type& p) const {
+ return std::hash<uint64_t>{}(p.first) ^ std::hash<uint64_t>{}(p.second);
+ }
+};
+
+template <>
+struct hash<std::pair<uint64_t, std::vector<uint64_t>>> {
+ using argument_type = std::pair<uint64_t, std::vector<uint64_t>>;
+ using result_type = size_t;
+
+ result_type operator()(const argument_type& p) const {
+ auto h = std::hash<uint64_t>{}(p.first);
+ for (auto v : p.second)
+ h = h ^ std::hash<uint64_t>{}(v);
+ return h;
+ }
+};
+
+} // namespace std
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class HeapProfileTracker {
+ public:
+ // Not the same as ProfilePacket.index. This gets only gets incremented when
+ // encountering a ProfilePacket that is not continued.
+ // This namespaces all other Source*Ids.
+ using ProfileIndex = uint64_t;
+
+ using SourceStringId = uint64_t;
+
+ struct SourceMapping {
+ SourceStringId build_id = 0;
+ uint64_t offset = 0;
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint64_t load_bias = 0;
+ SourceStringId name_id = 0;
+ };
+ using SourceMappingId = uint64_t;
+
+ struct SourceFrame {
+ SourceStringId name_id = 0;
+ SourceMappingId mapping_id = 0;
+ uint64_t rel_pc = 0;
+ };
+ using SourceFrameId = uint64_t;
+
+ using SourceCallstack = std::vector<SourceFrameId>;
+ using SourceCallstackId = uint64_t;
+
+ struct SourceAllocation {
+ uint64_t pid = 0;
+ uint64_t timestamp = 0;
+ SourceCallstackId callstack_id = 0;
+ uint64_t self_allocated = 0;
+ uint64_t self_freed = 0;
+ uint64_t alloc_count = 0;
+ uint64_t free_count = 0;
+ };
+
+ explicit HeapProfileTracker(TraceProcessorContext* context);
+
+ void AddString(ProfileIndex, SourceStringId, StringId);
+ void AddMapping(ProfileIndex, SourceMappingId, const SourceMapping&);
+ void AddFrame(ProfileIndex, SourceFrameId, const SourceFrame&);
+ void AddCallstack(ProfileIndex, SourceCallstackId, const SourceCallstack&);
+
+ void StoreAllocation(ProfileIndex, SourceAllocation);
+ void ApplyAllAllocations();
+
+ int64_t GetDatabaseFrameIdForTesting(ProfileIndex, SourceFrameId);
+
+ ~HeapProfileTracker();
+
+ private:
+ void AddAllocation(ProfileIndex, const SourceAllocation&);
+
+ base::Optional<StringId> FindString(ProfileIndex, SourceStringId);
+
+ std::unordered_map<std::pair<ProfileIndex, SourceStringId>, StringId>
+ string_map_;
+ std::unordered_map<std::pair<ProfileIndex, SourceMappingId>, int64_t>
+ mappings_;
+ std::unordered_map<std::pair<ProfileIndex, SourceFrameId>, int64_t> frames_;
+ std::unordered_map<std::pair<ProfileIndex, SourceCallstack>, int64_t>
+ callstacks_from_frames_;
+ std::unordered_map<std::pair<ProfileIndex, SourceCallstackId>, int64_t>
+ callstacks_;
+
+ std::vector<std::pair<ProfileIndex, SourceAllocation>> pending_allocs_;
+
+ TraceProcessorContext* const context_;
+ const StringId empty_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_HEAP_PROFILE_TRACKER_H_
diff --git a/src/trace_processor/heap_profile_tracker_unittest.cc b/src/trace_processor/heap_profile_tracker_unittest.cc
new file mode 100644
index 0000000..3e095be
--- /dev/null
+++ b/src/trace_processor/heap_profile_tracker_unittest.cc
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/heap_profile_tracker.h"
+
+#include "src/trace_processor/trace_processor_context.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+constexpr auto kFirstPacket = 0;
+constexpr auto kFirstPacketMappingNameId = 1;
+constexpr auto kFirstPacketBuildId = 2;
+constexpr auto kFirstPacketFrameNameId = 3;
+
+constexpr auto kFirstPacketMappingId = 1;
+constexpr auto kFirstPacketFrameId = 1;
+
+constexpr auto kSecondPacket = 1;
+constexpr auto kSecondPacketMappingNameId = 3;
+constexpr auto kSecondPacketBuildId = 2;
+constexpr auto kSecondPacketFrameNameId = 1;
+
+constexpr auto kSecondPacketFrameId = 2;
+constexpr auto kSecondPacketMappingId = 2;
+
+constexpr auto kMappingOffset = 123;
+constexpr auto kMappingStart = 234;
+constexpr auto kMappingEnd = 345;
+constexpr auto kMappingLoadBias = 456;
+
+static constexpr auto kFrameRelPc = 567;
+
+using ::testing::ElementsAre;
+
+class HeapProfileTrackerDupTest : public ::testing::Test {
+ public:
+ HeapProfileTrackerDupTest() {
+ context.storage.reset(new TraceStorage());
+ context.heap_profile_tracker.reset(new HeapProfileTracker(&context));
+
+ mapping_name = context.storage->InternString("[mapping]");
+ build = context.storage->InternString("[build id]");
+ frame_name = context.storage->InternString("[frame]");
+ }
+
+ protected:
+ void InsertMapping() {
+ context.heap_profile_tracker->AddString(
+ kFirstPacket, kFirstPacketMappingNameId, mapping_name);
+ context.heap_profile_tracker->AddString(
+ kSecondPacket, kSecondPacketMappingNameId, mapping_name);
+
+ context.heap_profile_tracker->AddString(kFirstPacket, kFirstPacketBuildId,
+ build);
+ context.heap_profile_tracker->AddString(kSecondPacket, kSecondPacketBuildId,
+ build);
+
+ HeapProfileTracker::SourceMapping first_frame;
+ first_frame.build_id = kFirstPacketBuildId;
+ first_frame.offset = kMappingOffset;
+ first_frame.start = kMappingStart;
+ first_frame.end = kMappingEnd;
+ first_frame.load_bias = kMappingLoadBias;
+ first_frame.name_id = kFirstPacketMappingNameId;
+
+ HeapProfileTracker::SourceMapping second_frame;
+ second_frame.build_id = kSecondPacketBuildId;
+ second_frame.offset = kMappingOffset;
+ second_frame.start = kMappingStart;
+ second_frame.end = kMappingEnd;
+ second_frame.load_bias = kMappingLoadBias;
+ second_frame.name_id = kSecondPacketMappingNameId;
+
+ context.heap_profile_tracker->AddMapping(
+ kFirstPacket, kFirstPacketMappingId, first_frame);
+ context.heap_profile_tracker->AddMapping(
+ kSecondPacket, kSecondPacketMappingId, second_frame);
+ }
+
+ void InsertFrame() {
+ InsertMapping();
+ context.heap_profile_tracker->AddString(
+ kFirstPacket, kFirstPacketFrameNameId, frame_name);
+ context.heap_profile_tracker->AddString(
+ kSecondPacket, kSecondPacketFrameNameId, frame_name);
+
+ HeapProfileTracker::SourceFrame first_frame;
+ first_frame.name_id = kFirstPacketFrameNameId;
+ first_frame.mapping_id = kFirstPacketMappingId;
+ first_frame.rel_pc = kFrameRelPc;
+
+ HeapProfileTracker::SourceFrame second_frame;
+ second_frame.name_id = kSecondPacketFrameNameId;
+ second_frame.mapping_id = kSecondPacketMappingId;
+ second_frame.rel_pc = kFrameRelPc;
+
+ context.heap_profile_tracker->AddFrame(kFirstPacket, kFirstPacketFrameId,
+ first_frame);
+ context.heap_profile_tracker->AddFrame(kSecondPacket, kSecondPacketFrameId,
+ second_frame);
+ }
+
+ void InsertCallsite() {
+ InsertFrame();
+
+ HeapProfileTracker::SourceCallstack first_callsite = {kFirstPacketFrameId,
+ kFirstPacketFrameId};
+ HeapProfileTracker::SourceCallstack second_callsite = {
+ kSecondPacketFrameId, kSecondPacketFrameId};
+
+ context.heap_profile_tracker->AddCallstack(kFirstPacket, 0, first_callsite);
+ context.heap_profile_tracker->AddCallstack(kSecondPacket, 0,
+ second_callsite);
+ }
+
+ StringId mapping_name;
+ StringId build;
+ StringId frame_name;
+ TraceProcessorContext context;
+};
+
+// Insert the same mapping from two different packets, with different strings
+// interned, and assert we only store one.
+TEST_F(HeapProfileTrackerDupTest, Mapping) {
+ InsertMapping();
+
+ EXPECT_THAT(context.storage->heap_profile_mappings().build_ids(),
+ ElementsAre(build));
+ EXPECT_THAT(context.storage->heap_profile_mappings().offsets(),
+ ElementsAre(kMappingOffset));
+ EXPECT_THAT(context.storage->heap_profile_mappings().starts(),
+ ElementsAre(kMappingStart));
+ EXPECT_THAT(context.storage->heap_profile_mappings().ends(),
+ ElementsAre(kMappingEnd));
+ EXPECT_THAT(context.storage->heap_profile_mappings().load_biases(),
+ ElementsAre(kMappingLoadBias));
+ EXPECT_THAT(context.storage->heap_profile_mappings().names(),
+ ElementsAre(mapping_name));
+}
+
+// Insert the same mapping from two different packets, with different strings
+// interned, and assert we only store one.
+TEST_F(HeapProfileTrackerDupTest, Frame) {
+ InsertFrame();
+
+ EXPECT_THAT(context.storage->heap_profile_frames().names(),
+ ElementsAre(frame_name));
+ EXPECT_THAT(context.storage->heap_profile_frames().mappings(),
+ ElementsAre(0));
+ EXPECT_THAT(context.storage->heap_profile_frames().rel_pcs(),
+ ElementsAre(kFrameRelPc));
+}
+
+// Insert the same callstack from two different packets, assert it is only
+// stored once.
+TEST_F(HeapProfileTrackerDupTest, Callstack) {
+ InsertCallsite();
+
+ EXPECT_THAT(context.storage->heap_profile_callsites().frame_depths(),
+ ElementsAre(0, 1));
+ EXPECT_THAT(context.storage->heap_profile_callsites().parent_callsite_ids(),
+ ElementsAre(0, 0));
+ EXPECT_THAT(context.storage->heap_profile_callsites().frame_ids(),
+ ElementsAre(0, 0));
+}
+
+int64_t FindCallstack(const TraceStorage& storage,
+ int64_t depth,
+ int64_t parent,
+ int64_t frame_id) {
+ const auto& callsites = storage.heap_profile_callsites();
+ for (size_t i = 0; i < callsites.frame_depths().size(); ++i) {
+ if (callsites.frame_depths()[i] == depth &&
+ callsites.parent_callsite_ids()[i] == parent &&
+ callsites.frame_ids()[i] == frame_id) {
+ return static_cast<int64_t>(i);
+ }
+ }
+ return -1;
+}
+
+// Insert multiple mappings, frames and callstacks and check result.
+TEST(HeapProfileTrackerTest, Functional) {
+ TraceProcessorContext context;
+ context.storage.reset(new TraceStorage());
+ context.heap_profile_tracker.reset(new HeapProfileTracker(&context));
+
+ HeapProfileTracker* hpt = context.heap_profile_tracker.get();
+
+ constexpr auto kPacket = 0;
+ uint64_t next_string_intern_id = 1;
+
+ const std::string build_ids[] = {"build1", "build2", "build3"};
+ uint64_t build_id_ids[base::ArraySize(build_ids)];
+ for (size_t i = 0; i < base::ArraySize(build_ids); ++i)
+ build_id_ids[i] = next_string_intern_id++;
+
+ const std::string mapping_names[] = {"map1", "map2", "map3"};
+ uint64_t mapping_name_ids[base::ArraySize(mapping_names)];
+ for (size_t i = 0; i < base::ArraySize(mapping_names); ++i)
+ mapping_name_ids[i] = next_string_intern_id++;
+
+ HeapProfileTracker::SourceMapping mappings[base::ArraySize(mapping_names)] =
+ {};
+ mappings[0].build_id = build_id_ids[0];
+ mappings[0].offset = 1;
+ mappings[0].start = 2;
+ mappings[0].end = 3;
+ mappings[0].load_bias = 0;
+ mappings[0].name_id = mapping_name_ids[0];
+
+ mappings[1].build_id = build_id_ids[1];
+ mappings[1].offset = 1;
+ mappings[1].start = 2;
+ mappings[1].end = 3;
+ mappings[1].load_bias = 1;
+ mappings[1].name_id = mapping_name_ids[1];
+
+ mappings[2].build_id = build_id_ids[2];
+ mappings[2].offset = 1;
+ mappings[2].start = 2;
+ mappings[2].end = 3;
+ mappings[2].load_bias = 2;
+ mappings[2].name_id = mapping_name_ids[2];
+
+ const std::string function_names[] = {"fun1", "fun2", "fun3", "fun4"};
+ uint64_t function_name_ids[base::ArraySize(function_names)];
+ for (size_t i = 0; i < base::ArraySize(function_names); ++i)
+ function_name_ids[i] = next_string_intern_id++;
+
+ HeapProfileTracker::SourceFrame frames[base::ArraySize(function_names)];
+ frames[0].name_id = function_name_ids[0];
+ frames[0].mapping_id = 0;
+ frames[0].rel_pc = 123;
+
+ frames[1].name_id = function_name_ids[1];
+ frames[1].mapping_id = 0;
+ frames[1].rel_pc = 123;
+
+ frames[2].name_id = function_name_ids[2];
+ frames[2].mapping_id = 1;
+ frames[2].rel_pc = 123;
+
+ frames[3].name_id = function_name_ids[3];
+ frames[3].mapping_id = 2;
+ frames[3].rel_pc = 123;
+
+ HeapProfileTracker::SourceCallstack callstacks[3];
+ callstacks[0] = {2, 1, 0};
+ callstacks[1] = {2, 1, 0, 1, 0};
+ callstacks[2] = {0, 2, 0, 1, 2};
+
+ for (size_t i = 0; i < base::ArraySize(build_ids); ++i) {
+ auto interned = context.storage->InternString(
+ {build_ids[i].data(), build_ids[i].size()});
+ hpt->AddString(kPacket, build_id_ids[i], interned);
+ }
+ for (size_t i = 0; i < base::ArraySize(mapping_names); ++i) {
+ auto interned = context.storage->InternString(
+ {mapping_names[i].data(), mapping_names[i].size()});
+ hpt->AddString(kPacket, mapping_name_ids[i], interned);
+ }
+ for (size_t i = 0; i < base::ArraySize(function_names); ++i) {
+ auto interned = context.storage->InternString(
+ {function_names[i].data(), function_names[i].size()});
+ hpt->AddString(kPacket, function_name_ids[i], interned);
+ }
+
+ for (size_t i = 0; i < base::ArraySize(mappings); ++i)
+ hpt->AddMapping(kPacket, i, mappings[i]);
+ for (size_t i = 0; i < base::ArraySize(frames); ++i)
+ hpt->AddFrame(kPacket, i, frames[i]);
+ for (size_t i = 0; i < base::ArraySize(callstacks); ++i)
+ hpt->AddCallstack(kPacket, i, callstacks[i]);
+
+ for (size_t i = 0; i < base::ArraySize(callstacks); ++i) {
+ int64_t parent = 0;
+ const HeapProfileTracker::SourceCallstack& callstack = callstacks[i];
+ for (size_t depth = 0; depth < callstack.size(); ++depth) {
+ auto frame_id =
+ hpt->GetDatabaseFrameIdForTesting(kPacket, callstack[depth]);
+ ASSERT_NE(frame_id, -1);
+ int64_t self = FindCallstack(
+ *context.storage, static_cast<int64_t>(depth), parent, frame_id);
+ ASSERT_NE(self, -1);
+ parent = self;
+ }
+ }
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index fdd5f1b..1f0fdbf 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -24,7 +24,7 @@
}
action("gen_merged_sql_metrics") {
- script = "../../../tools/gen_merged_sql_metrics"
+ script = "../../../tools/gen_merged_sql_metrics.py"
generated_header = "${target_gen_dir}/sql_metrics.h"
args = rebase_path(sql_files, root_build_dir) + [
"--cpp_out",
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index 696a6bd..9f96676 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -31,6 +31,7 @@
#include "src/trace_processor/clock_tracker.h"
#include "src/trace_processor/event_tracker.h"
#include "src/trace_processor/ftrace_descriptors.h"
+#include "src/trace_processor/heap_profile_tracker.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/syscall_tracker.h"
@@ -1273,14 +1274,90 @@
}
void ProtoTraceParser::ParseProfilePacket(ConstBytes blob) {
+ uint64_t index = 0;
protos::pbzero::ProfilePacket::Decoder packet(blob.data, blob.size);
+
for (auto it = packet.strings(); it; ++it) {
protos::pbzero::ProfilePacket::InternedString::Decoder entry(it->data(),
it->size());
const char* str = reinterpret_cast<const char*>(entry.str().data);
- context_->storage->InternString(base::StringView(str, entry.str().size));
+ auto str_id = context_->storage->InternString(
+ base::StringView(str, entry.str().size));
+ context_->heap_profile_tracker->AddString(index, entry.id(), str_id);
}
+
+ for (auto it = packet.mappings(); it; ++it) {
+ protos::pbzero::ProfilePacket::Mapping::Decoder entry(it->data(),
+ it->size());
+ HeapProfileTracker::SourceMapping src_mapping;
+ src_mapping.build_id = entry.build_id();
+ src_mapping.offset = entry.offset();
+ src_mapping.start = entry.start();
+ src_mapping.end = entry.end();
+ src_mapping.load_bias = entry.load_bias();
+ src_mapping.name_id = 0;
+ for (auto path_string_id_it = entry.path_string_ids(); path_string_id_it;
+ ++path_string_id_it)
+ src_mapping.name_id = path_string_id_it->as_uint64();
+ context_->heap_profile_tracker->AddMapping(index, entry.id(), src_mapping);
+ }
+
+ for (auto it = packet.frames(); it; ++it) {
+ protos::pbzero::ProfilePacket::Frame::Decoder entry(it->data(), it->size());
+ HeapProfileTracker::SourceFrame src_frame;
+ src_frame.name_id = entry.function_name_id();
+ src_frame.mapping_id = entry.mapping_id();
+ src_frame.rel_pc = entry.rel_pc();
+
+ context_->heap_profile_tracker->AddFrame(index, entry.id(), src_frame);
+ }
+
+ for (auto it = packet.callstacks(); it; ++it) {
+ protos::pbzero::ProfilePacket::Callstack::Decoder entry(it->data(),
+ it->size());
+ HeapProfileTracker::SourceCallstack src_callstack;
+ for (auto frame_it = entry.frame_ids(); frame_it; ++frame_it)
+ src_callstack.emplace_back(frame_it->as_uint64());
+
+ context_->heap_profile_tracker->AddCallstack(index, entry.id(),
+ src_callstack);
+ }
+
+ for (auto it = packet.process_dumps(); it; ++it) {
+ protos::pbzero::ProfilePacket::ProcessHeapSamples::Decoder entry(
+ it->data(), it->size());
+
+ int pid = static_cast<int>(entry.pid());
+
+ if (entry.buffer_corrupted())
+ context_->storage->IncrementIndexedStats(
+ stats::heapprofd_buffer_corrupted, pid);
+ if (entry.buffer_overran())
+ context_->storage->IncrementIndexedStats(stats::heapprofd_buffer_overran,
+ pid);
+ if (entry.rejected_concurrent())
+ context_->storage->IncrementIndexedStats(
+ stats::heapprofd_rejected_concurrent, pid);
+
+ for (auto sample_it = entry.samples(); sample_it; ++sample_it) {
+ protos::pbzero::ProfilePacket::HeapSample::Decoder sample(
+ sample_it->data(), sample_it->size());
+
+ HeapProfileTracker::SourceAllocation src_allocation;
+ src_allocation.pid = entry.pid();
+ src_allocation.timestamp = sample.timestamp();
+ src_allocation.callstack_id = sample.callstack_id();
+ src_allocation.self_allocated = sample.self_allocated();
+ src_allocation.self_freed = sample.self_freed();
+ src_allocation.alloc_count = sample.alloc_count();
+ src_allocation.free_count = sample.free_count();
+
+ context_->heap_profile_tracker->StoreAllocation(index, src_allocation);
+ }
+ }
+ if (!packet.continued())
+ index++;
}
void ProtoTraceParser::ParseSystemInfo(ConstBytes blob) {
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 66ce84d..8443c7f 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -91,7 +91,14 @@
F(vmstat_unknown_keys, kSingle, kError, kAnalysis), \
F(clock_sync_failure, kSingle, kError, kAnalysis), \
F(process_tracker_errors, kSingle, kError, kAnalysis), \
- F(json_tokenizer_failure, kSingle, kError, kTrace)
+ F(json_tokenizer_failure, kSingle, kError, kTrace), \
+ F(heapprofd_buffer_corrupted, kIndexed, kError, kTrace), \
+ F(heapprofd_buffer_overran, kIndexed, kError, kTrace), \
+ F(heapprofd_rejected_concurrent, kIndexed, kError, kTrace), \
+ F(heapprofd_invalid_string_id, kSingle, kError, kTrace), \
+ F(heapprofd_invalid_mapping_id, kSingle, kError, kTrace), \
+ F(heapprofd_invalid_frame_id, kSingle, kError, kTrace), \
+ F(heapprofd_invalid_callstack_id, kSingle, kError, kTrace)
// clang-format on
enum Type {
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index 7155348..5483dae 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -20,6 +20,7 @@
#include "src/trace_processor/chunked_trace_reader.h"
#include "src/trace_processor/clock_tracker.h"
#include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/heap_profile_tracker.h"
#include "src/trace_processor/json_trace_parser.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/proto_trace_parser.h"
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
index 2fd83f2..5a4f815 100644
--- a/src/trace_processor/trace_processor_context.h
+++ b/src/trace_processor/trace_processor_context.h
@@ -32,6 +32,7 @@
class TraceParser;
class TraceStorage;
class TraceSorter;
+class HeapProfileTracker;
class TraceProcessorContext {
public:
@@ -48,6 +49,7 @@
std::unique_ptr<TraceParser> parser;
std::unique_ptr<TraceSorter> sorter;
std::unique_ptr<ChunkedTraceReader> chunk_reader;
+ std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
};
} // namespace trace_processor
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index d12573d..588afaa 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -33,6 +33,7 @@
#include "src/trace_processor/event_tracker.h"
#include "src/trace_processor/fuchsia_trace_parser.h"
#include "src/trace_processor/fuchsia_trace_tokenizer.h"
+#include "src/trace_processor/heap_profile_tracker.h"
#include "src/trace_processor/instants_table.h"
#include "src/trace_processor/metrics/metrics.h"
#include "src/trace_processor/process_table.h"
@@ -230,6 +231,7 @@
context_.process_tracker.reset(new ProcessTracker(&context_));
context_.syscall_tracker.reset(new SyscallTracker(&context_));
context_.clock_tracker.reset(new ClockTracker(&context_));
+ context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
ArgsTable::RegisterTable(*db_, context_.storage.get());
ProcessTable::RegisterTable(*db_, context_.storage.get());
@@ -303,6 +305,7 @@
context_.sorter->ExtractEventsForced();
context_.event_tracker->FlushPendingEvents();
+ context_.heap_profile_tracker->ApplyAllAllocations();
BuildBoundsTable(*db_, context_.storage->GetTraceTimestampBoundsNs());
}
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index f6f687a..285296b 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -585,6 +585,133 @@
};
using StatsMap = std::array<Stats, stats::kNumKeys>;
+ class HeapProfileFrames {
+ public:
+ int64_t FindOrInsert(StringId name_id,
+ int64_t mapping_row,
+ int64_t rel_pc) {
+ // TODO(fmayer): More efficiently search for existing entries.
+ for (size_t i = 0; i < names_.size(); ++i) {
+ if (name_id == names_[i] && mapping_row == mappings_[i] &&
+ rel_pc == rel_pcs_[i]) {
+ return static_cast<int64_t>(i);
+ }
+ }
+
+ names_.emplace_back(name_id);
+ mappings_.emplace_back(mapping_row);
+ rel_pcs_.emplace_back(rel_pc);
+ return static_cast<int64_t>(names_.size()) - 1;
+ }
+
+ const std::deque<StringId>& names() const { return names_; }
+ const std::deque<int64_t>& mappings() const { return mappings_; }
+ const std::deque<int64_t>& rel_pcs() const { return rel_pcs_; }
+
+ private:
+ std::deque<StringId> names_;
+ std::deque<int64_t> mappings_;
+ std::deque<int64_t> rel_pcs_;
+ };
+
+ class HeapProfileCallsites {
+ public:
+ int64_t FindOrInsert(int64_t depth, int64_t parent_id, int64_t frame_row) {
+ // TODO(fmayer): More efficiently search for existing entries.
+ for (size_t i = 0; i < frame_depths_.size(); ++i) {
+ if (depth == frame_depths_[i] && parent_id == parent_callsite_ids_[i] &&
+ frame_row == frame_ids_[i]) {
+ return static_cast<int64_t>(i);
+ }
+ }
+ frame_depths_.emplace_back(depth);
+ parent_callsite_ids_.emplace_back(parent_id);
+ frame_ids_.emplace_back(frame_row);
+ return static_cast<int64_t>(frame_depths_.size()) - 1;
+ }
+
+ const std::deque<int64_t>& frame_depths() const { return frame_depths_; }
+ const std::deque<int64_t>& parent_callsite_ids() const {
+ return parent_callsite_ids_;
+ }
+ const std::deque<int64_t>& frame_ids() const { return frame_ids_; }
+
+ private:
+ std::deque<int64_t> frame_depths_;
+ std::deque<int64_t> parent_callsite_ids_;
+ std::deque<int64_t> frame_ids_;
+ };
+
+ class HeapProfileMappings {
+ public:
+ int64_t FindOrInsert(StringId build_id,
+ int64_t offset,
+ int64_t start,
+ int64_t end,
+ int64_t load_bias,
+ StringId name_id) {
+ // TODO(fmayer): More efficiently search for existing entries.
+ for (size_t i = 0; i < build_ids_.size(); ++i) {
+ if (build_id == build_ids_[i] && offset == offsets_[i] &&
+ start == starts_[i] && end == ends_[i] &&
+ load_bias == load_biases_[i] && name_id == names_[i]) {
+ return static_cast<int64_t>(i);
+ }
+ }
+
+ build_ids_.emplace_back(build_id);
+ offsets_.emplace_back(offset);
+ starts_.emplace_back(start);
+ ends_.emplace_back(end);
+ load_biases_.emplace_back(load_bias);
+ names_.emplace_back(name_id);
+ return static_cast<int64_t>(build_ids_.size()) - 1;
+ }
+
+ const std::deque<StringId>& build_ids() const { return build_ids_; }
+ const std::deque<int64_t>& offsets() const { return offsets_; }
+ const std::deque<int64_t>& starts() const { return starts_; }
+ const std::deque<int64_t>& ends() const { return ends_; }
+ const std::deque<int64_t>& load_biases() const { return load_biases_; }
+ const std::deque<StringId>& names() const { return names_; }
+
+ private:
+ std::deque<StringId> build_ids_;
+ std::deque<int64_t> offsets_;
+ std::deque<int64_t> starts_;
+ std::deque<int64_t> ends_;
+ std::deque<int64_t> load_biases_;
+ std::deque<StringId> names_;
+ };
+
+ class HeapProfileAllocations {
+ public:
+ void Insert(int64_t timestamp,
+ int64_t pid,
+ int64_t callsite_id,
+ int64_t count,
+ int64_t size) {
+ timestamps_.emplace_back(timestamp);
+ pids_.emplace_back(pid);
+ callsite_ids_.emplace_back(callsite_id);
+ counts_.emplace_back(count);
+ sizes_.emplace_back(size);
+ }
+
+ const std::deque<int64_t>& timestamps() const { return timestamps_; }
+ const std::deque<int64_t>& pids() const { return pids_; }
+ const std::deque<int64_t>& callsite_ids() const { return callsite_ids_; }
+ const std::deque<int64_t>& counts() const { return counts_; }
+ const std::deque<int64_t>& sizes() const { return sizes_; }
+
+ private:
+ std::deque<int64_t> timestamps_;
+ std::deque<int64_t> pids_;
+ std::deque<int64_t> callsite_ids_;
+ std::deque<int64_t> counts_;
+ std::deque<int64_t> sizes_;
+ };
+
void ResetStorage();
UniqueTid AddEmptyThread(uint32_t tid) {
@@ -628,6 +755,13 @@
stats_[key].value += increment;
}
+ // Example usage: IncrementIndexedStats(stats::cpu_failure, 1);
+ void IncrementIndexedStats(size_t key, int index, int64_t increment = 1) {
+ PERFETTO_DCHECK(key < stats::kNumKeys);
+ PERFETTO_DCHECK(stats::kTypes[key] == stats::kIndexed);
+ stats_[key].indexed_values[index] += increment;
+ }
+
// Example usage: SetIndexedStats(stats::cpu_failure, 1, 42);
void SetIndexedStats(size_t key, int index, int64_t value) {
PERFETTO_DCHECK(key < stats::kNumKeys);
@@ -695,6 +829,34 @@
const RawEvents& raw_events() const { return raw_events_; }
RawEvents* mutable_raw_events() { return &raw_events_; }
+ const HeapProfileMappings& heap_profile_mappings() const {
+ return heap_profile_mappings_;
+ }
+ HeapProfileMappings* mutable_heap_profile_mappings() {
+ return &heap_profile_mappings_;
+ }
+
+ const HeapProfileFrames& heap_profile_frames() const {
+ return heap_profile_frames_;
+ }
+ HeapProfileFrames* mutable_heap_profile_frames() {
+ return &heap_profile_frames_;
+ }
+
+ const HeapProfileCallsites& heap_profile_callsites() const {
+ return heap_profile_callsites_;
+ }
+ HeapProfileCallsites* mutable_heap_profile_callsites() {
+ return &heap_profile_callsites_;
+ }
+
+ const HeapProfileAllocations& heap_profile_allocations() const {
+ return heap_profile_allocations_;
+ }
+ HeapProfileAllocations* mutable_heap_profile_allocations() {
+ return &heap_profile_allocations_;
+ }
+
const StringPool& string_pool() const { return string_pool_; }
// |unique_processes_| always contains at least 1 element becuase the 0th ID
@@ -766,6 +928,11 @@
// trace.
RawEvents raw_events_;
AndroidLogs android_log_;
+
+ HeapProfileMappings heap_profile_mappings_;
+ HeapProfileFrames heap_profile_frames_;
+ HeapProfileCallsites heap_profile_callsites_;
+ HeapProfileAllocations heap_profile_allocations_;
};
} // namespace trace_processor
diff --git a/src/tracing/core/shared_memory_arbiter_impl.cc b/src/tracing/core/shared_memory_arbiter_impl.cc
index ce55c32..7f646c8 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl.cc
@@ -301,6 +301,19 @@
void SharedMemoryArbiterImpl::BindStartupTraceWriterRegistry(
std::unique_ptr<StartupTraceWriterRegistry> registry,
BufferID target_buffer) {
+ if (!task_runner_->RunsTasksOnCurrentThread()) {
+ auto weak_this = weak_ptr_factory_.GetWeakPtr();
+ auto* raw_reg = registry.release();
+ task_runner_->PostTask([weak_this, raw_reg, target_buffer]() {
+ std::unique_ptr<StartupTraceWriterRegistry> owned_reg(raw_reg);
+ if (!weak_this)
+ return;
+ weak_this->BindStartupTraceWriterRegistry(std::move(owned_reg),
+ target_buffer);
+ });
+ return;
+ }
+
// The registry will be owned by the arbiter, so it's safe to capture |this|
// in the callback.
auto on_bound_callback = [this](StartupTraceWriterRegistry* bound_registry) {
diff --git a/src/tracing/core/startup_trace_writer_registry.cc b/src/tracing/core/startup_trace_writer_registry.cc
index 6de4ab0..c3fcfce 100644
--- a/src/tracing/core/startup_trace_writer_registry.cc
+++ b/src/tracing/core/startup_trace_writer_registry.cc
@@ -81,6 +81,10 @@
arbiter_ = arbiter;
target_buffer_ = target_buffer;
task_runner_ = task_runner;
+ // Weakptrs should be valid on |task_runner|. For this, the factory needs to
+ // be created on |task_runner|, i.e. BindToArbiter must be called on
+ // |task_runner|.
+ PERFETTO_DCHECK(task_runner_->RunsTasksOnCurrentThread());
weak_ptr_factory_.reset(
new base::WeakPtrFactory<StartupTraceWriterRegistry>(this));
on_bound_callback_ = std::move(on_bound_callback);
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index b6b5eda..f2719fe 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -519,9 +519,13 @@
'gen_merged_sql_metrics',
)
module.tool_files = [
- 'tools/gen_merged_sql_metrics',
+ 'tools/gen_merged_sql_metrics.py',
]
- module.cmd = '$(location tools/gen_merged_sql_metrics) --cpp_out=$(out) $(in)'
+ module.cmd = ' '.join([
+ '$(location tools/gen_merged_sql_metrics.py)',
+ '--cpp_out=$(out)',
+ '$(in)',
+ ])
module.out = set(
src[src.index('gen/') + len('gen/'):]
for src in target_desc.get('outputs', [])
diff --git a/tools/gen_build b/tools/gen_build
index ade417f..d03f8d2 100755
--- a/tools/gen_build
+++ b/tools/gen_build
@@ -277,33 +277,6 @@
else:
self.line('{} = {},'.format(key, value), indent=1)
- def rule(self, type, name, visibility=None, srcs=None, hdrs=None, deps=None, is_pbzero=False, **kwargs):
- self.line('{}('.format(type))
- self.variable('name', name)
- if srcs:
- self.variable('srcs', srcs)
- if hdrs:
- self.variable('hdrs', hdrs)
-
- if type == 'proto_library' and not is_pbzero:
- if srcs:
- self.variable('has_services', 1)
- self.variable('cc_api_version', 2)
- if srcs:
- self.variable('cc_generic_services', 1)
-
- for key, value in kwargs.iteritems():
- self.variable(key, value)
-
- # Keep visibility and deps last.
- if visibility:
- self.variable('visibility', visibility)
-
- if type != 'filegroup':
- self.variable('deps', deps)
-
- self.line(')')
-
def header(self):
self.output.write(header)
@@ -311,7 +284,7 @@
class Target(object):
"""In-memory representation of a BUILD target."""
- def __init__(self, type, name, gn_name=None, **kwargs):
+ def __init__(self, type, name, gn_name=None):
assert type in ('cc_binary', 'cc_library', 'cc_proto_library',
'proto_library', 'filegroup', 'alias',
'pbzero_cc_proto_library', 'genrule', )
@@ -322,13 +295,41 @@
self.deps = set()
self.visibility = set()
self.gn_name = gn_name
- self.args = kwargs
+ self.is_pbzero = False
+ self.src_proto_library = None
+ self.outs = set()
+ self.cmd = None
+ self.tools = set()
def write(self, writer):
if self.gn_name:
writer.comment('GN target: {}'.format(self.gn_name))
- writer.rule(self.type, self.name, visibility=self.visibility,
- srcs=self.srcs, hdrs=self.hdrs, deps=self.deps, **self.args)
+
+ writer.line('{}('.format(self.type))
+ writer.variable('name', self.name)
+ writer.variable('srcs', self.srcs)
+ writer.variable('hdrs', self.hdrs)
+
+ if self.type == 'proto_library' and not self.is_pbzero:
+ if self.srcs:
+ writer.variable('has_services', 1)
+ writer.variable('cc_api_version', 2)
+ if self.srcs:
+ writer.variable('cc_generic_services', 1)
+
+ writer.variable('src_proto_library', self.src_proto_library)
+
+ writer.variable('outs', self.outs)
+ writer.variable('cmd', self.cmd)
+ writer.variable('tools', self.tools)
+
+ # Keep visibility and deps last.
+ writer.variable('visibility', self.visibility)
+
+ if type != 'filegroup':
+ writer.variable('deps', self.deps)
+
+ writer.line(')')
class Build(object):
@@ -463,6 +464,9 @@
if "gen_merged_sql_metrics" in dep_name:
dep_target = self.create_merged_sql_metrics_target(dep_name)
target.deps.add(Label("//third_party/perfetto:" + dep_target.name))
+
+ if target.type == 'cc_library' or target.type == 'cc_binary':
+ target.srcs.update(dep_target.outs)
elif args[0].endswith('/protoc'):
(proto_target, cc_target) = self.create_proto_target(dep_name)
if target.type == 'proto_library':
@@ -495,15 +499,15 @@
'genrule',
'gen_merged_sql_metrics',
gn_name=gn_target_name_no_toolchain,
- outs=set(
- Label(src[src.index('gen/') + len('gen/'):])
- for src in target_desc.get('outputs', [])
- ),
- cmd = '$(location gen_merged_sql_metrics_py) --cpp_out=$@ $SRCS',
- tools=[
- 'gen_merged_sql_metrics_py',
- ],
)
+ target.outs.update(
+ Label(src[src.index('gen/') + len('gen/'):])
+ for src in target_desc.get('outputs', [])
+ )
+ target.cmd = '$(location gen_merged_sql_metrics_py) --cpp_out=$@ $(SRCS)'
+ target.tools.update([
+ 'gen_merged_sql_metrics_py',
+ ])
target.srcs.update(
Label(label_to_path(src))
for src in target_desc.get('inputs', [])
@@ -516,7 +520,6 @@
target_desc = self.desc[gn_target_name]
args = target_desc['args']
- is_pbzero = any("pbzero" in arg for arg in args)
gn_target_name_no_toolchain = label_without_toolchain(gn_target_name)
stripped_path = gn_target_name_no_toolchain.replace("protos/perfetto/", "")
pretty_target_name = label_to_target_name_with_path(stripped_path)
@@ -526,21 +529,21 @@
proto_target = Target(
'proto_library',
pretty_target_name,
- gn_name=gn_target_name_no_toolchain,
- is_pbzero=is_pbzero
+ gn_name=gn_target_name_no_toolchain
)
+ proto_target.is_pbzero = any("pbzero" in arg for arg in args)
proto_target.srcs.update([
Label(label_to_path(src).replace('protos/', ''))
for src in target_desc.get('sources', [])
])
- if not is_pbzero:
+ if not proto_target.is_pbzero:
proto_target.visibility.add("//visibility:public")
self.proto_build.add_target(proto_target)
for dep_name in self.resolve_dependencies(gn_target_name):
self.apply_module_dependency(proto_target, dep_name)
- if is_pbzero:
+ if proto_target.is_pbzero:
# Remove all the protozero srcs from the proto_library.
proto_target.srcs.difference_update(
[src for src in proto_target.srcs if not src.label.endswith('.proto')])
@@ -548,21 +551,21 @@
# Remove all the non-proto deps from the proto_library and add to the cc
# library.
cc_deps = [
- dep for dep in proto_target.deps
- if not dep.label.startswith('//third_party/perfetto/protos')
+ dep for dep in proto_target.deps
+ if not dep.label.startswith('//third_party/perfetto/protos')
]
proto_target.deps.difference_update(cc_deps)
cc_target_name = proto_target.name + "_cc_proto"
- cc_target = Target('pbzero_cc_proto_library',
- cc_target_name, gn_name=gn_target_name_no_toolchain)
+ cc_target = Target('pbzero_cc_proto_library', cc_target_name,
+ gn_name=gn_target_name_no_toolchain)
cc_target.deps.add(Label('//third_party/perfetto:libprotozero'))
cc_target.deps.update(cc_deps)
# Add the proto_library to the cc_target.
- cc_target.args['src_proto_library'] = "//third_party/perfetto/protos:" + \
- proto_target.name
+ cc_target.src_proto_library = \
+ "//third_party/perfetto/protos:" + proto_target.name
self.proto_build.add_target(cc_target)
else:
diff --git a/tools/gen_merged_sql_metrics b/tools/gen_merged_sql_metrics.py
similarity index 98%
rename from tools/gen_merged_sql_metrics
rename to tools/gen_merged_sql_metrics.py
index 75745f8..071423c 100755
--- a/tools/gen_merged_sql_metrics
+++ b/tools/gen_merged_sql_metrics.py
@@ -41,6 +41,8 @@
* AUTOGENERATED BY tools/gen_merged_sql_metrics - DO NOT EDIT
*******************************************************************************
*/
+
+ #include <string.h>
'''
NAMESPACE_BEGIN = '''