Merge "Apply fixes to overview panel"
diff --git a/Android.bp b/Android.bp
index d891e49..2061821 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1730,6 +1730,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_analysis_analysis",
":perfetto_src_trace_processor_containers_containers",
+ ":perfetto_src_trace_processor_containers_containers_headers",
":perfetto_src_trace_processor_db_lib",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_ftrace_descriptors",
@@ -7518,6 +7519,11 @@
],
}
+// GN: //src/trace_processor/containers:containers_headers
+filegroup {
+ name: "perfetto_src_trace_processor_containers_containers_headers",
+}
+
// GN: //src/trace_processor/containers:unittests
filegroup {
name: "perfetto_src_trace_processor_containers_unittests",
@@ -8997,6 +9003,7 @@
":perfetto_src_protozero_unittests",
":perfetto_src_trace_processor_analysis_analysis",
":perfetto_src_trace_processor_containers_containers",
+ ":perfetto_src_trace_processor_containers_containers_headers",
":perfetto_src_trace_processor_containers_unittests",
":perfetto_src_trace_processor_db_lib",
":perfetto_src_trace_processor_db_unittests",
@@ -9293,6 +9300,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_analysis_analysis",
":perfetto_src_trace_processor_containers_containers",
+ ":perfetto_src_trace_processor_containers_containers_headers",
":perfetto_src_trace_processor_db_lib",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_ftrace_descriptors",
@@ -9441,6 +9449,7 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_analysis_analysis",
":perfetto_src_trace_processor_containers_containers",
+ ":perfetto_src_trace_processor_containers_containers_headers",
":perfetto_src_trace_processor_db_lib",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_ftrace_descriptors",
diff --git a/BUILD b/BUILD
index 37ac3de..b48c5ec 100644
--- a/BUILD
+++ b/BUILD
@@ -780,19 +780,35 @@
)
# GN target: //src/trace_processor/containers:containers
-filegroup(
+perfetto_cc_library(
name = "src_trace_processor_containers_containers",
srcs = [
+ ":src_trace_processor_containers_containers_headers",
"src/trace_processor/containers/bit_vector.cc",
- "src/trace_processor/containers/bit_vector.h",
"src/trace_processor/containers/bit_vector_iterators.cc",
+ "src/trace_processor/containers/nullable_vector.cc",
+ "src/trace_processor/containers/row_map.cc",
+ "src/trace_processor/containers/string_pool.cc",
+ ],
+ hdrs = [
+ ":include_perfetto_base_base",
+ ":include_perfetto_protozero_protozero",
+ ],
+ deps = [
+ ":src_base_base",
+ ],
+ linkstatic = True,
+)
+
+# GN target: //src/trace_processor/containers:containers_headers
+filegroup(
+ name = "src_trace_processor_containers_containers_headers",
+ srcs = [
+ "src/trace_processor/containers/bit_vector.h",
"src/trace_processor/containers/bit_vector_iterators.h",
"src/trace_processor/containers/null_term_string_view.h",
- "src/trace_processor/containers/nullable_vector.cc",
"src/trace_processor/containers/nullable_vector.h",
- "src/trace_processor/containers/row_map.cc",
"src/trace_processor/containers/row_map.h",
- "src/trace_processor/containers/string_pool.cc",
"src/trace_processor/containers/string_pool.h",
],
)
@@ -3419,7 +3435,7 @@
name = "trace_processor",
srcs = [
":src_trace_processor_analysis_analysis",
- ":src_trace_processor_containers_containers",
+ ":src_trace_processor_containers_containers_headers",
":src_trace_processor_db_lib",
":src_trace_processor_export_json",
":src_trace_processor_ftrace_descriptors",
@@ -3444,7 +3460,6 @@
":include_perfetto_ext_trace_processor_export_json",
":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
":include_perfetto_ext_traced_sys_stats_counters",
- ":include_perfetto_protozero_protozero",
":include_perfetto_trace_processor_basic_types",
":include_perfetto_trace_processor_storage",
":include_perfetto_trace_processor_trace_processor",
@@ -3484,6 +3499,7 @@
":protos_perfetto_trace_track_event_zero",
":protozero",
":src_base_base",
+ ":src_trace_processor_containers_containers",
":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
":src_trace_processor_importers_gen_cc_config_descriptor",
":src_trace_processor_importers_gen_cc_track_event_descriptor",
@@ -3515,7 +3531,7 @@
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
":src_trace_processor_analysis_analysis",
- ":src_trace_processor_containers_containers",
+ ":src_trace_processor_containers_containers_headers",
":src_trace_processor_db_lib",
":src_trace_processor_export_json",
":src_trace_processor_ftrace_descriptors",
@@ -3575,6 +3591,7 @@
":protozero",
":src_base_base",
":src_base_unix_socket",
+ ":src_trace_processor_containers_containers",
":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
":src_trace_processor_importers_gen_cc_config_descriptor",
":src_trace_processor_importers_gen_cc_track_event_descriptor",
@@ -3627,6 +3644,7 @@
":src_profiling_deobfuscator",
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
+ ":src_trace_processor_containers_containers_headers",
":tools_trace_to_text_pprofbuilder",
":tools_trace_to_text_utils",
],
@@ -3672,6 +3690,7 @@
":protos_perfetto_trace_track_event_zero",
":protos_third_party_pprof_zero",
":protozero",
+ ":src_trace_processor_containers_containers",
] + PERFETTO_CONFIG.deps.zlib,
linkstatic = True,
)
@@ -3694,7 +3713,7 @@
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
":src_trace_processor_analysis_analysis",
- ":src_trace_processor_containers_containers",
+ ":src_trace_processor_containers_containers_headers",
":src_trace_processor_db_lib",
":src_trace_processor_export_json",
":src_trace_processor_ftrace_descriptors",
@@ -3753,6 +3772,7 @@
":protos_third_party_pprof_zero",
":protozero",
":src_base_base",
+ ":src_trace_processor_containers_containers",
":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
":src_trace_processor_importers_gen_cc_config_descriptor",
":src_trace_processor_importers_gen_cc_track_event_descriptor",
diff --git a/include/perfetto/profiling/pprof_builder.h b/include/perfetto/profiling/pprof_builder.h
index 20b9d2e..df18c5a 100644
--- a/include/perfetto/profiling/pprof_builder.h
+++ b/include/perfetto/profiling/pprof_builder.h
@@ -17,7 +17,6 @@
#ifndef INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
#define INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
-#include <iostream>
#include <string>
#include <vector>
@@ -48,9 +47,19 @@
enum class ConversionMode { kHeapProfile, kPerfProfile };
+enum class ConversionFlags : uint64_t {
+ kNone = 0,
+ // Suffix frame names with additional information. Current annotations are
+ // specific to apps running within the Android runtime, and include
+ // information such as whether the given frame was interpreted / executed
+ // under JIT / etc.
+ kAnnotateFrames = 1
+};
+
bool TraceToPprof(trace_processor::TraceProcessor* tp,
std::vector<SerializedProfile>* output,
ConversionMode mode = ConversionMode::kHeapProfile,
+ uint64_t flags = 0,
uint64_t pid = 0,
const std::vector<uint64_t>& timestamps = {});
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 2dba9e1..28a4cb9 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -27,7 +27,7 @@
if (enable_perfetto_trace_processor_sqlite) {
static_library("trace_processor") {
complete_static_lib = true
- deps = [ ":lib" ]
+ public_deps = [ ":lib" ]
}
}
@@ -150,6 +150,7 @@
"../base",
"../protozero",
"containers",
+ "containers:containers_headers",
"importers:common",
"importers:gen_cc_chrome_track_event_descriptor",
"importers:gen_cc_track_event_descriptor",
diff --git a/src/trace_processor/containers/BUILD.gn b/src/trace_processor/containers/BUILD.gn
index 836ab22..40e3ae3 100644
--- a/src/trace_processor/containers/BUILD.gn
+++ b/src/trace_processor/containers/BUILD.gn
@@ -12,27 +12,46 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("../../../gn/perfetto_component.gni")
import("../../../gn/test.gni")
-source_set("containers") {
+# These headers are outside of include/, but used by
+# tools/trace_to_text/libpprofbuilder. In Bazel builds with strict header
+# checking, this gets converted to a filegroup of the headers, and therefore
+# allows their inclusion (as if they were directly part of the dependent
+# targets).
+# TODO(rsavitski): The more correct approach would require the use of
+# cc_library with 'hdrs' section (likely derived from 'public' in gn).
+# Otherwise we have to depend on both :containers and :containers_headers
+# everywhere for the sake of strict header checking post-bazel conversion, as
+# the filegroup isn't acting as a 'hdrs'.
+source_set("containers_headers") {
sources = [
- "bit_vector.cc",
"bit_vector.h",
- "bit_vector_iterators.cc",
"bit_vector_iterators.h",
"null_term_string_view.h",
- "nullable_vector.cc",
"nullable_vector.h",
- "row_map.cc",
"row_map.h",
- "string_pool.cc",
"string_pool.h",
]
+}
+
+# perfetto_component to turn this into a cc_library in Bazel, to satisfy the
+# hand-rolled ODR checking for targets including two static libs that include
+# this (unclear whether it is actually a problem).
+perfetto_component("containers") {
+ sources = [
+ "bit_vector.cc",
+ "bit_vector_iterators.cc",
+ "nullable_vector.cc",
+ "row_map.cc",
+ "string_pool.cc",
+ ]
+ public_deps = [ ":containers_headers" ]
deps = [
"../../../gn:default_deps",
- "../../../include/perfetto/base",
- "../../../include/perfetto/ext/base",
"../../../include/perfetto/protozero",
+ "../../base",
]
}
@@ -47,6 +66,7 @@
]
deps = [
":containers",
+ ":containers_headers",
"../../../gn:default_deps",
"../../../gn:gtest_and_gmock",
]
@@ -57,6 +77,7 @@
testonly = true
deps = [
":containers",
+ ":containers_headers",
"../../../gn:benchmark",
"../../../gn:default_deps",
]
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index f1a959f..5c61b1f 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -30,6 +30,7 @@
"../../../include/perfetto/ext/base",
"../../../include/perfetto/trace_processor",
"../containers",
+ "../containers:containers_headers",
]
}
diff --git a/src/trace_processor/metrics/android/power_drain_in_watts.sql b/src/trace_processor/metrics/android/power_drain_in_watts.sql
index d866bb0..1fbfec3 100644
--- a/src/trace_processor/metrics/android/power_drain_in_watts.sql
+++ b/src/trace_processor/metrics/android/power_drain_in_watts.sql
@@ -31,7 +31,23 @@
('power.PPVAR_VPH_PWR_WLAN_uws', 'wifi'),
('power.PPVAR_VPH_PWR_OLED_uws', 'display'),
('power.PPVAR_VPH_PWR_QTM525_uws', 'cellular'),
- ('power.PPVAR_VPH_PWR_RF_uws', 'cellular');
+ ('power.PPVAR_VPH_PWR_RF_uws', 'cellular'),
+ ('power.rails.aoc.logic', 'aoc'),
+ ('power.rails.aoc.memory', 'aoc'),
+ ('power.rails.cpu.big', 'cpu_big'),
+ ('power.rails.cpu.little', 'cpu_little'),
+ ('power.rails.cpu.mid', 'cpu_mid'),
+ ('power.rails.ddr.a', 'mem'),
+ ('power.rails.ddr.b', 'mem'),
+ ('power.rails.ddr.c', 'mem'),
+ ('power.rails.gpu', 'gpu'),
+ ('power.rails.display', 'display'),
+ ('power.rails.gps', 'gps'),
+ ('power.rails.memory.interface', 'mem'),
+ ('power.rails.modem', 'cellular'),
+ ('power.rails.radio.frontend', 'cellular'),
+ ('power.rails.system.fabric', 'soc'),
+ ('power.rails.wifi.bt', 'wifi');
-- Convert power counter data into table of events, where each event has
-- start timestamp, duration and the average power drain during its duration
@@ -72,3 +88,4 @@
JOIN counter_track ON (counter.track_id = counter_track.id)
WHERE counter_track.type = 'counter_track'
AND name LIKE "power.%";
+
diff --git a/src/trace_processor/storage/BUILD.gn b/src/trace_processor/storage/BUILD.gn
index 47e1195..30f50d8 100644
--- a/src/trace_processor/storage/BUILD.gn
+++ b/src/trace_processor/storage/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright (C) 20 The Android Open Source Project
+# Copyright (C) 2021 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.
@@ -26,6 +26,7 @@
"../../../include/perfetto/ext/base",
"../../../include/perfetto/trace_processor",
"../containers",
+ "../containers:containers_headers",
"../tables",
"../types",
]
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index e93c0b4..d793059 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -203,7 +203,7 @@
PERFETTO_TP_TABLE(PERFETTO_TP_PERF_SAMPLE_DEF);
// Symbolization data for a frame. Rows with the same symbol_set_id describe
-// one frame, with the bottom-most inlined frame having id == symbol_set_id.
+// one callframe, with the most-inlined symbol having id == symbol_set_id.
//
// For instance, if the function foo has an inlined call to the function bar,
// which has an inlined call to baz, the stack_profile_symbol table would look
@@ -212,9 +212,9 @@
// ```
// |id|symbol_set_id|name |source_file|line_number|
// |--|-------------|-------------|-----------|-----------|
-// |1 | 1 |foo |foo.cc | 60 |
+// |1 | 1 |baz |foo.cc | 36 |
// |2 | 1 |bar |foo.cc | 30 |
-// |3 | 1 |baz |foo.cc | 36 |
+// |3 | 1 |foo |foo.cc | 60 |
// ```
// @param name name of the function.
// @param source_file name of the source file containing the function.
diff --git a/src/trace_processor/types/BUILD.gn b/src/trace_processor/types/BUILD.gn
index 46d16b9..70d44b9 100644
--- a/src/trace_processor/types/BUILD.gn
+++ b/src/trace_processor/types/BUILD.gn
@@ -31,6 +31,7 @@
"../../../include/perfetto/ext/base",
"../../../include/perfetto/trace_processor",
"../containers",
+ "../containers:containers_headers",
]
}
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index c1674d8..e2c5960 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -71,6 +71,8 @@
"../../protos/third_party/pprof:zero",
"../../src/profiling/symbolizer",
"../../src/profiling/symbolizer:symbolize_database",
+ "../../src/trace_processor/containers:containers",
+ "../../src/trace_processor/containers:containers_headers",
]
sources = [ "pprof_builder.cc" ]
}
@@ -78,7 +80,7 @@
# Exposed in bazel builds.
static_library("libpprofbuilder") {
complete_static_lib = true
- deps = [ ":pprofbuilder" ]
+ public_deps = [ ":pprofbuilder" ]
}
# The core source files that are used both by the "full" version (the host
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 8384b7a..e4a9f1d 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -32,7 +32,6 @@
#include "tools/trace_to_text/trace_to_systrace.h"
#include "tools/trace_to_text/trace_to_text.h"
-
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <unistd.h>
#endif
@@ -43,17 +42,19 @@
int Usage(const char* argv0) {
fprintf(stderr,
- "Usage: %s systrace|json|ctrace|text|profile [--pid PID] "
- "[--timestamps TIMESTAMP1,TIMESTAMP2,...] "
- "[--truncate start|end] "
- "[--full-sort] "
- "[trace.pb] "
- "[trace.txt]\n"
- "\nProfile mode only:\n"
- "\t--perf generate a perf profile\n"
- "\t--timestamps TIMESTAMP1,TIMESTAMP2,... generate profiles "
- "only for these timestamps\n"
- "\t--pid PID generate profiles only for this process id\n",
+ "Usage: %s MODE [OPTIONS] [input file] [output file]\n"
+ "modes:\n"
+ " systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate\n"
+ "options:\n"
+ " [--truncate start|end]\n"
+ " [--full-sort]\n"
+ "\"profile\" mode options:\n"
+ " [--perf] generate a perf profile instead of a heap profile\n"
+ " [--no-annotations] do not suffix frame names with derived "
+ "annotations\n"
+ " [--timestamps TIMESTAMP1,TIMESTAMP2,...] generate profiles "
+ "only for these *specific* timestamps\n"
+ " [--pid PID] generate profiles only for this process id\n",
argv0);
return 1;
}
@@ -75,6 +76,7 @@
std::vector<uint64_t> timestamps;
bool full_sort = false;
bool perf_profile = false;
+ bool profile_no_annotations = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
printf("%s\n", base::GetVersionString());
@@ -103,6 +105,8 @@
}
} else if (strcmp(argv[i], "--perf") == 0) {
perf_profile = true;
+ } else if (strcmp(argv[i], "--no-annotations") == 0) {
+ profile_no_annotations = true;
} else if (strcmp(argv[i], "--full-sort") == 0) {
full_sort = true;
} else {
@@ -187,10 +191,11 @@
return TraceToText(input_stream, output_stream);
if (format == "profile") {
- return perf_profile ? TraceToPerfProfile(input_stream, output_stream, pid,
- timestamps)
- : TraceToHeapProfile(input_stream, output_stream, pid,
- timestamps);
+ return perf_profile
+ ? TraceToPerfProfile(input_stream, output_stream, pid,
+ timestamps, !profile_no_annotations)
+ : TraceToHeapProfile(input_stream, output_stream, pid,
+ timestamps, !profile_no_annotations);
}
if (format == "hprof")
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index fdb8717..588c438 100644
--- a/tools/trace_to_text/pprof_builder.cc
+++ b/tools/trace_to_text/pprof_builder.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "perfetto/profiling/pprof_builder.h"
-
#include "perfetto/base/build_config.h"
+#include "perfetto/profiling/pprof_builder.h"
+
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <cxxabi.h>
#endif
@@ -27,29 +27,120 @@
#include <algorithm>
#include <map>
#include <set>
+#include <unordered_map>
#include <utility>
#include <vector>
#include "tools/trace_to_text/utils.h"
#include "perfetto/base/logging.h"
-#include "perfetto/base/time.h"
+#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/packed_repeated_fields.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/trace_processor/trace_processor.h"
-
#include "src/profiling/symbolizer/symbolize_database.h"
#include "src/profiling/symbolizer/symbolizer.h"
+#include "src/trace_processor/containers/string_pool.h"
#include "protos/perfetto/trace/trace.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "protos/third_party/pprof/profile.pbzero.h"
+// Quick hint on navigating the file:
+// Conversions for both perf and heap profiles start with |TraceToPprof|.
+// Non-shared logic is in the |heap_profile| and |perf_profile| namespaces.
+//
+// To build one or more profiles, first the callstack information is queried
+// from the SQL tables, and converted into an in-memory representation by
+// |PreprocessLocations|. Then an instance of |GProfileBuilder| is used to
+// accumulate samples for that profile, and emit all additional information as a
+// serialized proto. Only the entities referenced by that particular
+// |GProfileBuilder| instance are emitted.
+//
+// See protos/third_party/pprof/profile.proto for the meaning of terms like
+// function/location/line.
+
+namespace {
+using StringId = ::perfetto::trace_processor::StringPool::Id;
+
+// In-memory representation of a Profile.Function.
+struct Function {
+ StringId name_id = StringId::Null();
+ StringId system_name_id = StringId::Null();
+ StringId filename_id = StringId::Null();
+
+ Function(StringId n, StringId s, StringId f)
+ : name_id(n), system_name_id(s), filename_id(f) {}
+
+ bool operator==(const Function& other) const {
+ return std::tie(name_id, system_name_id, filename_id) ==
+ std::tie(other.name_id, other.system_name_id, other.filename_id);
+ }
+};
+
+// In-memory representation of a Profile.Line.
+struct Line {
+ int64_t function_id = 0; // LocationTracker's interned Function id
+ int64_t line_no = 0;
+
+ Line(int64_t func, int64_t line) : function_id(func), line_no(line) {}
+
+ bool operator==(const Line& other) const {
+ return function_id == other.function_id && line_no == other.line_no;
+ }
+};
+
+// In-memory representation of a Profile.Location.
+struct Location {
+ int64_t mapping_id = 0; // sqlite row id
+ // Common case: location references a single function.
+ int64_t single_function_id = 0; // interned Function id
+ // Alternatively: multiple inlined functions, recovered via offline
+ // symbolisation. Leaf-first ordering.
+ std::vector<Line> inlined_functions;
+
+ Location(int64_t map, int64_t func, std::vector<Line> inlines)
+ : mapping_id(map),
+ single_function_id(func),
+ inlined_functions(std::move(inlines)) {}
+
+ bool operator==(const Location& other) const {
+ return std::tie(mapping_id, single_function_id, inlined_functions) ==
+ std::tie(other.mapping_id, other.single_function_id,
+ other.inlined_functions);
+ }
+};
+} // namespace
+
+template <>
+struct std::hash<Function> {
+ size_t operator()(const Function& loc) const {
+ perfetto::base::Hash hasher;
+ hasher.Update(loc.name_id.raw_id());
+ hasher.Update(loc.system_name_id.raw_id());
+ hasher.Update(loc.filename_id.raw_id());
+ return static_cast<size_t>(hasher.digest());
+ }
+};
+
+template <>
+struct std::hash<Location> {
+ size_t operator()(const Location& loc) const {
+ perfetto::base::Hash hasher;
+ hasher.Update(loc.mapping_id);
+ hasher.Update(loc.single_function_id);
+ for (auto line : loc.inlined_functions) {
+ hasher.Update(line.function_id);
+ hasher.Update(line.line_no);
+ }
+ return static_cast<size_t>(hasher.digest());
+ }
+};
+
namespace perfetto {
namespace trace_to_text {
-
namespace {
using ::perfetto::trace_processor::Iterator;
@@ -83,84 +174,6 @@
return ret;
}
-// Return map from callsite_id to list of frame_ids that make up the callstack.
-std::vector<std::vector<int64_t>> GetCallsiteToFrames(
- trace_processor::TraceProcessor* tp) {
- Iterator count_it =
- tp->ExecuteQuery("select count(*) from stack_profile_callsite;");
- if (!count_it.Next()) {
- PERFETTO_DFATAL_OR_ELOG("Failed to get number of callsites: %s",
- count_it.Status().message().c_str());
- return {};
- }
- int64_t count = count_it.Get(0).AsLong();
-
- Iterator it = tp->ExecuteQuery(
- "select id, parent_id, frame_id from stack_profile_callsite order by "
- "depth;");
- std::vector<std::vector<int64_t>> result(static_cast<size_t>(count));
- while (it.Next()) {
- int64_t id = it.Get(0).AsLong();
- int64_t frame_id = it.Get(2).AsLong();
- std::vector<int64_t>& path = result[static_cast<size_t>(id)];
- path.push_back(frame_id);
-
- auto parent_id_value = it.Get(1);
- if (!parent_id_value.is_null()) {
- const std::vector<int64_t>& parent_path =
- result[static_cast<size_t>(parent_id_value.AsLong())];
- path.insert(path.end(), parent_path.begin(), parent_path.end());
- }
- }
-
- if (!it.Status().ok()) {
- PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
- it.Status().message().c_str());
- return {};
- }
- return result;
-}
-
-base::Optional<int64_t> GetMaxSymbolId(trace_processor::TraceProcessor* tp) {
- auto max_symbol_id_it =
- tp->ExecuteQuery("select max(id) from stack_profile_symbol");
- if (!max_symbol_id_it.Next()) {
- PERFETTO_DFATAL_OR_ELOG("Failed to get max symbol set id: %s",
- max_symbol_id_it.Status().message().c_str());
- return base::nullopt;
- }
- auto value = max_symbol_id_it.Get(0);
- if (value.is_null())
- return base::nullopt;
- return base::make_optional(value.AsLong());
-}
-
-struct Line {
- int64_t symbol_id;
- uint32_t line_number;
-};
-
-std::map<int64_t, std::vector<Line>> GetSymbolSetIdToLines(
- trace_processor::TraceProcessor* tp) {
- std::map<int64_t, std::vector<Line>> result;
- Iterator it = tp->ExecuteQuery(
- "SELECT symbol_set_id, id, line_number FROM stack_profile_symbol;");
- while (it.Next()) {
- int64_t symbol_set_id = it.Get(0).AsLong();
- int64_t id = it.Get(1).AsLong();
- int64_t line_number = it.Get(2).AsLong();
- result[symbol_set_id].emplace_back(
- Line{id, static_cast<uint32_t>(line_number)});
- }
-
- if (!it.Status().ok()) {
- PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
- it.Status().message().c_str());
- return {};
- }
- return result;
-}
-
base::Optional<int64_t> GetStatsEntry(
trace_processor::TraceProcessor* tp,
const std::string& name,
@@ -182,73 +195,373 @@
return base::make_optional(it.Get(0).AsLong());
}
-// Helper for constructing |perftools.profiles.Profile| protos.
+// Interns Locations, Lines, and Functions. Interning is done by the entity's
+// contents, and has no relation to the row ids in the SQL tables.
+// Contains all data for the trace, so can be reused when emitting multiple
+// profiles.
+//
+// TODO(rsavitski): consider moving mappings into here as well. For now, they're
+// still emitted in a single scan during profile building. Mappings should be
+// unique-enough already in the SQL tables, with only incremental state clearing
+// duplicating entries.
+class LocationTracker {
+ public:
+ int64_t InternLocation(Location loc) {
+ auto it = locations_.find(loc);
+ if (it == locations_.end()) {
+ bool inserted = false;
+ std::tie(it, inserted) = locations_.emplace(
+ std::move(loc), static_cast<int64_t>(locations_.size()));
+ PERFETTO_DCHECK(inserted);
+ }
+ return it->second;
+ }
+
+ int64_t InternFunction(Function func) {
+ auto it = functions_.find(func);
+ if (it == functions_.end()) {
+ bool inserted = false;
+ std::tie(it, inserted) =
+ functions_.emplace(func, static_cast<int64_t>(functions_.size()));
+ PERFETTO_DCHECK(inserted);
+ }
+ return it->second;
+ }
+
+ bool IsCallsiteProcessed(int64_t callstack_id) const {
+ return callsite_to_locations_.find(callstack_id) !=
+ callsite_to_locations_.end();
+ }
+
+ void MaybeSetCallsiteLocations(int64_t callstack_id,
+ const std::vector<int64_t>& locs) {
+ // nop if already set
+ callsite_to_locations_.emplace(callstack_id, locs);
+ }
+
+ const std::vector<int64_t>& LocationsForCallstack(
+ int64_t callstack_id) const {
+ auto it = callsite_to_locations_.find(callstack_id);
+ PERFETTO_CHECK(callstack_id >= 0 && it != callsite_to_locations_.end());
+ return it->second;
+ }
+
+ const std::unordered_map<Location, int64_t>& AllLocations() const {
+ return locations_;
+ }
+ const std::unordered_map<Function, int64_t>& AllFunctions() const {
+ return functions_;
+ }
+
+ private:
+ // Root-first location ids for a given callsite id.
+ std::unordered_map<int64_t, std::vector<int64_t>> callsite_to_locations_;
+ std::unordered_map<Location, int64_t> locations_;
+ std::unordered_map<Function, int64_t> functions_;
+};
+
+struct PreprocessedInline {
+ StringId system_name_id = StringId::Null();
+ StringId filename_id = StringId::Null();
+ int64_t line_no = 0;
+
+ PreprocessedInline(StringId s, StringId f, int64_t line)
+ : system_name_id(s), filename_id(f), line_no(line) {}
+};
+
+std::unordered_map<int64_t, std::vector<PreprocessedInline>>
+PreprocessInliningInfo(trace_processor::TraceProcessor* tp,
+ trace_processor::StringPool* interner) {
+ std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlines;
+
+ // Most-inlined function (leaf) has the lowest id within a symbol set. Query
+ // such that the per-set line vectors are built up leaf-first.
+ Iterator it = tp->ExecuteQuery(
+ "select symbol_set_id, name, source_file, line_number from "
+ "stack_profile_symbol order by symbol_set_id asc, id asc;");
+ while (it.Next()) {
+ int64_t symbol_set_id = it.Get(0).AsLong();
+ auto func_sysname = it.Get(1).is_null() ? "" : it.Get(1).AsString();
+ auto filename = it.Get(2).is_null() ? "" : it.Get(2).AsString();
+ int64_t line_no = it.Get(3).AsLong();
+
+ inlines[symbol_set_id].emplace_back(interner->InternString(func_sysname),
+ interner->InternString(filename),
+ line_no);
+ }
+
+ if (!it.Status().ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+ it.Status().message().c_str());
+ return {};
+ }
+ return inlines;
+}
+
+// Extracts and interns the unique frames and locations (as defined by the proto
+// format) from the callstack SQL tables.
+//
+// Approach:
+// * for each callstack (callsite ids of the leaves):
+// * use experimental_annotated_callstack to build the full list of
+// constituent frames
+// * for each frame (root to leaf):
+// * intern the location and function(s)
+// * remember the mapping from callsite_id to the callstack so far (from
+// the root and including the frame being considered)
+//
+// Optionally mixes in the annotations as a frame name suffix (since there's no
+// good way to attach extra info to locations in the proto format). This relies
+// on the annotations (produced by experimental_annotated_callstack) to be
+// stable for a given callsite (equivalently: dependent only on their parents).
+LocationTracker PreprocessLocations(trace_processor::TraceProcessor* tp,
+ trace_processor::StringPool* interner,
+ bool annotate_frames) {
+ LocationTracker tracker;
+
+ // Keyed by symbol_set_id, discarded once this function converts the inlines
+ // into Line and Function entries.
+ std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlining_info =
+ PreprocessInliningInfo(tp, interner);
+
+ // Higher callsite ids most likely correspond to the deepest stacks, so we'll
+ // fill more of the overall callsite->location map by visiting the callsited
+ // in decreasing id order. Since processing a callstack also fills in the data
+ // for all parent callsites.
+ Iterator cid_it = tp->ExecuteQuery(
+ "select id from stack_profile_callsite order by id desc;");
+ while (cid_it.Next()) {
+ int64_t query_cid = cid_it.Get(0).AsLong();
+
+ // If the leaf has been processed, the rest of the stack is already known.
+ if (tracker.IsCallsiteProcessed(query_cid))
+ continue;
+
+ std::string annotated_query =
+ "select sp.id, sp.annotation, spf.mapping, "
+ "ifnull(spf.deobfuscated_name, spf.name), spf.symbol_set_id from "
+ "experimental_annotated_callstack(" +
+ std::to_string(query_cid) +
+ ") sp join stack_profile_frame spf on (sp.frame_id == spf.id) "
+ "order by depth asc";
+ Iterator c_it = tp->ExecuteQuery(annotated_query);
+
+ std::vector<int64_t> callstack_loc_ids;
+ while (c_it.Next()) {
+ int64_t cid = c_it.Get(0).AsLong();
+ int64_t mapping_id = c_it.Get(2).AsLong();
+ auto annotation = c_it.Get(1).is_null() ? "" : c_it.Get(1).AsString();
+ auto func_sysname = c_it.Get(3).is_null() ? "" : c_it.Get(3).AsString();
+ base::Optional<int64_t> symbol_set_id =
+ c_it.Get(4).is_null() ? base::nullopt
+ : base::make_optional(c_it.Get(4).AsLong());
+
+ Location loc(mapping_id, /*single_function_id=*/-1, {});
+
+ auto intern_function = [interner, &tracker, annotate_frames](
+ StringId func_sysname_id, StringId filename_id,
+ const std::string& anno) {
+ std::string func_name = interner->Get(func_sysname_id).ToStdString();
+ MaybeDemangle(&func_name);
+ if (annotate_frames && !anno.empty() && !func_name.empty())
+ func_name = func_name + " [" + anno + "]";
+ StringId func_name_id =
+ interner->InternString(base::StringView(func_name));
+ Function func(func_name_id, func_sysname_id, filename_id);
+ return tracker.InternFunction(func);
+ };
+
+ // Inlining information available
+ if (symbol_set_id.has_value()) {
+ auto it = inlining_info.find(*symbol_set_id);
+ if (it == inlining_info.end()) {
+ PERFETTO_DFATAL_OR_ELOG(
+ "Failed to find stack_profile_symbol entry for symbol_set_id "
+ "%" PRIi64 "",
+ *symbol_set_id);
+ return {};
+ }
+
+ // N inlined functions
+ for (const auto& line : it->second) {
+ int64_t func_id = intern_function(line.system_name_id,
+ line.filename_id, annotation);
+
+ loc.inlined_functions.emplace_back(func_id, line.line_no);
+ }
+ } else {
+ // Otherwise - single function
+ int64_t func_id =
+ intern_function(interner->InternString(func_sysname),
+ /*filename_id=*/StringId::Null(), annotation);
+ loc.single_function_id = func_id;
+ }
+
+ int64_t loc_id = tracker.InternLocation(std::move(loc));
+
+ // Update the tracker with the locations so far (for example, at depth 2,
+ // we'll have 3 root-most locations in |callstack_loc_ids|).
+ callstack_loc_ids.push_back(loc_id);
+ tracker.MaybeSetCallsiteLocations(cid, callstack_loc_ids);
+ }
+
+ if (!c_it.Status().ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+ c_it.Status().message().c_str());
+ return {};
+ }
+ }
+
+ if (!cid_it.Status().ok()) {
+ PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
+ cid_it.Status().message().c_str());
+ return {};
+ }
+
+ return tracker;
+}
+
+// Builds the |perftools.profiles.Profile| proto.
class GProfileBuilder {
public:
- GProfileBuilder(
- const std::vector<std::vector<int64_t>>& callsite_to_frames,
- const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines,
- int64_t max_symbol_id)
- : callsite_to_frames_(callsite_to_frames),
- symbol_set_id_to_lines_(symbol_set_id_to_lines),
- max_symbol_id_(max_symbol_id) {
- // The pprof format expects the first entry in the string table to be the
+ GProfileBuilder(const LocationTracker& locations,
+ trace_processor::StringPool* interner)
+ : locations_(locations), interner_(interner) {
+ // The pprof format requires the first entry in the string table to be the
// empty string.
- int64_t empty_id = Intern("");
+ int64_t empty_id = ToStringTableId(StringId::Null());
PERFETTO_CHECK(empty_id == 0);
}
void WriteSampleTypes(
const std::vector<std::pair<std::string, std::string>>& sample_types) {
- // The interner might eagerly append to the profile proto, prevent it from
- // breaking up other messages by making a separate pass.
- for (const auto& st : sample_types) {
- Intern(st.first);
- Intern(st.second);
- }
for (const auto& st : sample_types) {
auto* sample_type = result_->add_sample_type();
- sample_type->set_type(Intern(st.first));
- sample_type->set_unit(Intern(st.second));
+ sample_type->set_type(
+ ToStringTableId(interner_->InternString(base::StringView(st.first))));
+ sample_type->set_unit(ToStringTableId(
+ interner_->InternString(base::StringView(st.second))));
}
}
bool AddSample(const protozero::PackedVarInt& values, int64_t callstack_id) {
- const auto& frames = FramesForCallstack(callstack_id);
- if (frames.empty()) {
+ const auto& location_ids = locations_.LocationsForCallstack(callstack_id);
+ if (location_ids.empty()) {
PERFETTO_DFATAL_OR_ELOG(
"Failed to find frames for callstack id %" PRIi64 "", callstack_id);
return false;
}
- protozero::PackedVarInt location_ids;
- for (int64_t frame : frames)
- location_ids.Append(ToPprofId(frame));
+
+ // LocationTracker stores location lists root-first, but the pprof format
+ // requires leaf-first.
+ protozero::PackedVarInt packed_locs;
+ for (auto it = location_ids.rbegin(); it != location_ids.rend(); ++it)
+ packed_locs.Append(ToPprofId(*it));
auto* gsample = result_->add_sample();
gsample->set_value(values);
- gsample->set_location_id(location_ids);
+ gsample->set_location_id(packed_locs);
- // remember frames to be emitted
- seen_frames_.insert(frames.cbegin(), frames.cend());
-
+ // Remember the locations s.t. we only serialize the referenced ones.
+ seen_locations_.insert(location_ids.cbegin(), location_ids.cend());
return true;
}
std::string CompleteProfile(trace_processor::TraceProcessor* tp) {
std::set<int64_t> seen_mappings;
- std::set<int64_t> seen_symbol_ids;
+ std::set<int64_t> seen_functions;
- // Write the location info for frames referenced by the added samples.
- if (!WriteFrames(tp, &seen_mappings, &seen_symbol_ids))
+ if (!WriteLocations(&seen_mappings, &seen_functions))
+ return {};
+ if (!WriteFunctions(seen_functions))
return {};
if (!WriteMappings(tp, seen_mappings))
return {};
- if (!WriteSymbols(tp, seen_symbol_ids))
- return {};
+
+ WriteStringTable();
return result_.SerializeAsString();
}
private:
+ // Serializes the Profile.Location entries referenced by this profile.
+ bool WriteLocations(std::set<int64_t>* seen_mappings,
+ std::set<int64_t>* seen_functions) {
+ const std::unordered_map<Location, int64_t>& locations =
+ locations_.AllLocations();
+
+ size_t written_locations = 0;
+ for (const auto& loc_and_id : locations) {
+ const auto& loc = loc_and_id.first;
+ int64_t id = loc_and_id.second;
+
+ if (seen_locations_.find(id) == seen_locations_.end())
+ continue;
+
+ written_locations += 1;
+ seen_mappings->emplace(loc.mapping_id);
+
+ auto* glocation = result_->add_location();
+ glocation->set_id(ToPprofId(id));
+ glocation->set_mapping_id(ToPprofId(loc.mapping_id));
+
+ if (!loc.inlined_functions.empty()) {
+ for (const auto& line : loc.inlined_functions) {
+ seen_functions->insert(line.function_id);
+
+ auto* gline = glocation->add_line();
+ gline->set_function_id(ToPprofId(line.function_id));
+ gline->set_line(line.line_no);
+ }
+ } else {
+ seen_functions->insert(loc.single_function_id);
+
+ glocation->add_line()->set_function_id(
+ ToPprofId(loc.single_function_id));
+ }
+ }
+
+ if (written_locations != seen_locations_.size()) {
+ PERFETTO_DFATAL_OR_ELOG(
+ "Found only %zu/%zu locations during serialization.",
+ written_locations, seen_locations_.size());
+ return false;
+ }
+ return true;
+ }
+
+ // Serializes the Profile.Function entries referenced by this profile.
+ bool WriteFunctions(const std::set<int64_t>& seen_functions) {
+ const std::unordered_map<Function, int64_t>& functions =
+ locations_.AllFunctions();
+
+ size_t written_functions = 0;
+ for (const auto& func_and_id : functions) {
+ const auto& func = func_and_id.first;
+ int64_t id = func_and_id.second;
+
+ if (seen_functions.find(id) == seen_functions.end())
+ continue;
+
+ written_functions += 1;
+
+ auto* gfunction = result_->add_function();
+ gfunction->set_id(ToPprofId(id));
+ gfunction->set_name(ToStringTableId(func.name_id));
+ gfunction->set_system_name(ToStringTableId(func.system_name_id));
+ if (!func.filename_id.is_null())
+ gfunction->set_filename(ToStringTableId(func.filename_id));
+ }
+
+ if (written_functions != seen_functions.size()) {
+ PERFETTO_DFATAL_OR_ELOG(
+ "Found only %zu/%zu functions during serialization.",
+ written_functions, seen_functions.size());
+ return false;
+ }
+ return true;
+ }
+
+ // Serializes the Profile.Mapping entries referenced by this profile.
bool WriteMappings(trace_processor::TraceProcessor* tp,
const std::set<int64_t>& seen_mappings) {
Iterator mapping_it = tp->ExecuteQuery(
@@ -260,7 +573,8 @@
if (seen_mappings.find(id) == seen_mappings.end())
continue;
++mappings_no;
- auto interned_filename = Intern(mapping_it.Get(4).AsString());
+ auto interned_filename = ToStringTableId(
+ interner_->InternString(mapping_it.Get(4).AsString()));
auto* gmapping = result_->add_mapping();
gmapping->set_id(ToPprofId(id));
// Do not set the build_id here to avoid downstream services
@@ -285,145 +599,48 @@
return true;
}
- bool WriteSymbols(trace_processor::TraceProcessor* tp,
- const std::set<int64_t>& seen_symbol_ids) {
- Iterator symbol_it = tp->ExecuteQuery(
- "SELECT id, name, source_file FROM stack_profile_symbol");
- size_t symbols_no = 0;
- while (symbol_it.Next()) {
- int64_t id = symbol_it.Get(0).AsLong();
- if (seen_symbol_ids.find(id) == seen_symbol_ids.end())
- continue;
- ++symbols_no;
- const std::string& name = symbol_it.Get(1).AsString();
- std::string demangled_name = name;
- MaybeDemangle(&demangled_name);
-
- auto interned_demangled_name = Intern(demangled_name);
- auto interned_system_name = Intern(name);
- auto interned_filename = Intern(symbol_it.Get(2).AsString());
- auto* gfunction = result_->add_function();
- gfunction->set_id(ToPprofId(id));
- gfunction->set_name(interned_demangled_name);
- gfunction->set_system_name(interned_system_name);
- gfunction->set_filename(interned_filename);
+ void WriteStringTable() {
+ for (StringId id : string_table_) {
+ trace_processor::NullTermStringView s = interner_->Get(id);
+ result_->add_string_table(s.data(), s.size());
}
-
- if (!symbol_it.Status().ok()) {
- PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
- symbol_it.Status().message().c_str());
- return false;
- }
-
- if (symbols_no != seen_symbol_ids.size()) {
- PERFETTO_DFATAL_OR_ELOG("Missing symbols.");
- return false;
- }
- return true;
}
- bool WriteFrames(trace_processor::TraceProcessor* tp,
- std::set<int64_t>* seen_mappings,
- std::set<int64_t>* seen_symbol_ids) {
- Iterator frame_it = tp->ExecuteQuery(
- "SELECT spf.id, IFNULL(spf.deobfuscated_name, spf.name), spf.mapping, "
- "spf.rel_pc, spf.symbol_set_id "
- "FROM stack_profile_frame spf;");
- size_t frames_no = 0;
- while (frame_it.Next()) {
- int64_t frame_id = frame_it.Get(0).AsLong();
- if (seen_frames_.find(frame_id) == seen_frames_.end())
- continue;
- frames_no++;
- std::string frame_name = frame_it.Get(1).AsString();
- int64_t mapping_id = frame_it.Get(2).AsLong();
- int64_t rel_pc = frame_it.Get(3).AsLong();
- base::Optional<int64_t> symbol_set_id;
- if (!frame_it.Get(4).is_null())
- symbol_set_id = frame_it.Get(4).AsLong();
-
- seen_mappings->emplace(mapping_id);
- auto* glocation = result_->add_location();
- glocation->set_id(ToPprofId(frame_id));
- glocation->set_mapping_id(ToPprofId(mapping_id));
- // TODO(fmayer): Convert to abspc.
- // relpc + (mapping.start - (mapping.exact_offset -
- // mapping.start_offset)).
- glocation->set_address(static_cast<uint64_t>(rel_pc));
- if (symbol_set_id) {
- for (const Line& line : LineForSymbolSetId(*symbol_set_id)) {
- seen_symbol_ids->emplace(line.symbol_id);
- auto* gline = glocation->add_line();
- gline->set_line(line.line_number);
- gline->set_function_id(ToPprofId(line.symbol_id));
- }
- } else {
- int64_t synthesized_symbol_id = ++max_symbol_id_;
- std::string demangled_name = frame_name;
- MaybeDemangle(&demangled_name);
-
- auto* gline = glocation->add_line();
- gline->set_line(0);
- gline->set_function_id(ToPprofId(synthesized_symbol_id));
-
- auto interned_demangled_name = Intern(demangled_name);
- auto interned_system_name = Intern(frame_name);
- auto* gfunction = result_->add_function();
- gfunction->set_id(ToPprofId(synthesized_symbol_id));
- gfunction->set_name(interned_demangled_name);
- gfunction->set_system_name(interned_system_name);
- }
- }
-
- if (!frame_it.Status().ok()) {
- PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
- frame_it.Status().message().c_str());
- return false;
- }
- if (frames_no != seen_frames_.size()) {
- PERFETTO_DFATAL_OR_ELOG("Missing frames.");
- return false;
- }
- return true;
- }
-
- const std::vector<int64_t>& FramesForCallstack(int64_t callstack_id) {
- size_t callsite_idx = static_cast<size_t>(callstack_id);
- PERFETTO_CHECK(callstack_id >= 0 &&
- callsite_idx < callsite_to_frames_.size());
- return callsite_to_frames_[callsite_idx];
- }
-
- const std::vector<Line>& LineForSymbolSetId(int64_t symbol_set_id) {
- auto it = symbol_set_id_to_lines_.find(symbol_set_id);
- if (it == symbol_set_id_to_lines_.end())
- return empty_line_vector_;
- return it->second;
- }
-
- int64_t Intern(const std::string& s) {
- auto it = string_table_.find(s);
- if (it == string_table_.end()) {
- std::tie(it, std::ignore) =
- string_table_.emplace(s, string_table_.size());
- result_->add_string_table(s);
+ int64_t ToStringTableId(StringId interned_id) {
+ auto it = interning_remapper_.find(interned_id);
+ if (it == interning_remapper_.end()) {
+ int64_t table_id = static_cast<int64_t>(string_table_.size());
+ string_table_.push_back(interned_id);
+ bool inserted = false;
+ std::tie(it, inserted) =
+ interning_remapper_.emplace(interned_id, table_id);
+ PERFETTO_DCHECK(inserted);
}
return it->second;
}
+ // Contains all locations, lines, functions (in memory):
+ const LocationTracker& locations_;
+
+ // String interner, strings referenced by LocationTracker are already
+ // interned. The new internings will come from mappings, and sample types.
+ trace_processor::StringPool* interner_;
+
+ // The profile format uses the repeated string_table field's index as an
+ // implicit id, so these structures remap the interned strings into sequential
+ // ids. Only the strings referenced by this GProfileBuilder instance will be
+ // added to the table.
+ std::unordered_map<StringId, int64_t> interning_remapper_;
+ std::vector<StringId> string_table_;
+
+ // Profile proto being serialized.
protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
result_;
- std::map<std::string, int64_t> string_table_;
- const std::vector<std::vector<int64_t>>& callsite_to_frames_;
- const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines_;
- const std::vector<Line> empty_line_vector_;
- int64_t max_symbol_id_;
- std::set<int64_t> seen_frames_;
+ // Set of locations referenced by the added samples.
+ std::set<int64_t> seen_locations_;
};
-} // namespace
-
namespace heap_profile {
struct View {
const char* type;
@@ -543,13 +760,12 @@
static bool TraceToHeapPprof(trace_processor::TraceProcessor* tp,
std::vector<SerializedProfile>* output,
+ bool annotate_frames,
uint64_t target_pid,
const std::vector<uint64_t>& target_timestamps) {
- const auto callsite_to_frames = GetCallsiteToFrames(tp);
- const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
- base::Optional<int64_t> max_symbol_id = GetMaxSymbolId(tp);
- if (!max_symbol_id.has_value())
- return false;
+ trace_processor::StringPool interner;
+ LocationTracker locations =
+ PreprocessLocations(tp, &interner, annotate_frames);
bool any_fail = false;
Iterator it = tp->ExecuteQuery(
@@ -557,8 +773,7 @@
"from heap_profile_allocation hpa, "
"process p where p.upid = hpa.upid;");
while (it.Next()) {
- GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
- max_symbol_id.value());
+ GProfileBuilder builder(locations, &interner);
uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
uint64_t ts = static_cast<uint64_t>(it.Get(1).AsLong());
uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
@@ -686,12 +901,11 @@
// perf and heap profiles.
static bool TraceToPerfPprof(trace_processor::TraceProcessor* tp,
std::vector<SerializedProfile>* output,
+ bool annotate_frames,
uint64_t target_pid) {
- const auto callsite_to_frames = GetCallsiteToFrames(tp);
- const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
- base::Optional<int64_t> max_symbol_id = GetMaxSymbolId(tp);
- if (!max_symbol_id.has_value())
- return false;
+ trace_processor::StringPool interner;
+ LocationTracker locations =
+ PreprocessLocations(tp, &interner, annotate_frames);
LogTracePerfEventIssues(tp);
@@ -703,9 +917,7 @@
if (target_pid != 0 && process.pid != target_pid)
continue;
- GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
- max_symbol_id.value());
-
+ GProfileBuilder builder(locations, &interner);
builder.WriteSampleTypes({{"samples", "count"}});
std::string query = "select callsite_id from perf_sample where utid in (" +
@@ -733,17 +945,22 @@
return true;
}
} // namespace perf_profile
+} // namespace
bool TraceToPprof(trace_processor::TraceProcessor* tp,
std::vector<SerializedProfile>* output,
ConversionMode mode,
+ uint64_t flags,
uint64_t pid,
const std::vector<uint64_t>& timestamps) {
+ bool annotate_frames =
+ flags & static_cast<uint64_t>(ConversionFlags::kAnnotateFrames);
switch (mode) {
case (ConversionMode::kHeapProfile):
- return heap_profile::TraceToHeapPprof(tp, output, pid, timestamps);
+ return heap_profile::TraceToHeapPprof(tp, output, annotate_frames, pid,
+ timestamps);
case (ConversionMode::kPerfProfile):
- return perf_profile::TraceToPerfPprof(tp, output, pid);
+ return perf_profile::TraceToPerfPprof(tp, output, annotate_frames, pid);
}
PERFETTO_FATAL("unknown conversion option"); // for gcc
}
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index a394e2b..c71a827 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -50,6 +50,12 @@
namespace trace_to_text {
namespace {
+uint64_t ToConversionFlags(bool annotate_frames) {
+ return static_cast<uint64_t>(annotate_frames
+ ? ConversionFlags::kAnnotateFrames
+ : ConversionFlags::kNone);
+}
+
std::string GetRandomString(size_t n) {
std::random_device r;
auto rng = std::default_random_engine(r());
@@ -92,6 +98,7 @@
uint64_t pid,
std::vector<uint64_t> timestamps,
ConversionMode conversion_mode,
+ uint64_t conversion_flags,
std::string dirname_prefix,
std::function<std::string(const SerializedProfile&)> filename_fn) {
std::vector<SerializedProfile> profiles;
@@ -106,7 +113,8 @@
MaybeSymbolize(tp.get());
MaybeDeobfuscate(tp.get());
- TraceToPprof(tp.get(), &profiles, conversion_mode, pid, timestamps);
+ TraceToPprof(tp.get(), &profiles, conversion_mode, conversion_flags, pid,
+ timestamps);
if (profiles.empty()) {
return 0;
}
@@ -132,31 +140,33 @@
int TraceToHeapProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps) {
+ std::vector<uint64_t> timestamps,
+ bool annotate_frames) {
int file_idx = 0;
auto filename_fn = [&file_idx](const SerializedProfile& profile) {
return "heap_dump." + std::to_string(++file_idx) + "." +
std::to_string(profile.pid) + "." + profile.heap_name + ".pb";
};
- return TraceToProfile(input, output, pid, timestamps,
- ConversionMode::kHeapProfile, "heap_profile-",
- filename_fn);
+ return TraceToProfile(
+ input, output, pid, timestamps, ConversionMode::kHeapProfile,
+ ToConversionFlags(annotate_frames), "heap_profile-", filename_fn);
}
int TraceToPerfProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps) {
+ std::vector<uint64_t> timestamps,
+ bool annotate_frames) {
int file_idx = 0;
auto filename_fn = [&file_idx](const SerializedProfile& profile) {
return "profile." + std::to_string(++file_idx) + ".pid." +
std::to_string(profile.pid) + ".pb";
};
- return TraceToProfile(input, output, pid, timestamps,
- ConversionMode::kPerfProfile, "perf_profile-",
- filename_fn);
+ return TraceToProfile(
+ input, output, pid, timestamps, ConversionMode::kPerfProfile,
+ ToConversionFlags(annotate_frames), "perf_profile-", filename_fn);
}
} // namespace trace_to_text
diff --git a/tools/trace_to_text/trace_to_profile.h b/tools/trace_to_text/trace_to_profile.h
index 1c41aad..313b32b 100644
--- a/tools/trace_to_text/trace_to_profile.h
+++ b/tools/trace_to_text/trace_to_profile.h
@@ -27,13 +27,15 @@
int TraceToHeapProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps);
+ std::vector<uint64_t> timestamps,
+ bool annotate_frames);
// 0: success
int TraceToPerfProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps);
+ std::vector<uint64_t> timestamps,
+ bool annotate_frames);
} // namespace trace_to_text
} // namespace perfetto