profiling: factor callstack trie out into p/common/
+ leftovers from previous traced_perf review
Bug: 144281346
Change-Id: I74a8e7da9e9d66423dcfbf4edde02325b067959a
diff --git a/Android.bp b/Android.bp
index 5589098..12f93ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,6 +110,8 @@
":perfetto_src_base_unix_socket",
":perfetto_src_ipc_client",
":perfetto_src_ipc_common",
+ ":perfetto_src_profiling_common_callstack_trie",
+ ":perfetto_src_profiling_common_interner",
":perfetto_src_profiling_common_unwind_support",
":perfetto_src_profiling_memory_daemon",
":perfetto_src_profiling_memory_proc_utils",
@@ -1342,6 +1344,8 @@
":perfetto_src_ipc_common",
":perfetto_src_ipc_host",
":perfetto_src_perfetto_cmd_perfetto_atoms",
+ ":perfetto_src_profiling_common_callstack_trie",
+ ":perfetto_src_profiling_common_interner",
":perfetto_src_profiling_common_unwind_support",
":perfetto_src_profiling_memory_client",
":perfetto_src_profiling_memory_daemon",
@@ -5594,6 +5598,27 @@
],
}
+// GN: //src/profiling/common:callstack_trie
+filegroup {
+ name: "perfetto_src_profiling_common_callstack_trie",
+ srcs: [
+ "src/profiling/common/callstack_trie.cc",
+ ],
+}
+
+// GN: //src/profiling/common:interner
+filegroup {
+ name: "perfetto_src_profiling_common_interner",
+}
+
+// GN: //src/profiling/common:unittests
+filegroup {
+ name: "perfetto_src_profiling_common_unittests",
+ srcs: [
+ "src/profiling/common/interner_unittest.cc",
+ ],
+}
+
// GN: //src/profiling/common:unwind_support
filegroup {
name: "perfetto_src_profiling_common_unwind_support",
@@ -5687,7 +5712,6 @@
"src/profiling/memory/bookkeeping_unittest.cc",
"src/profiling/memory/client_unittest.cc",
"src/profiling/memory/heapprofd_producer_unittest.cc",
- "src/profiling/memory/interner_unittest.cc",
"src/profiling/memory/page_idle_checker_unittest.cc",
"src/profiling/memory/proc_utils_unittest.cc",
"src/profiling/memory/sampler_unittest.cc",
@@ -6970,6 +6994,9 @@
":perfetto_src_perfetto_cmd_protos_gen",
":perfetto_src_perfetto_cmd_trigger_producer",
":perfetto_src_perfetto_cmd_unittests",
+ ":perfetto_src_profiling_common_callstack_trie",
+ ":perfetto_src_profiling_common_interner",
+ ":perfetto_src_profiling_common_unittests",
":perfetto_src_profiling_common_unwind_support",
":perfetto_src_profiling_deobfuscator",
":perfetto_src_profiling_memory_client",
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index 84467e6..aae3085 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -57,6 +57,10 @@
]
}
+if (enable_perfetto_heapprofd || enable_perfetto_traced_perf) {
+ perfetto_unittests_targets += [ "src/profiling/common:unittests" ]
+}
+
if (enable_perfetto_heapprofd) {
perfetto_unittests_targets += [
"src/profiling/memory:unittests",
diff --git a/src/profiling/common/BUILD.gn b/src/profiling/common/BUILD.gn
index 7e0d767..8bd7784 100644
--- a/src/profiling/common/BUILD.gn
+++ b/src/profiling/common/BUILD.gn
@@ -13,6 +13,7 @@
# limitations under the License.
import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
source_set("unwind_support") {
public_deps = [ "../../../gn:libunwindstack" ]
@@ -25,3 +26,36 @@
"unwind_support.h",
]
}
+
+source_set("callstack_trie") {
+ deps = [
+ ":interner",
+ ":unwind_support",
+ "../../../gn:default_deps",
+ "../../../src/base",
+ ]
+ sources = [
+ "callstack_trie.cc",
+ "callstack_trie.h",
+ ]
+}
+
+source_set("interner") {
+ deps = [
+ "../../../gn:default_deps",
+ "../../../src/base",
+ ]
+ sources = [ "interner.h" ]
+}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ deps = [
+ ":interner",
+ "../../../gn:default_deps",
+ "../../../gn:gtest_and_gmock",
+ "../../base",
+ "../../base:test_support",
+ ]
+ sources = [ "interner_unittest.cc" ]
+}
diff --git a/src/profiling/common/callstack_trie.cc b/src/profiling/common/callstack_trie.cc
new file mode 100644
index 0000000..a97d3aa
--- /dev/null
+++ b/src/profiling/common/callstack_trie.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/common/callstack_trie.h"
+
+#include <vector>
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "src/profiling/common/interner.h"
+#include "src/profiling/common/unwind_support.h"
+
+namespace perfetto {
+namespace profiling {
+
+GlobalCallstackTrie::Node* GlobalCallstackTrie::GetOrCreateChild(
+ Node* self,
+ const Interned<Frame>& loc) {
+ Node* child = self->children_.Get(loc);
+ if (!child)
+ child = self->children_.Emplace(loc, ++next_callstack_id_, self);
+ return child;
+}
+
+std::vector<Interned<Frame>> GlobalCallstackTrie::BuildCallstack(
+ const Node* node) const {
+ std::vector<Interned<Frame>> res;
+ while (node != &root_) {
+ res.emplace_back(node->location_);
+ node = node->parent_;
+ }
+ return res;
+}
+
+GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite(
+ const std::vector<FrameData>& callstack) {
+ Node* node = &root_;
+ for (const FrameData& loc : callstack) {
+ node = GetOrCreateChild(node, InternCodeLocation(loc));
+ }
+ return node;
+}
+
+void GlobalCallstackTrie::IncrementNode(Node* node) {
+ while (node != nullptr) {
+ node->ref_count_ += 1;
+ node = node->parent_;
+ }
+}
+
+void GlobalCallstackTrie::DecrementNode(Node* node) {
+ PERFETTO_DCHECK(node->ref_count_ >= 1);
+
+ bool delete_prev = false;
+ Node* prev = nullptr;
+ while (node != nullptr) {
+ if (delete_prev)
+ node->children_.Remove(*prev);
+ node->ref_count_ -= 1;
+ delete_prev = node->ref_count_ == 0;
+ prev = node;
+ node = node->parent_;
+ }
+}
+
+Interned<Frame> GlobalCallstackTrie::InternCodeLocation(const FrameData& loc) {
+ Mapping map(string_interner_.Intern(loc.build_id));
+ map.exact_offset = loc.frame.map_exact_offset;
+ map.start_offset = loc.frame.map_elf_start_offset;
+ map.start = loc.frame.map_start;
+ map.end = loc.frame.map_end;
+ map.load_bias = loc.frame.map_load_bias;
+ base::StringSplitter sp(loc.frame.map_name, '/');
+ while (sp.Next())
+ map.path_components.emplace_back(string_interner_.Intern(sp.cur_token()));
+
+ Frame frame(mapping_interner_.Intern(std::move(map)),
+ string_interner_.Intern(loc.frame.function_name),
+ loc.frame.rel_pc);
+
+ return frame_interner_.Intern(frame);
+}
+
+Interned<Frame> GlobalCallstackTrie::MakeRootFrame() {
+ Mapping map(string_interner_.Intern(""));
+
+ Frame frame(mapping_interner_.Intern(std::move(map)),
+ string_interner_.Intern(""), 0);
+
+ return frame_interner_.Intern(frame);
+}
+
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/common/callstack_trie.h b/src/profiling/common/callstack_trie.h
new file mode 100644
index 0000000..b9bbf78
--- /dev/null
+++ b/src/profiling/common/callstack_trie.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
+#define SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/lookup_set.h"
+#include "src/profiling/common/interner.h"
+#include "src/profiling/common/unwind_support.h"
+
+namespace perfetto {
+namespace profiling {
+
+struct Mapping {
+ Mapping(Interned<std::string> b) : build_id(std::move(b)) {}
+
+ Interned<std::string> build_id;
+ uint64_t exact_offset = 0;
+ uint64_t start_offset = 0;
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint64_t load_bias = 0;
+ std::vector<Interned<std::string>> path_components{};
+
+ bool operator<(const Mapping& other) const {
+ return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
+ path_components) <
+ std::tie(other.build_id, other.exact_offset, other.start_offset,
+ other.start, other.end, other.load_bias,
+ other.path_components);
+ }
+ bool operator==(const Mapping& other) const {
+ return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
+ path_components) ==
+ std::tie(other.build_id, other.exact_offset, other.start_offset,
+ other.start, other.end, other.load_bias,
+ other.path_components);
+ }
+};
+
+struct Frame {
+ Frame(Interned<Mapping> m, Interned<std::string> fn_name, uint64_t pc)
+ : mapping(m), function_name(fn_name), rel_pc(pc) {}
+ Interned<Mapping> mapping;
+ Interned<std::string> function_name;
+ uint64_t rel_pc;
+
+ bool operator<(const Frame& other) const {
+ return std::tie(mapping, function_name, rel_pc) <
+ std::tie(other.mapping, other.function_name, other.rel_pc);
+ }
+
+ bool operator==(const Frame& other) const {
+ return std::tie(mapping, function_name, rel_pc) ==
+ std::tie(other.mapping, other.function_name, other.rel_pc);
+ }
+};
+
+// Graph of function callsites. A single instance can be used for callsites from
+// different processes. Each call site is represented by a
+// GlobalCallstackTrie::Node that is owned by the parent callsite. Each node has
+// a pointer to its parent, which means the function call-graph can be
+// reconstructed from a GlobalCallstackTrie::Node by walking down the parent
+// chain.
+//
+// For the following two callstacks:
+// * libc_init -> main -> foo -> alloc_buf
+// * libc_init -> main -> bar -> alloc_buf
+// The tree looks as following:
+//
+// libc_init libc_init
+// | |
+// main main
+// | |
+// foo bar
+// \ /
+// alloc_buf
+// |
+// [root_]
+//
+// TODO(fmayer): we currently insert starting with the leafmost frame. So the
+// root_ frame has all distinct leaf nodes as its immediate children, and
+// |CreateCallsite| returns a pointer to the "outermost" function node (so
+// there's a distinct libc_init node per callstack). Most likely less efficient
+// than interning in the "intuitive" way, but not guaranteed (consider a deeply
+// nested callchain fragment at the leaf, which is called from many places).
+class GlobalCallstackTrie {
+ public:
+ class Node {
+ public:
+ // This is opaque except to GlobalCallstackTrie.
+ friend class GlobalCallstackTrie;
+
+ // Allow building a node out of a frame for base::LookupSet.
+ Node(Interned<Frame> frame) : Node(std::move(frame), 0, nullptr) {}
+ Node(Interned<Frame> frame, uint64_t id)
+ : Node(std::move(frame), id, nullptr) {}
+ Node(Interned<Frame> frame, uint64_t id, Node* parent)
+ : id_(id), parent_(parent), location_(std::move(frame)) {}
+
+ uint64_t id() const { return id_; }
+
+ private:
+ Node* GetOrCreateChild(const Interned<Frame>& loc);
+
+ uint64_t ref_count_ = 0;
+ uint64_t id_;
+ Node* const parent_;
+ const Interned<Frame> location_;
+ base::LookupSet<Node, const Interned<Frame>, &Node::location_> children_;
+ };
+
+ GlobalCallstackTrie() = default;
+ GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
+ GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
+
+ // Moving this would invalidate the back pointers to the root_ node.
+ GlobalCallstackTrie(GlobalCallstackTrie&&) = delete;
+ GlobalCallstackTrie& operator=(GlobalCallstackTrie&&) = delete;
+
+ Node* CreateCallsite(const std::vector<FrameData>& callstack);
+ static void DecrementNode(Node* node);
+ static void IncrementNode(Node* node);
+
+ std::vector<Interned<Frame>> BuildCallstack(const Node* node) const;
+
+ private:
+ Node* GetOrCreateChild(Node* self, const Interned<Frame>& loc);
+
+ Interned<Frame> InternCodeLocation(const FrameData& loc);
+ Interned<Frame> MakeRootFrame();
+
+ Interner<std::string> string_interner_;
+ Interner<Mapping> mapping_interner_;
+ Interner<Frame> frame_interner_;
+
+ uint64_t next_callstack_id_ = 0;
+
+ Node root_{MakeRootFrame(), ++next_callstack_id_};
+};
+
+} // namespace profiling
+} // namespace perfetto
+
+namespace std {
+template <>
+struct hash<::perfetto::profiling::Mapping> {
+ using argument_type = ::perfetto::profiling::Mapping;
+ using result_type = size_t;
+ result_type operator()(const argument_type& mapping) {
+ size_t h =
+ std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id());
+ h ^= std::hash<uint64_t>{}(mapping.exact_offset);
+ h ^= std::hash<uint64_t>{}(mapping.start_offset);
+ h ^= std::hash<uint64_t>{}(mapping.start);
+ h ^= std::hash<uint64_t>{}(mapping.end);
+ h ^= std::hash<uint64_t>{}(mapping.load_bias);
+ for (const auto& path : mapping.path_components)
+ h ^= std::hash<uint64_t>{}(path.id());
+ return h;
+ }
+};
+
+template <>
+struct hash<::perfetto::profiling::Frame> {
+ using argument_type = ::perfetto::profiling::Frame;
+ using result_type = size_t;
+ result_type operator()(const argument_type& frame) {
+ size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id());
+ h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id());
+ h ^= std::hash<uint64_t>{}(frame.rel_pc);
+ return h;
+ }
+};
+} // namespace std
+
+#endif // SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
diff --git a/src/profiling/memory/interner.h b/src/profiling/common/interner.h
similarity index 95%
rename from src/profiling/memory/interner.h
rename to src/profiling/common/interner.h
index 2d3c677..7490ef2 100644
--- a/src/profiling/memory/interner.h
+++ b/src/profiling/common/interner.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef SRC_PROFILING_MEMORY_INTERNER_H_
-#define SRC_PROFILING_MEMORY_INTERNER_H_
+#ifndef SRC_PROFILING_COMMON_INTERNER_H_
+#define SRC_PROFILING_COMMON_INTERNER_H_
#include <stddef.h>
#include <stdint.h>
@@ -29,6 +29,7 @@
using InternID = uint32_t;
+// Interner that hands out refcounted references.
template <typename T>
class Interner {
private:
@@ -121,6 +122,7 @@
if (--entry->ref_count == 0)
entries_.erase(*entry);
}
+
InternID next_id = 1;
std::unordered_set<Entry, typename Entry::Hash> entries_;
static_assert(sizeof(Interned) == sizeof(void*),
@@ -138,4 +140,4 @@
} // namespace profiling
} // namespace perfetto
-#endif // SRC_PROFILING_MEMORY_INTERNER_H_
+#endif // SRC_PROFILING_COMMON_INTERNER_H_
diff --git a/src/profiling/memory/interner_unittest.cc b/src/profiling/common/interner_unittest.cc
similarity index 98%
rename from src/profiling/memory/interner_unittest.cc
rename to src/profiling/common/interner_unittest.cc
index e43b348..f7f1c21 100644
--- a/src/profiling/memory/interner_unittest.cc
+++ b/src/profiling/common/interner_unittest.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "src/profiling/memory/interner.h"
+#include "src/profiling/common/interner.h"
#include "test/gtest_and_gmock.h"
diff --git a/src/profiling/common/unwind_support.cc b/src/profiling/common/unwind_support.cc
index 53d0e4a..1d8bc14 100644
--- a/src/profiling/common/unwind_support.cc
+++ b/src/profiling/common/unwind_support.cc
@@ -120,7 +120,7 @@
FrameData UnwindingMetadata::AnnotateFrame(unwindstack::FrameData frame) {
std::string build_id;
- if (frame.map_name != "") {
+ if (!frame.map_name.empty()) {
unwindstack::MapInfo* map_info = fd_maps.Find(frame.pc);
if (map_info)
build_id = map_info->GetBuildID();
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 3c7adfd..c814b6b 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -140,6 +140,8 @@
"../../base:unix_socket",
"../../tracing/core",
"../../tracing/ipc/producer",
+ "../common:callstack_trie",
+ "../common:interner",
"../common:unwind_support",
]
public_deps = [
@@ -156,7 +158,6 @@
"bookkeeping_dump.h",
"heapprofd_producer.cc",
"heapprofd_producer.h",
- "interner.h",
"java_hprof_producer.cc",
"java_hprof_producer.h",
"page_idle_checker.cc",
@@ -207,7 +208,6 @@
"bookkeeping_unittest.cc",
"client_unittest.cc",
"heapprofd_producer_unittest.cc",
- "interner_unittest.cc",
"page_idle_checker_unittest.cc",
"proc_utils_unittest.cc",
"sampler_unittest.cc",
diff --git a/src/profiling/memory/bookkeeping.cc b/src/profiling/memory/bookkeeping.cc
index bd6ee97..979d88f 100644
--- a/src/profiling/memory/bookkeeping.cc
+++ b/src/profiling/memory/bookkeeping.cc
@@ -24,6 +24,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/common/callstack_trie.h"
namespace perfetto {
namespace profiling {
@@ -146,82 +147,6 @@
return alloc.value.retain_max.max;
}
-GlobalCallstackTrie::Node* GlobalCallstackTrie::GetOrCreateChild(
- Node* self,
- const Interned<Frame>& loc) {
- Node* child = self->children_.Get(loc);
- if (!child)
- child = self->children_.Emplace(loc, ++next_callstack_id_, self);
- return child;
-}
-
-std::vector<Interned<Frame>> GlobalCallstackTrie::BuildCallstack(
- const Node* node) const {
- std::vector<Interned<Frame>> res;
- while (node != &root_) {
- res.emplace_back(node->location_);
- node = node->parent_;
- }
- return res;
-}
-
-GlobalCallstackTrie::Node* GlobalCallstackTrie::CreateCallsite(
- const std::vector<FrameData>& callstack) {
- Node* node = &root_;
- for (const FrameData& loc : callstack) {
- node = GetOrCreateChild(node, InternCodeLocation(loc));
- }
- return node;
-}
-
-void GlobalCallstackTrie::IncrementNode(Node* node) {
- while (node != nullptr) {
- node->ref_count_ += 1;
- node = node->parent_;
- }
-}
-
-void GlobalCallstackTrie::DecrementNode(Node* node) {
- PERFETTO_DCHECK(node->ref_count_ >= 1);
-
- bool delete_prev = false;
- Node* prev = nullptr;
- while (node != nullptr) {
- if (delete_prev)
- node->children_.Remove(*prev);
- node->ref_count_ -= 1;
- delete_prev = node->ref_count_ == 0;
- prev = node;
- node = node->parent_;
- }
-}
-
-Interned<Frame> GlobalCallstackTrie::InternCodeLocation(const FrameData& loc) {
- Mapping map(string_interner_.Intern(loc.build_id));
- map.exact_offset = loc.frame.map_exact_offset;
- map.start_offset = loc.frame.map_elf_start_offset;
- map.start = loc.frame.map_start;
- map.end = loc.frame.map_end;
- map.load_bias = loc.frame.map_load_bias;
- base::StringSplitter sp(loc.frame.map_name, '/');
- while (sp.Next())
- map.path_components.emplace_back(string_interner_.Intern(sp.cur_token()));
-
- Frame frame(mapping_interner_.Intern(std::move(map)),
- string_interner_.Intern(loc.frame.function_name),
- loc.frame.rel_pc);
-
- return frame_interner_.Intern(frame);
-}
-
-Interned<Frame> GlobalCallstackTrie::MakeRootFrame() {
- Mapping map(string_interner_.Intern(""));
-
- Frame frame(mapping_interner_.Intern(std::move(map)),
- string_interner_.Intern(""), 0);
-
- return frame_interner_.Intern(frame);
-}
} // namespace profiling
} // namespace perfetto
diff --git a/src/profiling/memory/bookkeeping.h b/src/profiling/memory/bookkeeping.h
index c157e07..7ae384c 100644
--- a/src/profiling/memory/bookkeeping.h
+++ b/src/profiling/memory/bookkeeping.h
@@ -18,13 +18,11 @@
#define SRC_PROFILING_MEMORY_BOOKKEEPING_H_
#include <map>
-#include <string>
#include <vector>
#include "perfetto/base/time.h"
-#include "perfetto/ext/base/lookup_set.h"
-#include "perfetto/ext/base/string_splitter.h"
-#include "src/profiling/memory/interner.h"
+#include "src/profiling/common/callstack_trie.h"
+#include "src/profiling/common/interner.h"
#include "src/profiling/memory/unwound_messages.h"
// Below is an illustration of the bookkeeping system state where
@@ -91,129 +89,6 @@
namespace perfetto {
namespace profiling {
-class HeapTracker;
-
-struct Mapping {
- Mapping(Interned<std::string> b) : build_id(std::move(b)) {}
-
- Interned<std::string> build_id;
- uint64_t exact_offset = 0;
- uint64_t start_offset = 0;
- uint64_t start = 0;
- uint64_t end = 0;
- uint64_t load_bias = 0;
- std::vector<Interned<std::string>> path_components{};
-
- bool operator<(const Mapping& other) const {
- return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
- path_components) <
- std::tie(other.build_id, other.exact_offset, other.start_offset,
- other.start, other.end, other.load_bias,
- other.path_components);
- }
- bool operator==(const Mapping& other) const {
- return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
- path_components) ==
- std::tie(other.build_id, other.exact_offset, other.start_offset,
- other.start, other.end, other.load_bias,
- other.path_components);
- }
-};
-
-struct Frame {
- Frame(Interned<Mapping> m, Interned<std::string> fn_name, uint64_t pc)
- : mapping(m), function_name(fn_name), rel_pc(pc) {}
- Interned<Mapping> mapping;
- Interned<std::string> function_name;
- uint64_t rel_pc;
-
- bool operator<(const Frame& other) const {
- return std::tie(mapping, function_name, rel_pc) <
- std::tie(other.mapping, other.function_name, other.rel_pc);
- }
-
- bool operator==(const Frame& other) const {
- return std::tie(mapping, function_name, rel_pc) ==
- std::tie(other.mapping, other.function_name, other.rel_pc);
- }
-};
-
-// Graph of function callsites. This is shared between heap dumps for
-// different processes. Each call site is represented by a
-// GlobalCallstackTrie::Node that is owned by the parent (i.e. calling)
-// callsite. It has a pointer to its parent, which means the function
-// call-graph can be reconstructed from a GlobalCallstackTrie::Node by walking
-// down the pointers to the parents.
-class GlobalCallstackTrie {
- public:
- // Node in a tree of function traces that resulted in an allocation. For
- // instance, if alloc_buf is called from foo and bar, which are called from
- // main, the tree looks as following.
- //
- // alloc_buf alloc_buf
- // | |
- // foo bar
- // \ /
- // main
- // |
- // libc_init
- // |
- // [root_]
- //
- // allocations_ will hold a map from the pointers returned from malloc to
- // alloc_buf to the leafs of this tree.
- class Node {
- public:
- // This is opaque except to GlobalCallstackTrie.
- friend class GlobalCallstackTrie;
-
- Node(Interned<Frame> frame) : Node(std::move(frame), 0, nullptr) {}
- Node(Interned<Frame> frame, uint64_t id)
- : Node(std::move(frame), id, nullptr) {}
- Node(Interned<Frame> frame, uint64_t id, Node* parent)
- : id_(id), parent_(parent), location_(std::move(frame)) {}
-
- uint64_t id() const { return id_; }
-
- private:
- Node* GetOrCreateChild(const Interned<Frame>& loc);
-
- uint64_t ref_count_ = 0;
- uint64_t id_;
- Node* const parent_;
- const Interned<Frame> location_;
- base::LookupSet<Node, const Interned<Frame>, &Node::location_> children_;
- };
-
- GlobalCallstackTrie() = default;
- GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
- GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
-
- // Moving this would invalidate the back pointers to the root_ node.
- GlobalCallstackTrie(GlobalCallstackTrie&&) = delete;
- GlobalCallstackTrie& operator=(GlobalCallstackTrie&&) = delete;
-
- Node* CreateCallsite(const std::vector<FrameData>& locs);
- static void DecrementNode(Node* node);
- static void IncrementNode(Node* node);
-
- std::vector<Interned<Frame>> BuildCallstack(const Node* node) const;
-
- private:
- Node* GetOrCreateChild(Node* self, const Interned<Frame>& loc);
-
- Interned<Frame> InternCodeLocation(const FrameData& loc);
- Interned<Frame> MakeRootFrame();
-
- Interner<std::string> string_interner_;
- Interner<Mapping> mapping_interner_;
- Interner<Frame> frame_interner_;
-
- uint64_t next_callstack_id_ = 0;
-
- Node root_{MakeRootFrame(), ++next_callstack_id_};
-};
-
// Snapshot for memory allocations of a particular process. Shares callsites
// with other processes.
class HeapTracker {
@@ -460,36 +335,4 @@
} // namespace profiling
} // namespace perfetto
-namespace std {
-template <>
-struct hash<::perfetto::profiling::Mapping> {
- using argument_type = ::perfetto::profiling::Mapping;
- using result_type = size_t;
- result_type operator()(const argument_type& mapping) {
- size_t h =
- std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id());
- h ^= std::hash<uint64_t>{}(mapping.exact_offset);
- h ^= std::hash<uint64_t>{}(mapping.start_offset);
- h ^= std::hash<uint64_t>{}(mapping.start);
- h ^= std::hash<uint64_t>{}(mapping.end);
- h ^= std::hash<uint64_t>{}(mapping.load_bias);
- for (const auto& path : mapping.path_components)
- h ^= std::hash<uint64_t>{}(path.id());
- return h;
- }
-};
-
-template <>
-struct hash<::perfetto::profiling::Frame> {
- using argument_type = ::perfetto::profiling::Frame;
- using result_type = size_t;
- result_type operator()(const argument_type& frame) {
- size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id());
- h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id());
- h ^= std::hash<uint64_t>{}(frame.rel_pc);
- return h;
- }
-};
-} // namespace std
-
#endif // SRC_PROFILING_MEMORY_BOOKKEEPING_H_
diff --git a/src/profiling/memory/bookkeeping_dump.h b/src/profiling/memory/bookkeeping_dump.h
index 517d02c..a983781 100644
--- a/src/profiling/memory/bookkeeping_dump.h
+++ b/src/profiling/memory/bookkeeping_dump.h
@@ -29,8 +29,8 @@
#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/profiling/common/interner.h"
#include "src/profiling/memory/bookkeeping.h"
-#include "src/profiling/memory/interner.h"
namespace perfetto {
namespace profiling {
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 8969980..cb85212 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -411,6 +411,12 @@
return ret;
}
+// TODO(rsavitski): reconsider posting bookkeping tasks directly (without a
+// queue). Doesn't allow flushing only a given instance's backlog, but
+// we shouldn't have the main thread so backlogged for it to matter (reading
+// will break before that). On the other hand, the tick approach would let us
+// rate-limit bookkeeping more directly (by bounding the number of events
+// processed).
void PerfProducer::TickDataSourceBookkeep(DataSourceInstanceID ds_id) {
auto q_it = bookkeping_queues_.find(ds_id);
if (q_it == bookkeping_queues_.end()) {