Merge "Deflake TracingServiceImplTest.WriteIntoFileAndStopOnMaxSize."
diff --git a/Android.bp b/Android.bp
index 4b67947..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,13 +4675,13 @@
":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",
"tools/trace_to_text/ftrace_inode_handler.cc",
"tools/trace_to_text/main.cc",
"tools/trace_to_text/proto_full_utils.cc",
- "tools/trace_to_text/trace_to_summary.cc",
"tools/trace_to_text/trace_to_systrace.cc",
"tools/trace_to_text/trace_to_text.cc",
"tools/trace_to_text/utils.cc",
@@ -4616,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/perfetto_cmd/pbtxt_to_pb.cc b/src/perfetto_cmd/pbtxt_to_pb.cc
index ec4af7d..cf923d6 100644
--- a/src/perfetto_cmd/pbtxt_to_pb.cc
+++ b/src/perfetto_cmd/pbtxt_to_pb.cc
@@ -405,8 +405,8 @@
bool saw_colon_for_this_key = false;
bool saw_semicolon_for_this_value = true;
bool comment_till_eol = false;
- Token key;
- Token value;
+ Token key{};
+ Token value{};
for (size_t i = 0; i < input.size(); i++, column++) {
bool last_character = i + 1 == input.size();
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
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 72d22b4..7f5fbca 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -73,6 +73,8 @@
"sql_stats_table.cc",
"sql_stats_table.h",
"sqlite_utils.h",
+ "stats_table.cc",
+ "stats_table.h",
"storage_cursor.cc",
"storage_cursor.h",
"storage_schema.cc",
diff --git a/src/trace_processor/event_tracker.cc b/src/trace_processor/event_tracker.cc
index fdf1d4e..7119edd 100644
--- a/src/trace_processor/event_tracker.cc
+++ b/src/trace_processor/event_tracker.cc
@@ -55,7 +55,7 @@
// If the this events previous pid does not match the previous event's next
// pid, make a note of this.
if (prev_pid != pending_slice->pid) {
- context_->storage->AddMismatchedSchedSwitch();
+ context_->storage->mutable_stats()->mismatched_sched_switch_tids++;
}
size_t idx = pending_slice->storage_index;
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index f7e6a12..fc6ad1f 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -448,6 +448,7 @@
PERFETTO_DLOG("Could not find process associated with utid %" PRIu32
" when parsing mem counters.",
utid);
+ context_->storage->mutable_stats()->mem_counter_no_process++;
return;
}
@@ -670,6 +671,7 @@
PERFETTO_DLOG("Could not find process associated with utid %" PRIu32
" when parsing rss stat.",
utid);
+ context_->storage->mutable_stats()->rss_stat_no_process++;
return;
}
diff --git a/src/trace_processor/stats_table.cc b/src/trace_processor/stats_table.cc
new file mode 100644
index 0000000..afbb974
--- /dev/null
+++ b/src/trace_processor/stats_table.cc
@@ -0,0 +1,106 @@
+/*
+ * 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/trace_processor/stats_table.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+StatsTable::StatsTable(sqlite3*, const TraceStorage* storage)
+ : storage_(storage) {}
+
+void StatsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
+ Table::Register<StatsTable>(db, storage, "stats");
+}
+
+Table::Schema StatsTable::CreateSchema(int, const char* const*) {
+ return Schema(
+ {
+ Table::Column(Column::kKey, "key", ColumnType::kString),
+ Table::Column(Column::kValue, "value", ColumnType::kInt),
+ },
+ {Column::kKey});
+}
+
+std::unique_ptr<Table::Cursor> StatsTable::CreateCursor(const QueryConstraints&,
+ sqlite3_value**) {
+ return std::unique_ptr<Table::Cursor>(new Cursor(storage_));
+}
+
+int StatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+ return SQLITE_OK;
+}
+
+StatsTable::Cursor::Cursor(const TraceStorage* storage) : storage_(storage) {}
+
+int StatsTable::Cursor::Column(sqlite3_context* context, int N) {
+ switch (N) {
+ case Column::kKey:
+ sqlite3_result_text(context, KeyForRow(row_), -1, nullptr);
+ break;
+ case Column::kValue:
+ sqlite3_result_int(context, ValueForRow(row_));
+ break;
+ default:
+ PERFETTO_FATAL("Unknown column %d", N);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+int StatsTable::Cursor::Next() {
+ ++row_;
+ return SQLITE_OK;
+}
+
+int StatsTable::Cursor::Eof() {
+ return row_ >= Row::kMax;
+}
+
+const char* StatsTable::Cursor::KeyForRow(uint8_t row) {
+ switch (row) {
+ case StatsTable::Row::kMismatchedSchedSwitch:
+ return "mismatched_ss";
+ case StatsTable::Row::kRssStatNoProcess:
+ return "rss_stat_no_process";
+ case StatsTable::Row::kMemCounterNoProcess:
+ return "mem_count_no_process";
+ default:
+ PERFETTO_FATAL("Unknown row %u", row);
+ }
+}
+
+int StatsTable::Cursor::ValueForRow(uint8_t row) {
+ switch (row) {
+ case StatsTable::Row::kMismatchedSchedSwitch: {
+ auto val = storage_->stats().mismatched_sched_switch_tids;
+ return static_cast<int>(val);
+ }
+ case StatsTable::Row::kRssStatNoProcess: {
+ auto val = storage_->stats().rss_stat_no_process;
+ return static_cast<int>(val);
+ }
+ case StatsTable::Row::kMemCounterNoProcess: {
+ auto val = storage_->stats().mem_counter_no_process;
+ return static_cast<int>(val);
+ }
+ default:
+ PERFETTO_FATAL("Unknown row %u", row);
+ }
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/stats_table.h b/src/trace_processor/stats_table.h
new file mode 100644
index 0000000..2d2ff81
--- /dev/null
+++ b/src/trace_processor/stats_table.h
@@ -0,0 +1,72 @@
+/*
+ * 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_TRACE_PROCESSOR_STATS_TABLE_H_
+#define SRC_TRACE_PROCESSOR_STATS_TABLE_H_
+
+#include <limits>
+#include <memory>
+
+#include "src/trace_processor/table.h"
+#include "src/trace_processor/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class StatsTable : public Table {
+ public:
+ enum Row {
+ kMismatchedSchedSwitch = 0,
+ kRssStatNoProcess = 1,
+ kMemCounterNoProcess = 2,
+ kMax = kMemCounterNoProcess + 1
+ };
+ enum Column { kKey = 0, kValue = 1 };
+
+ static void RegisterTable(sqlite3* db, const TraceStorage* storage);
+
+ StatsTable(sqlite3*, const TraceStorage*);
+
+ // Table implementation.
+ Table::Schema CreateSchema(int argc, const char* const* argv) override;
+ std::unique_ptr<Table::Cursor> CreateCursor(const QueryConstraints&,
+ sqlite3_value**) override;
+ int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+
+ private:
+ class Cursor : public Table::Cursor {
+ public:
+ Cursor(const TraceStorage*);
+
+ // Implementation of Table::Cursor.
+ int Next() override;
+ int Eof() override;
+ int Column(sqlite3_context*, int N) override;
+
+ private:
+ const char* KeyForRow(uint8_t row);
+ int ValueForRow(uint8_t row);
+
+ uint8_t row_ = 0;
+ const TraceStorage* const storage_;
+ };
+
+ const TraceStorage* const storage_;
+};
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_STATS_TABLE_H_
diff --git a/src/trace_processor/table.h b/src/trace_processor/table.h
index d8cd20d..bccf710 100644
--- a/src/trace_processor/table.h
+++ b/src/trace_processor/table.h
@@ -159,6 +159,11 @@
// At registration time, the function should also pass true for |read_write|.
virtual int Update(int, sqlite3_value**, sqlite3_int64*);
+ void SetErrorMessage(char* error) {
+ sqlite3_free(zErrMsg);
+ zErrMsg = error;
+ }
+
const Schema& schema() { return schema_; }
private:
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 7189c1b..614cb21 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -34,6 +34,7 @@
#include "src/trace_processor/slice_tracker.h"
#include "src/trace_processor/span_join_operator_table.h"
#include "src/trace_processor/sql_stats_table.h"
+#include "src/trace_processor/stats_table.h"
#include "src/trace_processor/string_table.h"
#include "src/trace_processor/table.h"
#include "src/trace_processor/thread_table.h"
@@ -110,6 +111,7 @@
SpanJoinOperatorTable::RegisterTable(*db_, context_.storage.get());
WindowOperatorTable::RegisterTable(*db_, context_.storage.get());
InstantsTable::RegisterTable(*db_, context_.storage.get());
+ StatsTable::RegisterTable(*db_, context_.storage.get());
}
TraceProcessorImpl::~TraceProcessorImpl() = default;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 9c44cd1..1cc5d0d 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -175,7 +175,7 @@
break;
case protos::RawQueryResult_ColumnDesc_Type_LONG: {
auto value = res.columns(c).long_values(r);
- printf((value < 0xffffffll) ? "%20lld" : "%20llx", value);
+ printf("%20lld", value);
break;
}
}
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index b42b539..621bae7 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -64,7 +64,9 @@
virtual ~TraceStorage();
struct Stats {
- uint64_t mismatched_sched_switch_tids_ = 0;
+ uint64_t mismatched_sched_switch_tids = 0;
+ uint64_t rss_stat_no_process = 0;
+ uint64_t mem_counter_no_process = 0;
};
// Information about a unique process seen in a trace.
@@ -285,8 +287,6 @@
return static_cast<UniquePid>(unique_processes_.size() - 1);
}
- void AddMismatchedSchedSwitch() { ++stats_.mismatched_sched_switch_tids_; }
-
// Return an unqiue identifier for the contents of each string.
// The string is copied internally and can be destroyed after this called.
// Virtual for testing.
@@ -334,6 +334,9 @@
const Instants& instants() const { return instants_; }
Instants* mutable_instants() { return &instants_; }
+ const Stats& stats() const { return stats_; }
+ Stats* mutable_stats() { return &stats_; }
+
const std::deque<std::string>& string_pool() const { return string_pool_; }
// |unique_processes_| always contains at least 1 element becuase the 0th ID
@@ -352,7 +355,7 @@
using StringHash = uint64_t;
- // Metadata counters for events being added.
+ // Stats about parsing the trace.
Stats stats_;
// One entry for each CPU in the trace.
diff --git a/src/trace_processor/window_operator_table.cc b/src/trace_processor/window_operator_table.cc
index 396e18b..6e48e1f 100644
--- a/src/trace_processor/window_operator_table.cc
+++ b/src/trace_processor/window_operator_table.cc
@@ -81,9 +81,18 @@
if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL)
return SQLITE_READONLY;
- quantum_ = static_cast<uint64_t>(sqlite3_value_int64(argv[3]));
- window_start_ = static_cast<uint64_t>(sqlite3_value_int64(argv[4]));
- window_dur_ = static_cast<uint64_t>(sqlite3_value_int64(argv[5]));
+ uint64_t new_quantum = static_cast<uint64_t>(sqlite3_value_int64(argv[3]));
+ uint64_t new_start = static_cast<uint64_t>(sqlite3_value_int64(argv[4]));
+ uint64_t new_dur = static_cast<uint64_t>(sqlite3_value_int64(argv[5]));
+ if (new_dur == 0) {
+ auto* err = sqlite3_mprintf("Cannot set duration of window table to zero.");
+ SetErrorMessage(err);
+ return SQLITE_ERROR;
+ }
+
+ quantum_ = new_quantum;
+ window_start_ = new_start;
+ window_dur_ = new_dur;
return SQLITE_OK;
}
diff --git a/src/tracing/core/patch_list.h b/src/tracing/core/patch_list.h
index bdfd028..c356006 100644
--- a/src/tracing/core/patch_list.h
+++ b/src/tracing/core/patch_list.h
@@ -30,6 +30,8 @@
// messages when a proto is fragmented over several chunks. These patches are
// sent out-of-band to the tracing service, after having returned the initial
// chunks of the fragment.
+// TODO(crbug.com/904477): Re-disable the move constructors when all usses of
+// this class have been fixed.
class Patch {
public:
using PatchContent = std::array<uint8_t, SharedMemoryABI::kPacketHeaderSize>;
@@ -54,8 +56,6 @@
private:
Patch& operator=(const Patch&) = delete;
- Patch(Patch&&) noexcept = delete;
- Patch& operator=(Patch&&) = delete;
};
// Note: the protozero::Message(s) will take pointers to the |size_field| of
diff --git a/test/trace_processor/android_sched_and_ps_stats.out b/test/trace_processor/android_sched_and_ps_stats.out
new file mode 100644
index 0000000..37889b6
--- /dev/null
+++ b/test/trace_processor/android_sched_and_ps_stats.out
@@ -0,0 +1,4 @@
+"key","value"
+"mismatched_ss",9
+"rss_stat_no_process",0
+"mem_count_no_process",0
diff --git a/test/trace_processor/index b/test/trace_processor/index
index ecdcebb..ea24c2d 100644
--- a/test/trace_processor/index
+++ b/test/trace_processor/index
@@ -4,6 +4,7 @@
../data/android_sched_and_ps.pb smoke_window.sql android_sched_and_ps_smoke_window.out
../data/android_sched_and_ps.pb slice_span_join_b118665515.sql android_sched_and_ps_slice_span_join_b118665515.out
../data/android_sched_and_ps.pb b119301023.sql android_sched_and_ps_b119301023.out
+../data/android_sched_and_ps.pb stats.sql android_sched_and_ps_stats.out
synth_1.py smoke.sql synth_1_smoke.out
synth_1.py filter_sched.sql synth_1_filter_sched.out
synth_1.py filter_counters.sql synth_1_filter_counters.out
diff --git a/test/trace_processor/stats.sql b/test/trace_processor/stats.sql
new file mode 100644
index 0000000..96f15bc
--- /dev/null
+++ b/test/trace_processor/stats.sql
@@ -0,0 +1 @@
+select * from stats;
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 08b095c..cec37f4 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -32,7 +32,6 @@
"ftrace_inode_handler.h",
"main.cc",
"process_formatter.h",
- "trace_to_summary.h",
"trace_to_systrace.cc",
"trace_to_systrace.h",
"trace_to_text.h",
@@ -63,7 +62,6 @@
sources = [
"proto_full_utils.cc",
"proto_full_utils.h",
- "trace_to_summary.cc",
"trace_to_text.cc",
]
}
diff --git a/tools/trace_to_text/ftrace_event_formatter.cc b/tools/trace_to_text/ftrace_event_formatter.cc
index 913188a..2357eef 100644
--- a/tools/trace_to_text/ftrace_event_formatter.cc
+++ b/tools/trace_to_text/ftrace_event_formatter.cc
@@ -3534,13 +3534,21 @@
uint64_t timestamp,
uint32_t cpu,
const protos::FtraceEvent& event,
- const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map) {
+ const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map,
+ std::unordered_map<uint32_t /*tid*/, std::string>& thread_names) {
// Sched_switch events contain the thread name so use that in the prefix.
std::string name;
if (event.has_sched_switch()) {
name = event.sched_switch().prev_comm();
+ thread_names[event.pid()] = event.sched_switch().prev_comm();
} else {
- name = "<...>";
+ // For non sched switch events use name stored from a sched switch event.
+ auto it = thread_names.find(event.pid());
+ if (it != thread_names.end()) {
+ name = it->second;
+ } else {
+ name = "<...>";
+ }
}
std::string line = FormatEventText(event);
diff --git a/tools/trace_to_text/ftrace_event_formatter.h b/tools/trace_to_text/ftrace_event_formatter.h
index 593cee2..0309a0e 100644
--- a/tools/trace_to_text/ftrace_event_formatter.h
+++ b/tools/trace_to_text/ftrace_event_formatter.h
@@ -30,7 +30,8 @@
uint64_t timestamp,
uint32_t cpu,
const protos::FtraceEvent&,
- const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map);
+ const std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/>& thread_map,
+ std::unordered_map<uint32_t /*tid*/, std::string>& thread_names);
} // namespace perfetto
diff --git a/tools/trace_to_text/lite_fallbacks.cc b/tools/trace_to_text/lite_fallbacks.cc
index 1f9df32..82e09ff 100644
--- a/tools/trace_to_text/lite_fallbacks.cc
+++ b/tools/trace_to_text/lite_fallbacks.cc
@@ -15,21 +15,15 @@
*/
// This file is used when targeting the protobuf-lite only target. It provides
-// fallback implementations for TraceToText and TraceToSummary which simply
-// return an error message.
+// fallback implementations for TraceToText which simply return an error
+// message.
#include "perfetto/base/logging.h"
-#include "tools/trace_to_text/trace_to_summary.h"
#include "tools/trace_to_text/trace_to_text.h"
namespace perfetto {
namespace trace_to_text {
-int TraceToSummary(std::istream*, std::ostream*, bool) {
- PERFETTO_FATAL(
- "The 'summary' command is not available in lite builds of trace_to_text");
-}
-
int TraceToText(std::istream*, std::ostream*) {
PERFETTO_FATAL(
"The 'text' command is not available in lite builds of trace_to_text");
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 72f5d22..68ca085 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -18,7 +18,6 @@
#include <iostream>
#include "perfetto/base/logging.h"
-#include "tools/trace_to_text/trace_to_summary.h"
#include "tools/trace_to_text/trace_to_systrace.h"
#include "tools/trace_to_text/trace_to_text.h"
@@ -26,7 +25,7 @@
int Usage(const char* argv0) {
printf(
- "Usage: %s systrace|json|text|summary|short_summary [trace.proto] "
+ "Usage: %s systrace|json|text [trace.proto] "
"[trace.txt]\n",
argv0);
return 1;
@@ -79,12 +78,5 @@
if (format == "text")
return perfetto::trace_to_text::TraceToText(input_stream, output_stream);
- if (format == "summary")
- return perfetto::trace_to_text::TraceToSummary(input_stream, output_stream,
- /* compact_output */ false);
- if (format == "short_summary")
- return perfetto::trace_to_text::TraceToSummary(input_stream, output_stream,
- /* compact_output */ true);
-
return Usage(argv[0]);
}
diff --git a/tools/trace_to_text/trace_to_summary.cc b/tools/trace_to_text/trace_to_summary.cc
deleted file mode 100644
index 34dacad..0000000
--- a/tools/trace_to_text/trace_to_summary.cc
+++ /dev/null
@@ -1,463 +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 "tools/trace_to_text/trace_to_summary.h"
-
-#include <inttypes.h>
-
-#include <stdio.h>
-#include <sys/ioctl.h>
-
-#include <algorithm>
-#include <fstream>
-#include <functional>
-#include <iostream>
-#include <istream>
-#include <limits>
-#include <map>
-#include <memory>
-#include <ostream>
-#include <utility>
-
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/text_format.h>
-
-#include "perfetto/base/build_config.h"
-#include "perfetto/base/logging.h"
-#include "perfetto/traced/sys_stats_counters.h"
-#include "tools/trace_to_text/ftrace_inode_handler.h"
-#include "tools/trace_to_text/process_formatter.h"
-#include "tools/trace_to_text/proto_full_utils.h"
-#include "tools/trace_to_text/utils.h"
-
-#include "perfetto/trace/ftrace/ftrace_event.pb.h"
-#include "perfetto/trace/ftrace/ftrace_stats.pb.h"
-#include "perfetto/trace/trace.pb.h"
-#include "perfetto/trace/trace_packet.pb.h"
-
-namespace perfetto {
-namespace trace_to_text {
-
-namespace {
-
-using google::protobuf::Descriptor;
-using google::protobuf::DynamicMessageFactory;
-using google::protobuf::FileDescriptor;
-using google::protobuf::Message;
-using google::protobuf::TextFormat;
-using google::protobuf::compiler::DiskSourceTree;
-using google::protobuf::compiler::Importer;
-using google::protobuf::io::OstreamOutputStream;
-
-using protos::FtraceEvent;
-using protos::FtraceEventBundle;
-using protos::InodeFileMap;
-using protos::PrintFtraceEvent;
-using protos::ProcessTree;
-using protos::Trace;
-using protos::TracePacket;
-using protos::FtraceStats;
-using protos::FtraceStats_Phase_START_OF_TRACE;
-using protos::FtraceStats_Phase_END_OF_TRACE;
-using protos::SysStats;
-using Entry = protos::InodeFileMap::Entry;
-using Process = protos::ProcessTree::Process;
-
-void PrintFtraceTrack(std::ostream* output,
- const uint64_t& start,
- const uint64_t& end,
- const std::multiset<uint64_t>& ftrace_timestamps) {
- constexpr char kFtraceTrackName[] = "ftrace ";
- size_t width = GetTerminalWidth();
- size_t bucket_count = width - strlen(kFtraceTrackName);
- size_t bucket_size = static_cast<size_t>(end - start) / bucket_count;
- size_t max = 0;
- std::vector<size_t> buckets(bucket_count);
- for (size_t i = 0; i < bucket_count; i++) {
- auto low = ftrace_timestamps.lower_bound(i * bucket_size + start);
- auto high = ftrace_timestamps.upper_bound((i + 1) * bucket_size + start);
- buckets[i] = static_cast<size_t>(std::distance(low, high));
- max = std::max(max, buckets[i]);
- }
-
- std::vector<std::string> out =
- std::vector<std::string>({" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇"});
- *output << "-------------------- " << kFtraceTrackName
- << "--------------------\n";
- char line[2048];
- for (size_t i = 0; i < bucket_count; i++) {
- sprintf(
- line, "%s",
- out[std::min(buckets[i] / (max / out.size()), out.size() - 1)].c_str());
- *output << std::string(line);
- }
- *output << "\n\n";
-}
-
-void PrintFtraceStats(std::ostream* output,
- uint64_t overwrite_count,
- std::map<FtraceEvent::EventCase, uint64_t> event_counts,
- const FtraceStats& before_stats,
- const FtraceStats& after_stats,
- bool compact_output) {
- if (!compact_output)
- *output << "--------------------Ftrace Stats-------------------\n";
-
- char line[2048];
- if (compact_output) {
- sprintf(line, "ftrace_overwrite_count,%" PRIu64 "\n", overwrite_count);
- } else {
- sprintf(line, "Events overwritten: %" PRIu64 "\n", overwrite_count);
- }
- *output << std::string(line);
-
- DiskSourceTree dst;
- dst.MapPath("perfetto", "protos/perfetto");
- perfetto::trace_to_text::MultiFileErrorCollectorImpl mfe;
- Importer importer(&dst, &mfe);
- const FileDescriptor* parsed_file =
- importer.Import("perfetto/trace/ftrace/ftrace_event.proto");
-
- DynamicMessageFactory dmf;
- const Descriptor* ftrace_descriptor = parsed_file->message_type(0);
- for (const auto& event_to_count : event_counts) {
- const std::string& event_name =
- ftrace_descriptor->FindFieldByNumber(event_to_count.first)->name();
- uint64_t count = event_to_count.second;
- if (compact_output) {
- sprintf(line, "%s,%" PRIu64 "\n", event_name.c_str(), count);
- } else {
- sprintf(line, "%s count: %" PRIu64 "\n", event_name.c_str(), count);
- }
- *output << std::string(line);
- }
-
- uint64_t before_total_overrun = 0;
- uint64_t after_total_overrun = 0;
- for (const auto& cpu_stats : before_stats.cpu_stats()) {
- before_total_overrun += cpu_stats.overrun();
- }
- for (const auto& cpu_stats : after_stats.cpu_stats()) {
- after_total_overrun += cpu_stats.overrun();
- }
-
- if (compact_output) {
- sprintf(line, "total_overrun,%" PRIu64 "\n",
- after_total_overrun - before_total_overrun);
- } else {
- sprintf(line, "total_overrun: %" PRIu64 " (= %" PRIu64 " - %" PRIu64 ")\n",
- after_total_overrun - before_total_overrun, after_total_overrun,
- before_total_overrun);
- }
- *output << std::string(line);
-
- if (!compact_output)
- *output << "\n";
-}
-
-void PrintInodeStats(std::ostream* output,
- const std::set<uint64_t>& ftrace_inodes,
- const uint64_t& ftrace_inode_count,
- const std::set<uint64_t>& resolved_map_inodes,
- const std::set<uint64_t>& resolved_scan_inodes,
- bool compact_output) {
- if (!compact_output)
- *output << "--------------------Inode Stats-------------------\n";
-
- char line[2048];
- if (compact_output) {
- sprintf(line, "events_inodes,%" PRIu64 "\n", ftrace_inode_count);
- } else {
- sprintf(line, "Events with inodes: %" PRIu64 "\n", ftrace_inode_count);
- }
- *output << std::string(line);
-
- if (compact_output) {
- sprintf(line, "events_unique_inodes,%zu\n", ftrace_inodes.size());
- } else {
- sprintf(line, "Unique inodes from events: %zu\n", ftrace_inodes.size());
- }
- *output << std::string(line);
-
- if (compact_output) {
- sprintf(line, "resolved_inodes_static,%zu\n", resolved_map_inodes.size());
- } else {
- sprintf(line, "Resolved inodes from static map: %zu\n",
- resolved_map_inodes.size());
- }
- *output << std::string(line);
-
- if (compact_output) {
- sprintf(line, "resolved_inodes_scan_cache,%zu\n",
- resolved_scan_inodes.size());
- } else {
- sprintf(line, "Resolved inodes from scan and cache: %zu\n",
- resolved_scan_inodes.size());
- }
- *output << std::string(line);
-
- std::set<uint64_t> resolved_inodes;
- set_union(resolved_map_inodes.begin(), resolved_map_inodes.end(),
- resolved_scan_inodes.begin(), resolved_scan_inodes.end(),
- std::inserter(resolved_inodes, resolved_inodes.begin()));
-
- if (compact_output) {
- sprintf(line, "total_resolved_inodes,%zu\n", resolved_inodes.size());
- } else {
- sprintf(line, "Total resolved inodes: %zu\n", resolved_inodes.size());
- }
- *output << std::string(line);
-
- std::set<uint64_t> intersect;
- set_intersection(resolved_inodes.begin(), resolved_inodes.end(),
- ftrace_inodes.begin(), ftrace_inodes.end(),
- std::inserter(intersect, intersect.begin()));
-
- size_t unresolved_inodes = ftrace_inodes.size() - intersect.size();
- if (compact_output) {
- sprintf(line, "unresolved_inodes,%zu\n", unresolved_inodes);
- } else {
- sprintf(line, "Unresolved inodes: %zu\n", unresolved_inodes);
- }
- *output << std::string(line);
-
- size_t unexpected_inodes = resolved_inodes.size() - intersect.size();
- if (compact_output) {
- sprintf(line, "unexpected_inodes_fs,%zu\n", unexpected_inodes);
- } else {
- sprintf(line, "Unexpected inodes from filesystem: %zu\n",
- unexpected_inodes);
- }
- *output << std::string(line);
-
- if (!compact_output)
- *output << "\n";
-}
-
-void PrintProcessStats(std::ostream* output,
- const std::set<pid_t>& tids_in_tree,
- const std::set<pid_t>& tids_in_events,
- bool compact_output) {
- if (!compact_output)
- *output << "----------------Process Tree Stats----------------\n";
-
- char tid[2048];
- if (compact_output) {
- sprintf(tid, "unique_thread_process,%zu\n", tids_in_tree.size());
- } else {
- sprintf(tid, "Unique thread ids in process tree: %zu\n",
- tids_in_tree.size());
- }
- *output << std::string(tid);
-
- char tid_event[2048];
- if (compact_output) {
- sprintf(tid_event, "unique_thread_ftrace,%zu\n", tids_in_events.size());
- } else {
- sprintf(tid_event, "Unique thread ids in ftrace events: %zu\n",
- tids_in_events.size());
- }
- *output << std::string(tid_event);
-
- std::set<pid_t> intersect;
- set_intersection(tids_in_tree.begin(), tids_in_tree.end(),
- tids_in_events.begin(), tids_in_events.end(),
- std::inserter(intersect, intersect.begin()));
-
- char matching[2048];
- size_t thread_id_process_info =
- (intersect.size() * 100) / tids_in_events.size();
- if (compact_output) {
- sprintf(matching,
- "tids_with_pinfo,%zu\ntids,%zu\ntids_with_pinfo_percentage,%zu\n",
- intersect.size(), tids_in_events.size(), thread_id_process_info);
- } else {
- sprintf(matching, "Thread ids with process info: %zu/%zu -> %zu %%\n",
- intersect.size(), tids_in_events.size(), thread_id_process_info);
- }
- *output << std::string(matching);
-
- if (!compact_output)
- *output << "\n";
-}
-
-void PrintTraceStats(std::ostream* output,
- const protos::TraceStats& stats,
- bool compact_output) {
- if (compact_output)
- return;
- *output << "--------------------Trace Stats-------------------\n";
- size_t buf_num = 0;
- for (const auto& buf : stats.buffer_stats()) {
- *output << "Buffer " << buf_num++ << "\n"
- << " bytes_written: " << buf.bytes_written() << "\n"
- << " chunks_written: " << buf.chunks_written() << "\n"
- << " chunks_overwritten: " << buf.chunks_overwritten() << "\n"
- << " write_wrap_count: " << buf.write_wrap_count() << "\n"
- << " patches_succeeded: " << buf.patches_succeeded() << "\n"
- << " patches_failed: " << buf.patches_failed() << "\n"
- << " readaheads_succeeded: " << buf.readaheads_succeeded() << "\n"
- << " readaheads_failed: " << buf.readaheads_failed() << "\n"
- << " abi_violations: " << buf.abi_violations() << "\n";
- }
- *output << "producers_connected: " << stats.producers_connected() << "\n"
- << "producers_seen: " << stats.producers_seen() << "\n"
- << "data_sources_reg: " << stats.data_sources_registered() << "\n"
- << "data_sources_seen: " << stats.data_sources_seen() << "\n"
- << "tracing_sessions: " << stats.tracing_sessions() << "\n"
- << "total_buffers: " << stats.total_buffers() << "\n";
-}
-
-} // namespace
-
-int TraceToSummary(std::istream* input,
- std::ostream* output,
- bool compact_output) {
- uint64_t ftrace_start = std::numeric_limits<uint64_t>::max();
- uint64_t ftrace_end = 0;
- uint64_t boottime_start = std::numeric_limits<uint64_t>::max();
- uint64_t boottime_end = 0;
- uint64_t ftrace_overwrites = 0;
- std::map<FtraceEvent::EventCase, uint64_t> ftrace_event_counts;
- std::multiset<uint64_t> ftrace_timestamps;
- std::set<pid_t> tids_in_tree;
- std::set<pid_t> tids_in_events;
- std::set<uint64_t> ftrace_inodes;
- uint64_t ftrace_inode_count = 0;
- std::set<uint64_t> resolved_map_inodes;
- std::set<uint64_t> resolved_scan_inodes;
- protos::TraceStats last_stats;
-
- FtraceStats before_stats;
- FtraceStats after_stats;
-
- ForEachPacketInTrace(
- input,
- [&ftrace_start, &ftrace_end, &ftrace_overwrites, &ftrace_event_counts,
- &before_stats, &after_stats, &ftrace_timestamps, &tids_in_tree,
- &tids_in_events, &ftrace_inodes, &ftrace_inode_count,
- &resolved_map_inodes, &resolved_scan_inodes, &last_stats,
- &boottime_start, &boottime_end](const protos::TracePacket& packet) {
- if (packet.has_process_tree()) {
- const ProcessTree& tree = packet.process_tree();
- for (Process process : tree.processes()) {
- tids_in_tree.insert(process.pid());
- for (ProcessTree::Thread thread : process.threads_deprecated())
- tids_in_tree.insert(thread.tid());
- }
- for (ProcessTree::Thread thread : tree.threads())
- tids_in_tree.insert(thread.tid());
- }
-
- if (packet.has_inode_file_map()) {
- const InodeFileMap& inode_file_map = packet.inode_file_map();
- const auto& mount_points = inode_file_map.mount_points();
- bool from_scan = std::find(mount_points.begin(), mount_points.end(),
- "/data") != mount_points.end();
- for (const auto& entry : inode_file_map.entries())
- if (from_scan)
- resolved_scan_inodes.insert(entry.inode_number());
- else
- resolved_map_inodes.insert(entry.inode_number());
- }
-
- if (packet.has_trace_stats())
- last_stats = packet.trace_stats();
-
- if (packet.has_ftrace_stats()) {
- const auto& ftrace_stats = packet.ftrace_stats();
- if (ftrace_stats.phase() == FtraceStats_Phase_START_OF_TRACE) {
- before_stats = ftrace_stats;
- // TODO(hjd): Check not yet set.
- } else if (ftrace_stats.phase() == FtraceStats_Phase_END_OF_TRACE) {
- after_stats = ftrace_stats;
- // TODO(hjd): Check not yet set.
- } else {
- // TODO(hjd): Error here.
- }
- }
-
- if (packet.has_clock_snapshot()) {
- for (const auto& clock : packet.clock_snapshot().clocks()) {
- if (clock.type() == protos::ClockSnapshot_Clock_Type_MONOTONIC) {
- boottime_start =
- std::min<uint64_t>(boottime_start, clock.timestamp());
- boottime_end =
- std::max<uint64_t>(boottime_end, clock.timestamp());
- }
- }
- }
-
- if (!packet.has_ftrace_events())
- return;
-
- const FtraceEventBundle& bundle = packet.ftrace_events();
- ftrace_overwrites += bundle.overwrite_count();
-
- uint64_t inode_number = 0;
- for (const FtraceEvent& event : bundle.event()) {
- ftrace_event_counts[event.event_case()] += 1;
-
- if (ParseInode(event, &inode_number)) {
- ftrace_inodes.insert(inode_number);
- ftrace_inode_count++;
- }
- if (event.pid()) {
- tids_in_events.insert(static_cast<int>(event.pid()));
- }
- if (event.timestamp()) {
- ftrace_start = std::min<uint64_t>(ftrace_start, event.timestamp());
- ftrace_end = std::max<uint64_t>(ftrace_end, event.timestamp());
- ftrace_timestamps.insert(event.timestamp());
- }
- }
- });
-
- fprintf(stderr, "\n");
-
- char line[2048];
- uint64_t ftrace_duration = (ftrace_end - ftrace_start) / (1000 * 1000);
- if (compact_output) {
- sprintf(line, "ftrace duration,%" PRIu64 "\n", ftrace_duration);
- } else {
- sprintf(line, "Ftrace duration: %" PRIu64 "ms\n", ftrace_duration);
- }
- *output << std::string(line);
-
- uint64_t boottime_duration = (boottime_end - boottime_start) / (1000 * 1000);
- if (compact_output) {
- sprintf(line, "boottime duration,%" PRIu64 "\n", boottime_duration);
- } else {
- sprintf(line, "Boottime duration: %" PRIu64 "ms\n", boottime_duration);
- }
- *output << std::string(line);
-
- if (!compact_output)
- PrintFtraceTrack(output, ftrace_start, ftrace_end, ftrace_timestamps);
- PrintFtraceStats(output, ftrace_overwrites, ftrace_event_counts, before_stats,
- after_stats, compact_output);
- PrintProcessStats(output, tids_in_tree, tids_in_events, compact_output);
- PrintInodeStats(output, ftrace_inodes, ftrace_inode_count,
- resolved_map_inodes, resolved_scan_inodes, compact_output);
- PrintTraceStats(output, last_stats, compact_output);
-
- return 0;
-}
-
-} // namespace trace_to_text
-} // namespace perfetto
diff --git a/tools/trace_to_text/trace_to_summary.h b/tools/trace_to_text/trace_to_summary.h
deleted file mode 100644
index 1b3120d..0000000
--- a/tools/trace_to_text/trace_to_summary.h
+++ /dev/null
@@ -1,32 +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 TOOLS_TRACE_TO_TEXT_TRACE_TO_SUMMARY_H_
-#define TOOLS_TRACE_TO_TEXT_TRACE_TO_SUMMARY_H_
-
-#include <iostream>
-
-namespace perfetto {
-namespace trace_to_text {
-
-int TraceToSummary(std::istream* input,
- std::ostream* output,
- bool compact_output);
-
-} // namespace trace_to_text
-} // namespace perfetto
-
-#endif // TOOLS_TRACE_TO_TEXT_TRACE_TO_SUMMARY_H_
diff --git a/tools/trace_to_text/trace_to_systrace.cc b/tools/trace_to_text/trace_to_systrace.cc
index 19818a9..0b2cc12 100644
--- a/tools/trace_to_text/trace_to_systrace.cc
+++ b/tools/trace_to_text/trace_to_systrace.cc
@@ -100,6 +100,7 @@
std::vector<std::string> proc_dump;
std::vector<std::string> thread_dump;
std::unordered_map<uint32_t /*tid*/, uint32_t /*tgid*/> thread_map;
+ std::unordered_map<uint32_t /*tid*/, std::string> thread_names;
std::vector<const char*> meminfo_strs = BuildMeminfoCounterNames();
std::vector<const char*> vmstat_strs = BuildVmstatCounterNames();
@@ -107,7 +108,7 @@
std::vector<protos::TracePacket> packets_to_process;
ForEachPacketInTrace(
- input, [&thread_map, &packets_to_process, &proc_dump,
+ input, [&thread_map, &packets_to_process, &proc_dump, &thread_names,
&thread_dump](const protos::TracePacket& packet) {
if (!packet.has_process_tree()) {
packets_to_process.emplace_back(std::move(packet));
@@ -125,6 +126,9 @@
// Populate thread map for matching tids to tgids.
thread_map[static_cast<uint32_t>(thread.tid())] =
static_cast<uint32_t>(thread.tgid());
+ if (thread.has_name()) {
+ thread_names[static_cast<uint32_t>(thread.tid())] = thread.name();
+ }
std::string t = FormatThread(thread);
thread_dump.emplace_back(t);
}
@@ -135,7 +139,7 @@
const FtraceEventBundle& bundle = packet.ftrace_events();
for (const FtraceEvent& event : bundle.event()) {
std::string line = FormatFtraceEvent(event.timestamp(), bundle.cpu(),
- event, thread_map);
+ event, thread_map, thread_names);
if (line == "")
continue;
ftrace_sorted.emplace(event.timestamp(), line);
@@ -153,7 +157,8 @@
sprintf(str, "C|1|%s|%" PRIu64, meminfo_strs[meminfo.key()],
static_cast<uint64_t>(meminfo.value()));
event.mutable_print()->set_buf(str);
- ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
+ ftrace_sorted.emplace(
+ ts, FormatFtraceEvent(ts, 0, event, thread_map, thread_names));
}
for (const auto& vmstat : sys_stats.vmstat()) {
FtraceEvent event;
@@ -164,7 +169,8 @@
sprintf(str, "C|1|%s|%" PRIu64, vmstat_strs[vmstat.key()],
static_cast<uint64_t>(vmstat.value()));
event.mutable_print()->set_buf(str);
- ftrace_sorted.emplace(ts, FormatFtraceEvent(ts, 0, event, thread_map));
+ ftrace_sorted.emplace(
+ ts, FormatFtraceEvent(ts, 0, event, thread_map, thread_names));
}
}
}
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index 26e46ba..bb987f9 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -63,7 +63,7 @@
if (isQuantized) {
windowStartNs = Math.floor(windowStartNs / bucketSizeNs) * bucketSizeNs;
}
- const windowDurNs = endNs - windowStartNs;
+ const windowDurNs = Math.max(1, endNs - windowStartNs);
this.query(`update window_${this.trackState.id} set
window_start=${windowStartNs},
diff --git a/ui/src/tracks/process_summary/controller.ts b/ui/src/tracks/process_summary/controller.ts
index 462b525..b61838f 100644
--- a/ui/src/tracks/process_summary/controller.ts
+++ b/ui/src/tracks/process_summary/controller.ts
@@ -66,7 +66,7 @@
// |resolution| is in s/px we want # ns for 10px window:
const bucketSizeNs = Math.round(resolution * 10 * 1e9);
const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
- const windowDurNs = endNs - windowStartNs;
+ const windowDurNs = Math.max(1, endNs - windowStartNs);
this.query(`update ${this.tableName('window')} set
window_start=${windowStartNs},