Merge "Use python3 for profiling scripts."
diff --git a/.gn b/.gn
index 692a951..d7ecabe 100644
--- a/.gn
+++ b/.gn
@@ -13,3 +13,4 @@
# limitations under the License.
buildconfig = "//gn/standalone/BUILDCONFIG.gn"
+script_executable = "python3"
diff --git a/BUILD b/BUILD
index 83099f4..f61f3d7 100644
--- a/BUILD
+++ b/BUILD
@@ -3564,7 +3564,6 @@
":src_profiling_deobfuscator",
":src_profiling_symbolizer_symbolize_database",
":src_profiling_symbolizer_symbolizer",
- ":src_trace_processor_containers_containers",
":tools_trace_to_text_pprofbuilder",
":tools_trace_to_text_utils",
],
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index a59c492..98022c9 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -1058,6 +1058,7 @@
visibility = _buildtools_visibility
cflags = [
"-DJSON_USE_EXCEPTION=0",
+ "-Wno-deprecated-declarations",
# Using -isystem instead of include_dirs (-I), so we don't need to suppress
# warnings coming from third-party headers. Doing so would mask warnings in
diff --git a/debian/control b/debian/control
index be52af4..d8cd8ea 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@
Section: kernel
Priority: optional
Maintainer: Sami Kyostila <skyostil@google.com>
-Build-Depends: debhelper (>= 10)
+Build-Depends: debhelper (>= 10), python3, git
Standards-Version: 3.9.8
Homepage: https://perfetto.dev
Vcs-Git: https://android.googlesource.com/platform/external/perfetto/
diff --git a/gn/write_buildflag_header.py b/gn/write_buildflag_header.py
index 8045cce..617ab7d 100644
--- a/gn/write_buildflag_header.py
+++ b/gn/write_buildflag_header.py
@@ -67,7 +67,7 @@
guard = '%s_' % args.out.upper()
guard = guard.replace('/', '_').replace('\\', '_').replace('.', '_')
lines = []
- lines.append('// Generated by %s' % __file__)
+ lines.append('// Generated by %s' % os.path.basename(__file__))
lines.append('')
lines.append('// fix_include_guards: off')
lines.append('#ifndef %s' % guard)
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index 7a34606..f0179c6 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// Generated by ../../gn/write_buildflag_header.py
+// Generated by write_buildflag_header.py
// fix_include_guards: off
#ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index ada8006..6745b3f 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-// Generated by ../../gn/write_buildflag_header.py
+// Generated by write_buildflag_header.py
// fix_include_guards: off
#ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/profiling/pprof_builder.h b/include/perfetto/profiling/pprof_builder.h
index df18c5a..20b9d2e 100644
--- a/include/perfetto/profiling/pprof_builder.h
+++ b/include/perfetto/profiling/pprof_builder.h
@@ -17,6 +17,7 @@
#ifndef INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
#define INCLUDE_PERFETTO_PROFILING_PPROF_BUILDER_H_
+#include <iostream>
#include <string>
#include <vector>
@@ -47,19 +48,9 @@
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/include/perfetto/protozero/proto_utils.h b/include/perfetto/protozero/proto_utils.h
index bb2e5ce..841042b 100644
--- a/include/perfetto/protozero/proto_utils.h
+++ b/include/perfetto/protozero/proto_utils.h
@@ -276,6 +276,21 @@
using message_type = MessageType;
};
+namespace internal {
+
+// Ideally we would create variables of FieldMetadata<...> type directly,
+// but before C++17's support for constexpr inline variables arrive, we have to
+// actually use pointers to inline functions instead to avoid having to define
+// symbols in *.pbzero.cc files.
+//
+// Note: protozero bindings will generate Message::kFieldName variable and which
+// can then be passed to TRACE_EVENT macro for inline writing of typed messages.
+// The fact that the former can be passed to the latter is a part of the stable
+// API, while the particular type is not and users should not rely on it.
+template <typename T>
+using FieldMetadataHelper = T (*)(void);
+
+} // namespace internal
} // namespace proto_utils
} // namespace protozero
diff --git a/include/perfetto/tracing/internal/write_track_event_args.h b/include/perfetto/tracing/internal/write_track_event_args.h
index 8263110..9a68ab5 100644
--- a/include/perfetto/tracing/internal/write_track_event_args.h
+++ b/include/perfetto/tracing/internal/write_track_event_args.h
@@ -100,7 +100,8 @@
FieldMetadataType>::value>>
PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(
EventContext event_ctx,
- const FieldMetadataType& field_name,
+ protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>
+ field_name,
ArgValue&& arg_value,
Args&&... args) {
static_assert(
diff --git a/include/perfetto/tracing/traced_proto.h b/include/perfetto/tracing/traced_proto.h
index d88dbc1..88ee99e 100644
--- a/include/perfetto/tracing/traced_proto.h
+++ b/include/perfetto/tracing/traced_proto.h
@@ -166,9 +166,10 @@
} // namespace internal
template <typename MessageType, typename FieldMetadataType, typename ValueType>
-void WriteIntoTracedProto(TracedProto<MessageType> message,
- const FieldMetadataType&,
- ValueType&& value) {
+void WriteIntoTracedProto(
+ TracedProto<MessageType> message,
+ protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadataType>,
+ ValueType&& value) {
static_assert(
std::is_base_of<protozero::proto_utils::FieldMetadataBase,
FieldMetadataType>::value,
diff --git a/infra/ci/config.py b/infra/ci/config.py
index f35178e..be325ab 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -145,6 +145,7 @@
if len(sys.argv) > 1 and sys.argv[1] == 'js':
jsn = json.dumps(vars, indent=2)
- print('// Auto-generated by %s, do not edit.\n' % __file__)
+ print('// Auto-generated by %s, do not edit.\n' %
+ os.path.basename(__file__))
print('\'use strict\';\n')
print('const cfg = JSON.parse(`%s`);\n' % jsn.replace(r'\"', r'\\\"'))
diff --git a/infra/perfetto.dev/build.js b/infra/perfetto.dev/build.js
index 086234d..1fc657c 100644
--- a/infra/perfetto.dev/build.js
+++ b/infra/perfetto.dev/build.js
@@ -73,7 +73,11 @@
// Check that deps are current before starting.
const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps');
- const depsArgs = ['--check-only', '/dev/null', '--ui'];
+
+ // --filter=nodejs is to match what cloud_build_entrypoint.sh passes to
+ // install-build-deps. It doesn't bother installing the full toolchains
+ // because, unlike the Perfetto UI, it doesn't need Wasm.
+ const depsArgs = ['--check-only=/dev/null', '--ui', '--filter=nodejs'];
exec(installBuildDeps, depsArgs);
console.log('Entering', cfg.outDir);
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 84d9af3..c76e064 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -926,6 +926,9 @@
}
if (save_to_incidentd_) {
+ if (!uuid_.empty()) {
+ PERFETTO_LOG("go/trace-uuid/%s", uuid_.c_str());
+ }
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
SaveTraceIntoDropboxAndIncidentOrCrash();
#endif
diff --git a/src/profiling/common/producer_support.cc b/src/profiling/common/producer_support.cc
index d6a58ec..fc92714 100644
--- a/src/profiling/common/producer_support.cc
+++ b/src/profiling/common/producer_support.cc
@@ -61,10 +61,6 @@
return true;
}
- if (ds_config.enable_extra_guardrails()) {
- return false; // no extra guardrails on user builds.
- }
-
uint64_t uid_without_profile = uid % kAidUserOffset;
if (uid_without_profile < kAidAppStart || kAidAppEnd < uid_without_profile) {
// TODO(fmayer): relax this.
@@ -82,9 +78,14 @@
PERFETTO_ELOG("Failed to parse packages.list.");
return false;
}
- if (pkg.uid == uid_without_profile &&
- (pkg.profileable_from_shell || pkg.debuggable)) {
- return true;
+ if (pkg.uid == uid_without_profile) {
+ if (pkg.profileable) {
+ return true;
+ }
+ if (!ds_config.enable_extra_guardrails() &&
+ (pkg.profileable_from_shell || pkg.debuggable)) {
+ return true;
+ }
}
}
return false;
diff --git a/src/profiling/common/producer_support_unittest.cc b/src/profiling/common/producer_support_unittest.cc
index df2129a..4b94e89 100644
--- a/src/profiling/common/producer_support_unittest.cc
+++ b/src/profiling/common/producer_support_unittest.cc
@@ -32,7 +32,7 @@
EXPECT_TRUE(CanProfileAndroid(ds_config, 1, "userdebug", "/dev/null"));
}
-TEST(CanProfileAndroidTest, NonUserNonProfileableApp) {
+TEST(CanProfileAndroidTest, NonUserNonProfileableByShellApp) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(false);
auto tmp = base::TempFile::Create();
@@ -44,7 +44,7 @@
EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
}
-TEST(CanProfileAndroidTest, NonUserNonProfileableAppExtraGuardrails) {
+TEST(CanProfileAndroidTest, NonUserNonProfileableByShellAppExtraGuardrails) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(true);
auto tmp = base::TempFile::Create();
@@ -56,7 +56,7 @@
EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "userdebug", tmp.path()));
}
-TEST(CanProfileAndroidTest, UserProfileableApp) {
+TEST(CanProfileAndroidTest, UserProfileableByShellApp) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(false);
auto tmp = base::TempFile::Create();
@@ -68,7 +68,7 @@
EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
}
-TEST(CanProfileAndroidTest, UserProfileableAppExtraGuardrails) {
+TEST(CanProfileAndroidTest, UserProfileableByShellAppExtraGuardrails) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(true);
auto tmp = base::TempFile::Create();
@@ -80,7 +80,19 @@
EXPECT_FALSE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
}
-TEST(CanProfileAndroidTest, UserProfileableAppMultiuser) {
+TEST(CanProfileAndroidTest, UserProfileableAppExtraGuardrails) {
+ DataSourceConfig ds_config;
+ ds_config.set_enable_extra_guardrails(true);
+ auto tmp = base::TempFile::Create();
+ constexpr char content[] =
+ "invalid.example.profileable 10001 0 "
+ "/data/user/0/invalid.example.profileable default:targetSdkVersion=10000 "
+ "none 2 1\n";
+ base::WriteAll(tmp.fd(), content, sizeof(content));
+ EXPECT_TRUE(CanProfileAndroid(ds_config, 10001, "user", tmp.path()));
+}
+
+TEST(CanProfileAndroidTest, UserProfileableByShellAppMultiuser) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(false);
auto tmp = base::TempFile::Create();
@@ -92,7 +104,7 @@
EXPECT_TRUE(CanProfileAndroid(ds_config, 210001, "user", tmp.path()));
}
-TEST(CanProfileAndroidTest, UserNonProfileableApp) {
+TEST(CanProfileAndroidTest, UserNonProfileableByShellApp) {
DataSourceConfig ds_config;
ds_config.set_enable_extra_guardrails(false);
auto tmp = base::TempFile::Create();
diff --git a/src/profiling/perf/unwinding.h b/src/profiling/perf/unwinding.h
index 2eb296f..398747a 100644
--- a/src/profiling/perf/unwinding.h
+++ b/src/profiling/perf/unwinding.h
@@ -17,6 +17,7 @@
#ifndef SRC_PROFILING_PERF_UNWINDING_H_
#define SRC_PROFILING_PERF_UNWINDING_H_
+#include <condition_variable>
#include <map>
#include <thread>
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index e2d3192..c3dc58b 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -831,16 +831,25 @@
void GenerateFieldMetadata(const std::string& message_cpp_type,
const FieldDescriptor* field) {
- const char* code_stub =
- "using $field_metadata_type$ =\n"
- " ::protozero::proto_utils::FieldMetadata<\n"
- " $field_id$,\n"
- " ::protozero::proto_utils::RepetitionType::$repetition_type$,\n"
- " ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$,\n"
- " $cpp_type$,\n"
- " $message_cpp_type$>;\n"
- "static constexpr const $field_metadata_type$& $field_metadata_var$ = "
- "{};\n";
+ const char* code_stub = R"(
+using $field_metadata_type$ =
+ ::protozero::proto_utils::FieldMetadata<
+ $field_id$,
+ ::protozero::proto_utils::RepetitionType::$repetition_type$,
+ ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$,
+ $cpp_type$,
+ $message_cpp_type$>;
+
+// Ceci n'est pas une pipe.
+// This is actually a variable of FieldMetadataHelper<FieldMetadata<...>>
+// type (and users are expected to use it as such, hence kCamelCase name).
+// It is declared as a function to keep protozero bindings header-only as
+// inline constexpr variables are not available until C++17 (while inline
+// functions are).
+// TODO(altimin): Use inline variable instead after adopting C++17.
+static constexpr $field_metadata_type$ $field_metadata_var$() { return {}; }
+)";
+
stub_h_->Print(code_stub, "field_id", std::to_string(field->number()),
"repetition_type", FieldToRepetitionType(field),
"proto_field_type", FieldToProtoSchemaType(field),
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 16e7908..6c80e87 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -195,16 +195,10 @@
context_->clock_tracker->ToTraceTime(converted_clock_id, timestamp);
if (!trace_ts.has_value()) {
// ToTraceTime() will increase the |clock_sync_failure| stat on failure.
- static const char seq_extra_err[] =
- " Because the clock id is sequence-scoped, the ClockSnapshot must "
- "be emitted on the same TraceWriter sequence of the packet that "
- "refers to that clock id.";
- return util::ErrStatus(
- "Failed to convert TracePacket's timestamp from clock_id=%" PRIu32
- " seq_id=%" PRIu32
- ". This is usually due to the lack of a prior ClockSnapshot "
- "proto.%s",
- timestamp_clock_id, seq_id, is_seq_scoped ? seq_extra_err : "");
+ // We don't return an error here as it will cause the trace to stop
+ // parsing. Instead, we rely on the stat increment in ToTraceTime() to
+ // inform the user about the error.
+ return util::OkStatus();
}
timestamp = trace_ts.value();
}
diff --git a/src/trace_processor/metrics/android/android_sysui_cuj.sql b/src/trace_processor/metrics/android/android_sysui_cuj.sql
index 67f8ccf..2a0bba1 100644
--- a/src/trace_processor/metrics/android/android_sysui_cuj.sql
+++ b/src/trace_processor/metrics/android/android_sysui_cuj.sql
@@ -277,7 +277,7 @@
WHERE remainder <> "")
SELECT frame_number, jank_cause
FROM split_jank_type
- WHERE jank_cause NOT IN ('', 'App Deadline Missed', 'None')
+ WHERE jank_cause NOT IN ('', 'App Deadline Missed', 'None', 'Buffer Stuffing')
ORDER BY frame_number ASC;
DROP TABLE IF EXISTS android_sysui_cuj_missed_frames_hwui_times;
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index d793059..e93c0b4 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 callframe, with the most-inlined symbol having id == symbol_set_id.
+// one frame, with the bottom-most inlined frame 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 |baz |foo.cc | 36 |
+// |1 | 1 |foo |foo.cc | 60 |
// |2 | 1 |bar |foo.cc | 30 |
-// |3 | 1 |foo |foo.cc | 60 |
+// |3 | 1 |baz |foo.cc | 36 |
// ```
// @param name name of the function.
// @param source_file name of the source file containing the function.
diff --git a/src/traced/probes/packages_list/packages_list_parser.cc b/src/traced/probes/packages_list/packages_list_parser.cc
index bcbccaa..e00e296 100644
--- a/src/traced/probes/packages_list/packages_list_parser.cc
+++ b/src/traced/probes/packages_list/packages_list_parser.cc
@@ -50,12 +50,13 @@
}
case 6: {
char* end;
- long long profilable_from_shell = strtoll(ss.cur_token(), &end, 10);
+ long long profilable_level = strtoll(ss.cur_token(), &end, 10);
if ((*end != '\0' && *end != '\n') || *ss.cur_token() == '\0') {
PERFETTO_ELOG("Failed to parse packages.list profilable_from_shell.");
return false;
}
- package->profileable_from_shell = profilable_from_shell != 0;
+ package->profileable_from_shell = profilable_level != 0;
+ package->profileable = profilable_level == 2;
break;
}
case 7: {
diff --git a/src/traced/probes/packages_list/packages_list_parser.h b/src/traced/probes/packages_list/packages_list_parser.h
index 3deb3c5..ee35ddd 100644
--- a/src/traced/probes/packages_list/packages_list_parser.h
+++ b/src/traced/probes/packages_list/packages_list_parser.h
@@ -26,6 +26,7 @@
std::string name;
uint64_t uid = 0;
bool debuggable = false;
+ bool profileable = false;
bool profileable_from_shell = false;
int64_t version_code = 0;
};
diff --git a/src/tracing/traced_value_unittest.cc b/src/tracing/traced_value_unittest.cc
index b82d6fc..66c86db 100644
--- a/src/tracing/traced_value_unittest.cc
+++ b/src/tracing/traced_value_unittest.cc
@@ -91,7 +91,7 @@
ASSERT_TYPE_SUPPORTED(const int*);
ASSERT_TYPE_SUPPORTED(void*);
ASSERT_TYPE_SUPPORTED(const void*);
-ASSERT_TYPE_SUPPORTED(nullptr_t);
+ASSERT_TYPE_SUPPORTED(std::nullptr_t);
ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType*);
ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType*);
diff --git a/test/trace_processor/graphics/android_sysui_cuj.out b/test/trace_processor/graphics/android_sysui_cuj.out
index cf6c74e..1cdea90 100644
--- a/test/trace_processor/graphics/android_sysui_cuj.out
+++ b/test/trace_processor/graphics/android_sysui_cuj.out
@@ -63,7 +63,6 @@
number: 8
ts: 400000000
dur: 10000000
- jank_cause: "Buffer Stuffing"
vsync: 120
}
}
diff --git a/test/trace_processor/graphics/android_sysui_cuj_event.out b/test/trace_processor/graphics/android_sysui_cuj_event.out
index e36ea72..4519815 100644
--- a/test/trace_processor/graphics/android_sysui_cuj_event.out
+++ b/test/trace_processor/graphics/android_sysui_cuj_event.out
@@ -6,4 +6,3 @@
"slice","SHADE_ROW_EXPAND - jank cause",70000000,10000000,"RenderThread - scheduler"
"slice","SHADE_ROW_EXPAND - jank cause",100000000,15000000,"GPU completion - long completion time"
"slice","SHADE_ROW_EXPAND - jank cause",200000000,15000000,"SurfaceFlinger GPU Deadline Missed,SurfaceFlinger Scheduling"
-"slice","SHADE_ROW_EXPAND - jank cause",400000000,15000000,"Buffer Stuffing"
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 560c1ba..0bfa958 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -208,7 +208,7 @@
Dependency(
'buildtools/android-unwinding',
'https://android.googlesource.com/platform/system/unwinding.git',
- '5f15cdbdb71db0eb6512e9432a63b8c4075a6c00', 'all', 'all'),
+ 'b296bb6fcaa7f59e0ed57bb7a4238ee0f9bfb5bf', 'all', 'all'),
Dependency('buildtools/android-logging',
'https://android.googlesource.com/platform/system/logging.git',
'7b36b566c9113fc703d68f76e8f40c0c2432481c', 'all', 'all'),
diff --git a/tools/trace_processor b/tools/trace_processor
index 5b84f0c..d15dd62 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -21,7 +21,7 @@
# cat ./trace_processor | python -
BASH_FALLBACK = """ "
-exec python - "$@" <<'#'EOF
+exec python3 - "$@" <<'#'EOF
#"""
import hashlib
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 1ef6484..c1674d8 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -71,7 +71,6 @@
"../../protos/third_party/pprof:zero",
"../../src/profiling/symbolizer",
"../../src/profiling/symbolizer:symbolize_database",
- "../../src/trace_processor/containers:containers",
]
sources = [ "pprof_builder.cc" ]
}
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index e4a9f1d..8384b7a 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -32,6 +32,7 @@
#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
@@ -42,19 +43,17 @@
int Usage(const char* argv0) {
fprintf(stderr,
- "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",
+ "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",
argv0);
return 1;
}
@@ -76,7 +75,6 @@
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());
@@ -105,8 +103,6 @@
}
} 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 {
@@ -191,11 +187,10 @@
return TraceToText(input_stream, output_stream);
if (format == "profile") {
- return perf_profile
- ? TraceToPerfProfile(input_stream, output_stream, pid,
- timestamps, !profile_no_annotations)
- : TraceToHeapProfile(input_stream, output_stream, pid,
- timestamps, !profile_no_annotations);
+ return perf_profile ? TraceToPerfProfile(input_stream, output_stream, pid,
+ timestamps)
+ : TraceToHeapProfile(input_stream, output_stream, pid,
+ timestamps);
}
if (format == "hprof")
diff --git a/tools/trace_to_text/pprof_builder.cc b/tools/trace_to_text/pprof_builder.cc
index 588c438..fdb8717 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/base/build_config.h"
-
#include "perfetto/profiling/pprof_builder.h"
+#include "perfetto/base/build_config.h"
+
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <cxxabi.h>
#endif
@@ -27,120 +27,29 @@
#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/ext/base/hash.h"
+#include "perfetto/base/time.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;
@@ -174,6 +83,84 @@
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,
@@ -195,373 +182,73 @@
return base::make_optional(it.Get(0).AsLong());
}
-// 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.
+// Helper for constructing |perftools.profiles.Profile| protos.
class GProfileBuilder {
public:
- 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
+ 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
// empty string.
- int64_t empty_id = ToStringTableId(StringId::Null());
+ int64_t empty_id = Intern("");
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(
- ToStringTableId(interner_->InternString(base::StringView(st.first))));
- sample_type->set_unit(ToStringTableId(
- interner_->InternString(base::StringView(st.second))));
+ sample_type->set_type(Intern(st.first));
+ sample_type->set_unit(Intern(st.second));
}
}
bool AddSample(const protozero::PackedVarInt& values, int64_t callstack_id) {
- const auto& location_ids = locations_.LocationsForCallstack(callstack_id);
- if (location_ids.empty()) {
+ const auto& frames = FramesForCallstack(callstack_id);
+ if (frames.empty()) {
PERFETTO_DFATAL_OR_ELOG(
"Failed to find frames for callstack id %" PRIi64 "", callstack_id);
return false;
}
-
- // 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));
+ protozero::PackedVarInt location_ids;
+ for (int64_t frame : frames)
+ location_ids.Append(ToPprofId(frame));
auto* gsample = result_->add_sample();
gsample->set_value(values);
- gsample->set_location_id(packed_locs);
+ gsample->set_location_id(location_ids);
- // Remember the locations s.t. we only serialize the referenced ones.
- seen_locations_.insert(location_ids.cbegin(), location_ids.cend());
+ // remember frames to be emitted
+ seen_frames_.insert(frames.cbegin(), frames.cend());
+
return true;
}
std::string CompleteProfile(trace_processor::TraceProcessor* tp) {
std::set<int64_t> seen_mappings;
- std::set<int64_t> seen_functions;
+ std::set<int64_t> seen_symbol_ids;
- if (!WriteLocations(&seen_mappings, &seen_functions))
- return {};
- if (!WriteFunctions(seen_functions))
+ // Write the location info for frames referenced by the added samples.
+ if (!WriteFrames(tp, &seen_mappings, &seen_symbol_ids))
return {};
if (!WriteMappings(tp, seen_mappings))
return {};
-
- WriteStringTable();
+ if (!WriteSymbols(tp, seen_symbol_ids))
+ return {};
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(
@@ -573,8 +260,7 @@
if (seen_mappings.find(id) == seen_mappings.end())
continue;
++mappings_no;
- auto interned_filename = ToStringTableId(
- interner_->InternString(mapping_it.Get(4).AsString()));
+ auto interned_filename = Intern(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
@@ -599,48 +285,145 @@
return true;
}
- void WriteStringTable() {
- for (StringId id : string_table_) {
- trace_processor::NullTermStringView s = interner_->Get(id);
- result_->add_string_table(s.data(), s.size());
+ 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);
}
+
+ 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;
}
- 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);
+ 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);
}
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_;
- // Set of locations referenced by the added samples.
- std::set<int64_t> seen_locations_;
+ std::set<int64_t> seen_frames_;
};
+} // namespace
+
namespace heap_profile {
struct View {
const char* type;
@@ -760,12 +543,13 @@
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) {
- trace_processor::StringPool interner;
- LocationTracker locations =
- PreprocessLocations(tp, &interner, annotate_frames);
+ 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;
bool any_fail = false;
Iterator it = tp->ExecuteQuery(
@@ -773,7 +557,8 @@
"from heap_profile_allocation hpa, "
"process p where p.upid = hpa.upid;");
while (it.Next()) {
- GProfileBuilder builder(locations, &interner);
+ GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
+ max_symbol_id.value());
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());
@@ -901,11 +686,12 @@
// perf and heap profiles.
static bool TraceToPerfPprof(trace_processor::TraceProcessor* tp,
std::vector<SerializedProfile>* output,
- bool annotate_frames,
uint64_t target_pid) {
- trace_processor::StringPool interner;
- LocationTracker locations =
- PreprocessLocations(tp, &interner, annotate_frames);
+ 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;
LogTracePerfEventIssues(tp);
@@ -917,7 +703,9 @@
if (target_pid != 0 && process.pid != target_pid)
continue;
- GProfileBuilder builder(locations, &interner);
+ GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
+ max_symbol_id.value());
+
builder.WriteSampleTypes({{"samples", "count"}});
std::string query = "select callsite_id from perf_sample where utid in (" +
@@ -945,22 +733,17 @@
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, annotate_frames, pid,
- timestamps);
+ return heap_profile::TraceToHeapPprof(tp, output, pid, timestamps);
case (ConversionMode::kPerfProfile):
- return perf_profile::TraceToPerfPprof(tp, output, annotate_frames, pid);
+ return perf_profile::TraceToPerfPprof(tp, output, 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 c71a827..a394e2b 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -50,12 +50,6 @@
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());
@@ -98,7 +92,6 @@
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;
@@ -113,8 +106,7 @@
MaybeSymbolize(tp.get());
MaybeDeobfuscate(tp.get());
- TraceToPprof(tp.get(), &profiles, conversion_mode, conversion_flags, pid,
- timestamps);
+ TraceToPprof(tp.get(), &profiles, conversion_mode, pid, timestamps);
if (profiles.empty()) {
return 0;
}
@@ -140,33 +132,31 @@
int TraceToHeapProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps,
- bool annotate_frames) {
+ std::vector<uint64_t> timestamps) {
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,
- ToConversionFlags(annotate_frames), "heap_profile-", filename_fn);
+ return TraceToProfile(input, output, pid, timestamps,
+ ConversionMode::kHeapProfile, "heap_profile-",
+ filename_fn);
}
int TraceToPerfProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps,
- bool annotate_frames) {
+ std::vector<uint64_t> timestamps) {
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,
- ToConversionFlags(annotate_frames), "perf_profile-", filename_fn);
+ return TraceToProfile(input, output, pid, timestamps,
+ ConversionMode::kPerfProfile, "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 313b32b..1c41aad 100644
--- a/tools/trace_to_text/trace_to_profile.h
+++ b/tools/trace_to_text/trace_to_profile.h
@@ -27,15 +27,13 @@
int TraceToHeapProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps,
- bool annotate_frames);
+ std::vector<uint64_t> timestamps);
// 0: success
int TraceToPerfProfile(std::istream* input,
std::ostream* output,
uint64_t pid,
- std::vector<uint64_t> timestamps,
- bool annotate_frames);
+ std::vector<uint64_t> timestamps);
} // namespace trace_to_text
} // namespace perfetto
diff --git a/tools/traceconv b/tools/traceconv
index c0cff60..bb90486 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -21,7 +21,7 @@
# cat ./traceconv | python -
BASH_FALLBACK = """ "
-exec python - "$@" <<'#'EOF
+exec python3 - "$@" <<'#'EOF
#"""
import hashlib
diff --git a/tools/write_version_header.py b/tools/write_version_header.py
index 0325f6f..dd02a69 100755
--- a/tools/write_version_header.py
+++ b/tools/write_version_header.py
@@ -123,7 +123,7 @@
guard = '%s_' % args.cpp_out.upper()
guard = re.sub(r'[^\w]', '_', guard)
lines = []
- lines.append('// Generated by %s' % __file__)
+ lines.append('// Generated by %s' % os.path.basename(__file__))
lines.append('')
lines.append('#ifndef %s' % guard)
lines.append('#define %s' % guard)
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 6d53d32..160b544 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
"channels": [
{
"name": "stable",
- "rev": "b7ab2469bd84ac0a4883c7f6a8a67e73903459b0"
+ "rev": "06350fe212f5051d9e890411ca24ab4a84b33ae1"
},
{
"name": "canary",
- "rev": "06350fe212f5051d9e890411ca24ab4a84b33ae1"
+ "rev": "aaab326dc5b38858d516171ba68f4542e9e92d65"
},
{
"name": "autopush",
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 65c78d0..8d47424 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -519,11 +519,13 @@
'accessibility',
'AccountFetcherService',
'android_webview',
+ 'aogh',
'audio',
'base',
'benchmark',
'blink',
'blink.animations',
+ 'blink.bindings',
'blink.console',
'blink_gc',
'blink.net',
@@ -534,8 +536,13 @@
'browser',
'browsing_data',
'CacheStorage',
+ 'Calculators',
+ 'CameraStream',
'camera',
+ 'cast_app',
'cast_perf_test',
+ 'cast.mdns',
+ 'cast.mdns.socket',
'cast.stream',
'cc',
'cc.debug',
@@ -545,14 +552,17 @@
'compositor',
'content',
'content_capture',
+ 'device',
'devtools',
+ 'devtools.contrast',
'devtools.timeline',
+ 'disk_cache',
'download',
'download_service',
'drm',
'drmcursor',
'dwrite',
- 'DXVA Decoding',
+ 'DXVA_Decoding',
'EarlyJava',
'evdev',
'event',
@@ -563,10 +573,12 @@
'fonts',
'GAMEPAD',
'gpu',
+ 'gpu.angle',
'gpu.capture',
'headless',
'hwoverlays',
'identity',
+ 'ime',
'IndexedDB',
'input',
'io',
@@ -594,12 +606,17 @@
'omnibox',
'oobe',
'ozone',
+ 'partition_alloc',
'passwords',
'p2p',
'page-serialization',
+ 'paint_preview',
'pepper',
+ 'PlatformMalloc',
+ 'power',
'ppapi',
- 'ppapi proxy',
+ 'ppapi_proxy',
+ 'print',
'rail',
'renderer',
'renderer_host',
@@ -610,21 +627,26 @@
'sequence_manager',
'service_manager',
'ServiceWorker',
+ 'sharing',
'shell',
'shortcut_viewer',
'shutdown',
'SiteEngagement',
'skia',
+ 'sql',
+ 'stadia_media',
+ 'stadia_rtc',
'startup',
'sync',
'sync_lock_contention',
- 'thread_pool',
'test_gpu',
- 'test_tracing',
+ 'thread_pool',
'toplevel',
+ 'toplevel.flow',
'ui',
'v8',
'v8.execute',
+ 'v8.wasm',
'ValueStoreFrontend::Backend',
'views',
'views.frame',
@@ -637,6 +659,7 @@
'webrtc',
'xr',
'disabled-by-default-animation-worklet',
+ 'disabled-by-default-audio',
'disabled-by-default-audio-worklet',
'disabled-by-default-blink.debug',
'disabled-by-default-blink.debug.display_lock',
@@ -654,6 +677,7 @@
'disabled-by-default-cc.debug.scheduler',
'disabled-by-default-cc.debug.scheduler.frames',
'disabled-by-default-cc.debug.scheduler.now',
+ 'disabled-by-default-content.verbose',
'disabled-by-default-cpu_profiler',
'disabled-by-default-cpu_profiler.debug',
'disabled-by-default-devtools.screenshot',
@@ -668,34 +692,41 @@
'disabled-by-default-gpu_cmd_queue',
'disabled-by-default-gpu.dawn',
'disabled-by-default-gpu.debug',
- 'disabled-by-default-gpu_decoder',
+ 'disabled-by-default-gpu.decoder',
'disabled-by-default-gpu.device',
'disabled-by-default-gpu.service',
+ 'disabled-by-default-gpu.vulkan.vma',
'disabled-by-default-histogram_samples',
'disabled-by-default-ipc.flow',
'disabled-by-default-java-heap-profiler',
'disabled-by-default-layer-element',
+ 'disabled-by-default-layout_shift.debug',
'disabled-by-default-lifecycles',
'disabled-by-default-loading',
+ 'disabled-by-default-mediastream',
'disabled-by-default-memory-infra',
'disabled-by-default-memory-infra.v8.code_stats',
+ 'disabled-by-default-mojom',
'disabled-by-default-net',
'disabled-by-default-network',
'disabled-by-default-paint-worklet',
'disabled-by-default-power',
'disabled-by-default-renderer.scheduler',
'disabled-by-default-renderer.scheduler.debug',
+ 'disabled-by-default-sandbox',
'disabled-by-default-sequence_manager',
'disabled-by-default-sequence_manager.debug',
'disabled-by-default-sequence_manager.verbose_snapshots',
'disabled-by-default-skia',
'disabled-by-default-skia.gpu',
'disabled-by-default-skia.gpu.cache',
+ 'disabled-by-default-skia.shaders',
'disabled-by-default-SyncFileSystem',
'disabled-by-default-system_stats',
'disabled-by-default-thread_pool_diagnostics',
'disabled-by-default-toplevel.flow',
'disabled-by-default-toplevel.ipc',
+ 'disabled-by-default-user_action_samples',
'disabled-by-default-v8.compile',
'disabled-by-default-v8.cpu_profiler',
'disabled-by-default-v8.cpu_profiler.hires',
@@ -705,17 +736,24 @@
'disabled-by-default-v8.runtime',
'disabled-by-default-v8.runtime_stats',
'disabled-by-default-v8.runtime_stats_sampling',
+ 'disabled-by-default-v8.stack_trace',
'disabled-by-default-v8.turbofan',
'disabled-by-default-v8.wasm',
+ 'disabled-by-default-v8.wasm.detailed',
+ 'disabled-by-default-v8.wasm.turbofan',
'disabled-by-default-video_and_image_capture',
'disabled-by-default-viz.debug.overlay_planes',
+ 'disabled-by-default-viz.gpu_composite_time',
'disabled-by-default-viz.hit_testing_flow',
'disabled-by-default-viz.overdraw',
'disabled-by-default-viz.quads',
'disabled-by-default-viz.surface_id_flow',
'disabled-by-default-viz.surface_lifetime',
'disabled-by-default-viz.triangles',
+ 'disabled-by-default-webaudio.audionode',
+ 'disabled-by-default-webrtc',
'disabled-by-default-worker.scheduler',
+ 'disabled-by-default-xr.debug',
];
}