profiling: Write bookkeeping to trace
Also retain more information about mappings.
See example trace: https://fmayer.users.x20web.corp.google.com/www/heapprofd_trace.textpb
Test: m
Test: flash sailfish
Test: profile system_server
Change-Id: Ia5db70fbffbf9ec58a6d7a942d52d7df2f2d409a
diff --git a/Android.bp b/Android.bp
index cfeeb8d..3881385 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
":perfetto_protos_perfetto_trace_trusted_lite_gen",
@@ -56,7 +57,6 @@
"src/profiling/memory/main.cc",
"src/profiling/memory/record_reader.cc",
"src/profiling/memory/socket_listener.cc",
- "src/profiling/memory/string_interner.cc",
"src/profiling/memory/unwinding.cc",
"src/profiling/memory/wire_protocol.cc",
"src/protozero/message.cc",
@@ -113,6 +113,7 @@
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
@@ -180,6 +181,7 @@
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
":perfetto_protos_perfetto_trace_trusted_lite_gen",
@@ -279,6 +281,7 @@
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
@@ -307,6 +310,7 @@
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
":perfetto_protos_perfetto_trace_trusted_lite_gen",
@@ -391,6 +395,7 @@
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
@@ -450,6 +455,8 @@
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_lite_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
@@ -564,6 +571,8 @@
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
@@ -3600,6 +3609,74 @@
],
}
+// GN target: //protos/perfetto/trace/profiling:lite_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_profiling_lite_gen",
+ srcs: [
+ "protos/perfetto/trace/profiling/profile_packet.proto",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pb.cc",
+ ],
+}
+
+// GN target: //protos/perfetto/trace/profiling:lite_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
+ srcs: [
+ "protos/perfetto/trace/profiling/profile_packet.proto",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pb.h",
+ ],
+ export_include_dirs: [
+ "protos",
+ ],
+}
+
+// GN target: //protos/perfetto/trace/profiling:zero_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_profiling_zero_gen",
+ srcs: [
+ "protos/perfetto/trace/profiling/profile_packet.proto",
+ ],
+ tools: [
+ "aprotoc",
+ "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.cc",
+ ],
+}
+
+// GN target: //protos/perfetto/trace/profiling:zero_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+ srcs: [
+ "protos/perfetto/trace/profiling/profile_packet.proto",
+ ],
+ tools: [
+ "aprotoc",
+ "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.h",
+ ],
+ export_include_dirs: [
+ "protos",
+ ],
+}
+
// GN target: //protos/perfetto/trace/ps:lite_gen
genrule {
name: "perfetto_protos_perfetto_trace_ps_lite_gen",
@@ -4158,6 +4235,7 @@
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
":perfetto_protos_perfetto_trace_trusted_lite_gen",
@@ -4239,6 +4317,7 @@
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
@@ -4255,6 +4334,7 @@
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
"perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
@@ -4281,6 +4361,7 @@
":perfetto_protos_perfetto_trace_ftrace_lite_gen",
":perfetto_protos_perfetto_trace_lite_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_lite_gen",
":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
],
@@ -4300,6 +4381,7 @@
"perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
],
@@ -4311,6 +4393,7 @@
"perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
],
@@ -4340,6 +4423,8 @@
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
":perfetto_protos_perfetto_trace_lite_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_zero_gen",
":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
@@ -4406,14 +4491,13 @@
"src/profiling/memory/client_unittest.cc",
"src/profiling/memory/heapprofd_integrationtest.cc",
"src/profiling/memory/heapprofd_producer.cc",
+ "src/profiling/memory/interner_unittest.cc",
"src/profiling/memory/record_reader.cc",
"src/profiling/memory/record_reader_unittest.cc",
"src/profiling/memory/sampler.cc",
"src/profiling/memory/sampler_unittest.cc",
"src/profiling/memory/socket_listener.cc",
"src/profiling/memory/socket_listener_unittest.cc",
- "src/profiling/memory/string_interner.cc",
- "src/profiling/memory/string_interner_unittest.cc",
"src/profiling/memory/unwinding.cc",
"src/profiling/memory/unwinding_unittest.cc",
"src/profiling/memory/wire_protocol.cc",
@@ -4550,6 +4634,8 @@
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
@@ -4589,6 +4675,7 @@
":perfetto_protos_perfetto_trace_ftrace_lite_gen",
":perfetto_protos_perfetto_trace_lite_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
+ ":perfetto_protos_perfetto_trace_profiling_lite_gen",
":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
"tools/trace_to_text/ftrace_event_formatter.cc",
@@ -4615,6 +4702,7 @@
"perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
],
diff --git a/BUILD.gn b/BUILD.gn
index a6d4a63..f5a0eb1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -54,8 +54,6 @@
":traced",
":traced_probes",
"protos/perfetto/config:merged_config", # For syntax-checking the proto.
- "protos/perfetto/profiling:lite", # For syntax-checking the proto.
- # TODO(fmayer): Remove once used.
"src/ipc/protoc_plugin:ipc_plugin($host_toolchain)",
"tools:protoc_helper",
]
@@ -298,6 +296,7 @@
executable("heapprofd") {
deps = [
"gn:default_deps",
+ "protos/perfetto/trace:zero",
"src/base",
"src/base:unix_socket",
"src/profiling/memory:daemon",
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 7cef317..a29eddc 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -38,6 +38,7 @@
"chrome:zero",
"filesystem:zero",
"ftrace:zero",
+ "profiling:zero",
"ps:zero",
"sys_stats:zero",
]
@@ -56,6 +57,7 @@
"chrome:lite",
"filesystem:lite",
"ftrace:lite",
+ "profiling:lite",
"ps:lite",
"sys_stats:lite",
]
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 8f00dcf..e7a85e4 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -192,6 +192,7 @@
// removed field with id 33
// removed field with id 34
// removed field with id 35
+ // removed field with id 37
// This field is emitted at periodic intervals (~10s) and
// contains always the binary representation of the UUID
diff --git a/protos/perfetto/profiling/BUILD.gn b/protos/perfetto/trace/profiling/BUILD.gn
similarity index 88%
rename from protos/perfetto/profiling/BUILD.gn
rename to protos/perfetto/trace/profiling/BUILD.gn
index 4eaf38b..a16d5eb 100644
--- a/protos/perfetto/profiling/BUILD.gn
+++ b/protos/perfetto/trace/profiling/BUILD.gn
@@ -12,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import("../../../gn/perfetto.gni")
-import("../../../gn/proto_library.gni")
-import("../../../gn/protozero_library.gni")
+import("../../../../gn/perfetto.gni")
+import("../../../../gn/proto_library.gni")
+import("../../../../gn/protozero_library.gni")
profiling_proto_names = [ "profile_packet.proto" ]
diff --git a/protos/perfetto/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
similarity index 96%
rename from protos/perfetto/profiling/profile_packet.proto
rename to protos/perfetto/trace/profiling/profile_packet.proto
index 23943b7..f6bf95c 100644
--- a/protos/perfetto/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -17,7 +17,7 @@
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
-package perfetto.proto;
+package perfetto.protos;
message ProfilePacket {
// either a function or library name.
@@ -40,7 +40,7 @@
message Callstack {
optional uint64 id = 1;
// Frames of this callstack. Bottom frame first.
- repeated Frame frames = 2;
+ repeated uint64 frame_ids = 2;
}
repeated Mapping mappings = 4;
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 7d0f8af..e7852a5 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -28,6 +28,7 @@
import "perfetto/trace/sys_stats/sys_stats.proto";
import "perfetto/trace/test_event.proto";
import "perfetto/trace/trace_stats.proto";
+import "perfetto/trace/profiling/profile_packet.proto";
package perfetto.protos;
@@ -55,6 +56,7 @@
TraceConfig trace_config = 33;
FtraceStats ftrace_stats = 34;
TraceStats trace_stats = 35;
+ ProfilePacket profile_packet = 37;
// This field is emitted at periodic intervals (~10s) and
// contains always the binary representation of the UUID
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 8fc7552..9514ea9 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -48,18 +48,21 @@
"../../tracing",
"../../tracing:ipc",
]
+ public_deps = [
+ "../../../protos/perfetto/trace:zero",
+ "../../../protos/perfetto/trace/profiling:zero",
+ ]
sources = [
"bookkeeping.cc",
"bookkeeping.h",
"heapprofd_producer.cc",
"heapprofd_producer.h",
+ "interner.h",
"queue_messages.h",
"record_reader.cc",
"record_reader.h",
"socket_listener.cc",
"socket_listener.h",
- "string_interner.cc",
- "string_interner.h",
"unwinding.cc",
"unwinding.h",
]
@@ -99,10 +102,10 @@
"bounded_queue_unittest.cc",
"client_unittest.cc",
"heapprofd_integrationtest.cc",
+ "interner_unittest.cc",
"record_reader_unittest.cc",
"sampler_unittest.cc",
"socket_listener_unittest.cc",
- "string_interner_unittest.cc",
"unwinding_unittest.cc",
"wire_protocol_unittest.cc",
]
diff --git a/src/profiling/memory/bookkeeping.cc b/src/profiling/memory/bookkeeping.cc
index 51e0d30..8f78d25 100644
--- a/src/profiling/memory/bookkeeping.cc
+++ b/src/profiling/memory/bookkeeping.cc
@@ -27,19 +27,22 @@
namespace perfetto {
namespace profiling {
+namespace {
+using ::perfetto::protos::pbzero::ProfilePacket;
+}
GlobalCallstackTrie::Node* GlobalCallstackTrie::Node::GetOrCreateChild(
- const InternedCodeLocation& loc) {
+ const Interner<Frame>::Interned& loc) {
Node* child = children_.Get(loc);
if (!child)
child = children_.Emplace(loc, this);
return child;
}
-std::vector<InternedCodeLocation> GlobalCallstackTrie::Node::BuildCallstack()
- const {
+std::vector<Interner<Frame>::Interned>
+GlobalCallstackTrie::Node::BuildCallstack() const {
const Node* node = this;
- std::vector<InternedCodeLocation> res;
+ std::vector<Interner<Frame>::Interned> res;
while (node) {
res.emplace_back(node->location_);
node = node->parent_;
@@ -47,10 +50,11 @@
return res;
}
-void HeapTracker::RecordMalloc(const std::vector<CodeLocation>& callstack,
- uint64_t address,
- uint64_t size,
- uint64_t sequence_number) {
+void HeapTracker::RecordMalloc(
+ const std::vector<unwindstack::FrameData>& callstack,
+ uint64_t address,
+ uint64_t size,
+ uint64_t sequence_number) {
auto it = allocations_.find(address);
if (it != allocations_.end()) {
if (it->second.sequence_number > sequence_number) {
@@ -104,28 +108,22 @@
allocations_.erase(leaf_it);
}
-void HeapTracker::Dump(int fd) {
- // TODO(fmayer): This should dump protocol buffers into the perfetto service.
- // For now, output a text file compatible with flamegraph.pl.
+void HeapTracker::Dump(
+ ProfilePacket::ProcessHeapSamples* proto,
+ std::set<GlobalCallstackTrie::Node*>* callstacks_to_dump) {
for (const auto& p : allocations_) {
- std::string data;
const Allocation& alloc = p.second;
- const std::vector<InternedCodeLocation> callstack =
- alloc.node->BuildCallstack();
- for (auto it = callstack.begin(); it != callstack.end(); ++it) {
- if (it != callstack.begin())
- data += ";";
- data += it->function_name.str();
- }
- data += " " + std::to_string(alloc.total_size) + "\n";
- base::WriteAll(fd, data.c_str(), data.size());
+ callstacks_to_dump->emplace(alloc.node);
+ ProfilePacket::HeapSample* sample = proto->add_samples();
+ sample->set_callstack_id(alloc.node->id());
+ sample->set_cumulative_allocated(alloc.total_size);
}
}
uint64_t GlobalCallstackTrie::GetCumSizeForTesting(
- const std::vector<CodeLocation>& callstack) {
+ const std::vector<unwindstack::FrameData>& callstack) {
Node* node = &root_;
- for (const CodeLocation& loc : callstack) {
+ for (const unwindstack::FrameData& loc : callstack) {
node = node->children_.Get(InternCodeLocation(loc));
if (node == nullptr)
return 0;
@@ -134,11 +132,11 @@
}
GlobalCallstackTrie::Node* GlobalCallstackTrie::IncrementCallsite(
- const std::vector<CodeLocation>& callstack,
+ const std::vector<unwindstack::FrameData>& callstack,
uint64_t size) {
Node* node = &root_;
node->cum_size_ += size;
- for (const CodeLocation& loc : callstack) {
+ for (const unwindstack::FrameData& loc : callstack) {
node = node->GetOrCreateChild(InternCodeLocation(loc));
node->cum_size_ += size;
}
@@ -160,6 +158,75 @@
}
}
+Interner<Frame>::Interned GlobalCallstackTrie::InternCodeLocation(
+ const unwindstack::FrameData& loc) {
+ Mapping map{};
+ map.offset = loc.map_offset;
+ map.start = loc.map_start;
+ map.end = loc.map_end;
+ map.load_bias = loc.map_load_bias;
+ base::StringSplitter sp(loc.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.function_name), loc.rel_pc);
+
+ return frame_interner_.Intern(frame);
+}
+
+Interner<Frame>::Interned GlobalCallstackTrie::MakeRootFrame() {
+ Mapping map{};
+
+ Frame frame(mapping_interner_.Intern(std::move(map)),
+ string_interner_.Intern(""), 0);
+
+ return frame_interner_.Intern(frame);
+}
+
+void DumpState::WriteMap(ProfilePacket* packet,
+ const Interner<Mapping>::Interned map) {
+ auto map_it_and_inserted = dumped_mappings.emplace(map.id());
+ if (map_it_and_inserted.second) {
+ for (const Interner<std::string>::Interned& str : map->path_components)
+ WriteString(packet, str);
+
+ auto mapping = packet->add_mappings();
+ mapping->set_offset(map->offset);
+ mapping->set_start(map->start);
+ mapping->set_end(map->end);
+ mapping->set_load_bias(map->load_bias);
+ for (const Interner<std::string>::Interned& str : map->path_components)
+ mapping->add_path_string_ids(str.id());
+ }
+}
+
+void DumpState::WriteFrame(ProfilePacket* packet,
+ Interner<Frame>::Interned frame) {
+ WriteMap(packet, frame->mapping);
+ WriteString(packet, frame->function_name);
+ bool inserted;
+ std::tie(std::ignore, inserted) = dumped_frames.emplace(frame.id());
+ if (inserted) {
+ auto frame_proto = packet->add_frames();
+ frame_proto->set_id(frame.id());
+ frame_proto->set_function_name_id(frame->function_name.id());
+ frame_proto->set_mapping_id(frame->mapping.id());
+ frame_proto->set_rel_pc(frame->rel_pc);
+ }
+}
+
+void DumpState::WriteString(ProfilePacket* packet,
+ const Interner<std::string>::Interned& str) {
+ bool inserted;
+ std::tie(std::ignore, inserted) = dumped_strings.emplace(str.id());
+ if (inserted) {
+ auto interned_string = packet->add_strings();
+ interned_string->set_id(str.id());
+ interned_string->set_str(str->c_str(), str->size());
+ }
+}
+
void BookkeepingThread::HandleBookkeepingRecord(BookkeepingRecord* rec) {
BookkeepingData* bookkeeping_data = nullptr;
if (rec->pid != 0) {
@@ -178,20 +245,45 @@
if (!trace_writer)
return;
PERFETTO_LOG("Dumping heaps");
+ std::set<GlobalCallstackTrie::Node*> callstacks_to_dump;
+ TraceWriter::TracePacketHandle trace_packet =
+ trace_writer->NewTracePacket();
+ auto profile_packet = trace_packet->set_profile_packet();
+ for (const pid_t pid : dump_rec.pids) {
+ ProfilePacket::ProcessHeapSamples* sample =
+ profile_packet->add_process_dumps();
+ auto it = bookkeeping_data_.find(pid);
+ if (it == bookkeeping_data_.end())
+ continue;
+
+ PERFETTO_LOG("Dumping %d ", it->first);
+ it->second.heap_tracker.Dump(sample, &callstacks_to_dump);
+ }
+
+ // TODO(fmayer): For incremental dumps, this should be owned by the
+ // producer. This way we can keep track on what we dumped accross multiple
+ // dumps.
+ DumpState dump_state;
+
+ for (GlobalCallstackTrie::Node* node : callstacks_to_dump) {
+ // There need to be two separate loops over built_callstack because
+ // protozero cannot interleave different messages.
+ auto built_callstack = node->BuildCallstack();
+ for (const Interner<Frame>::Interned& frame : built_callstack)
+ dump_state.WriteFrame(profile_packet, frame);
+ ProfilePacket::Callstack* callstack = profile_packet->add_callstacks();
+ callstack->set_id(node->id());
+ for (const Interner<Frame>::Interned& frame : built_callstack)
+ callstack->add_frame_ids(frame.id());
+ }
+
+ // We cannot garbage collect until we have finished dumping, as the state
+ // in DumpState points into the GlobalCallstackTrie.
for (const pid_t pid : dump_rec.pids) {
auto it = bookkeeping_data_.find(pid);
if (it == bookkeeping_data_.end())
continue;
- std::string dump_file_name = file_name_ + "." + std::to_string(it->first);
- PERFETTO_LOG("Dumping %d to %s", it->first, dump_file_name.c_str());
- base::ScopedFile fd =
- base::OpenFile(dump_file_name, O_WRONLY | O_CREAT, 0644);
- if (fd)
- it->second.heap_tracker.Dump(fd.get());
- else
- PERFETTO_PLOG("Failed to open %s", dump_file_name.c_str());
- // Garbage collect for processes that already went away.
if (it->second.ref_count == 0) {
std::lock_guard<std::mutex> l(bookkeeping_mutex_);
it = bookkeeping_data_.erase(it);
@@ -211,11 +303,8 @@
}
} else if (rec->record_type == BookkeepingRecord::Type::Malloc) {
AllocRecord& alloc_rec = rec->alloc_record;
- std::vector<CodeLocation> code_locations;
- for (unwindstack::FrameData& frame : alloc_rec.frames)
- code_locations.emplace_back(frame.map_name, frame.function_name);
bookkeeping_data->heap_tracker.RecordMalloc(
- code_locations, alloc_rec.alloc_metadata.alloc_address,
+ alloc_rec.frames, alloc_rec.alloc_metadata.alloc_address,
alloc_rec.alloc_metadata.total_size,
alloc_rec.alloc_metadata.sequence_number);
} else {
diff --git a/src/profiling/memory/bookkeeping.h b/src/profiling/memory/bookkeeping.h
index e90d957..2ec1491 100644
--- a/src/profiling/memory/bookkeeping.h
+++ b/src/profiling/memory/bookkeeping.h
@@ -17,47 +17,59 @@
#ifndef SRC_PROFILING_MEMORY_BOOKKEEPING_H_
#define SRC_PROFILING_MEMORY_BOOKKEEPING_H_
-#include "perfetto/base/lookup_set.h"
-#include "src/profiling/memory/bounded_queue.h"
-#include "src/profiling/memory/queue_messages.h"
-#include "src/profiling/memory/string_interner.h"
-
#include <map>
#include <string>
#include <vector>
+#include "perfetto/base/lookup_set.h"
+#include "perfetto/base/string_splitter.h"
+#include "perfetto/trace/profiling/profile_packet.pbzero.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+#include "src/profiling/memory/bounded_queue.h"
+#include "src/profiling/memory/interner.h"
+#include "src/profiling/memory/queue_messages.h"
+
namespace perfetto {
namespace profiling {
class HeapTracker;
-struct CodeLocation {
- CodeLocation(std::string map_n, std::string function_n)
- : map_name(std::move(map_n)), function_name(std::move(function_n)) {}
+struct Mapping {
+ uint64_t build_id;
+ uint64_t offset;
+ uint64_t start;
+ uint64_t end;
+ uint64_t load_bias;
+ std::vector<Interner<std::string>::Interned> path_components;
- std::string map_name;
- std::string function_name;
+ bool operator<(const Mapping& other) const {
+ return std::tie(build_id, offset, start, end, load_bias, path_components) <
+ std::tie(other.build_id, other.offset, other.start, other.end,
+ other.load_bias, other.path_components);
+ }
};
-// Internal data-structure for GlobalCallstackTrie to save memory if the same
-// function is named multiple times.
-struct InternedCodeLocation {
- StringInterner::InternedString map_name;
- StringInterner::InternedString function_name;
+struct Frame {
+ Frame(Interner<Mapping>::Interned m,
+ Interner<std::string>::Interned fn_name,
+ uint64_t pc)
+ : mapping(m), function_name(fn_name), rel_pc(pc) {}
+ Interner<Mapping>::Interned mapping;
+ Interner<std::string>::Interned function_name;
+ uint64_t rel_pc;
- bool operator<(const InternedCodeLocation& other) const {
- if (map_name.id() == other.map_name.id())
- return function_name.id() < other.function_name.id();
- return map_name.id() < other.map_name.id();
+ 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.
+// 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
@@ -81,19 +93,20 @@
// This is opaque except to GlobalCallstackTrie.
friend class GlobalCallstackTrie;
- Node(InternedCodeLocation location) : Node(std::move(location), nullptr) {}
- Node(InternedCodeLocation location, Node* parent)
- : parent_(parent), location_(std::move(location)) {}
+ Node(Interner<Frame>::Interned frame) : Node(std::move(frame), nullptr) {}
+ Node(Interner<Frame>::Interned frame, Node* parent)
+ : parent_(parent), location_(std::move(frame)) {}
- std::vector<InternedCodeLocation> BuildCallstack() const;
+ std::vector<Interner<Frame>::Interned> BuildCallstack() const;
+ uintptr_t id() const { return reinterpret_cast<uintptr_t>(this); }
private:
- Node* GetOrCreateChild(const InternedCodeLocation& loc);
+ Node* GetOrCreateChild(const Interner<Frame>::Interned& loc);
uint64_t cum_size_ = 0;
Node* const parent_;
- const InternedCodeLocation location_;
- base::LookupSet<Node, const InternedCodeLocation, &Node::location_>
+ const Interner<Frame>::Interned location_;
+ base::LookupSet<Node, const Interner<Frame>::Interned, &Node::location_>
children_;
};
@@ -101,18 +114,35 @@
GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
- uint64_t GetCumSizeForTesting(const std::vector<CodeLocation>& stack);
- Node* IncrementCallsite(const std::vector<CodeLocation>& locs, uint64_t size);
+ uint64_t GetCumSizeForTesting(
+ const std::vector<unwindstack::FrameData>& stack);
+ Node* IncrementCallsite(const std::vector<unwindstack::FrameData>& locs,
+ uint64_t size);
static void DecrementNode(Node* node, uint64_t size);
private:
- InternedCodeLocation InternCodeLocation(const CodeLocation& loc) {
- return {interner_.Intern(loc.map_name),
- interner_.Intern(loc.function_name)};
- }
+ Interner<Frame>::Interned InternCodeLocation(
+ const unwindstack::FrameData& loc);
+ Interner<Frame>::Interned MakeRootFrame();
- StringInterner interner_;
- Node root_{{interner_.Intern(""), interner_.Intern("")}};
+ Interner<std::string> string_interner_;
+ Interner<Mapping> mapping_interner_;
+ Interner<Frame> frame_interner_;
+
+ Node root_{MakeRootFrame()};
+};
+
+struct DumpState {
+ void WriteMap(protos::pbzero::ProfilePacket* packet,
+ const Interner<Mapping>::Interned map);
+ void WriteFrame(protos::pbzero::ProfilePacket* packet,
+ const Interner<Frame>::Interned frame);
+ void WriteString(protos::pbzero::ProfilePacket* packet,
+ const Interner<std::string>::Interned& str);
+
+ std::set<InternID> dumped_strings;
+ std::set<InternID> dumped_frames;
+ std::set<InternID> dumped_mappings;
};
// Snapshot for memory allocations of a particular process. Shares callsites
@@ -123,12 +153,13 @@
explicit HeapTracker(GlobalCallstackTrie* callsites)
: callsites_(callsites) {}
- void RecordMalloc(const std::vector<CodeLocation>& stack,
+ void RecordMalloc(const std::vector<unwindstack::FrameData>& stack,
uint64_t address,
uint64_t size,
uint64_t sequence_number);
void RecordFree(uint64_t address, uint64_t sequence_number);
- void Dump(int fd);
+ void Dump(protos::pbzero::ProfilePacket::ProcessHeapSamples* proto,
+ std::set<GlobalCallstackTrie::Node*>* callstacks_to_dump);
private:
static constexpr uint64_t kNoopFree = 0;
@@ -195,7 +226,8 @@
};
struct BookkeepingData {
- // Ownership of callsites remains with caller and has to outlive this object.
+ // Ownership of callsites remains with caller and has to outlive this
+ // object.
explicit BookkeepingData(GlobalCallstackTrie* callsites)
: heap_tracker(callsites) {}
diff --git a/src/profiling/memory/bookkeeping_unittest.cc b/src/profiling/memory/bookkeeping_unittest.cc
index a000cce..8049ae6 100644
--- a/src/profiling/memory/bookkeeping_unittest.cc
+++ b/src/profiling/memory/bookkeeping_unittest.cc
@@ -23,16 +23,39 @@
namespace profiling {
namespace {
-std::vector<CodeLocation> stack() {
- return {
- {"map1", "fun1"}, {"map2", "fun2"},
- };
+std::vector<unwindstack::FrameData> stack() {
+ std::vector<unwindstack::FrameData> res;
+ unwindstack::FrameData data{};
+ data.function_name = "fun1";
+ data.map_name = "map1";
+ res.emplace_back(std::move(data));
+ data = {};
+ data.function_name = "fun2";
+ data.map_name = "map2";
+ res.emplace_back(std::move(data));
+ return res;
}
-std::vector<CodeLocation> stack2() {
- return {
- {"map1", "fun1"}, {"map3", "fun3"},
- };
+std::vector<unwindstack::FrameData> stack2() {
+ std::vector<unwindstack::FrameData> res;
+ unwindstack::FrameData data{};
+ data.function_name = "fun1";
+ data.map_name = "map1";
+ res.emplace_back(std::move(data));
+ data = {};
+ data.function_name = "fun3";
+ data.map_name = "map3";
+ res.emplace_back(std::move(data));
+ return res;
+}
+
+std::vector<unwindstack::FrameData> topframe() {
+ std::vector<unwindstack::FrameData> res;
+ unwindstack::FrameData data{};
+ data.function_name = "fun1";
+ data.map_name = "map1";
+ res.emplace_back(std::move(data));
+ return res;
}
TEST(BookkeepingTest, Basic) {
@@ -42,11 +65,11 @@
hd.RecordMalloc(stack(), 1, 5, sequence_number++);
hd.RecordMalloc(stack2(), 2, 2, sequence_number++);
- ASSERT_EQ(c.GetCumSizeForTesting({{"map1", "fun1"}}), 7);
+ ASSERT_EQ(c.GetCumSizeForTesting(topframe()), 7);
hd.RecordFree(2, sequence_number++);
- ASSERT_EQ(c.GetCumSizeForTesting({{"map1", "fun1"}}), 5);
+ ASSERT_EQ(c.GetCumSizeForTesting(topframe()), 5);
hd.RecordFree(1, sequence_number++);
- ASSERT_EQ(c.GetCumSizeForTesting({{"map1", "fun1"}}), 0);
+ ASSERT_EQ(c.GetCumSizeForTesting(topframe()), 0);
}
TEST(BookkeepingTest, TwoHeapTrackers) {
@@ -58,9 +81,9 @@
hd.RecordMalloc(stack(), 1, 5, sequence_number++);
hd2.RecordMalloc(stack2(), 2, 2, sequence_number++);
- ASSERT_EQ(c.GetCumSizeForTesting({{"map1", "fun1"}}), 7);
+ ASSERT_EQ(c.GetCumSizeForTesting(topframe()), 7);
}
- ASSERT_EQ(c.GetCumSizeForTesting({{"map1", "fun1"}}), 5);
+ ASSERT_EQ(c.GetCumSizeForTesting(topframe()), 5);
}
TEST(BookkeepingTest, ReplaceAlloc) {
diff --git a/src/profiling/memory/interner.h b/src/profiling/memory/interner.h
new file mode 100644
index 0000000..5de578f
--- /dev/null
+++ b/src/profiling/memory/interner.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_MEMORY_INTERNER_H_
+#define SRC_PROFILING_MEMORY_INTERNER_H_
+
+#include <stddef.h>
+#include <set>
+
+namespace perfetto {
+namespace profiling {
+
+using InternID = uintptr_t;
+
+template <typename T>
+class Interner {
+ private:
+ struct Entry {
+ Entry(T d, Interner<T>* in) : data(std::move(d)), interner(in) {}
+ bool operator<(const Entry& other) const { return data < other.data; }
+
+ const T data;
+ size_t ref_count = 0;
+ Interner<T>* interner;
+ };
+
+ public:
+ class Interned {
+ public:
+ friend class Interner<T>;
+ Interned(Entry* entry) : entry_(entry) {}
+ Interned(const Interned& other) : entry_(other.entry_) {
+ if (entry_ != nullptr)
+ entry_->ref_count++;
+ }
+
+ Interned(Interned&& other) noexcept : entry_(other.entry_) {
+ other.entry_ = nullptr;
+ }
+
+ Interned& operator=(Interned&& other) {
+ entry_ = other.entry_;
+ other.entry_ = nullptr;
+ return *this;
+ }
+
+ Interned& operator=(Interned& other) noexcept {
+ entry_ = other.entry_;
+ if (entry_ != nullptr)
+ entry_->ref_count++;
+ return *this;
+ }
+
+ const T& data() const { return entry_->data; }
+
+ InternID id() const { return reinterpret_cast<InternID>(entry_); }
+
+ ~Interned() {
+ if (entry_ != nullptr)
+ entry_->interner->Return(entry_);
+ }
+
+ bool operator<(const Interned& other) const {
+ if (entry_ == nullptr || other.entry_ == nullptr)
+ return entry_ < other.entry_;
+ return *entry_ < *(other.entry_);
+ }
+
+ const T* operator->() const { return &entry_->data; }
+
+ private:
+ Interner::Entry* entry_;
+ };
+
+ Interned Intern(const T& data) {
+ auto itr = entries_.emplace(data, this);
+ Entry& entry = const_cast<Entry&>(*itr.first);
+ entry.ref_count++;
+ return Interned(&entry);
+ }
+ size_t entry_count_for_testing() { return entries_.size(); }
+
+ private:
+ void Return(Entry* entry) {
+ if (--entry->ref_count == 0)
+ entries_.erase(*entry);
+ }
+ std::set<Entry> entries_;
+ static_assert(sizeof(Interned) == sizeof(void*),
+ "interned things should be small");
+};
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // SRC_PROFILING_MEMORY_INTERNER_H_
diff --git a/src/profiling/memory/interner_unittest.cc b/src/profiling/memory/interner_unittest.cc
new file mode 100644
index 0000000..d94fa17
--- /dev/null
+++ b/src/profiling/memory/interner_unittest.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/interner.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+TEST(InternerStringTest, Basic) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ ASSERT_EQ(interned_str.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 0);
+}
+
+TEST(InternerStringTest, TwoStrings) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ Interner<std::string>::Interned other_interned_str = interner.Intern("bar");
+ ASSERT_EQ(interned_str.data(), "foo");
+ ASSERT_EQ(other_interned_str.data(), "bar");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 0);
+}
+
+TEST(InternerStringTest, TwoReferences) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ ASSERT_EQ(interned_str.data(), "foo");
+ Interner<std::string>::Interned interned_str2 = interner.Intern("foo");
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str2.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 0);
+}
+
+TEST(InternerStringTest, Move) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ {
+ Interner<std::string>::Interned interned_str2(std::move(interned_str));
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str2.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 0);
+ }
+}
+
+TEST(InternerStringTest, Copy) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ {
+ Interner<std::string>::Interned interned_str2(interned_str);
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str2.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str.data(), "foo");
+ }
+}
+
+TEST(InternerStringTest, MoveAssign) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ {
+ Interner<std::string>::Interned interned_str2 = std::move(interned_str);
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str2.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 0);
+ }
+}
+
+TEST(InternerStringTest, CopyAssign) {
+ Interner<std::string> interner;
+ {
+ Interner<std::string>::Interned interned_str = interner.Intern("foo");
+ {
+ Interner<std::string>::Interned interned_str2 = interned_str;
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str2.data(), "foo");
+ }
+ ASSERT_EQ(interner.entry_count_for_testing(), 1);
+ ASSERT_EQ(interned_str.data(), "foo");
+ }
+}
+
+} // namespace
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/memory/string_interner.cc b/src/profiling/memory/string_interner.cc
deleted file mode 100644
index f913fcf..0000000
--- a/src/profiling/memory/string_interner.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/profiling/memory/string_interner.h"
-
-namespace perfetto {
-namespace profiling {
-
-StringInterner::Entry::Entry(std::string s, StringInterner* in)
- : string(std::move(s)), interner(in) {}
-
-bool StringInterner::Entry::operator<(const Entry& other) const {
- return string < other.string;
-}
-
-StringInterner::InternedString::InternedString(Entry* entry) : entry_(entry) {}
-
-const std::string& StringInterner::InternedString::str() const {
- return entry_->string;
-}
-
-void* StringInterner::InternedString::id() const {
- return entry_;
-}
-
-StringInterner::InternedString::~InternedString() {
- if (entry_ != nullptr)
- entry_->interner->Return(entry_);
-}
-
-StringInterner::InternedString StringInterner::Intern(const std::string& str) {
- auto itr = entries_.emplace(str, this);
- Entry& entry = const_cast<Entry&>(*itr.first);
- entry.ref_count++;
- return InternedString(&entry);
-}
-
-StringInterner::InternedString::InternedString(const InternedString& other)
- : entry_(other.entry_) {
- if (entry_ != nullptr)
- entry_->ref_count++;
-}
-
-StringInterner::InternedString::InternedString(InternedString&& other) noexcept
- : entry_(other.entry_) {
- other.entry_ = nullptr;
-}
-
-StringInterner::InternedString& StringInterner::InternedString::operator=(
- InternedString other) {
- std::swap(*this, other);
- return *this;
-}
-
-size_t StringInterner::entry_count_for_testing() {
- return entries_.size();
-}
-
-void StringInterner::Return(Entry* entry) {
- if (--entry->ref_count == 0)
- entries_.erase(*entry);
-}
-
-} // namespace profiling
-} // namespace perfetto
diff --git a/src/profiling/memory/string_interner.h b/src/profiling/memory/string_interner.h
deleted file mode 100644
index 29f0c22..0000000
--- a/src/profiling/memory/string_interner.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_PROFILING_MEMORY_STRING_INTERNER_H_
-#define SRC_PROFILING_MEMORY_STRING_INTERNER_H_
-
-#include <stddef.h>
-#include <set>
-#include <string>
-
-namespace perfetto {
-namespace profiling {
-
-class StringInterner {
- private:
- struct Entry {
- Entry(std::string string, StringInterner* interner);
- bool operator<(const Entry& other) const;
-
- const std::string string;
- size_t ref_count = 0;
- StringInterner* interner;
- };
-
- public:
- class InternedString {
- public:
- friend class StringInterner;
- InternedString(StringInterner::Entry* str);
- InternedString(const InternedString& other);
- InternedString(InternedString&& other) noexcept;
- InternedString& operator=(InternedString other);
-
- const std::string& str() const;
- void* id() const;
- ~InternedString();
-
- private:
- StringInterner::Entry* entry_;
- };
-
- InternedString Intern(const std::string& str);
- size_t entry_count_for_testing();
-
- private:
- void Return(Entry* entry);
- std::set<Entry> entries_;
-};
-
-static_assert(sizeof(StringInterner::InternedString) == sizeof(void*),
- "interned strings should be small");
-
-} // namespace profiling
-} // namespace perfetto
-
-#endif // SRC_PROFILING_MEMORY_STRING_INTERNER_H_
diff --git a/src/profiling/memory/string_interner_unittest.cc b/src/profiling/memory/string_interner_unittest.cc
deleted file mode 100644
index 28dd619..0000000
--- a/src/profiling/memory/string_interner_unittest.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/profiling/memory/string_interner.h"
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-
-namespace perfetto {
-namespace profiling {
-namespace {
-
-TEST(StringInternerTest, Basic) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- ASSERT_EQ(interned_str.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 0);
-}
-
-TEST(StringInternerTest, TwoStrings) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- StringInterner::InternedString other_interned_str = interner.Intern("bar");
- ASSERT_EQ(interned_str.str(), "foo");
- ASSERT_EQ(other_interned_str.str(), "bar");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 0);
-}
-
-TEST(StringInternerTest, TwoReferences) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- ASSERT_EQ(interned_str.str(), "foo");
- StringInterner::InternedString interned_str2 = interner.Intern("foo");
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str2.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 0);
-}
-
-TEST(StringInternerTest, Move) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- {
- StringInterner::InternedString interned_str2(std::move(interned_str));
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str2.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 0);
- }
-}
-
-TEST(StringInternerTest, Copy) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- {
- StringInterner::InternedString interned_str2(interned_str);
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str2.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str.str(), "foo");
- }
-}
-
-TEST(StringInternerTest, MoveAssign) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- {
- StringInterner::InternedString interned_str2 = std::move(interned_str);
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str2.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 0);
- }
-}
-
-TEST(StringInternerTest, CopyAssign) {
- StringInterner interner;
- {
- StringInterner::InternedString interned_str = interner.Intern("foo");
- {
- StringInterner::InternedString interned_str2 = interned_str;
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str2.str(), "foo");
- }
- ASSERT_EQ(interner.entry_count_for_testing(), 1);
- ASSERT_EQ(interned_str.str(), "foo");
- }
-}
-
-} // namespace
-} // namespace profiling
-} // namespace perfetto