Merge "Fix bug in GC sched reporting." into main
diff --git a/Android.bp b/Android.bp
index 5daf537..fa82c12 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2549,6 +2549,7 @@
":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
+ ":perfetto_src_trace_processor_util_winscope_proto_mapping",
":perfetto_src_trace_processor_util_zip_reader",
":perfetto_src_traced_probes_android_game_intervention_list_android_game_intervention_list",
":perfetto_src_traced_probes_android_log_android_log",
@@ -5480,6 +5481,7 @@
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
+ "protos/perfetto/metrics/chrome/histogram_summaries.proto",
"protos/perfetto/metrics/chrome/long_latency.proto",
"protos/perfetto/metrics/chrome/media_metric.proto",
"protos/perfetto/metrics/chrome/performance_mark_hashes.proto",
@@ -6775,6 +6777,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -6788,6 +6791,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7204,6 +7208,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -7217,6 +7222,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7295,6 +7301,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cma.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.gen.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.gen.cc",
@@ -7308,6 +7315,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.gen.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.cc",
@@ -7386,6 +7394,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/cma.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.gen.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.gen.h",
@@ -7399,6 +7408,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.gen.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h",
@@ -7473,6 +7483,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -7486,6 +7497,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7563,6 +7575,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cma.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.pb.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.pb.cc",
@@ -7576,6 +7589,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pb.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pb.cc",
@@ -7653,6 +7667,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/cma.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.pb.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.pb.h",
@@ -7666,6 +7681,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pb.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pb.h",
@@ -7740,6 +7756,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -7753,6 +7770,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7831,6 +7849,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cma.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.pbzero.cc",
@@ -7844,6 +7863,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.cc",
@@ -7922,6 +7942,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/clk.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/cma.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/compaction.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/cpm_trace.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/dcvsh.pbzero.h",
@@ -7935,6 +7956,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h",
@@ -11532,6 +11554,7 @@
name: "perfetto_src_profiling_perf_producer_unittests",
srcs: [
"src/profiling/perf/event_config_unittest.cc",
+ "src/profiling/perf/frame_pointer_unwinder_unittest.cc",
"src/profiling/perf/perf_producer_unittest.cc",
"src/profiling/perf/unwind_queue_unittest.cc",
],
@@ -11557,6 +11580,7 @@
filegroup {
name: "perfetto_src_profiling_perf_unwinding",
srcs: [
+ "src/profiling/perf/frame_pointer_unwinder.cc",
"src/profiling/perf/unwinding.cc",
],
}
@@ -13211,6 +13235,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
"src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
@@ -13419,6 +13444,7 @@
"src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc",
],
}
@@ -13569,6 +13595,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/helpers.sql",
"src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/raw_dominator_tree.sql",
"src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/callstacks.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/summary_tree.sql",
"src/trace_processor/perfetto_sql/stdlib/android/memory/process.sql",
"src/trace_processor/perfetto_sql/stdlib/android/monitor_contention.sql",
"src/trace_processor/perfetto_sql/stdlib/android/network_packets.sql",
@@ -13594,19 +13621,7 @@
"src/trace_processor/perfetto_sql/stdlib/android/winscope/windowmanager.sql",
"src/trace_processor/perfetto_sql/stdlib/callstacks/stack_profile.sql",
"src/trace_processor/perfetto_sql/stdlib/chrome/**/*.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/args.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/counters.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql",
"src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/metadata.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql",
"src/trace_processor/perfetto_sql/stdlib/export/to_firefox_profile.sql",
"src/trace_processor/perfetto_sql/stdlib/graphs/critical_path.sql",
"src/trace_processor/perfetto_sql/stdlib/graphs/dominator_tree.sql",
@@ -13639,6 +13654,7 @@
"src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/views.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/tables.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/trace_bounds.sql",
+ "src/trace_processor/perfetto_sql/stdlib/sched/latency.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/states.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql",
@@ -13651,6 +13667,7 @@
"src/trace_processor/perfetto_sql/stdlib/slices/flow.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/hierarchy.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
+ "src/trace_processor/perfetto_sql/stdlib/slices/time_in_state.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
"src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql",
"src/trace_processor/perfetto_sql/stdlib/stacks/cpu_profiling.sql",
@@ -14112,6 +14129,11 @@
name: "perfetto_src_trace_processor_util_util",
}
+// GN: //src/trace_processor/util:winscope_proto_mapping
+filegroup {
+ name: "perfetto_src_trace_processor_util_winscope_proto_mapping",
+}
+
// GN: //src/trace_processor/util:zip_reader
filegroup {
name: "perfetto_src_trace_processor_util_zip_reader",
@@ -15129,6 +15151,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -15142,6 +15165,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -15734,6 +15758,7 @@
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_unittests",
":perfetto_src_trace_processor_util_util",
+ ":perfetto_src_trace_processor_util_winscope_proto_mapping",
":perfetto_src_trace_processor_util_zip_reader",
":perfetto_src_trace_redaction_trace_redaction",
":perfetto_src_trace_redaction_unittests",
@@ -16468,6 +16493,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -16481,6 +16507,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -16787,6 +16814,7 @@
":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
+ ":perfetto_src_trace_processor_util_winscope_proto_mapping",
":perfetto_src_trace_processor_util_zip_reader",
"src/trace_processor/trace_processor_shell.cc",
],
@@ -17051,6 +17079,11 @@
cflags: [
"-DZLIB_IMPLEMENTATION",
],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.profiling",
+ ],
+ min_sdk_version: "35",
}
// GN: //src/traceconv:traceconv
@@ -17210,6 +17243,7 @@
":perfetto_src_trace_processor_util_trace_blob_view_reader",
":perfetto_src_trace_processor_util_trace_type",
":perfetto_src_trace_processor_util_util",
+ ":perfetto_src_trace_processor_util_winscope_proto_mapping",
":perfetto_src_trace_processor_util_zip_reader",
":perfetto_src_traceconv_lib",
":perfetto_src_traceconv_main",
diff --git a/BUILD b/BUILD
index 380c9d7..64c1e49 100644
--- a/BUILD
+++ b/BUILD
@@ -152,6 +152,113 @@
linkstatic = True,
)
+# GN target: //src/shared_lib:libperfetto_c
+perfetto_cc_library(
+ name = "libperfetto_c",
+ srcs = [
+ ":src_android_stats_android_stats",
+ ":src_android_stats_perfetto_atoms",
+ ":src_protozero_filtering_bytecode_common",
+ ":src_protozero_filtering_bytecode_parser",
+ ":src_protozero_filtering_message_filter",
+ ":src_protozero_filtering_string_filter",
+ ":src_shared_lib_intern_map",
+ ":src_shared_lib_shared_lib",
+ ":src_tracing_client_api_without_backends",
+ ":src_tracing_common",
+ ":src_tracing_core_core",
+ ":src_tracing_in_process_backend",
+ ":src_tracing_ipc_common",
+ ":src_tracing_ipc_consumer_consumer",
+ ":src_tracing_ipc_default_socket",
+ ":src_tracing_ipc_producer_producer",
+ ":src_tracing_ipc_service_service",
+ ":src_tracing_platform_impl",
+ ":src_tracing_service_service",
+ ":src_tracing_system_backend",
+ ],
+ hdrs = [
+ ":include_perfetto_base_base",
+ ":include_perfetto_ext_base_base",
+ ":include_perfetto_ext_ipc_ipc",
+ ":include_perfetto_ext_tracing_core_core",
+ ":include_perfetto_ext_tracing_ipc_ipc",
+ ":include_perfetto_protozero_protozero",
+ ":include_perfetto_public_abi_base",
+ ":include_perfetto_public_abi_public",
+ ":include_perfetto_public_base",
+ ":include_perfetto_public_protos_protos",
+ ":include_perfetto_public_protozero",
+ ":include_perfetto_public_public",
+ ":include_perfetto_tracing_core_core",
+ ":include_perfetto_tracing_core_forward_decls",
+ ":include_perfetto_tracing_tracing",
+ ],
+ defines = [
+ "PERFETTO_SHLIB_SDK_IMPLEMENTATION",
+ ],
+ visibility = PERFETTO_CONFIG.public_visibility,
+ deps = [
+ ":perfetto_ipc",
+ ":protos_perfetto_common_cpp",
+ ":protos_perfetto_common_zero",
+ ":protos_perfetto_config_android_cpp",
+ ":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_ftrace_cpp",
+ ":protos_perfetto_config_ftrace_zero",
+ ":protos_perfetto_config_gpu_cpp",
+ ":protos_perfetto_config_gpu_zero",
+ ":protos_perfetto_config_inode_file_cpp",
+ ":protos_perfetto_config_inode_file_zero",
+ ":protos_perfetto_config_interceptors_cpp",
+ ":protos_perfetto_config_interceptors_zero",
+ ":protos_perfetto_config_power_cpp",
+ ":protos_perfetto_config_power_zero",
+ ":protos_perfetto_config_process_stats_cpp",
+ ":protos_perfetto_config_process_stats_zero",
+ ":protos_perfetto_config_profiling_cpp",
+ ":protos_perfetto_config_profiling_zero",
+ ":protos_perfetto_config_statsd_cpp",
+ ":protos_perfetto_config_statsd_zero",
+ ":protos_perfetto_config_sys_stats_cpp",
+ ":protos_perfetto_config_sys_stats_zero",
+ ":protos_perfetto_config_system_info_cpp",
+ ":protos_perfetto_config_system_info_zero",
+ ":protos_perfetto_config_track_event_cpp",
+ ":protos_perfetto_config_track_event_zero",
+ ":protos_perfetto_config_zero",
+ ":protos_perfetto_ipc_cpp",
+ ":protos_perfetto_ipc_ipc",
+ ":protos_perfetto_trace_android_winscope_common_zero",
+ ":protos_perfetto_trace_android_winscope_regular_zero",
+ ":protos_perfetto_trace_android_zero",
+ ":protos_perfetto_trace_chrome_zero",
+ ":protos_perfetto_trace_etw_zero",
+ ":protos_perfetto_trace_filesystem_zero",
+ ":protos_perfetto_trace_ftrace_zero",
+ ":protos_perfetto_trace_gpu_zero",
+ ":protos_perfetto_trace_interned_data_zero",
+ ":protos_perfetto_trace_minimal_zero",
+ ":protos_perfetto_trace_non_minimal_zero",
+ ":protos_perfetto_trace_perfetto_zero",
+ ":protos_perfetto_trace_power_zero",
+ ":protos_perfetto_trace_profiling_zero",
+ ":protos_perfetto_trace_ps_zero",
+ ":protos_perfetto_trace_statsd_zero",
+ ":protos_perfetto_trace_sys_stats_zero",
+ ":protos_perfetto_trace_system_info_zero",
+ ":protos_perfetto_trace_track_event_cpp",
+ ":protos_perfetto_trace_track_event_zero",
+ ":protos_perfetto_trace_translation_zero",
+ ":protozero",
+ ":src_base_base",
+ ":src_base_clock_snapshots",
+ ":src_base_version",
+ ],
+ linkstatic = True,
+)
+
# GN target: //src/tools/proto_filter:proto_filter
perfetto_cc_binary(
name = "proto_filter",
@@ -306,6 +413,7 @@
":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
+ ":src_trace_processor_util_winscope_proto_mapping",
":src_trace_processor_util_zip_reader",
],
hdrs = [
@@ -394,6 +502,72 @@
linkstatic = True,
)
+# GN target: //src/traceconv:libpprofbuilder
+perfetto_cc_library(
+ name = "libpprofbuilder",
+ srcs = [
+ ":src_profiling_deobfuscator",
+ ":src_profiling_symbolizer_symbolize_database",
+ ":src_profiling_symbolizer_symbolizer",
+ ":src_trace_processor_util_build_id",
+ ":src_traceconv_pprofbuilder",
+ ":src_traceconv_utils",
+ ],
+ hdrs = [
+ ":include_perfetto_base_base",
+ ":include_perfetto_ext_base_base",
+ ":include_perfetto_profiling_pprof_builder",
+ ":include_perfetto_protozero_protozero",
+ ":include_perfetto_public_abi_base",
+ ":include_perfetto_public_base",
+ ":include_perfetto_public_protozero",
+ ":include_perfetto_trace_processor_basic_types",
+ ":include_perfetto_trace_processor_storage",
+ ":include_perfetto_trace_processor_trace_processor",
+ ],
+ visibility = PERFETTO_CONFIG.public_visibility,
+ deps = [
+ ":protos_perfetto_common_zero",
+ ":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_ftrace_zero",
+ ":protos_perfetto_config_gpu_zero",
+ ":protos_perfetto_config_inode_file_zero",
+ ":protos_perfetto_config_interceptors_zero",
+ ":protos_perfetto_config_power_zero",
+ ":protos_perfetto_config_process_stats_zero",
+ ":protos_perfetto_config_profiling_zero",
+ ":protos_perfetto_config_statsd_zero",
+ ":protos_perfetto_config_sys_stats_zero",
+ ":protos_perfetto_config_system_info_zero",
+ ":protos_perfetto_config_track_event_zero",
+ ":protos_perfetto_config_zero",
+ ":protos_perfetto_trace_android_winscope_common_zero",
+ ":protos_perfetto_trace_android_winscope_regular_zero",
+ ":protos_perfetto_trace_android_zero",
+ ":protos_perfetto_trace_chrome_zero",
+ ":protos_perfetto_trace_etw_zero",
+ ":protos_perfetto_trace_filesystem_zero",
+ ":protos_perfetto_trace_ftrace_zero",
+ ":protos_perfetto_trace_gpu_zero",
+ ":protos_perfetto_trace_interned_data_zero",
+ ":protos_perfetto_trace_minimal_zero",
+ ":protos_perfetto_trace_non_minimal_zero",
+ ":protos_perfetto_trace_perfetto_zero",
+ ":protos_perfetto_trace_power_zero",
+ ":protos_perfetto_trace_profiling_zero",
+ ":protos_perfetto_trace_ps_zero",
+ ":protos_perfetto_trace_statsd_zero",
+ ":protos_perfetto_trace_sys_stats_zero",
+ ":protos_perfetto_trace_system_info_zero",
+ ":protos_perfetto_trace_track_event_zero",
+ ":protos_perfetto_trace_translation_zero",
+ ":protos_third_party_pprof_zero",
+ ":protozero",
+ ":src_trace_processor_containers_containers",
+ ] + PERFETTO_CONFIG.deps.zlib,
+ linkstatic = True,
+)
+
# GN target: //test:client_api_example
perfetto_cc_binary(
name = "client_api_example",
@@ -2367,6 +2541,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
"src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
@@ -2651,6 +2826,8 @@
"src/trace_processor/perfetto_sql/intrinsics/table_functions/flamegraph_construction_algorithms.h",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.cc",
"src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc",
+ "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h",
],
)
@@ -2777,6 +2954,7 @@
name = "src_trace_processor_perfetto_sql_stdlib_android_memory_heap_profile_heap_profile",
srcs = [
"src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/callstacks.sql",
+ "src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/summary_tree.sql",
],
)
@@ -2863,19 +3041,6 @@
srcs = glob(["src/trace_processor/perfetto_sql/stdlib/chrome/**/*.sql"]),
)
-# GN target: //src/trace_processor/perfetto_sql/stdlib/common:common
-perfetto_filegroup(
- name = "src_trace_processor_perfetto_sql_stdlib_common_common",
- srcs = [
- "src/trace_processor/perfetto_sql/stdlib/common/args.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/counters.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
- "src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql",
- ],
-)
-
# GN target: //src/trace_processor/perfetto_sql/stdlib/counters:counters
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_counters_counters",
@@ -2884,19 +3049,6 @@
],
)
-# GN target: //src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common:common
-perfetto_filegroup(
- name = "src_trace_processor_perfetto_sql_stdlib_deprecated_v42_common_common",
- srcs = [
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/metadata.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql",
- "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql",
- ],
-)
-
# GN target: //src/trace_processor/perfetto_sql/stdlib/export:export
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_export_export",
@@ -3015,6 +3167,7 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_sched_sched",
srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/sched/latency.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/states.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql",
@@ -3034,6 +3187,7 @@
"src/trace_processor/perfetto_sql/stdlib/slices/flow.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/hierarchy.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/slices.sql",
+ "src/trace_processor/perfetto_sql/stdlib/slices/time_in_state.sql",
"src/trace_processor/perfetto_sql/stdlib/slices/with_context.sql",
],
)
@@ -3131,9 +3285,7 @@
":src_trace_processor_perfetto_sql_stdlib_android_winscope_winscope",
":src_trace_processor_perfetto_sql_stdlib_callstacks_callstacks",
":src_trace_processor_perfetto_sql_stdlib_chrome_chrome_sql",
- ":src_trace_processor_perfetto_sql_stdlib_common_common",
":src_trace_processor_perfetto_sql_stdlib_counters_counters",
- ":src_trace_processor_perfetto_sql_stdlib_deprecated_v42_common_common",
":src_trace_processor_perfetto_sql_stdlib_export_export",
":src_trace_processor_perfetto_sql_stdlib_graphs_graphs",
":src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
@@ -3495,6 +3647,14 @@
],
)
+# GN target: //src/trace_processor/util:winscope_proto_mapping
+perfetto_filegroup(
+ name = "src_trace_processor_util_winscope_proto_mapping",
+ srcs = [
+ "src/trace_processor/util/winscope_proto_mapping.h",
+ ],
+)
+
# GN target: //src/trace_processor/util:zip_reader
perfetto_filegroup(
name = "src_trace_processor_util_zip_reader",
@@ -5072,6 +5232,7 @@
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
+ "protos/perfetto/metrics/chrome/histogram_summaries.proto",
"protos/perfetto/metrics/chrome/long_latency.proto",
"protos/perfetto/metrics/chrome/media_metric.proto",
"protos/perfetto/metrics/chrome/performance_mark_hashes.proto",
@@ -5453,6 +5614,7 @@
"protos/perfetto/trace/ftrace/clk.proto",
"protos/perfetto/trace/ftrace/cma.proto",
"protos/perfetto/trace/ftrace/compaction.proto",
+ "protos/perfetto/trace/ftrace/cpm_trace.proto",
"protos/perfetto/trace/ftrace/cpuhp.proto",
"protos/perfetto/trace/ftrace/cros_ec.proto",
"protos/perfetto/trace/ftrace/dcvsh.proto",
@@ -5466,6 +5628,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -6394,115 +6557,6 @@
],
)
-# GN target: //src/shared_lib:libperfetto_c
-perfetto_cc_library(
- name = "libperfetto_c",
- srcs = [
- ":src_android_stats_android_stats",
- ":src_android_stats_perfetto_atoms",
- ":src_protozero_filtering_bytecode_common",
- ":src_protozero_filtering_bytecode_parser",
- ":src_protozero_filtering_message_filter",
- ":src_protozero_filtering_string_filter",
- ":src_shared_lib_intern_map",
- ":src_shared_lib_shared_lib",
- ":src_tracing_client_api_without_backends",
- ":src_tracing_common",
- ":src_tracing_core_core",
- ":src_tracing_in_process_backend",
- ":src_tracing_ipc_common",
- ":src_tracing_ipc_consumer_consumer",
- ":src_tracing_ipc_default_socket",
- ":src_tracing_ipc_producer_producer",
- ":src_tracing_ipc_service_service",
- ":src_tracing_platform_impl",
- ":src_tracing_service_service",
- ":src_tracing_system_backend",
- ],
- hdrs = [
- ":include_perfetto_base_base",
- ":include_perfetto_ext_base_base",
- ":include_perfetto_ext_ipc_ipc",
- ":include_perfetto_ext_tracing_core_core",
- ":include_perfetto_ext_tracing_ipc_ipc",
- ":include_perfetto_protozero_protozero",
- ":include_perfetto_public_abi_base",
- ":include_perfetto_public_abi_public",
- ":include_perfetto_public_base",
- ":include_perfetto_public_protos_protos",
- ":include_perfetto_public_protozero",
- ":include_perfetto_public_public",
- ":include_perfetto_tracing_core_core",
- ":include_perfetto_tracing_core_forward_decls",
- ":include_perfetto_tracing_tracing",
- ],
- defines = [
- "PERFETTO_SHLIB_SDK_IMPLEMENTATION",
- ],
- visibility = [
- "//visibility:public",
- ],
- deps = [
- ":perfetto_ipc",
- ":protos_perfetto_common_cpp",
- ":protos_perfetto_common_zero",
- ":protos_perfetto_config_android_cpp",
- ":protos_perfetto_config_android_zero",
- ":protos_perfetto_config_cpp",
- ":protos_perfetto_config_ftrace_cpp",
- ":protos_perfetto_config_ftrace_zero",
- ":protos_perfetto_config_gpu_cpp",
- ":protos_perfetto_config_gpu_zero",
- ":protos_perfetto_config_inode_file_cpp",
- ":protos_perfetto_config_inode_file_zero",
- ":protos_perfetto_config_interceptors_cpp",
- ":protos_perfetto_config_interceptors_zero",
- ":protos_perfetto_config_power_cpp",
- ":protos_perfetto_config_power_zero",
- ":protos_perfetto_config_process_stats_cpp",
- ":protos_perfetto_config_process_stats_zero",
- ":protos_perfetto_config_profiling_cpp",
- ":protos_perfetto_config_profiling_zero",
- ":protos_perfetto_config_statsd_cpp",
- ":protos_perfetto_config_statsd_zero",
- ":protos_perfetto_config_sys_stats_cpp",
- ":protos_perfetto_config_sys_stats_zero",
- ":protos_perfetto_config_system_info_cpp",
- ":protos_perfetto_config_system_info_zero",
- ":protos_perfetto_config_track_event_cpp",
- ":protos_perfetto_config_track_event_zero",
- ":protos_perfetto_config_zero",
- ":protos_perfetto_ipc_cpp",
- ":protos_perfetto_ipc_ipc",
- ":protos_perfetto_trace_android_winscope_common_zero",
- ":protos_perfetto_trace_android_winscope_regular_zero",
- ":protos_perfetto_trace_android_zero",
- ":protos_perfetto_trace_chrome_zero",
- ":protos_perfetto_trace_etw_zero",
- ":protos_perfetto_trace_filesystem_zero",
- ":protos_perfetto_trace_ftrace_zero",
- ":protos_perfetto_trace_gpu_zero",
- ":protos_perfetto_trace_interned_data_zero",
- ":protos_perfetto_trace_minimal_zero",
- ":protos_perfetto_trace_non_minimal_zero",
- ":protos_perfetto_trace_perfetto_zero",
- ":protos_perfetto_trace_power_zero",
- ":protos_perfetto_trace_profiling_zero",
- ":protos_perfetto_trace_ps_zero",
- ":protos_perfetto_trace_statsd_zero",
- ":protos_perfetto_trace_sys_stats_zero",
- ":protos_perfetto_trace_system_info_zero",
- ":protos_perfetto_trace_track_event_cpp",
- ":protos_perfetto_trace_track_event_zero",
- ":protos_perfetto_trace_translation_zero",
- ":protozero",
- ":src_base_base",
- ":src_base_clock_snapshots",
- ":src_base_version",
- ],
- linkstatic = True,
-)
-
# GN target: //src/trace_processor:trace_processor
perfetto_cc_library(
name = "trace_processor",
@@ -6597,6 +6651,7 @@
":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
+ ":src_trace_processor_util_winscope_proto_mapping",
":src_trace_processor_util_zip_reader",
],
hdrs = [
@@ -6804,6 +6859,7 @@
":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
+ ":src_trace_processor_util_winscope_proto_mapping",
":src_trace_processor_util_zip_reader",
"src/trace_processor/trace_processor_shell.cc",
],
@@ -6878,74 +6934,6 @@
PERFETTO_CONFIG.deps.demangle_wrapper,
)
-# GN target: //src/traceconv:libpprofbuilder
-perfetto_cc_library(
- name = "libpprofbuilder",
- srcs = [
- ":src_profiling_deobfuscator",
- ":src_profiling_symbolizer_symbolize_database",
- ":src_profiling_symbolizer_symbolizer",
- ":src_trace_processor_util_build_id",
- ":src_traceconv_pprofbuilder",
- ":src_traceconv_utils",
- ],
- hdrs = [
- ":include_perfetto_base_base",
- ":include_perfetto_ext_base_base",
- ":include_perfetto_profiling_pprof_builder",
- ":include_perfetto_protozero_protozero",
- ":include_perfetto_public_abi_base",
- ":include_perfetto_public_base",
- ":include_perfetto_public_protozero",
- ":include_perfetto_trace_processor_basic_types",
- ":include_perfetto_trace_processor_storage",
- ":include_perfetto_trace_processor_trace_processor",
- ],
- visibility = [
- "//visibility:public",
- ],
- deps = [
- ":protos_perfetto_common_zero",
- ":protos_perfetto_config_android_zero",
- ":protos_perfetto_config_ftrace_zero",
- ":protos_perfetto_config_gpu_zero",
- ":protos_perfetto_config_inode_file_zero",
- ":protos_perfetto_config_interceptors_zero",
- ":protos_perfetto_config_power_zero",
- ":protos_perfetto_config_process_stats_zero",
- ":protos_perfetto_config_profiling_zero",
- ":protos_perfetto_config_statsd_zero",
- ":protos_perfetto_config_sys_stats_zero",
- ":protos_perfetto_config_system_info_zero",
- ":protos_perfetto_config_track_event_zero",
- ":protos_perfetto_config_zero",
- ":protos_perfetto_trace_android_winscope_common_zero",
- ":protos_perfetto_trace_android_winscope_regular_zero",
- ":protos_perfetto_trace_android_zero",
- ":protos_perfetto_trace_chrome_zero",
- ":protos_perfetto_trace_etw_zero",
- ":protos_perfetto_trace_filesystem_zero",
- ":protos_perfetto_trace_ftrace_zero",
- ":protos_perfetto_trace_gpu_zero",
- ":protos_perfetto_trace_interned_data_zero",
- ":protos_perfetto_trace_minimal_zero",
- ":protos_perfetto_trace_non_minimal_zero",
- ":protos_perfetto_trace_perfetto_zero",
- ":protos_perfetto_trace_power_zero",
- ":protos_perfetto_trace_profiling_zero",
- ":protos_perfetto_trace_ps_zero",
- ":protos_perfetto_trace_statsd_zero",
- ":protos_perfetto_trace_sys_stats_zero",
- ":protos_perfetto_trace_system_info_zero",
- ":protos_perfetto_trace_track_event_zero",
- ":protos_perfetto_trace_translation_zero",
- ":protos_third_party_pprof_zero",
- ":protozero",
- ":src_trace_processor_containers_containers",
- ] + PERFETTO_CONFIG.deps.zlib,
- linkstatic = True,
-)
-
# GN target: //src/traceconv:traceconv
perfetto_cc_binary(
name = "traceconv",
@@ -7062,6 +7050,7 @@
":src_trace_processor_util_trace_blob_view_reader",
":src_trace_processor_util_trace_type",
":src_trace_processor_util_util",
+ ":src_trace_processor_util_winscope_proto_mapping",
":src_trace_processor_util_zip_reader",
":src_traceconv_lib",
":src_traceconv_main",
diff --git a/CHANGELOG b/CHANGELOG
index 60f31dc..2a33ef5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,10 @@
inserted directly into it.
* Removed the `uid_track` table. It was redundant as no data was
inserted directly into it.
+ * Removed `common` package. It has been deprecated since `v42` and most
+ functionality has been moved into other packages. Notably, the time
+ conversion functions can be found in `time.conversion` module, and
+ `thread_slice` is available with `slices.with_context`.
Trace Processor:
*
UI:
diff --git a/DIR_METADATA b/DIR_METADATA
index 592fce2..6c28780 100644
--- a/DIR_METADATA
+++ b/DIR_METADATA
@@ -2,3 +2,6 @@
component: "Speed>Tracing"
}
team_email: "tracing@chromium.org"
+buganizer_public: {
+ component_id: 1457213
+}
diff --git a/bazel/deps.bzl b/bazel/deps.bzl
index d97179a..4334036 100644
--- a/bazel/deps.bzl
+++ b/bazel/deps.bzl
@@ -95,9 +95,11 @@
_add_repo_if_not_existing(
http_archive,
name = "bazel_skylib",
- sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d",
- strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b",
- url = "https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz",
+ sha256 = "bc283cdfcd526a52c3201279cda4bc298652efa898b10b4db0837dc51652756f",
+ urls = [
+ "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz",
+ "https://github.com/bazelbuild/bazel-skylib/releases/download/1.7.1/bazel-skylib-1.7.1.tar.gz",
+ ]
)
def _add_repo_if_not_existing(repo_rule, name, **kwargs):
diff --git a/docs/analysis/common-queries.md b/docs/analysis/common-queries.md
index 6675bc0..e69de29 100644
--- a/docs/analysis/common-queries.md
+++ b/docs/analysis/common-queries.md
@@ -1,102 +0,0 @@
-# PerfettoSQL Common Queries
-
-This page acts as a reference guide for queries which often appear when
-performing ad-hoc analysis.
-
-## Computing CPU time for slices
-If collecting traces which including scheduling information (i.e. from ftrace)
-as well as userspace slices (i.e. from atrace), the actual time spent running
-on a CPU for each userspace slice can be computed: this is commonly known as
-the "CPU time" for a slice.
-
-Firstly, setup the views to simplify subsequent queries:
-```
-DROP VIEW IF EXISTS slice_with_utid;
-CREATE VIEW slice_with_utid AS
-SELECT
- ts,
- dur,
- slice.name as slice_name,
- slice.id as slice_id, utid,
- thread.name as thread_name
-FROM slice
-JOIN thread_track ON thread_track.id = slice.track_id
-JOIN thread USING (utid);
-
-DROP TABLE IF EXISTS slice_thread_state_breakdown;
-CREATE VIRTUAL TABLE slice_thread_state_breakdown
-USING SPAN_LEFT_JOIN(
- slice_with_utid PARTITIONED utid,
- thread_state PARTITIONED utid
-);
-```
-
-Then, to compute the CPU time for all slices in the trace:
-```
-SELECT slice_id, slice_name, SUM(dur) AS cpu_time
-FROM slice_thread_state_breakdown
-WHERE state = 'Running'
-GROUP BY slice_id;
-```
-
-You can also compute CPU time for a specific slice:
-```
-SELECT slice_name, SUM(dur) AS cpu_time
-FROM slice_thread_state_breakdown
-WHERE slice_id = <your slice id> AND state = 'Running';
-```
-
-These queries can be varied easily to compute other similar metrics.
-For example to get the time spent "runnable" and in "uninterruptible sleep":
-```
-SELECT
- slice_id,
- slice_name,
- SUM(IIF(state = 'R', dur, 0)) AS runnable_time,
- SUM(IIF(state = 'D', dur, 0)) AS uninterruptible_time
-FROM slice_thread_state_breakdown
-GROUP BY slice_id;
-```
-
-## Computing scheduling time by woken threads
-A given thread might cause other threads to wake up i.e. because work was
-scheduled on them. For a given thread, the amount of time threads it
-woke up ran for can be a good proxy to understand how much work is being
-spawned.
-
-To compute this, the following query can be used:
-```
-SELECT
- SUM((
- SELECT dur FROM sched
- WHERE
- sched.ts > wakee_runnable.ts AND
- wakee_runnable.utid = wakee_runnable.utid
- ORDER BY ts
- LIMIT 1
- )) AS scheduled_dur
-FROM thread AS waker
-JOIN thread_state AS wakee_runnable ON waker.utid = wakee_runnable.waker_utid
-WHERE waker.name = <your waker thread name here>
-```
-
-To do this for all the threads in the trace simultaenously:
-```
-SELECT
- waker_process.name AS process_name,
- waker.name AS thread_name,
- SUM((
- SELECT dur FROM sched
- WHERE
- sched.ts > wakee_runnable.ts AND
- sched.utid = wakee_runnable.utid
- ORDER BY ts
- LIMIT 1
- )) AS scheduled_dur
-FROM thread AS waker
-JOIN process AS waker_process USING (upid)
-JOIN thread_state AS wakee_runnable ON waker.utid = wakee_runnable.waker_utid
-WHERE waker.utid != 0
-GROUP BY 1, 2
-ORDER BY 3 desc
-```
diff --git a/docs/concepts/config.md b/docs/concepts/config.md
index c4326e6..1badce4 100644
--- a/docs/concepts/config.md
+++ b/docs/concepts/config.md
@@ -4,11 +4,12 @@
in Perfetto all tracing data sources are idle by default and record data only
when instructed to do so.
-Data sources record data only when one (or more) tracing sessions are active.
-A tracing session is started by invoking the `perfetto` cmdline client and
-passing a config (see QuickStart guide for
+Data sources record data only when one (or more) tracing sessions are active. A
+tracing session is started by invoking the `perfetto` cmdline client and passing
+a config (see QuickStart guide for
[Android](/docs/quickstart/android-tracing.md),
-[Linux](/docs/quickstart/linux-tracing.md), or [Chrome on desktop](/docs/quickstart/chrome-tracing.md)).
+[Linux](/docs/quickstart/linux-tracing.md), or
+[Chrome on desktop](/docs/quickstart/chrome-tracing.md)).
A simple trace config looks like this:
@@ -31,7 +32,7 @@
}
}
-````
+```
And is used as follows:
@@ -43,7 +44,7 @@
[`/test/configs/`](/test/configs/).
NOTE: If you are tracing on Android using adb and experiencing problems, see
- [the Android section](#android) below.
+[the Android section](#android) below.
## TraceConfig
@@ -51,38 +52,40 @@
([reference docs](/docs/reference/trace-config-proto.autogen)) that defines:
1. The general behavior of the whole tracing system, e.g.:
- * The max duration of the trace.
- * The number of in-memory buffers and their size.
- * The max size of the output trace file.
+
+ - The max duration of the trace.
+ - The number of in-memory buffers and their size.
+ - The max size of the output trace file.
2. Which data sources to enable and their configuration, e.g.:
- * For the [kernel tracing data source](/docs/data-sources/cpu-scheduling.md)
- , which ftrace events to enable.
- * For the [heap profiler](/docs/data-sources/native-heap-profiler.md), the
- target process name and sampling rate.
-
- See the _data sources_ section of the docs for details on how to
- configure the data sources bundled with Perfetto.
-3. The `{data source} x {buffer}` mappings: which buffer each data
- source should write into (see [buffers section](#buffers) below).
+ - For the [kernel tracing data source](/docs/data-sources/cpu-scheduling.md)
+ , which ftrace events to enable.
+ - For the [heap profiler](/docs/data-sources/native-heap-profiler.md), the
+ target process name and sampling rate.
-The tracing service (`traced`) acts as a configuration dispatcher: it receives
-a config from the `perfetto` cmdline client (or any other
+ See the _data sources_ section of the docs for details on how to configure
+ the data sources bundled with Perfetto.
+
+3. The `{data source} x {buffer}` mappings: which buffer each data source should
+ write into (see [buffers section](#buffers) below).
+
+The tracing service (`traced`) acts as a configuration dispatcher: it receives a
+config from the `perfetto` cmdline client (or any other
[Consumer](/docs/concepts/service-model.md#consumer)) and forwards parts of the
config to the various [Producers](/docs/concepts/service-model.md#producer)
connected.
When a tracing session is started by a consumer, the tracing service will:
-* Read the outer section of the TraceConfig (e.g. `duration_ms`, `buffers`) and
+- Read the outer section of the TraceConfig (e.g. `duration_ms`, `buffers`) and
use that to determine its own behavior.
-* Read the list of data sources in the `data_sources` section. For each data
+- Read the list of data sources in the `data_sources` section. For each data
source listed in the config, if a corresponding name (`"linux.ftrace"` in the
example below) was registered, the service will ask the producer process to
- start that data source, passing it the raw bytes of the
- [`DataSourceConfig` subsection][dss] verbatim to the data source (See
- backward/forward compat section below).
+ start that data source, passing it the raw bytes of the [`DataSourceConfig`
+ subsection][dss] verbatim to the data source (See backward/forward compat
+ section below).
![TraceConfig diagram](/docs/images/trace_config.png)
@@ -109,10 +112,10 @@
Each buffer has a fill policy which is either:
-* RING_BUFFER (default): the buffer behaves like a ring buffer and writes when
+- RING_BUFFER (default): the buffer behaves like a ring buffer and writes when
full will wrap over and replace the oldest trace data in the buffer.
-* DISCARD: the buffer stops accepting data once full. Further write attempts are
+- DISCARD: the buffer stops accepting data once full. Further write attempts are
dropped.
WARNING: DISCARD can have unexpected side-effect with data sources that commit
@@ -121,36 +124,34 @@
A trace config must define at least one buffer to be valid. In the simplest case
all data sources will write their trace data into the same buffer.
- While this is
-fine for most basic cases, it can be problematic in cases where different data
-sources write at significantly different rates.
+While this is fine for most basic cases, it can be problematic in cases where
+different data sources write at significantly different rates.
For instance, imagine a trace config that enables both:
-1. The kernel scheduler tracer. On a typical Android phone this records
- ~10000 events/second, writing ~1 MB/s of trace data into the buffer.
+1. The kernel scheduler tracer. On a typical Android phone this records ~10000
+ events/second, writing ~1 MB/s of trace data into the buffer.
2. Memory stat polling. This data source writes the contents of /proc/meminfo
- into the trace buffer and is configured to poll every 5 seconds, writing
- ~100 KB per poll interval.
+ into the trace buffer and is configured to poll every 5 seconds, writing ~100
+ KB per poll interval.
If both data sources are configured to write into the same buffer and such
buffer is set to 4MB, most traces will contain only one memory snapshot. There
are very good chances that most traces won't contain any memory snapshot at all,
-even if the 2nd data sources was working perfectly.
-This is because during the 5 s. polling interval, the scheduler data source can
-end up filling the whole buffer, pushing the memory snapshot data out of the
-buffer.
+even if the 2nd data sources was working perfectly. This is because during the 5
+s. polling interval, the scheduler data source can end up filling the whole
+buffer, pushing the memory snapshot data out of the buffer.
## Dynamic buffer mapping
-Data-source <> buffer mappings are dynamic in Perfetto.
-In the simplest case a tracing session can define only one buffer. By default,
-all data sources will record data into that one buffer.
+Data-source <> buffer mappings are dynamic in Perfetto. In the simplest case a
+tracing session can define only one buffer. By default, all data sources will
+record data into that one buffer.
In cases like the example above, it might be preferable separating these data
-sources into different buffers.
-This can be achieved with the `target_buffer` field of the TraceConfig.
+sources into different buffers. This can be achieved with the `target_buffer`
+field of the TraceConfig.
![Buffer mapping](/docs/images/trace_config_buffer_mapping.png)
@@ -189,11 +190,11 @@
#### Text format
-It is the preferred format for human-driven workflows and exploration. It
-allows to pass directly the text file in the PBTX (ProtoBuf TeXtual
-representation) syntax, for the schema defined in the
-[trace_config.proto](/protos/perfetto/config/trace_config.proto)
-(see [reference docs](/docs/reference/trace-config-proto.autogen))
+It is the preferred format for human-driven workflows and exploration. It allows
+to pass directly the text file in the PBTX (ProtoBuf TeXtual representation)
+syntax, for the schema defined in the
+[trace_config.proto](/protos/perfetto/config/trace_config.proto) (see
+[reference docs](/docs/reference/trace-config-proto.autogen))
When using this mode pass the `--txt` flag to `perfetto` to indicate the config
should be interpreted as a PBTX file:
@@ -212,10 +213,9 @@
#### Binary format
It is the preferred format for machine-to-machine (M2M) interaction. It involves
-passing the protobuf-encoded binary of the TraceConfig message.
-This can be obtained passing the PBTX in input to the protobuf's `protoc`
-compiler (which can be downloaded
-[here](https://github.com/protocolbuffers/protobuf/releases)).
+passing the protobuf-encoded binary of the TraceConfig message. This can be
+obtained passing the PBTX in input to the protobuf's `protoc` compiler (which
+can be downloaded [here](https://github.com/protocolbuffers/protobuf/releases)).
```bash
cd ~/code/perfetto # external/perfetto in the Android tree.
@@ -236,9 +236,9 @@
By default Perfetto keeps the full trace buffer(s) in memory and writes it into
the destination file (the `-o` cmdline argument) only at the end of the tracing
-session. This is to reduce the perf-intrusiveness of the tracing system.
-This, however, limits the max size of the trace to the physical memory size of
-the device, which is often too limiting.
+session. This is to reduce the perf-intrusiveness of the tracing system. This,
+however, limits the max size of the trace to the physical memory size of the
+device, which is often too limiting.
In some cases (e.g., benchmarks, hard to repro cases) it is desirable to capture
traces that are way larger than that, at the cost of extra I/O overhead.
@@ -246,29 +246,26 @@
To achieve that, Perfetto allows to periodically write the trace buffers into
the target file (or stdout) using the following TraceConfig fields:
-* `write_into_file (bool)`:
-When true periodically drains the trace buffers into the output
-file. When this option is enabled, the userspace buffers need to be just
-big enough to hold tracing data between two write periods.
-The buffer sizing depends on the activity of the device.
-The data rate of a typical trace is ~1-4 MB/s. So a 16MB in-memory buffer can
-hold for up write periods of ~4 seconds before starting to lose data.
+- `write_into_file (bool)`: When true periodically drains the trace buffers into
+ the output file. When this option is enabled, the userspace buffers need to be
+ just big enough to hold tracing data between two write periods. The buffer
+ sizing depends on the activity of the device. The data rate of a typical trace
+ is ~1-4 MB/s. So a 16MB in-memory buffer can hold for up write periods of ~4
+ seconds before starting to lose data.
-* `file_write_period_ms (uint32)`:
-Overrides the default drain period (5s). Shorter periods require a smaller
-userspace buffer but increase the performance intrusiveness of tracing. If
-the period given is less than 100ms, the tracing service will use a period
-of 100ms.
+- `file_write_period_ms (uint32)`: Overrides the default drain period (5s).
+ Shorter periods require a smaller userspace buffer but increase the
+ performance intrusiveness of tracing. If the period given is less than 100ms,
+ the tracing service will use a period of 100ms.
-* `max_file_size_bytes (uint64)`:
-If set, stops the tracing session after N bytes have been written. Used to
-cap the size of the trace.
+- `max_file_size_bytes (uint64)`: If set, stops the tracing session after N
+ bytes have been written. Used to cap the size of the trace.
For a complete example of a working trace config in long-tracing mode see
[`/test/configs/long_trace.cfg`](/test/configs/long_trace.cfg).
Summary: to capture a long trace just set `write_into_file:true`, set a long
- `duration_ms` and use an in-memory buffer size of 32MB or more.
+`duration_ms` and use an in-memory buffer size of 32MB or more.
## Data-source specific config
@@ -276,7 +273,8 @@
data-source-specific behaviors. At the proto schema level, this is defined in
the `DataSourceConfig` section of `TraceConfig`:
-From [data_source_config.proto](/protos/perfetto/config/data_source_config.proto):
+From
+[data_source_config.proto](/protos/perfetto/config/data_source_config.proto):
```protobuf
message TraceConfig {
@@ -312,42 +310,43 @@
implements data sources.
#### A note on backwards/forward compatibility
+
The tracing service will route the raw binary blob of the `DataSourceConfig`
message to the data sources with a matching name, without attempting to decode
and re-encode it. If the `DataSourceConfig` section of the trace config contains
a new field that didn't exist at the time when the service was built, the
-service will still pass the `DataSourceConfig` through to the data source.
-This allows to introduced new data sources without needing the service to
-know anything about them upfront.
+service will still pass the `DataSourceConfig` through to the data source. This
+allows to introduced new data sources without needing the service to know
+anything about them upfront.
TODO: we are aware of the fact that today extending the `DataSourceConfig` with
a custom proto requires changing the `data_source_config.proto` in the Perfetto
-repo, which is unideal for external projects. The long-term plan is to reserve
-a range of fields for non-upstream extensions and provide generic templated
+repo, which is unideal for external projects. The long-term plan is to reserve a
+range of fields for non-upstream extensions and provide generic templated
accessors for client code. Until then, we accept patches upstream to introduce
ad-hoc configurations for your own data sources.
## Multi-process data sources
Some data sources are singletons. E.g., in the case of scheduler tracing that
-Perfetto ships on Android, there is only data source for the whole system,
-owned by the `traced_probes` service.
+Perfetto ships on Android, there is only data source for the whole system, owned
+by the `traced_probes` service.
However, in the general case multiple processes can advertise the same data
source. This is the case, for instance, when using the
[Perfetto SDK](/docs/instrumentation/tracing-sdk.md) for userspace
instrumentation.
-If this happens, when starting a tracing session that specifies that data
-source in the trace config, Perfetto by default will ask all processes that
-advertise that data source to start it.
+If this happens, when starting a tracing session that specifies that data source
+in the trace config, Perfetto by default will ask all processes that advertise
+that data source to start it.
In some cases it might be desirable to further limit the enabling of the data
source to a specific process (or set of processes). That is possible through the
`producer_name_filter` and `producer_name_regex_filter`.
NOTE: the typical Perfetto run-time model is: one process == one Perfetto
- Producer; one Producer typically hosts multiple data sources.
+Producer; one Producer typically hosts multiple data sources.
When those filters are set, the Perfetto tracing service will activate the data
source only in the subset of producers matching the filter.
@@ -380,8 +379,8 @@
which is based on triggers. The overall idea is to declare in the trace config
itself:
-* A set of triggers, which are just free-form strings.
-* Whether a given trigger should cause the trace to be started or stopped, and
+- A set of triggers, which are just free-form strings.
+- Whether a given trigger should cause the trace to be started or stopped, and
the start/stop delay.
Why using triggers? Why can't one just start perfetto or kill(SIGTERM) it when
@@ -393,12 +392,12 @@
Triggers offer a way to unprivileged apps to control, in a limited fashion, the
lifecycle of a tracing session. The conceptual model is:
-* The privileged Consumer (see
- [_Service model_](/docs/concepts/service-model.md)), i.e. the entity
- that is normally authorized to start tracing (e.g., adb shell in Android),
- declares upfront what are the possible trigger names for the trace and what
- they will do.
-* Unprivileged entities (any random app process) can activate those triggers.
+- The privileged Consumer (see
+ [_Service model_](/docs/concepts/service-model.md)), i.e. the entity that is
+ normally authorized to start tracing (e.g., adb shell in Android), declares
+ upfront what are the possible trigger names for the trace and what they will
+ do.
+- Unprivileged entities (any random app process) can activate those triggers.
Unprivileged entities don't get a say on what the triggers will do, they only
communicate that an event happened.
@@ -417,12 +416,13 @@
Start triggers allow activating a tracing session only after some significant
event has happened. Passing a trace config that has `START_TRACING` trigger
-causes the tracing session to stay idle (i.e. not recording any data) until either
-the trigger is hit or the `trigger_timeout_ms` timeout is hit.
+causes the tracing session to stay idle (i.e. not recording any data) until
+either the trigger is hit or the `trigger_timeout_ms` timeout is hit.
`trace_duration_ms` and triggered traces can not be used at the same time.
Example config:
+
```protobuf
# If the "myapp_is_slow" is hit, the trace starts recording data and will be
# stopped after 5s.
@@ -450,12 +450,13 @@
signal.
This can be used to use perfetto in flight-recorder mode. By starting a trace
-with buffers configured in `RING_BUFFER` mode and `STOP_TRACING` triggers,
-the trace will be recorded in a loop and finalized when the culprit event is
+with buffers configured in `RING_BUFFER` mode and `STOP_TRACING` triggers, the
+trace will be recorded in a loop and finalized when the culprit event is
detected. This is key for events where the root cause is in the recent past
(e.g., the app detects a slow scroll or a missing frame).
Example config:
+
```protobuf
# If no trigger is hit, the trace will end after 30s.
trigger_timeout_ms: 30000
@@ -478,21 +479,20 @@
On Android, there are some caveats around using `adb shell`
-* Ctrl+C, which normally causes a graceful termination of the trace, is not
+- Ctrl+C, which normally causes a graceful termination of the trace, is not
propagated by ADB when using `adb shell perfetto` but only when using an
interactive PTY-based session via `adb shell`.
-* On non-rooted devices before Android 12, the config can only be passed as
+- On non-rooted devices before Android 12, the config can only be passed as
`cat config | adb shell perfetto -c -` (-: stdin) because of over-restrictive
SELinux rules. Since Android 12 `/data/misc/perfetto-configs` can be used for
storing configs.
-* On devices before Android 10, adb cannot directly pull
+- On devices before Android 10, adb cannot directly pull
`/data/misc/perfetto-traces`. Use
`adb shell cat /data/misc/perfetto-traces/trace > trace` to work around.
-* When capturing longer traces, e.g. in the context of benchmarks or CI, use
+- When capturing longer traces, e.g. in the context of benchmarks or CI, use
`PID=$(perfetto --background)` and then `kill $PID` to stop.
-
## Other resources
-* [TraceConfig Reference](/docs/reference/trace-config-proto.autogen)
-* [Buffers and dataflow](/docs/concepts/buffers.md)
+- [TraceConfig Reference](/docs/reference/trace-config-proto.autogen)
+- [Buffers and dataflow](/docs/concepts/buffers.md)
diff --git a/docs/contributing/common-tasks.md b/docs/contributing/common-tasks.md
index 43c5421..4a76189 100644
--- a/docs/contributing/common-tasks.md
+++ b/docs/contributing/common-tasks.md
@@ -24,7 +24,7 @@
- Running the file cannot generate any data. There can be only `CREATE PERFETTO {FUNCTION|TABLE|VIEW|MACRO}` statements inside.
- The name of each standard library object needs to start with `{module_name}_` or be prefixed with an underscore(`_`) for internal objects.
- The names must only contain lower and upper case letters and underscores. When a module is included (using the `INCLUDE PERFETTO MODULE`) the internal objects should not be treated as an API.
+ The names must only contain lower and upper case letters and underscores. When a module is included (using the `INCLUDE PERFETTO MODULE`) the internal objects should not be treated as an API.
- Every table or view should have [a schema](/docs/analysis/perfetto-sql-syntax.md#tableview-schema).
### Documentation
@@ -98,11 +98,11 @@
arg_set_id INT
)
AS
-SELECT
- slice_name,
- slice_ts,
- slice_dur,
- thread_name,
+SELECT
+ slice_name,
+ slice_ts,
+ slice_dur,
+ thread_name,
arg_set_id
FROM thread_slices_for_all_launches
WHERE launch_id = $launch_id AND slice_name GLOB $slice_name;
@@ -156,3 +156,17 @@
1. Go to `protos/perfetto/trace_processor/trace_processor.proto`
2. Increment `TRACE_PROCESSOR_CURRENT_API_VERSION`
3. Add a comment explaining what has changed.
+
+## Update statsd descriptor
+
+Perfetto has limited support for statsd atoms it does not know about.
+
+* Must be referred to using `raw_atom_id` in the config.
+* Show up as `atom_xxx.field_yyy` in trace processor.
+* Only top level messages are parsed.
+
+To update Perfetto's descriptor and handle new atoms from AOSP without these
+limitations:
+
+1. Run `tools/update-statsd-descriptor`.
+2. Upload and land your change as normal.
diff --git a/docs/contributing/ui-plugins.md b/docs/contributing/ui-plugins.md
index 60a6796..cc65f43 100644
--- a/docs/contributing/ui-plugins.md
+++ b/docs/contributing/ui-plugins.md
@@ -3,7 +3,8 @@
Perfetto.
## Create a plugin
-The guide below explains how to create a plugin for the Perfetto UI.
+The guide below explains how to create a plugin for the Perfetto UI. You can
+browse the public plugin API [here](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/public).
### Prepare for UI development
First we need to prepare the UI development environment. You will need to use a
@@ -48,6 +49,8 @@
- Navigate to the plugins page:
[localhost:10000/#!/plugins](http://localhost:10000/#!/plugins).
- Ctrl-F for your plugin name and enable it.
+- Enabling/disabling plugins requires a restart of the UI, so refresh the page
+ to start your plugin.
Later you can request for your plugin to be enabled by default. Follow the
[default plugins](#default-plugins) section for this.
@@ -58,81 +61,212 @@
upload your CL to the codereview tool.
- Once uploaded add `stevegolton@google.com` as a reviewer for your CL.
-## Plugin extension points
-Plugins can extend a handful of specific places in the UI. The sections below
-show these extension points and give examples of how they can be used.
+## Plugin Lifecycle
+To demonstrate the plugin's lifecycle, this is a minimal plugin that implements
+the key lifecycle hooks:
-### Commands
-Commands are user issuable shortcuts for actions in the UI. They can be accessed
-via the omnibox.
+```ts
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
-Follow the [create a plugin](#create-a-plugin) to get an initial skeleton for
-your plugin.
-
-To add your first command, add a call to `ctx.registerCommand()` in either your
-`onActivate()` or `onTraceLoad()` hooks. The recommendation is to register
-commands in `onActivate()` by default unless they require something from
-`PluginContextTrace` which is not available on `PluginContext`.
-
-The tradeoff is that commands registered in `onTraceLoad()` are only available
-while a trace is loaded, whereas commands registered in `onActivate()` are
-available all the time the plugin is active.
-
-```typescript
-class MyPlugin implements PerfettoPlugin {
- onActivate(ctx: PluginContext): void {
- ctx.registerCommand(
- {
- id: 'dev.perfetto.ExampleSimpleCommand#LogHelloPlugin',
- name: 'Log "Hello, plugin!"',
- callback: () => console.log('Hello, plugin!'),
- },
- );
+ static onActivate(app: App): void {
+ // Called once on app startup
+ console.log('MyPlugin::onActivate()', app.pluginId);
+ // Note: It's rare that plugins would need this hook as most plugins are
+ // interested in trace details. Thus, this function can usually be omitted.
}
- onTraceLoad(ctx: PluginContextTrace): void {
- ctx.registerCommand(
- {
- id: 'dev.perfetto.ExampleSimpleTraceCommand#LogHelloTrace',
- name: 'Log "Hello, trace!"',
- callback: () => console.log('Hello, trace!'),
- },
- );
+ constructor(trace: Trace) {
+ // Called each time a trace is loaded
+ console.log('MyPlugin::constructor()', trace.traceInfo.traceTitle);
+ }
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ // Called each time a trace is loaded
+ console.log('MyPlugin::onTraceLoad()', trace.traceInfo.traceTitle);
+ // Note this function returns a promise, so any any async calls should be
+ // completed before this promise resolves as the app using this promise for
+ // timing and plugin synchronization.
}
}
```
-Here `id` is a unique string which identifies this command. The `id` should be
-prefixed with the plugin id followed by a `#`. All command `id`s must be unique
-system-wide. `name` is a human readable name for the command, which is shown in
-the command palette. Finally `callback()` is the callback which actually
-performs the action.
+You can run this plugin with devtools to see the log messages in the console,
+which should give you a feel for the plugin lifecycle. Try opening a few traces
+one after another.
-Commands are removed automatically when their context disappears. Commands
-registered with the `PluginContext` are removed when the plugin is deactivated,
-and commands registered with the `PluginContextTrace` are removed when the trace
-is unloaded.
+`onActivate()` runs shortly after Perfetto starts up, before a trace is loaded.
+This is where the you'll configure your plugin's capabilities that aren't trace
+dependent. At this point the plugin's class is not instantiated, so you'll
+notice `onActivate()` hook is a static class member. `onActivate()` is only ever
+called once, regardless of the number of traces loaded.
+
+`onActivate()` is passed an `App` object which the plugin can use to configure
+core capabilities such as commands, sidebar items and pages. Capabilities
+registered on the App interface are persisted throughout the lifetime of the app
+(practically forever until the tab is closed), in contrast to what happens for
+the same methods on the `Trace` object (see below).
+
+The plugin class in instantiated when a trace is loaded (a new plugin instance
+is created for each trace). `onTraceLoad()` is called immediately after the
+class is instantiated, which is where you'll configure your plugin's trace
+dependent capabilities.
+
+`onTraceLoad()` is passed a `Trace` object which the plugin can use to configure
+entities that are scoped to a specific trace, such as tracks and tabs. `Trace`
+is a superset of `App`, so anything you can do with `App` you can also do with
+`Trace`, however, capabilities registered on `Trace` will typically be discarded
+when a new trace is loaded.
+
+A plugin will typically register capabilities with the core and return quickly.
+But these capabilities usually contain objects and callbacks which are called
+into later by the core during the runtime of the app. Most capabilities require
+a `Trace` or an `App` to do anything useful so these are usually bound into the
+capabilities at registration time using JavaScript classes or closures.
+
+```ts
+// Toy example: Code will not compile.
+async onTraceLoad(trace: Trace) {
+ // `trace` is captured in the closure and used later by the app
+ trace.regsterXYZ(() => trace.xyz);
+}
+```
+
+That way, the callback is bound to a specific trace object which and the trace
+object can outlive the runtime of the `onTraceLoad()` function, which is a very
+common pattern in Perfetto plugins.
+
+> Note: Some capabilities can be registered on either the `App` or the `Trace`
+> object (i.e. in `onActivate()` or in `onTraceLoad()`), if in doubt about which
+> one to use, use `onTraceLoad()` as this is more than likely the one you want.
+> Most plugins add tracks and tabs that depend on the trace. You'd usually have
+> to be doing something out of the ordinary if you need to use `onActivate()`.
+
+### Performance
+`onActivate()` and `onTraceLoad()` should generally complete as quickly as
+possible, however sometimes `onTraceLoad()` may need to perform async operations
+on trace processor such as performing queries and/or creating views and tables.
+Thus, `onTraceLoad()` should return a promise (or you can simply make it an
+async function). When this promise resolves it tells the core that the plugin is
+fully initialized.
+
+> Note: It's important that any async operations done in onTraceLoad() are
+> awaited so that all async operations are completed by the time the promise is
+> resolved. This is so that plugins can be properly timed and synchronized.
+
+
+```ts
+// GOOD
+async onTraceLoad(trace: Trace) {
+ await trace.engine.query(...);
+}
+
+// BAD
+async onTraceLoad(trace: Trace) {
+ // Note the missing await!
+ trace.engine.query(...);
+}
+```
+
+## Extension Points
+Plugins can extend functionality of Perfetto by registering capabilities via
+extension points on the `App` or `Trace` objects.
+
+The following sections delve into more detail on each extension point and
+provide examples of how they can be used.
+
+### Commands
+Commands are user issuable shortcuts for actions in the UI. They are invoked via
+the command palette which can be opened by pressing Ctrl+Shift+P (or Cmd+Shift+P
+on Mac), or by typing a '>' into the omnibox.
+
+To add a command, add a call to `registerCommand()` on either your
+`onActivate()` or `onTraceLoad()` hooks. The recommendation is to register
+commands in `onTraceLoad()` by default unless you very specifically want the
+command to be available before a trace has loaded.
+
+Example of a command that doesn't require a trace.
+```ts
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ static onActivate(app: App) {
+ app.commands.registerCommand({
+ id: `${app.pluginId}#SayHello`,
+ name: 'Say hello',
+ callback: () => console.log('Hello, world!'),
+ });
+ }
+}
+```
+
+Example of a command that requires a trace object - in this case the trace
+title.
+```ts
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ trace.commands.registerCommand({
+ id: `${trace.pluginId}#LogTraceTitle`,
+ name: 'Log trace title',
+ callback: () => console.log(trace.info.traceTitle),
+ });
+ }
+}
+```
+
+> Notice that the trace object is captured in the closure, so it can be used
+> after the onTraceLoad() function has returned. This is a very common pattern
+> in Perfetto plugins.
+
+Command arguments explained:
+- `id` is a unique string which identifies this command. The `id` should be
+prefixed with the plugin id followed by a `#`. All command `id`s must be unique
+system-wide.
+- `name` is a human readable name for the command, which is shown in the command
+palette.
+- `callback()` is the callback which actually performs the action.
+
+#### Async commands
+It's common that commands will perform async operations in their callbacks. It's
+recommended to use async/await for this rather than `.then().catch()`. The
+easiest way to do this is to make the callback an async function.
+
+```ts
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ trace.commands.registerCommand({
+ id: `${trace.pluginId}#QueryTraceProcessor`,
+ name: 'Query trace processor',
+ callback: async () => {
+ const results = await trace.engine.query(...);
+ // use results...
+ },
+ });
+ }
+}
+```
+
+If the callback is async (i.e. it returns a promise), nothing special happens.
+The command is still fire-n-forget as far as the core is concerned.
Examples:
-- [dev.perfetto.ExampleSimpleCommand](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts).
+- [com.example.ExampleSimpleCommand](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/com.example.ExampleSimpleCommand/index.ts).
- [perfetto.CoreCommands](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/core_plugins/commands/index.ts).
-- [dev.perfetto.ExampleState](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/dev.perfetto.ExampleState/index.ts).
+- [com.example.ExampleState](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/com.example.ExampleState/index.ts).
-#### Hotkeys
-
-A default hotkey may be provided when registering a command.
+### Hotkeys
+A hotkey may be associated with a command at registration time.
```typescript
-ctx.registerCommand({
- id: 'dev.perfetto.ExampleSimpleCommand#LogHelloWorld',
- name: 'Log "Hello, World!"',
- callback: () => console.log('Hello, World!'),
+ctx.commands.registerCommand({
+ ...
defaultHotkey: 'Shift+H',
});
```
-Even though the hotkey is a string, it's format checked at compile time using
-typescript's [template literal
+Despite the fact that the hotkey is a string, its format is checked at compile
+time using typescript's [template literal
types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html).
See
@@ -140,151 +274,179 @@
for more details on how the hotkey syntax works, and for the available keys and
modifiers.
+Note this is referred to as the 'default' hotkey because we may introduce a
+feature in the future where users can modify their hotkeys, though this doesn't
+exist at the moment.
+
### Tracks
-#### Defining Tracks
-Tracks describe how to render a track and how to respond to mouse interaction.
-However, the interface is a WIP and should be considered unstable. This
-documentation will be added to over the next few months after the design is
-finalised.
+In order to add a new track to the timeline, you'll need to create two entities:
+- A track 'renderer' which controls what the track looks like and how it fetches
+ data from trace processor.
+- A track 'node' controls where the track appears in the workspace.
-#### Reusing Existing Tracks
-Creating tracks from scratch is difficult and the API is currently a WIP, so it
-is strongly recommended to use one of our existing base classes which do a lot
-of the heavy lifting for you. These base classes also provide a more stable
-layer between your track and the (currently unstable) track API.
+Track renderers are powerful but complex, so it's, so it's strongly advised not
+to create your own. Instead, by far the easiest way to get started with tracks
+is to use the `createQuerySliceTrack` and `createQueryCounterTrack` helpers.
-For example, if your track needs to show slices from a given a SQL expression (a
-very common pattern), extend the `NamedSliceTrack` abstract base class and
-implement `getSqlSource()`, which should return a query with the following
-columns:
-
-- `id: INTEGER`: A unique ID for the slice.
-- `ts: INTEGER`: The timestamp of the start of the slice.
-- `dur: INTEGER`: The duration of the slice.
-- `depth: INTEGER`: Integer value defining how deep the slice should be drawn in
- the track, 0 being rendered at the top of the track, and increasing numbers
- being drawn towards the bottom of the track.
-- `name: TEXT`: Text to be rendered on the slice and in the popup.
-
-For example, the following track describes a slice track that displays all
-slices that begin with the letter 'a'.
+Example:
```ts
-class MyTrack extends NamedSliceTrack {
- getSqlSource(): string {
- return `
- SELECT
- id,
- ts,
- dur,
- depth,
- name
- from slice
- where name like 'a%'
- `;
- }
-}
-```
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
-#### Registering Tracks
-Plugins may register tracks with Perfetto using
-`PluginContextTrace.registerTrack()`, usually in their `onTraceLoad` function.
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ const title = 'My Track';
+ const uri = `${trace.pluginId}#MyTrack`;
+ const query = 'select * from slice where track_id = 123';
-```ts
-class MyPlugin implements PerfettoPlugin {
- onTraceLoad(ctx: PluginContextTrace): void {
- ctx.registerTrack({
- uri: 'dev.MyPlugin#ExampleTrack',
- displayName: 'My Example Track',
- trackFactory: ({trackKey}) => {
- return new MyTrack({engine: ctx.engine, trackKey});
+ // Create a new track renderer based on a query
+ const track = await createQuerySliceTrack({
+ trace,
+ uri,
+ data: {
+ sqlSource: query,
},
});
+
+ // Register the track renderer with the core
+ trace.tracks.registerTrack({uri, title, track});
+
+ // Create a track node that references the track renderer using its uri
+ const track = new TrackNode({uri, title});
+
+ // Add the track node to the current workspace
+ trace.workspace.addChildInOrder(track);
}
}
```
-#### Default Tracks
-The "default" tracks are a list of tracks that are added to the timeline when a
-fresh trace is loaded (i.e. **not** when loading a trace from a permalink). This
-list is copied into the timeline after the trace has finished loading, at which
-point control is handed over to the user, allowing them add, remove and reorder
-tracks as they please. Thus it only makes sense to add default tracks in your
-plugin's `onTraceLoad` function, as adding a default track later will have no
-effect.
+See [the source](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/public/lib/tracks/query_slice_track.ts)
+for detailed usage.
+
+You can also add a counter track using `createQueryCounterTrack` which works in
+a similar way.
```ts
-class MyPlugin implements PerfettoPlugin {
- onTraceLoad(ctx: PluginContextTrace): void {
- ctx.registerTrack({
- // ... as above ...
- });
+import {createQueryCounterTrack} from '../../public/lib/tracks/query_counter_track';
- ctx.addDefaultTrack({
- uri: 'dev.MyPlugin#ExampleTrack',
- displayName: 'My Example Track',
- sortKey: PrimaryTrackSortKey.ORDINARY_TRACK,
- });
- }
-}
-```
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ const title = 'My Counter Track';
+ const uri = `${trace.pluginId}#MyCounterTrack`;
+ const query = 'select * from counter where track_id = 123';
-Registering and adding a default track is such a common pattern that there is a
-shortcut for doing both in one go: `PluginContextTrace.registerStaticTrack()`,
-which saves having to repeat the URI and display name.
-
-```ts
-class MyPlugin implements PerfettoPlugin {
- onTraceLoad(ctx: PluginContextTrace): void {
- ctx.registerStaticTrack({
- uri: 'dev.MyPlugin#ExampleTrack',
- displayName: 'My Example Track',
- trackFactory: ({trackKey}) => {
- return new MyTrack({engine: ctx.engine, trackKey});
- },
- sortKey: PrimaryTrackSortKey.COUNTER_TRACK,
- });
- }
-}
-```
-
-#### Adding Tracks Directly
-Sometimes plugins might want to add a track to the timeline immediately, usually
-as a result of a command or on some other user action such as a button click. We
-can do this using `PluginContext.timeline.addTrack()`.
-
-```ts
-class MyPlugin implements PerfettoPlugin {
- onTraceLoad(ctx: PluginContextTrace): void {
- ctx.registerTrack({
- // ... as above ...
- });
-
- // Register a command that directly adds a new track to the timeline
- ctx.registerCommand({
- id: 'dev.MyPlugin#AddMyTrack',
- name: 'Add my track',
- callback: () => {
- ctx.timeline.addTrack(
- 'dev.MyPlugin#ExampleTrack',
- 'My Example Track'
- );
+ // Create a new track renderer based on a query
+ const track = await createQueryCounterTrack({
+ trace,
+ uri,
+ data: {
+ sqlSource: query,
},
});
+
+ // Register the track renderer with the core
+ trace.tracks.registerTrack({uri, title, track});
+
+ // Create a track node that references the track renderer using its uri
+ const track = new TrackNode({uri, title});
+
+ // Add the track node to the current workspace
+ trace.workspace.addChildInOrder(track);
}
}
```
+See [the source](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/public/lib/tracks/query_counter_track.ts)
+for detailed usage.
+
+#### Grouping Tracks
+Any track can have children. Just add child nodes any `TrackNode` object using
+its `addChildXYZ()` methods. Nested tracks are rendered as a collapsible tree.
+
+```ts
+const group = new TrackNode({title: 'Group'});
+trace.workspace.addChildInOrder(group);
+group.addChildLast(new TrackNode({title: 'Child Track A'}));
+group.addChildLast(new TrackNode({title: 'Child Track B'}));
+group.addChildLast(new TrackNode({title: 'Child Track C'}));
+```
+
+Tracks nodes with children can be collapsed and expanded manually by the user at
+runtime, or programmatically using their `expand()` and `collapse()` methods. By
+default tracks are collapsed, so to have tracks automatically expanded on
+startup you'll need to call `expand()` after adding the track node.
+
+```ts
+group.expand();
+```
+
+![Nested tracks](../images/ui-plugins/nested_tracks.png)
+
+Summary tracks are behave slightly differently to ordinary tracks. Summary
+tracks:
+- Are rendered with a light blue background when collapsed, dark blue when
+ expanded.
+- Stick to the top of the viewport when scrolling.
+- Area selections made on the track apply to child tracks instead of the summary
+ track itself.
+
+To create a summary track, set the `isSummary: true` option in its initializer
+list at creation time or set its `isSummary` property to true after creation.
+
+```ts
+const group = new TrackNode({title: 'Group', isSummary: true});
+// ~~~ or ~~~
+group.isSummary = true;
+```
+
+![Summary track](../images/ui-plugins/summary_track.png)
+
+Examples
+- [com.example.ExampleNestedTracks](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/com.example.ExampleNestedTracks/index.ts).
+
+#### Track Ordering
+Tracks can be manually reordered using the `addChildXYZ()` functions available on
+the track node api, including `addChildFirst()`, `addChildLast()`,
+`addChildBefore()`, and `addChildAfter()`.
+
+See [the workspace source](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/public/workspace.ts) for detailed usage.
+
+However, when several plugins add tracks to the same node or the workspace, no
+single plugin has complete control over the sorting of child nodes within this
+node. Thus, the sortOrder property is be used to decentralize the sorting logic
+between plugins.
+
+In order to do this we simply give the track a `sortOrder` and call
+`addChildInOrder()` on the parent node and the track will be placed before the
+first track with a higher `sortOrder` in the list. (i.e. lower `sortOrder`s appear
+higher in the stack).
+
+```ts
+// PluginA
+workspace.addChildInOrder(new TrackNode({title: 'Foo', sortOrder: 10}));
+
+// Plugin B
+workspace.addChildInOrder(new TrackNode({title: 'Bar', sortOrder: -10}));
+```
+
+Now it doesn't matter which order plugin are initialized, track `Bar` will
+appear above track `Foo` (unless reordered later).
+
+If no `sortOrder` is defined, the track assumes a `sortOrder` of 0.
+
+> It is recommended to always use `addChildInOrder()` in plugins when adding
+> tracks to the `workspace`, especially if you want your plugin to be enabled by
+> default, as this will ensure it respects the sortOrder of other plugins.
+
+
### Tabs
Tabs are a useful way to display contextual information about the trace, the
current selection, or to show the results of an operation.
-To register a tab from a plugin, use the `PluginContextTrace.registerTab`
-method.
+To register a tab from a plugin, use the `Trace.registerTab` method.
```ts
-import m from 'mithril';
-import {Tab, Plugin, PluginContext, PluginContextTrace} from '../../public';
-
class MyTab implements Tab {
render(): m.Children {
return m('div', 'Hello from my tab');
@@ -295,11 +457,11 @@
}
}
-class MyPlugin implements PerfettoPlugin {
- onActivate(_: PluginContext): void {}
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- ctx.registerTab({
- uri: 'dev.MyPlugin#MyTab',
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ trace.registerTab({
+ uri: `${trace.pluginId}#MyTab`,
content: new MyTab(),
});
}
@@ -320,8 +482,8 @@
Alternatively, tabs may be shown or hidden programmatically using the tabs API.
```ts
-ctx.tabs.showTab('dev.MyPlugin#MyTab');
-ctx.tabs.hideTab('dev.MyPlugin#MyTab');
+trace.tabs.showTab(`${trace.pluginId}#MyTab`);
+trace.tabs.hideTab(`${trace.pluginId}#MyTab`);
```
Tabs have the following properties:
@@ -345,9 +507,9 @@
registering the tab.
```ts
-ctx.registerTab({
+trace.registerTab({
isEphemeral: true,
- uri: 'dev.MyPlugin#MyTab',
+ uri: `${trace.pluginId}#MyTab`,
content: new MyEphemeralTab(),
});
```
@@ -360,13 +522,6 @@
```ts
import m from 'mithril';
import {uuidv4} from '../../base/uuid';
-import {
- Plugin,
- PluginContext,
- PluginContextTrace,
- PluginDescriptor,
- Tab,
-} from '../../public';
class MyNameTab implements Tab {
constructor(private name: string) {}
@@ -378,21 +533,21 @@
}
}
-class MyPlugin implements PerfettoPlugin {
- onActivate(_: PluginContext): void {}
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- ctx.registerCommand({
- id: 'dev.MyPlugin#AddNewEphemeralTab',
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.registerCommand({
+ id: `${trace.pluginId}#AddNewEphemeralTab`,
name: 'Add new ephemeral tab',
- callback: () => handleCommand(ctx),
+ callback: () => handleCommand(trace),
});
}
}
-function handleCommand(ctx: PluginContextTrace): void {
+function handleCommand(trace: Trace): void {
const name = prompt('What is your name');
if (name) {
- const uri = 'dev.MyPlugin#MyName' + uuidv4();
+ const uri = `${trace.pluginId}#MyName${uuidv4()}`;
// This makes the tab available to perfetto
ctx.registerTab({
isEphemeral: true,
@@ -404,11 +559,6 @@
ctx.tabs.showTab(uri);
}
}
-
-export const plugin: PluginDescriptor = {
- pluginId: 'dev.MyPlugin',
- plugin: MyPlugin,
-};
```
### Details Panels & The Current Selection Tab
@@ -422,10 +572,10 @@
For example:
```ts
-class MyPlugin implements PerfettoPlugin {
- onActivate(_: PluginContext): void {}
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- ctx.registerDetailsPanel({
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace) {
+ trace.registerDetailsPanel({
render(selection: Selection) {
if (canHandleSelection(selection)) {
return m('div', 'Details for selection');
@@ -451,6 +601,142 @@
is undefined. This is a limitation of the current approach and will be updated
to a more democratic contribution model in the future.
+### Sidebar Menu Items
+Plugins can add new entries to the sidebar menu which appears on the left hand
+side of the UI. These entries can include:
+- Commands
+- Links
+- Arbitrary Callbacks
+
+#### Commands
+If a command is referenced, the command name and hotkey are displayed on the
+sidebar item.
+```ts
+trace.commands.registerCommand({
+ id: 'sayHi',
+ name: 'Say hi',
+ callback: () => window.alert('hi'),
+ defaultHotkey: 'Shift+H',
+});
+
+trace.sidebar.addMenuItem({
+ commandId: 'sayHi',
+ section: 'support',
+ icon: 'waving_hand',
+});
+```
+
+#### Links
+If an href is present, the sidebar will be used as a link. This can be an
+internal link to a page, or an external link.
+```ts
+trace.sidebar.addMenuItem({
+ section: 'navigation',
+ text: 'Plugins',
+ href: '#!/plugins',
+});
+```
+
+#### Callbacks
+Sidebar items can be instructed to execute arbitrary callbacks when the button
+is clicked.
+```ts
+trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Copy secrets to clipboard',
+ action: () => copyToClipboard('...'),
+});
+```
+
+If the action returns a promise, the sidebar item will show a little spinner
+animation until the promise returns.
+
+```ts
+trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Prepare the data...',
+ action: () => new Promise((r) => setTimeout(r, 1000)),
+});
+```
+Optional params for all types of sidebar items:
+- `icon` - A material design icon to be displayed next to the sidebar menu item.
+ See full list [here](https://fonts.google.com/icons).
+- `tooltip` - Displayed on hover
+- `section` - Where to place the menu item.
+ - `navigation`
+ - `current_trace`
+ - `convert_trace`
+ - `example_traces`
+ - `support`
+- `sortOrder` - The higher the sortOrder the higher the bar.
+
+See the [sidebar source](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/public/sidebar.ts)
+for more detailed usage.
+
+### Pages
+Pages are entities that can be routed via the URL args, and whose content take
+up the entire available space to the right of the sidebar and underneath the
+topbar. Examples of pages are the timeline, record page, and query page, just to
+name a few common examples.
+
+E.g.
+```
+http://ui.perfetto.dev/#!/viewer <-- 'viewer' is is the current page.
+```
+
+Pages are added from a plugin by calling the `pages.registerPage` function.
+
+Pages can be trace-less or trace-ful. Trace-less pages are pages that are to be
+displayed when no trace is loaded - i.e. the record page. Trace-ful pages are
+displayed only when a trace is loaded, as they typically require a trace to work
+with.
+
+You'll typically register trace-less pages in your plugin's `onActivate()`
+function and trace-full pages in either `onActivate()` or `onTraceLoad()`. If
+users navigate to a trace-ful page before a trace is loaded the homepage will be
+shown instead.
+
+> Note: You don't need to bind the `Trace` object for pages unlike other
+> extension points, Perfetto will inject a trace object for you.
+
+Pages should be mithril components that accept `PageWithTraceAttrs` for
+trace-ful pages or `PageAttrs` for trace-less pages.
+
+Example of a trace-less page:
+```ts
+import m from 'mithril';
+import {PageAttrs} from '../../public/page';
+
+class MyPage implements m.ClassComponent<PageAttrs> {
+ view(vnode: m.CVnode<PageAttrs>) {
+ return `The trace title is: ${vnode.attrs.trace.traceInfo.traceTitle}`;
+ }
+}
+
+// ~~~ snip ~~~
+
+app.pages.registerPage({route: '/mypage', page: MyPage, traceless: true});
+```
+
+```ts
+import m from 'mithril';
+import {PageWithTraceAttrs} from '../../public/page';
+
+class MyPage implements m.ClassComponent<PageWithTraceAttrs> {
+ view(_vnode_: m.CVnode<PageWithTraceAttrs>) {
+ return 'Hello from my page';
+ }
+}
+
+// ~~~ snip ~~~
+
+app.pages.registerPage({route: '/mypage', page: MyPage});
+```
+
+Examples:
+- [dev.perfetto.ExplorePage](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/ui/src/plugins/dev.perfetto.ExplorePage/index.ts).
+
+
### Metric Visualisations
TBD
@@ -504,12 +790,13 @@
}
```
-To access permalink state, call `mountStore()` on your `PluginContextTrace`
+To access permalink state, call `mountStore()` on your `Trace`
object, passing in a migration function.
```typescript
-class MyPlugin implements PerfettoPlugin {
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- const store = ctx.mountStore(migrate);
+default export class implements PerfettoPlugin {
+ static readonly id = 'com.example.MyPlugin';
+ async onTraceLoad(trace: Trace): Promise<void> {
+ const store = trace.mountStore(migrate);
}
}
diff --git a/docs/data-sources/syscalls.md b/docs/data-sources/syscalls.md
index 3e6156d..8e769e8 100644
--- a/docs/data-sources/syscalls.md
+++ b/docs/data-sources/syscalls.md
@@ -15,13 +15,13 @@
At the UI level system calls are shown inlined with the per-thread slice tracks:
-![](/docs/images/syscalls.png "System calls in the thread tracks")
+![](/docs/images/syscalls.png 'System calls in the thread tracks')
## SQL
At the SQL level, syscalls are no different than any other userspace slice
event. They get interleaved in the per-thread slice stack and can be easily
-filtered by looking for the 'sys_' prefix:
+filtered by looking for the 'sys\_' prefix:
```sql
select ts, dur, t.name as thread, s.name, depth from slices as s
@@ -30,14 +30,14 @@
where s.name like 'sys_%'
```
-ts | dur | thread | name
----|-----|--------|------
-856325324372751 | 439867648 | s.nexuslauncher | sys_epoll_pwait
-856325324376970 | 990 | FpsThrottlerThr | sys_recvfrom
-856325324378376 | 2657 | surfaceflinger | sys_ioctl
-856325324419574 | 1250 | android.anim.lf | sys_recvfrom
-856325324428168 | 27344 | android.anim.lf | sys_ioctl
-856325324451345 | 573 | FpsThrottlerThr | sys_getuid
+| ts | dur | thread | name |
+| --------------- | --------- | --------------- | --------------- |
+| 856325324372751 | 439867648 | s.nexuslauncher | sys_epoll_pwait |
+| 856325324376970 | 990 | FpsThrottlerThr | sys_recvfrom |
+| 856325324378376 | 2657 | surfaceflinger | sys_ioctl |
+| 856325324419574 | 1250 | android.anim.lf | sys_recvfrom |
+| 856325324428168 | 27344 | android.anim.lf | sys_ioctl |
+| 856325324451345 | 573 | FpsThrottlerThr | sys_getuid |
## TraceConfig
diff --git a/docs/images/ui-plugins/nested_tracks.png b/docs/images/ui-plugins/nested_tracks.png
new file mode 100644
index 0000000..a9e87bc
--- /dev/null
+++ b/docs/images/ui-plugins/nested_tracks.png
Binary files differ
diff --git a/docs/images/ui-plugins/summary_track.png b/docs/images/ui-plugins/summary_track.png
new file mode 100644
index 0000000..96999dc
--- /dev/null
+++ b/docs/images/ui-plugins/summary_track.png
Binary files differ
diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md
index a77d363..a3b7c85a 100644
--- a/docs/instrumentation/tracing-sdk.md
+++ b/docs/instrumentation/tracing-sdk.md
@@ -5,27 +5,26 @@
When using the Tracing SDK there are two main aspects to consider:
-1. Whether you are interested only in tracing events coming from your own app
- or want to collect full-stack traces that overlay app trace events with
- system trace events like scheduler traces, syscalls or any other Perfetto
- data source.
+1. Whether you are interested only in tracing events coming from your own app or
+ want to collect full-stack traces that overlay app trace events with system
+ trace events like scheduler traces, syscalls or any other Perfetto data
+ source.
2. For app-specific tracing, whether you need to trace simple types of timeline
- events (e.g., slices, counters) or need to define complex data sources with a
- custom strongly-typed schema (e.g., for dumping the state of a subsystem of
- your app into the trace).
+ events (e.g., slices, counters) or need to define complex data sources with a
+ custom strongly-typed schema (e.g., for dumping the state of a subsystem of
+ your app into the trace).
For Android-only instrumentation, the advice is to keep using the existing
-[android.os.Trace (SDK)][atrace-sdk] / [ATrace_* (NDK)][atrace-ndk] if they
+[android.os.Trace (SDK)][atrace-sdk] / [ATrace\_\* (NDK)][atrace-ndk] if they
are sufficient for your use cases. Atrace-based instrumentation is fully
-supported in Perfetto.
-See the [Data Sources -> Android System -> Atrace Instrumentation][atrace-ds]
-for details.
+supported in Perfetto. See the [Data Sources -> Android System -> Atrace
+Instrumentation][atrace-ds] for details.
## Getting started
-TIP: The code from these examples is also available [in the
-repository](/examples/sdk/README.md).
+TIP: The code from these examples is also available
+[in the repository](/examples/sdk/README.md).
To start using the Client API, first check out the latest SDK release:
@@ -105,9 +104,8 @@
Track events are the suggested option when dealing with app-specific tracing as
they take care of a number of subtleties (e.g., thread safety, flushing, string
-interning).
-Track events are time bounded events (e.g., slices, counter) based on simple
-`TRACE_EVENT` annotation tags in the codebase, like this:
+interning). Track events are time bounded events (e.g., slices, counter) based
+on simple `TRACE_EVENT` annotation tags in the codebase, like this:
```c++
#include <perfetto.h>
@@ -167,15 +165,14 @@
### Custom data sources
For most uses, track events are the most straightforward way of instrumenting
-apps for tracing. However, in some rare circumstances they are not
-flexible enough, e.g., when the data doesn't fit the notion of a track or is
-high volume enough that it needs a strongly typed schema to minimize the size of
-each event. In this case, you can implement a *custom data source* for
-Perfetto.
+apps for tracing. However, in some rare circumstances they are not flexible
+enough, e.g., when the data doesn't fit the notion of a track or is high volume
+enough that it needs a strongly typed schema to minimize the size of each event.
+In this case, you can implement a _custom data source_ for Perfetto.
Unlike track events, when working with custom data sources, you will also need
-corresponding changes in [trace processor](/docs/analysis/trace-processor.md)
-to enable importing your data format.
+corresponding changes in [trace processor](/docs/analysis/trace-processor.md) to
+enable importing your data format.
A custom data source is a subclass of `perfetto::DataSource`. Perfetto will
automatically create one instance of the class for each tracing session it is
@@ -248,9 +245,9 @@
```
If necessary the `Trace()` method can access the custom data source state
-(`my_custom_state` in the example above). Doing so, will take a mutex to
-ensure data source isn't destroyed (e.g., because of stopping tracing) while
-the `Trace()` method is called on another thread. For example:
+(`my_custom_state` in the example above). Doing so, will take a mutex to ensure
+data source isn't destroyed (e.g., because of stopping tracing) while the
+`Trace()` method is called on another thread. For example:
```C++
CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
@@ -261,9 +258,9 @@
## In-process vs System mode
-The two modes are not mutually exclusive. An app can be configured to work
-in both modes and respond both to in-process tracing requests and system
-tracing requests. Both modes generate the same trace file format.
+The two modes are not mutually exclusive. An app can be configured to work in
+both modes and respond both to in-process tracing requests and system tracing
+requests. Both modes generate the same trace file format.
### In-process mode
@@ -275,8 +272,8 @@
`TracingInitArgs.backends = perfetto::kInProcessBackend` when initializing the
SDK, see examples below.
-This mode is used to generate traces that contain only events emitted by
-the app, but not other types of events (e.g. scheduler traces).
+This mode is used to generate traces that contain only events emitted by the
+app, but not other types of events (e.g. scheduler traces).
The main advantage is that by running fully in-process, it doesn't require any
special OS privileges and the profiled process can control the lifecycle of
@@ -293,32 +290,32 @@
`TracingInitArgs.backends = perfetto::kSystemBackend` when initializing the SDK,
see examples below.
-The main advantage of this mode is that it is possible to create fused traces where
-app events are overlaid on the same timeline of OS events. This enables
+The main advantage of this mode is that it is possible to create fused traces
+where app events are overlaid on the same timeline of OS events. This enables
full-stack performance investigations, looking all the way through syscalls and
kernel scheduling events.
-The main limitation of this mode is that it requires the external `traced` daemon
-to be up and running and reachable through the UNIX socket connection.
+The main limitation of this mode is that it requires the external `traced`
+daemon to be up and running and reachable through the UNIX socket connection.
This is suggested for local debugging or lab testing scenarios where the user
(or the test harness) can control the OS deployment (e.g., sideload binaries on
Android).
When using system mode, the tracing session must be controlled from the outside,
-using the `perfetto` command-line client
-(See [reference](/docs/reference/perfetto-cli)). This is because when collecting
+using the `perfetto` command-line client (See
+[reference](/docs/reference/perfetto-cli)). This is because when collecting
system traces, tracing data producers are not allowed to read back the trace
data as it might disclose information about other processes and allow
side-channel attacks.
-* On Android 9 (Pie) and beyond, traced is shipped as part of the platform.
-* On older versions of Android, traced can be built from sources using the
- the [standalone NDK-based workflow](/docs/contributing/build-instructions.md)
- and sideloaded via adb shell.
-* On Linux and MacOS and Windows `traced` must be built and run separately. See
+- On Android 9 (Pie) and beyond, traced is shipped as part of the platform.
+- On older versions of Android, traced can be built from sources using the the
+ [standalone NDK-based workflow](/docs/contributing/build-instructions.md) and
+ sideloaded via adb shell.
+- On Linux and MacOS and Windows `traced` must be built and run separately. See
the [Linux quickstart](/docs/quickstart/linux-tracing.md) for instructions.
-* On Windows the tracing protocol works over TCP/IP (
+- On Windows the tracing protocol works over TCP/IP (
[127.0.0.1:32278](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/src/tracing/ipc/default_socket.cc;l=75;drc=4f88a2fdfd3801c109d5e927b8206f9756288b12)
) + named shmem.
@@ -370,12 +367,12 @@
```
TIP: API methods with `Blocking` in their name will suspend the calling thread
- until the respective operation is complete. There are also asynchronous
- variants that don't have this limitation.
+until the respective operation is complete. There are also asynchronous variants
+that don't have this limitation.
-Now that tracing is active, instruct your app to perform the operation you
-want to record. After that, stop tracing and collect the
-protobuf-formatted trace data:
+Now that tracing is active, instruct your app to perform the operation you want
+to record. After that, stop tracing and collect the protobuf-formatted trace
+data:
```C++
tracing_session->StopBlocking();
@@ -388,9 +385,9 @@
output.close();
```
-To save memory with longer traces, you can also tell Perfetto to write
-directly into a file by passing a file descriptor into Setup(), remembering
-to close the file after tracing is done:
+To save memory with longer traces, you can also tell Perfetto to write directly
+into a file by passing a file descriptor into Setup(), remembering to close the
+file after tracing is done:
```C++
int fd = open("example.perfetto-trace", O_RDWR | O_CREAT | O_TRUNC, 0600);
@@ -401,8 +398,9 @@
close(fd);
```
-The resulting trace file can be directly opened in the [Perfetto
-UI](https://ui.perfetto.dev) or the [Trace Processor](/docs/analysis/trace-processor.md).
+The resulting trace file can be directly opened in the
+[Perfetto UI](https://ui.perfetto.dev) or the
+[Trace Processor](/docs/analysis/trace-processor.md).
[ipc]: /docs/design-docs/api-and-abi.md#socket-protocol
[atrace-ds]: /docs/data-sources/atrace.md
diff --git a/docs/quickstart/callstack-sampling.md b/docs/quickstart/callstack-sampling.md
index 30fd74a..f597e5c 100644
--- a/docs/quickstart/callstack-sampling.md
+++ b/docs/quickstart/callstack-sampling.md
@@ -2,12 +2,12 @@
## Prerequisites
-* [ADB](https://developer.android.com/studio/command-line/adb) installed.
-* A device running Android T+.
-* Either a debuggable (`userdebug`/`eng`) Android image, or the apps to be
- profiled need to be
- [marked as profileable or debuggable](https://developer.android.com/guide/topics/manifest/profileable-element)
- in their manifests.
+- [ADB](https://developer.android.com/studio/command-line/adb) installed.
+- A device running Android T+.
+- Either a debuggable (`userdebug`/`eng`) Android image, or the apps to be
+ profiled need to be
+ [marked as profileable or debuggable](https://developer.android.com/guide/topics/manifest/profileable-element)
+ in their manifests.
## Capture a CPU profile
@@ -130,6 +130,6 @@
`cpu_profile` will also write separate profiles for each process that it
profiled in the output directory, and those can be visualized using
-[`pprof`](https://github.com/google/pprof). You can merge them into one
-by passing all of them to pprof, e.g.
+[`pprof`](https://github.com/google/pprof). You can merge them into one by
+passing all of them to pprof, e.g.
`pprof /tmp/perf_profile-240105114948clvad/*`.
diff --git a/docs/toc.md b/docs/toc.md
index 01be2af..deaeb5d 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -48,7 +48,6 @@
* [Standard Library](analysis/stdlib-docs.autogen)
* [Syntax](analysis/perfetto-sql-syntax.md)
* [Prelude tables](analysis/sql-tables.autogen)
- * [Common Queries](analysis/common-queries.md)
* [Built-ins](analysis/builtin.md)
* [Analysis at scale](#)
* [Batch Trace Processor](analysis/batch-trace-processor.md)
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 6bc1760..af540c2 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -107,6 +107,7 @@
"generator_plugin_options",
"include_dirs",
"proto_data_sources",
+ "proto_deps",
"proto_in_dir",
"proto_out_dir",
"sources",
@@ -153,6 +154,7 @@
"defines",
"generator_plugin_options",
"include_dirs",
+ "proto_deps",
"proto_in_dir",
"proto_out_dir",
"sources",
@@ -205,6 +207,7 @@
"defines",
"extra_configs",
"include_dirs",
+ "proto_deps",
"proto_in_dir",
"proto_out_dir",
"generator_plugin_options",
@@ -286,40 +289,6 @@
# build generators and for generating descriptors.
source_set_target_name =
string_replace(target_name, expansion_token, "source_set")
- group(source_set_target_name) {
- public_deps_ = []
- if (defined(invoker.public_deps)) {
- foreach(dep, invoker.public_deps) {
- # Get the absolute target path
- mapped_dep = string_replace(dep, expansion_token, "source_set")
- public_deps_ += [ mapped_dep ]
- }
- }
-
- deps = []
- if (defined(invoker.deps)) {
- foreach(dep, invoker.deps) {
- mapped_dep = string_replace(dep, expansion_token, "source_set")
- deps += [ mapped_dep ]
- }
- }
- deps += public_deps_
-
- sources = []
- foreach(source, invoker.sources) {
- sources += [ get_path_info(source, "abspath") ]
- }
-
- metadata = {
- proto_library_sources = sources
- proto_import_dirs = import_dirs_
- exports = []
- foreach(dep, public_deps_) {
- exports +=
- [ get_label_info(dep, "dir") + ":" + get_label_info(dep, "name") ]
- }
- }
- }
# This config is necessary for Chrome proto_library build rule to work
# correctly.
@@ -328,6 +297,42 @@
inputs = invoker.sources
}
+ group(source_set_target_name) {
+ # To propagate indirect inputs dependencies to descendant tareget, we use
+ # public_deps and public_configs in this target.
+ public_deps = []
+ exports_ = []
+ if (defined(invoker.public_deps)) {
+ foreach(dep, invoker.public_deps) {
+ # Get the absolute target path
+ mapped_dep = string_replace(dep, expansion_token, "source_set")
+ public_deps += [ mapped_dep ]
+ exports_ += [ get_label_info(mapped_dep, "dir") + ":" +
+ get_label_info(mapped_dep, "name") ]
+ }
+ }
+
+ if (defined(invoker.deps)) {
+ foreach(dep, invoker.deps) {
+ mapped_dep = string_replace(dep, expansion_token, "source_set")
+ public_deps += [ mapped_dep ]
+ }
+ }
+
+ sources = []
+ foreach(source, invoker.sources) {
+ sources += [ get_path_info(source, "abspath") ]
+ }
+
+ public_configs = [ ":${source_set_input_config_name}" ]
+
+ metadata = {
+ proto_library_sources = sources
+ proto_import_dirs = import_dirs_
+ exports = exports_
+ }
+ }
+
# Generate the descriptor if the option is set.
if (defined(invoker.generate_descriptor)) {
target_name_ = string_replace(target_name, expansion_token, "descriptor")
@@ -385,6 +390,7 @@
proto_out_dir = proto_path
generator_plugin_options = "wrapper_namespace=pbzero"
deps = all_deps_
+ proto_deps = [ ":$source_set_target_name" ]
propagate_imports_configs = propagate_imports_configs_
import_dirs = import_dirs_
forward_variables_from(invoker, vars_to_forward)
@@ -395,6 +401,7 @@
proto_out_dir = proto_path
generator_plugin_options = "wrapper_namespace=gen"
deps = all_deps_
+ proto_deps = [ ":$source_set_target_name" ]
propagate_imports_configs = propagate_imports_configs_
import_dirs = import_dirs_
forward_variables_from(invoker, vars_to_forward)
@@ -405,6 +412,7 @@
proto_in_dir = proto_path
proto_out_dir = proto_path
generator_plugin_options = "wrapper_namespace=gen"
+ proto_deps = [ ":$source_set_target_name" ]
deps = all_deps_ + [ ":$cpp_target_name_" ]
propagate_imports_configs = propagate_imports_configs_
import_dirs = import_dirs_
@@ -419,6 +427,7 @@
cc_generator_options = "lite=true:"
propagate_imports_configs = propagate_imports_configs_
import_dirs = import_dirs_
+ proto_deps = [ ":${source_set_target_name}" ]
forward_variables_from(invoker, vars_to_forward)
}
} else {
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 93ed9c9..ed86c02 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -22,6 +22,10 @@
template("proto_library") {
assert(defined(invoker.sources))
+
+ # This is used in chromium build.
+ not_needed(invoker, [ "proto_deps" ])
+
proto_sources = invoker.sources
# All the proto imports should be relative to the project root.
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index fd3b0a6..01db0b1 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -144,12 +144,6 @@
} \
} while (false)
-// This internal macro is unused from the repo now, but some improper usage
-// remain outside of the repo.
-// TODO(b/294800182): Remove this.
-#define PERFETTO_INTERNAL_TRACK_EVENT(...) \
- PERFETTO_INTERNAL_TRACK_EVENT_WITH_METHOD(TraceForCategory, ##__VA_ARGS__)
-
// C++17 doesn't like a move constructor being defined for the EventFinalizer
// class but C++11 and MSVC doesn't compile without it being defined so support
// both.
diff --git a/infra/perfetto.dev/BUILD.gn b/infra/perfetto.dev/BUILD.gn
index 25ff547..b81fa3d 100644
--- a/infra/perfetto.dev/BUILD.gn
+++ b/infra/perfetto.dev/BUILD.gn
@@ -358,6 +358,22 @@
mdtargets += [ ":mdfile_${source}" ]
}
+# Files which have been removed/renamed/moved and now have HTTP redirections in
+# src/assets/script.js
+removed_renamed_moved_files = [ "analysis/common-queries.md" ]
+
+foreach(source, removed_renamed_moved_files) {
+ filename = rebase_path(string_replace(source, ".md", ""),
+ rebase_path("../../docs", root_build_dir))
+ md_to_html("mdfile_${source}") {
+ markdown = "src/empty.md"
+ html_template = "src/template_markdown.html"
+ out_html = "docs/${filename}"
+ deps = [ ":gen_toc" ]
+ }
+ mdtargets += [ ":mdfile_${source}" ]
+}
+
group("all_mdfiles") {
deps = mdtargets
}
diff --git a/infra/perfetto.dev/src/assets/script.js b/infra/perfetto.dev/src/assets/script.js
index aa368bc..6c4d04d 100644
--- a/infra/perfetto.dev/src/assets/script.js
+++ b/infra/perfetto.dev/src/assets/script.js
@@ -21,26 +21,6 @@
let tocEventHandlersInstalled = false;
let resizeObserver = undefined;
-// Handles redirects from the old docs.perfetto.dev.
-const legacyRedirectMap = {
- '#/contributing': '/docs/contributing/getting-started#community',
- '#/build-instructions': '/docs/contributing/build-instructions',
- '#/testing': '/docs/contributing/testing',
- '#/app-instrumentation': '/docs/instrumentation/tracing-sdk',
- '#/recording-traces': '/docs/instrumentation/tracing-sdk#recording',
- '#/running': '/docs/quickstart/android-tracing',
- '#/long-traces': '/docs/concepts/config#long-traces',
- '#/detached-mode': '/docs/concepts/detached-mode',
- '#/heapprofd': '/docs/data-sources/native-heap-profiler',
- '#/java-hprof': '/docs/data-sources/java-heap-profiler',
- '#/trace-processor': '/docs/analysis/trace-processor',
- '#/analysis': '/docs/analysis/trace-processor#annotations',
- '#/metrics': '/docs/analysis/metrics',
- '#/traceconv': '/docs/quickstart/traceconv',
- '#/clock-sync': '/docs/concepts/clock-sync',
- '#/architecture': '/docs/concepts/service-model',
-};
-
function doAfterLoadEvent(action) {
if (onloadFired) {
return action();
@@ -89,7 +69,7 @@
toc.appendChild(li);
doAfterLoadEvent(() => {
tocAnchors.push(
- {top: anchor.offsetTop + anchor.offsetHeight / 2, obj: link});
+ { top: anchor.offsetTop + anchor.offsetHeight / 2, obj: link });
});
}
tocContainer.innerHTML = '';
@@ -101,7 +81,7 @@
return;
tocEventHandlersInstalled = true;
const doc = document.querySelector('.doc');
- const passive = {passive: true};
+ const passive = { passive: true };
if (doc) {
const offY = doc.offsetTop;
doc.addEventListener('mousemove', (e) => onMouseMove(offY, e), passive);
@@ -111,9 +91,9 @@
}
window.addEventListener('scroll', () => onScroll(), passive);
resizeObserver = new ResizeObserver(() => requestAnimationFrame(() => {
- updateNav();
- updateTOC();
- }));
+ updateNav();
+ updateTOC();
+ }));
resizeObserver.observe(doc);
}
@@ -255,7 +235,7 @@
function setupSearch() {
const URL =
- 'https://www.googleapis.com/customsearch/v1?key=AIzaSyBTD2XJkQkkuvDn76LSftsgWOkdBz9Gfwo&cx=007128963598137843411:8suis14kcmy&q='
+ 'https://www.googleapis.com/customsearch/v1?key=AIzaSyBTD2XJkQkkuvDn76LSftsgWOkdBz9Gfwo&cx=007128963598137843411:8suis14kcmy&q='
const searchContainer = document.getElementById('search');
const searchBox = document.getElementById('search-box');
const searchRes = document.getElementById('search-res')
@@ -345,7 +325,40 @@
document.documentElement.style.setProperty('--anim-enabled', '1')
});
+// Handles redirects from the old docs.perfetto.dev.
+const legacyRedirectMap = {
+ '#/contributing': '/docs/contributing/getting-started#community',
+ '#/build-instructions': '/docs/contributing/build-instructions',
+ '#/testing': '/docs/contributing/testing',
+ '#/app-instrumentation': '/docs/instrumentation/tracing-sdk',
+ '#/recording-traces': '/docs/instrumentation/tracing-sdk#recording',
+ '#/running': '/docs/quickstart/android-tracing',
+ '#/long-traces': '/docs/concepts/config#long-traces',
+ '#/detached-mode': '/docs/concepts/detached-mode',
+ '#/heapprofd': '/docs/data-sources/native-heap-profiler',
+ '#/java-hprof': '/docs/data-sources/java-heap-profiler',
+ '#/trace-processor': '/docs/analysis/trace-processor',
+ '#/analysis': '/docs/analysis/trace-processor#annotations',
+ '#/metrics': '/docs/analysis/metrics',
+ '#/traceconv': '/docs/quickstart/traceconv',
+ '#/clock-sync': '/docs/concepts/clock-sync',
+ '#/architecture': '/docs/concepts/service-model',
+};
+
const fragment = location.hash.split('?')[0].replace('.md', '');
if (fragment in legacyRedirectMap) {
location.replace(legacyRedirectMap[fragment]);
-}
\ No newline at end of file
+}
+
+// Pages which have been been removed/renamed/moved and need to be redirected
+// to their new home.
+const redirectMap = {
+ // stdlib docs is not a perfect replacement but is good enough until we write
+ // a proper, Android specific query codelab page.
+ // TODO(lalitm): switch to that page when it's ready.
+ '/docs/analysis/common-queries': '/docs/analysis/stdlib-docs',
+};
+
+if (location.pathname in redirectMap) {
+ location.replace(redirectMap[location.pathname]);
+}
diff --git a/infra/perfetto.dev/src/assets/style.scss b/infra/perfetto.dev/src/assets/style.scss
index 63d6fc1..0a0ff2e 100644
--- a/infra/perfetto.dev/src/assets/style.scss
+++ b/infra/perfetto.dev/src/assets/style.scss
@@ -15,234 +15,235 @@
// Common + CSS reset
// -----------------------------------------------------------------------------
:root {
- --site-header-height: 50px;
- --home-highlights-height: 128px;
- --content-max-width: 1100px;
- --anim-ease: cubic-bezier(0.4, 0.0, 0.2, 1);
+ --site-header-height: 50px;
+ --home-highlights-height: 128px;
+ --content-max-width: 1100px;
+ --anim-ease: cubic-bezier(0.4, 0, 0.2, 1);
- // This is set to 1 by JS after onload. This is to prevent flickering on
- // page load on the nav bar and other entries while transitioning in their
- // initial state.
- --anim-enabled: 0;
+ // This is set to 1 by JS after onload. This is to prevent flickering on
+ // page load on the nav bar and other entries while transitioning in their
+ // initial state.
+ --anim-enabled: 0;
- --anim-time: calc(0.15s * var(--anim-enabled));
+ --anim-time: calc(0.15s * var(--anim-enabled));
}
$wide: "(max-width: 1100px)";
$mobile: "(max-width: 768px)";
@mixin minimal-scrollbar {
- &::-webkit-scrollbar {
- width: 8px;
- background-color: transparent;
- }
- &::-webkit-scrollbar-thumb {
- background-color: #ccc;
- border-radius: 8px;
- }
+ &::-webkit-scrollbar {
+ width: 8px;
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-thumb {
+ background-color: #ccc;
+ border-radius: 8px;
+ }
}
@media (max-aspect-ratio: 1/1) {
- :root {
- --home-highlights-height: 256px;
- }
+ :root {
+ --home-highlights-height: 256px;
+ }
}
* {
- box-sizing: border-box;
- -webkit-tap-highlight-color: none;
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: none;
}
html {
- font-family: Roboto, sans-serif;
- -webkit-font-smoothing: antialiased;
+ font-family: Roboto, sans-serif;
+ -webkit-font-smoothing: antialiased;
}
html,
body {
- padding: 0;
- margin: 0;
+ padding: 0;
+ margin: 0;
}
h1,
h2,
h3 {
- font-family: inherit;
- font-size: inherit;
- font-weight: inherit;
- padding: 0;
- margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: inherit;
+ padding: 0;
+ margin: 0;
}
// -----------------------------------------------------------------------------
// Site header
// -----------------------------------------------------------------------------
.site-header {
- background-color: hsl(210, 30%, 16%);
- color: hsl(210, 17%, 98%);
- position: sticky; // Sticky so the .docs element below doesn't start @ 0.
+ background-color: hsl(210, 30%, 16%);
+ color: hsl(210, 17%, 98%);
+ position: sticky; // Sticky so the .docs element below doesn't start @ 0.
+ top: 0;
+ width: 100%;
+ --sh-padding-y: 5px;
+ max-height: var(--site-header-height);
+ padding: var(--sh-padding-y) 30px;
+ box-shadow: rgba(0, 0, 0, 0.3) 0 3px 3px 0;
+ overflow: hidden;
+ display: flex;
+ z-index: 10;
+ transition: max-height var(--anim-ease) var(--anim-time);
+ &.expanded {
+ max-height: 100vh;
+ }
+ .brand {
+ img {
+ height: 40px;
+ vertical-align: bottom;
+ }
+ font-weight: 200;
+ font-size: 28px;
+ flex-grow: 1;
+ .brand-docs {
+ text-transform: uppercase;
+ font-size: 14px;
+ color: #ecba2a;
+ vertical-align: bottom;
+ line-height: 30px;
+ font-weight: 400;
+ }
+ }
+ > *:not(:first-child) {
+ line-height: calc(var(--site-header-height) - var(--sh-padding-y) * 2);
+ font-family: "Source Sans Pro", sans-serif;
+ font-weight: 400;
+ font-size: 1.1rem;
+ margin: 0 20px;
+ color: hsl(210, 17%, 85%);
+ }
+ a {
+ text-decoration: none;
+ &:hover {
+ color: hsl(210, 17%, 100%);
+ }
+ }
+ .menu {
+ visibility: hidden;
+ font-family: "Material Icons Round";
+ font-size: 24px;
+ text-align: center;
+ position: absolute;
+ right: 0;
top: 0;
- width: 100%;
- --sh-padding-y: 5px;
- max-height: var(--site-header-height);
- padding: var(--sh-padding-y) 30px;
- box-shadow: rgba(0, 0, 0, 0.3) 0 3px 3px 0;
- overflow: hidden;
- display: flex;
- z-index: 10;
- transition: max-height var(--anim-ease) var(--anim-time);
- &.expanded {
- max-height: 100vh;
- }
- .brand {
- img {
- height: 40px;
- vertical-align: bottom;
- }
- font-weight: 200;
- font-size: 28px;
- flex-grow: 1;
- .brand-docs {
- text-transform: uppercase;
- font-size: 14px;
- color: #ecba2a;
- vertical-align: bottom;
- line-height: 30px;
- font-weight: 400;
- }
- }
- >*:not(:first-child) {
- line-height: calc(var(--site-header-height) - var(--sh-padding-y) * 2);
- font-family: 'Source Sans Pro', sans-serif;
- font-weight: 400;
- font-size: 1.1rem;
- margin: 0 20px;
- color: hsl(210, 17%, 85%);
- }
- a {
- text-decoration: none;
- &:hover {
- color: hsl(210, 17%, 100%);
- }
+ line-height: var(--site-header-height);
+ }
+
+ @media #{$mobile} {
+ flex-direction: column;
+ > *:not(:first-child) {
+ margin-left: 40px;
}
.menu {
- visibility: hidden;
- font-family: 'Material Icons Round';
- font-size: 24px;
- text-align: center;
- position: absolute;
- right: 0;
- top: 0;
- line-height: var(--site-header-height);
+ visibility: visible;
}
-
- @media #{$mobile} {
- flex-direction: column;
- >*:not(:first-child) {
- margin-left: 40px;
- }
- .menu {
- visibility: visible;
- }
- }
+ }
}
-
#search {
- position: relative;
- flex-grow: 0;
- transition: flex-grow cubic-bezier(1, 0.01, 1, 1) var(--anim-time), background-color ease var(--anim-time);
- padding: 0;
+ position: relative;
+ flex-grow: 0;
+ transition: flex-grow cubic-bezier(1, 0.01, 1, 1) var(--anim-time),
+ background-color ease var(--anim-time);
+ padding: 0;
+ &::before {
+ visibility: hidden;
+ user-select: none;
+ content: "";
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: var(--site-header-height);
+ bottom: 0;
+ z-index: -100;
+ background-color: rgba(255, 255, 255, 0.8);
+ backdrop-filter: blur(3px);
+ opacity: 0;
+ transition: opacity ease var(--anim-time), visibility 0s;
+ }
+ &:focus-within {
+ flex-grow: 1000;
&::before {
- visibility: hidden;
- user-select: none;
- content: '';
- position: fixed;
- left: 0;
- right: 0;
- top: var(--site-header-height);
- bottom: 0;
- z-index: -100;
- background-color: rgba(255, 255, 255, 0.8);
- backdrop-filter: blur(3px);
- opacity: 0;
- transition: opacity ease var(--anim-time), visibility 0s;
-
+ display: block;
+ opacity: 1;
+ visibility: visible;
}
- &:focus-within {
- flex-grow: 1000;
- &::before {
- display: block;
- opacity: 1;
- visibility: visible;
- }
- #search-res {
- display: block;
- }
-
- }
-
- @media #{$mobile} {
- display: none;
- }
-
- #search-box {
- width: 100%;
- height: 32px;
- font-size: 1rem;;
- color: #333;
- background-color: rgba(255, 255, 255, 0.9);
- border: 1px solid #eee;
- border-radius: 2px;
- background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z"/></svg>');
- background-repeat: no-repeat;
- background-size: contain;
- padding-left: 40px;
- outline: none;
- &:hover, &:focus {
- background-color: rgba(255, 255, 255, 0.95);
- }
- }
-
#search-res {
- display: none;
- background-color: rgba(255, 255, 255, 1.0);
- border: 1px solid #eee;
- box-shadow: #aaa 0px 1px 5px;
- color: #333;
- line-height: initial;
- margin-top: -4px;
- overflow-x: auto;
- position: fixed;
- top: var(--site-header-height);
- max-height: calc(100vh - var(--site-header-height));
- z-index: 10;
- >div {
- padding: 10px;
- margin: 0;
- &:hover {
- background-color: #f0f0f0;
- }
- }
- .sr-title {
- color: #333;
- font-weight: bold;
- }
- .sr-snippet {
- color: #444;
- font-size: 0.9rem;
- }
+ display: block;
+ }
+ }
- a { text-decoration: none; }
- a:hover { color: initial };
+ @media #{$mobile} {
+ display: none;
+ }
- &:empty {
- visibility: hidden;
- }
+ #search-box {
+ width: 100%;
+ height: 32px;
+ font-size: 1rem;
+ color: #333;
+ background-color: rgba(255, 255, 255, 0.9);
+ border: 1px solid #eee;
+ border-radius: 2px;
+ background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z"/></svg>');
+ background-repeat: no-repeat;
+ background-size: contain;
+ padding-left: 40px;
+ outline: none;
+ &:hover,
+ &:focus {
+ background-color: rgba(255, 255, 255, 0.95);
+ }
+ }
+
+ #search-res {
+ display: none;
+ background-color: rgba(255, 255, 255, 1);
+ border: 1px solid #eee;
+ box-shadow: #aaa 0px 1px 5px;
+ color: #333;
+ line-height: initial;
+ margin-top: -4px;
+ overflow-x: auto;
+ position: fixed;
+ top: var(--site-header-height);
+ max-height: calc(100vh - var(--site-header-height));
+ z-index: 10;
+ > div {
+ padding: 10px;
+ margin: 0;
+ &:hover {
+ background-color: #f0f0f0;
+ }
+ }
+ .sr-title {
+ color: #333;
+ font-weight: bold;
+ }
+ .sr-snippet {
+ color: #444;
+ font-size: 0.9rem;
}
-}
+ a {
+ text-decoration: none;
+ }
+ a:hover {
+ color: initial;
+ }
+ &:empty {
+ visibility: hidden;
+ }
+ }
+}
// -----------------------------------------------------------------------------
// Site footer
@@ -250,722 +251,759 @@
// Footer in the index page.
.site-footer {
- background-color: hsl(210, 30%, 16%);
- padding: 1em 0;
- font-size: 14px;
- color: #fff;
- text-align: center;
- ul {
- list-style: none;
- margin: 0;
- padding: 0;
- li {
- display: inline;
- padding: 0 10px;
- &:not(:last-child) {
- border-right: solid 1px #fff;
- }
- }
+ background-color: hsl(210, 30%, 16%);
+ padding: 1em 0;
+ font-size: 14px;
+ color: #fff;
+ text-align: center;
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ li {
+ display: inline;
+ padding: 0 10px;
+ &:not(:last-child) {
+ border-right: solid 1px #fff;
+ }
}
- a,
- a:visited {
- text-decoration: none;
- color: inherit;
- }
- .docs-footer-notice { display: none; }
+ }
+ a,
+ a:visited {
+ text-decoration: none;
+ color: inherit;
+ }
+ .docs-footer-notice {
+ display: none;
+ }
}
// Footer overrides for the /docs/ page.
.docs .site-footer {
- grid-area: footer;
- background: transparent;
- color: #666;
- text-align: left;
- margin: 0 20px;
- padding: 12px 0;
+ grid-area: footer;
+ background: transparent;
+ color: #666;
+ text-align: left;
+ margin: 0 20px;
+ padding: 12px 0;
- .docs-footer-notice {
- padding: 0;
- margin: 0;
- display: block;
- }
+ .docs-footer-notice {
+ padding: 0;
+ margin: 0;
+ display: block;
+ }
- ul { display: none; }
+ ul {
+ display: none;
+ }
}
// -----------------------------------------------------------------------------
// Site content
// -----------------------------------------------------------------------------
.site-content {
- .section-wrapper {
- border-bottom: solid 1px #eee;
- &:nth-child(2n+1) {
- background-color: hsl(210, 17%, 98%);
- }
+ .section-wrapper {
+ border-bottom: solid 1px #eee;
+ &:nth-child(2n + 1) {
+ background-color: hsl(210, 17%, 98%);
}
- section {
+ }
+ section {
+ display: block;
+ position: relative;
+ overflow: hidden;
+ padding: 0 20px;
+ margin: 0 auto;
+ max-width: calc(var(--content-max-width) + 2 * 20px);
+ }
+
+ .banner {
+ height: calc(
+ 100vh - var(--home-highlights-height) - var(--site-header-height)
+ );
+ @media (max-height: 639px) {
+ // If the screen is too short (e.g. smartphone in landscape mode)
+ // move the highlights sections (the four tiles) out of the visible
+ // viewport.
+ height: calc(100vh - var(--site-header-height));
+ }
+ min-height: 25vw;
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-template-rows: 1fr 1fr 5fr;
+ h1,
+ h2 {
+ margin: auto;
+ font-family: "Source Sans Pro", sans-serif;
+ text-align: center;
+ color: hsl(0, 0, 35%);
+ span {
+ white-space: nowrap;
+ }
+ }
+ h1 {
+ font-size: 2.5rem;
+ font-size: calc(min(4rem, 8vw, 6vh));
+ font-weight: 400;
+ padding-top: calc(max(1rem, 2vh));
+ }
+ h2 {
+ font-size: 1.25rem;
+ font-size: calc(min(2rem, 6vw, 4vh));
+ font-weight: 200;
+ padding-top: 10px;
+ }
+ .home-img {
+ padding: 1rem 0;
+ overflow: hidden;
+ position: relative;
+ display: flex;
+ img {
+ max-height: 100%;
+ max-width: 100%;
+ margin: auto;
display: block;
- position: relative;
- overflow: hidden;
- padding: 0 20px;
- margin: 0 auto;
- max-width: calc(var(--content-max-width) + 2 * 20px);
+ }
}
+ }
- .banner {
- height: calc(100vh - var(--home-highlights-height) - var(--site-header-height));
- @media (max-height: 639px) {
- // If the screen is too short (e.g. smartphone in landscape mode)
- // move the highlights sections (the four tiles) out of the visible
- // viewport.
- height: calc(100vh - var(--site-header-height));
+ .home-highlights {
+ &:before {
+ border-top: 1px solid hsl(210, 17%, 90%);
+ }
+ height: var(--home-highlights-height);
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ grid-template-rows: 1fr;
+ background-color: #fff;
+ z-index: 2;
+ @media (max-aspect-ratio: 1/1) {
+ grid-template-columns: repeat(2, 1fr);
+ }
+ > a {
+ color: hsl(0, 0, 20%);
+ font-size: 22px;
+ font-weight: 400;
+ text-align: center;
+ padding: 20px 0;
+ font-family: "Source Sans Pro", sans-serif;
+ text-decoration: none;
+ .icon {
+ background-image: url("/assets/sprite.png");
+ background-repeat: no-repeat;
+ width: 64px;
+ height: 64px;
+ margin: auto;
+ background-size: 256px 128px;
+ filter: grayscale(1);
+ transition: filter ease var(--anim-time);
+ }
+ &:nth-child(1) .icon {
+ background-position: 0 -64px;
+ }
+ &:nth-child(2) .icon {
+ background-position: -64px -64px;
+ }
+ &:nth-child(3) .icon {
+ background-position: -128px -64px;
+ }
+ &:nth-child(4) .icon {
+ background-position: -192px -64px;
+ }
+ &:hover {
+ background-color: hsl(210, 17%, 90%);
+ .icon {
+ filter: grayscale(0);
}
- min-height: 25vw;
- display: grid;
+ }
+ }
+ }
+ .home-section {
+ min-height: calc(min(100vh - var(--site-header-height), 800px));
+ padding: 5% 20px;
+ display: grid;
+ grid-template-rows: 1fr;
+ grid-column-gap: 4vw;
+ > img {
+ grid-area: img;
+ max-width: 100%;
+ max-height: 55vh;
+ margin: auto;
+ margin-top: 40px;
+ }
+ h2,
+ > div {
+ grid-area: content;
+ }
+ h2 {
+ font-family: "Source Sans Pro", sans-serif;
+ font-weight: 600;
+ font-size: 2.5rem;
+ color: #333;
+ text-align: center;
+ }
+ &:nth-child(2n) {
+ grid-template-columns: 5fr 4fr;
+ grid-template-areas: "content img";
+ h2 {
+ padding: 0 0 0 50px;
+ text-align: left;
+ }
+ }
+ &:nth-child(2n + 1) {
+ grid-template-columns: 4fr 5fr;
+ grid-template-areas: "img content";
+ h2 {
+ padding: 0 50px 0 0;
+ text-align: left;
+ }
+ }
+ @media (max-aspect-ratio: 1/1) {
+ padding: 5vh 20px;
+ &:nth-child(n) {
+ grid-template-rows: auto auto;
grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr 5fr;
- h1,
+ grid-template-areas: "img" "content";
+ grid-row-gap: 30px;
h2 {
- margin: auto;
- font-family: 'Source Sans Pro', sans-serif;
- text-align: center;
- color: hsl(0, 0, 35%);
- span {
- white-space: nowrap;
- }
+ padding: 0;
+ text-align: center;
}
- h1 {
- font-size: 2.5rem;
- font-size: calc(min(4rem, 8vw, 6vh));
- font-weight: 400;
- padding-top: calc(max(1rem, 2vh));
- }
- h2 {
- font-size: 1.25rem;
- font-size: calc(min(2rem, 6vw, 4vh));
- font-weight: 200;
- padding-top: 10px;
- }
- .home-img {
- padding: 1rem 0;
- overflow: hidden;
- position: relative;
- display: flex;
- img {
- max-height: 100%;
- max-width: 100%;
- margin: auto;
- display: block;
- }
- }
+ }
+ > img {
+ padding: 0 10vw;
+ }
}
-
-
- .home-highlights {
- &:before {
- border-top: 1px solid hsl(210, 17%, 90%);
+ div {
+ grid-area: content;
+ .button {
+ display: inline-block;
+ background: #337ab7;
+ font-weight: 500;
+ color: #fff;
+ border-radius: 6px;
+ font-size: 18px;
+ padding: 10px 16px;
+ transition: background-color ease var(--anim-time);
+ text-decoration: none;
+ &:hover {
+ background: #286090;
}
- height: var(--home-highlights-height);
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- grid-template-rows: 1fr;
- background-color: #fff;
- z-index: 2;
- @media (max-aspect-ratio: 1/1) {
- grid-template-columns: repeat(2, 1fr);
- }
- >a {
- color: hsl(0, 0, 20%);
- font-size: 22px;
- font-weight: 400;
- text-align: center;
- padding: 20px 0;
- font-family: 'Source Sans Pro', sans-serif;
- text-decoration: none;
- .icon {
- background-image: url('/assets/sprite.png');
- background-repeat: no-repeat;
- width: 64px;
- height: 64px;
- margin: auto;
- background-size: 256px 128px;
- filter: grayscale(1);
- transition: filter ease var(--anim-time);
- }
- &:nth-child(1) .icon {
- background-position: 0 -64px;
- }
- &:nth-child(2) .icon {
- background-position: -64px -64px;
- }
- &:nth-child(3) .icon {
- background-position: -128px -64px;
- }
- &:nth-child(4) .icon {
- background-position: -192px -64px;
- }
- &:hover {
- background-color: hsl(210, 17%, 90%);
- .icon {
- filter: grayscale(0);
- }
- }
- }
+ }
}
- .home-section {
- min-height: calc(min(100vh - var(--site-header-height), 800px));
- padding: 5% 20px;
- display: grid;
- grid-template-rows: 1fr;
- grid-column-gap: 4vw;
- >img {
- grid-area: img;
- max-width: 100%;
- max-height: 55vh;
- margin: auto;
- margin-top: 40px;
- }
- h2,
- >div {
- grid-area: content;
- }
- h2 {
- font-family: 'Source Sans Pro', sans-serif;
- font-weight: 600;
- font-size: 2.5rem;
- color: #333;
- text-align: center;
- }
- &:nth-child(2n) {
- grid-template-columns: 5fr 4fr;
- grid-template-areas: "content img";
- h2 {
- padding: 0 0 0 50px;
- text-align: left;
- }
- }
- &:nth-child(2n+1) {
- grid-template-columns: 4fr 5fr;
- grid-template-areas: "img content";
- h2 {
- padding: 0 50px 0 0;
- text-align: left;
- }
- }
- @media (max-aspect-ratio: 1/1) {
- padding: 5vh 20px;
- &:nth-child(n) {
- grid-template-rows: auto auto;
- grid-template-columns: 1fr;
- grid-template-areas: "img" "content";
- grid-row-gap: 30px;
- h2 {
- padding: 0;
- text-align: center;
- }
- }
- >img {
- padding: 0 10vw;
- }
- }
- div {
- grid-area: content;
- .button {
- display: inline-block;
- background: #337ab7;
- font-weight: 500;
- color: #fff;
- border-radius: 6px;
- font-size: 18px;
- padding: 10px 16px;
- transition: background-color ease var(--anim-time);
- text-decoration: none;
- &:hover {
- background: #286090;
- }
- }
- }
- .home-item {
- display: grid;
- grid-template-rows: auto auto;
- grid-template-columns: 50px auto;
- grid-template-areas: "img title" "img label";
- grid-column-gap: 20px;
- padding: 20px 30px;
- margin: 10px 0;
- // border: 1px solid #eee;
- font-family: 'Source Sans Pro', sans-serif;
- color: #111111;
- transition: filter var(--anim-ease) var(--anim-time), background-color var(--anim-ease) var(--anim-time), transform var(--anim-ease) var(--anim-time), box-shadow linear var(--anim-time);
- border-radius: 6px;
- filter: opacity(0.6);
- &:hover {
- background-color: hsla(0, 0, 0, 0.02);
- filter: opacity(1);
- transform: scale(1.01);
- }
- >img,
- >i {
- grid-area: img;
- margin: auto;
- font-size: 50px;
- }
- >h3 {
- grid-area: title;
- font-size: 1.25rem;
- line-height: 20px;
- font-weight: 600;
- }
- >p {
- grid-area: label;
- font-size: 1rem;
- font-weight: 400;
- margin: 1em 0;
- }
- }
+ .home-item {
+ display: grid;
+ grid-template-rows: auto auto;
+ grid-template-columns: 50px auto;
+ grid-template-areas: "img title" "img label";
+ grid-column-gap: 20px;
+ padding: 20px 30px;
+ margin: 10px 0;
+ // border: 1px solid #eee;
+ font-family: "Source Sans Pro", sans-serif;
+ color: #111111;
+ transition: filter var(--anim-ease) var(--anim-time),
+ background-color var(--anim-ease) var(--anim-time),
+ transform var(--anim-ease) var(--anim-time),
+ box-shadow linear var(--anim-time);
+ border-radius: 6px;
+ filter: opacity(0.6);
+ &:hover {
+ background-color: hsla(0, 0, 0, 0.02);
+ filter: opacity(1);
+ transform: scale(1.01);
+ }
+ > img,
+ > i {
+ grid-area: img;
+ margin: auto;
+ font-size: 50px;
+ }
+ > h3 {
+ grid-area: title;
+ font-size: 1.25rem;
+ line-height: 20px;
+ font-weight: 600;
+ }
+ > p {
+ grid-area: label;
+ font-size: 1rem;
+ font-weight: 400;
+ margin: 1em 0;
+ }
}
+ }
}
// -----------------------------------------------------------------------------
// Docs
// -----------------------------------------------------------------------------
.docs {
- min-height: 100vh;
- display: grid;
- --nav-width: 240px;
- --toc-width: 180px;
+ min-height: 100vh;
+ display: grid;
+ --nav-width: 240px;
+ --toc-width: 180px;
- // 1665px is the clientWidth on a macbook pro. Adjust the layout so that
- // the max-width of the central .doc fits precisely when the browser is
- // full-screen on a macbook.
- --max-doc-width: calc(1665px - var(--toc-width) - var(--nav-width));
+ // 1665px is the clientWidth on a macbook pro. Adjust the layout so that
+ // the max-width of the central .doc fits precisely when the browser is
+ // full-screen on a macbook.
+ --max-doc-width: calc(1665px - var(--toc-width) - var(--nav-width));
- grid-template-columns: var(--nav-width) minmax(auto, var(--max-doc-width)) var(--toc-width);
- grid-template-rows: 1fr max-content;
- grid-template-areas: "nav doc toc" "nav footer toc";
+ grid-template-columns: var(--nav-width) minmax(auto, var(--max-doc-width)) var(
+ --toc-width
+ );
+ grid-template-rows: 1fr max-content;
+ grid-template-areas: "nav doc toc" "nav footer toc";
- background-color: hsl(210, 10%, 97%);
- .nav {
- grid-area: nav;
- border-right: 1px solid hsl(210, 30%, 90%);
- background-color: #fefefe;
- padding: 20px 0;
- padding-right: 16px;
+ background-color: hsl(210, 10%, 97%);
+ .nav {
+ grid-area: nav;
+ border-right: 1px solid hsl(210, 30%, 90%);
+ background-color: #fefefe;
+ padding: 20px 0;
+ padding-right: 16px;
- position: sticky;
- top: var(--site-header-height);
- height: calc(100vh - var(--site-header-height));
- overflow-y: auto;
- @include minimal-scrollbar;
+ position: sticky;
+ top: var(--site-header-height);
+ height: calc(100vh - var(--site-header-height));
+ overflow-y: auto;
+ @include minimal-scrollbar;
- a {
- color: inherit;
- text-decoration: none;
- line-height: 24px;
- display: flex;
- transition: background-color var(--anim-ease) var(--anim-time),
- visibility linear var(--anim-time);
- border-radius: 0 10px 10px 0;
- -webkit-tap-highlight-color: transparent;
- &[href] {
- &:hover {
- color: #000;
- background-color: #f1f3f4;
- }
- &.selected {
- background-color: #ecba2a;
- }
- }
+ a {
+ color: inherit;
+ text-decoration: none;
+ line-height: 24px;
+ display: flex;
+ transition: background-color var(--anim-ease) var(--anim-time),
+ visibility linear var(--anim-time);
+ border-radius: 0 10px 10px 0;
+ -webkit-tap-highlight-color: transparent;
+ &[href] {
+ &:hover {
+ color: #000;
+ background-color: #f1f3f4;
}
-
- ul {
- list-style: none;
- margin: 0;
- padding: 0;
- overflow: hidden;
- li {
- font-size: 1rem;
- font-weight: 400;
- font-family: 'Source Sans Pro', sans-serif;
- color: #4a4a4a;
- max-width: 100%;
- margin: 3px 0;
- }
- p { margin: 0; }
+ &.selected {
+ background-color: #ecba2a;
}
-
- // Applies only to outer-level submenus.
- >ul {
- position: static; // Otherwise gets v-centered in the middle.
- > li {
- padding-bottom: 10px;
- margin-bottom: 10px;
- font-weight: 600;
- color: #111;
-
- &:not(:last-child) {
- border-bottom: 1px solid #eee;
- }
-
- &.compressible {
- > p > a::after {
- content: 'keyboard_arrow_up';
- font-family: 'Material Icons Round';
- font-size: 24px;
- width: 24px;
- transition: transform var(--anim-ease) var(--anim-time);
- margin: 0 0 0 auto;
- font-weight: 200;
- color: #666;
- }
- > ul {
- transition: max-height var(--anim-ease) var(--anim-time),
- opacity var(--anim-ease) var(--anim-time);
- opacity: 1;
- }
- &.compressed {
- // The JS will compute and set the maxHeight on each
- // element depending on the size of their children.
- // !important is needed to override the element-inline
- // max-height property set by JS, which is prioritary.
- > ul {
- max-height: 0 !important;
- visibility: hidden;
- opacity: 0;
- }
- > p > a::after {
- transform: scaleY(-1);
- }
- }
- } // .compressible
-
- }
- }
-
- li a {
- padding-left: 16px;
- }
- li li a {
- padding-left: 30px;
- }
- li li li a {
- padding-left: 44px;
- }
- .expanded a::after {
- transform: rotate(180deg);
- }
+ }
}
- .doc {
- grid-area: doc;
- background-color: #fff;
- margin: 20px;
- padding: 30px 40px;
- font-family: Roboto, sans-serif;
+
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ li {
font-size: 1rem;
font-weight: 400;
- line-height: 24px;
- -webkit-font-smoothing: antialiased;
+ font-family: "Source Sans Pro", sans-serif;
color: #4a4a4a;
- position: relative;
- box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .1), 0 1px 3px 1px rgba(60, 64, 67, .15);
- overflow: hidden;
+ max-width: 100%;
+ margin: 3px 0;
+ }
+ p {
+ margin: 0;
+ }
+ }
- a {
- text-decoration: none;
- &:link { color: #007b83; }
- &:visited { color: #8e3317; }
- &:hover { color: #009da8; }
- &[href^="http"] {
- // External link.
- &:after {
- content: 'open_in_new';
- font-family: 'Material Icons Round';
- color: #666;
- text-decoration: none;
- margin-left: 2px;
- margin-right: 4px;
- vertical-align: bottom;
- }
- }
+ // Applies only to outer-level submenus.
+ > ul {
+ position: static; // Otherwise gets v-centered in the middle.
+ > li {
+ padding-bottom: 10px;
+ margin-bottom: 10px;
+ font-weight: 600;
+ color: #111;
+
+ &:not(:last-child) {
+ border-bottom: 1px solid #eee;
}
- h1,
- h2,
- h3 {
- margin: 10px 0;
- padding: 0;
- padding-top: 30px;
- }
- h1 {
- font-size: 2.25rem;
- line-height: 2.25rem;
- margin: 0;
- padding: 0;
- margin-bottom: 1.5rem;
- font-family: 'Source Sans Pro', sans-serif;
- }
- h2 {
- font-size: 1.5rem;
- border-bottom: 1px solid #e8eaed;
- padding-bottom: 6px;
- }
- h3 {
- font-size: 1.25rem;
- }
- * {
- max-width: 100%;
- }
-
- img[alt$="screenshot"] {
- box-shadow: 0 0 10px 2px #eee;
- }
-
- code:not(.code-block) {
- background: hsla(210, 17%, 90%, 0.2);
- border: 1px solid #E8EAED;
- border-radius: 6px;
- padding: 1px 4px;
- }
- .code-block {
- overflow-x: auto;
- white-space: pre;
- border-radius: 6px;
- box-shadow: 1px 1px 6px #999;
- border-top: 5px solid #8BC34A;
- }
- // Hide mermaid graphs until they are rendered, this is to avoid showing
- // the mermaid source while the renderer generates the SVG.
- .mermaid {
- transition: opacity var(--anim-ease) var(--anim-time);
- &:not(.rendered) {
- opacity: 0;
- }
- }
- .anchor {
- margin-left: -29px;
- padding-right: 5px;
- text-decoration: none;
- position: absolute;
- padding-top: var(--site-header-height);
- margin-top: calc(-1 * var(--site-header-height));
- outline: none;
- opacity: 0;
- transition: opacity var(--anim-ease) var(--anim-time);
- &::before {
- content: 'insert_link';
- font-family: 'Material Icons Round';
- color: #333;
- font-size: 24px;
- }
- }
- *:hover .anchor {
+ &.compressible {
+ > p > a::after {
+ content: "keyboard_arrow_up";
+ font-family: "Material Icons Round";
+ font-size: 24px;
+ width: 24px;
+ transition: transform var(--anim-ease) var(--anim-time);
+ margin: 0 0 0 auto;
+ font-weight: 200;
+ color: #666;
+ }
+ > ul {
+ transition: max-height var(--anim-ease) var(--anim-time),
+ opacity var(--anim-ease) var(--anim-time);
opacity: 1;
+ }
+ &.compressed {
+ // The JS will compute and set the maxHeight on each
+ // element depending on the size of their children.
+ // !important is needed to override the element-inline
+ // max-height property set by JS, which is prioritary.
+ > ul {
+ max-height: 0 !important;
+ visibility: hidden;
+ opacity: 0;
+ }
+ > p > a::after {
+ transform: scaleY(-1);
+ }
+ }
+ } // .compressible
+ }
+ }
+
+ li a {
+ padding-left: 16px;
+ }
+ li li a {
+ padding-left: 30px;
+ }
+ li li li a {
+ padding-left: 44px;
+ }
+ .expanded a::after {
+ transform: rotate(180deg);
+ }
+ }
+ .doc {
+ grid-area: doc;
+ background-color: #fff;
+ margin: 20px;
+ padding: 30px 40px;
+ font-family: Roboto, sans-serif;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 24px;
+ -webkit-font-smoothing: antialiased;
+ color: #4a4a4a;
+ position: relative;
+ box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.1),
+ 0 1px 3px 1px rgba(60, 64, 67, 0.15);
+ overflow: hidden;
+
+ a {
+ text-decoration: none;
+ &:link {
+ color: #007b83;
+ }
+ &:visited {
+ color: #8e3317;
+ }
+ &:hover {
+ color: #009da8;
+ }
+ &[href^="http"] {
+ // External link.
+ &:after {
+ content: "open_in_new";
+ font-family: "Material Icons Round";
+ color: #666;
+ text-decoration: none;
+ margin-left: 2px;
+ margin-right: 4px;
+ vertical-align: bottom;
}
+ }
+ }
+
+ h1,
+ h2,
+ h3 {
+ margin: 10px 0;
+ padding: 0;
+ padding-top: 30px;
+ }
+ h1 {
+ font-size: 2.25rem;
+ line-height: 2.25rem;
+ margin: 0;
+ padding: 0;
+ margin-bottom: 1.5rem;
+ font-family: "Source Sans Pro", sans-serif;
+ }
+ h2 {
+ font-size: 1.5rem;
+ border-bottom: 1px solid #e8eaed;
+ padding-bottom: 6px;
+ }
+ h3 {
+ font-size: 1.25rem;
+ }
+ * {
+ max-width: 100%;
+ }
+
+ img[alt$="screenshot"] {
+ box-shadow: 0 0 10px 2px #eee;
+ }
+
+ code:not(.code-block) {
+ background: hsla(210, 17%, 90%, 0.2);
+ border: 1px solid #e8eaed;
+ border-radius: 6px;
+ padding: 1px 4px;
+ }
+ .code-block {
+ overflow-x: auto;
+ white-space: pre;
+ border-radius: 6px;
+ box-shadow: 1px 1px 6px #999;
+ border-top: 5px solid #8bc34a;
+ }
+ // Hide mermaid graphs until they are rendered, this is to avoid showing
+ // the mermaid source while the renderer generates the SVG.
+ .mermaid {
+ transition: opacity var(--anim-ease) var(--anim-time);
+ &:not(.rendered) {
+ opacity: 0;
+ }
+ }
+ .anchor {
+ margin-left: -29px;
+ padding-right: 5px;
+ text-decoration: none;
+ position: absolute;
+ padding-top: var(--site-header-height);
+ margin-top: calc(-1 * var(--site-header-height));
+ outline: none;
+ opacity: 0;
+ transition: opacity var(--anim-ease) var(--anim-time);
+ &::before {
+ content: "insert_link";
+ font-family: "Material Icons Round";
+ color: #333;
+ font-size: 24px;
+ }
+ }
+ *:hover .anchor {
+ opacity: 1;
+ }
+ code {
+ font-family: "Roboto Mono", monospace;
+ font-size: 14px;
+ }
+ table {
+ width: 100%;
+ font-size: 14px;
+ border-spacing: 0;
+ border-collapse: collapse;
+ th,
+ td {
+ padding: 8px;
+ border: 0 solid #dadce0;
+ border-top-width: 1px;
+ border-bottom-width: 1px;
+ }
+ tr {
+ height: 20px;
+ }
+ tr:target {
+ background-color: #ecba2a;
+ }
+ thead {
+ text-align: left;
+ background-color: #e8eaed;
+ color: #202124;
+ }
+ }
+
+ &[data-md-file^="/docs/reference/"] {
+ h1,
+ h2,
+ h3 {
code {
- font-family: 'Roboto Mono', monospace;
- font-size: 14px;
+ margin-left: 20px;
+ color: #666;
}
- table {
- width: 100%;
- font-size: 14px;
- border-spacing: 0;
- border-collapse: collapse;
- th, td {
- padding: 8px;
- border: 0 solid #dadce0;
- border-top-width: 1px;
- border-bottom-width: 1px;
-
- }
- tr {
- height: 20px;
- }
- tr:target {
- background-color: #ecba2a;
- }
- thead {
- text-align: left;
- background-color: #e8eaed;
- color: #202124;
- }
+ }
+ table {
+ width: 100%;
+ font-size: 14px;
+ border-spacing: 0;
+ border-collapse: collapse;
+ th,
+ td {
+ padding: 8px;
+ border: 0 solid #dadce0;
+ border-top-width: 1px;
+ border-bottom-width: 1px;
}
+ tr {
+ height: 20px;
+ }
+ thead {
+ text-align: left;
+ background-color: #e8eaed;
+ color: #202124;
+ }
+ td {
+ &:first-child {
+ background: #f7f7f7;
+ }
- &[data-md-file^="/docs/reference/"] {
- h1, h2, h3 {
- code {
- margin-left: 20px;
- color: #666;
- }
- }
- table {
- width: 100%;
- font-size: 14px;
- border-spacing: 0;
- border-collapse: collapse;
- th, td {
- padding: 8px;
- border: 0 solid #dadce0;
- border-top-width: 1px;
- border-bottom-width: 1px;
-
- }
- tr {
- height: 20px;
- }
- thead {
- text-align: left;
- background-color: #e8eaed;
- color: #202124;
- }
- td {
- &:first-child { background: #f7f7f7; }
-
- /* Not really 100% but makes sure that the description
+ /* Not really 100% but makes sure that the description
* column takes most of the width */
- &:last-child { width: 80%; }
- }
- }
+ &:last-child {
+ width: 80%;
+ }
}
-
- .callout {
- padding: .5rem .5rem .5rem 2rem;
- border: none;
- border-radius: 2px;
- margin-left: auto;
- margin-right: auto;
- width: 90%;
- border-left: 3px solid transparent;
- box-shadow: 0 0.2rem 0.5rem rgba(0,0,0,.05), 0 0 0.05rem rgba(0,0,0,.1);
-
- &:before {
- font-family: 'Material Icons Round';
- position: absolute;
- font-size: 1.5rem;
- margin-left: -1.75rem;
- margin-top: -2px;
- }
-
- &.note {
- background-color: #E8F0FE;
- border-color: #1967D2;
- color: #1967D2;
- &:before { content: 'bookmark'; }
- }
-
- &.summary {
- background-color: #E4F7FB;
- border-color: #129EAF;
- color: #129EAF;
- &:before { content: 'sms'; }
- }
-
- &.tip {
- background-color: #E6F4EA;
- border-color: #188038;
- color: #188038;
- &:before { content: 'star'; }
- }
-
- &.todo {
- background-color: #F1F3F4;
- border-color: #5F6368;
- color: #5F6368;
- &:before { content: 'error'; }
- }
-
- &.warning {
- background-color: #FCE8E6;
- border-color: #C5221F;
- color: #C5221F;
- &:before { content: 'warning'; }
- }
- }
+ }
}
- .toc {
- grid-area: toc;
- padding: 20px 16px 20px 0;
- position: sticky;
- top: var(--site-header-height);
- height: calc(100vh - var(--site-header-height));
- overflow-y: auto;
- @include minimal-scrollbar;
+ .callout {
+ padding: 0.5rem 0.5rem 0.5rem 2rem;
+ border: none;
+ border-radius: 2px;
+ margin-left: auto;
+ margin-right: auto;
+ width: 90%;
+ border-left: 3px solid transparent;
+ box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05),
+ 0 0 0.05rem rgba(0, 0, 0, 0.1);
- font-family: 'Source Sans Pro', sans-serif;
- word-break: break-word;
- a {
- text-decoration: none;
+ &:before {
+ font-family: "Material Icons Round";
+ position: absolute;
+ font-size: 1.5rem;
+ margin-left: -1.75rem;
+ margin-top: -2px;
+ }
+
+ &.note {
+ background-color: #e8f0fe;
+ border-color: #1967d2;
+ color: #1967d2;
+ &:before {
+ content: "bookmark";
}
- a,
- a:visited {
- color: #333;
+ }
+
+ &.summary {
+ background-color: #e4f7fb;
+ border-color: #129eaf;
+ color: #129eaf;
+ &:before {
+ content: "sms";
}
- a.highlighted {
- font-weight: 500;
- color: hsl(45, 100%, 40%);
+ }
+
+ &.tip {
+ background-color: #e6f4ea;
+ border-color: #188038;
+ color: #188038;
+ &:before {
+ content: "star";
}
- font-size: 0.875rem;
- ul {
- list-style: none;
- margin: 0;
- padding: 0;
- li {
- margin: 5px 0;
- /* This make it so that a single word gets elided but if there
+ }
+
+ &.todo {
+ background-color: #f1f3f4;
+ border-color: #5f6368;
+ color: #5f6368;
+ &:before {
+ content: "error";
+ }
+ }
+
+ &.warning {
+ background-color: #fce8e6;
+ border-color: #c5221f;
+ color: #c5221f;
+ &:before {
+ content: "warning";
+ }
+ }
+ }
+ }
+ .toc {
+ grid-area: toc;
+ padding: 20px 16px 20px 0;
+
+ position: sticky;
+ top: var(--site-header-height);
+ height: calc(100vh - var(--site-header-height));
+ overflow-y: auto;
+ @include minimal-scrollbar;
+
+ font-family: "Source Sans Pro", sans-serif;
+ word-break: break-word;
+ a {
+ text-decoration: none;
+ }
+ a,
+ a:visited {
+ color: #333;
+ }
+ a.highlighted {
+ font-weight: 500;
+ color: hsl(45, 100%, 40%);
+ }
+ font-size: 0.875rem;
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ li {
+ margin: 5px 0;
+ /* This make it so that a single word gets elided but if there
* are multiple words they span across lines. */
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: break-spaces;
- word-break: normal;
- }
- }
- >ul {
- border-left: 4px solid #ecba2a;
- padding-left: 10px;
- position: static; // Otherwise gets v-centered in the middle.
- top: calc(var(--site-header-height) + 25px);
- }
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: break-spaces;
+ word-break: normal;
+ }
}
+ > ul {
+ border-left: 4px solid #ecba2a;
+ padding-left: 10px;
+ position: static; // Otherwise gets v-centered in the middle.
+ top: calc(var(--site-header-height) + 25px);
+ }
+ }
- @media #{$wide} {
- grid-template-columns: var(--nav-width) auto 0;
- .toc { display: none; }
+ @media #{$wide} {
+ grid-template-columns: var(--nav-width) auto 0;
+ .toc {
+ display: none;
}
- @media #{$mobile} {
+ }
+ @media #{$mobile} {
+ display: block;
+ .doc {
+ margin: 0;
+ padding: 20px;
+ }
+ .nav {
+ // JS will persistently toggle to .after_first_click. This is to
+ // avoid spurious transitions on page load.
+ display: none;
+
+ --nav-width-mobile: calc(min(90vw, 360px));
+ width: var(--nav-width-mobile);
+ position: fixed;
+ z-index: 2;
+ height: 100vh;
+ overflow-y: auto;
+ top: var(--site-header-height);
+ transition: transform var(--anim-ease) var(--anim-time),
+ box-shadow var(--anim-ease) var(--anim-time),
+ visibility ease var(--anim-time);
+ transform: translateX(calc(-1 * var(--nav-width-mobile)));
+ visibility: hidden;
+ > ul {
+ position: static;
+ top: 0;
+ }
+ &.after_first_click {
display: block;
- .doc {
- margin: 0;
- padding: 20px;
- }
- .nav {
- // JS will persistently toggle to .after_first_click. This is to
- // avoid spurious transitions on page load.
- display: none;
-
- --nav-width-mobile: calc(min(90vw, 360px));
- width: var(--nav-width-mobile);
- position: fixed;
- z-index: 2;
- height: 100vh;
- overflow-y: auto;
- top: var(--site-header-height);
- transition: transform var(--anim-ease) var(--anim-time),
- box-shadow var(--anim-ease) var(--anim-time),
- visibility ease var(--anim-time);
- transform: translateX(calc(-1 * var(--nav-width-mobile)));
- visibility: hidden;
- >ul {
- position: static;
- top: 0;
- }
- &.after_first_click {
- display: block;
- }
- &.expanded {
- visibility: visible;
- transform: translateX(0);
- box-shadow: 0 1px 0 100vw rgba(0,0,0,0.4);
- }
- }
+ }
+ &.expanded {
+ visibility: visible;
+ transform: translateX(0);
+ box-shadow: 0 1px 0 100vw rgba(0, 0, 0, 0.4);
+ }
}
+ }
}
diff --git a/infra/perfetto.dev/src/empty.md b/infra/perfetto.dev/src/empty.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/infra/perfetto.dev/src/empty.md
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index b422b3e..ba88077 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -1982,6 +1982,8 @@
UNWIND_SKIP = 1;
// Use libunwindstack (default):
UNWIND_DWARF = 2;
+ // Use userspace frame pointer unwinder:
+ UNWIND_FRAME_POINTER = 3;
}
}
@@ -2724,276 +2726,13 @@
ATOM_NOTIFICATION_MEMORY_USE = 10174;
ATOM_HDR_CAPABILITIES = 10175;
ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
- ATOM_WS_WEAR_TIME_SESSION = 610;
- ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
- ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
- ATOM_WS_CALL_DURATION_REPORTED = 628;
- ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
- ATOM_WS_CALL_INTERACTION_REPORTED = 630;
- ATOM_WS_ON_BODY_STATE_CHANGED = 787;
- ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
- ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
- ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
- ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
- ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
- ATOM_SETTINGS_SPA_REPORTED = 622;
- ATOM_PDF_LOAD_REPORTED = 859;
- ATOM_PDF_API_USAGE_REPORTED = 860;
- ATOM_PDF_SEARCH_REPORTED = 861;
- ATOM_HDMI_EARC_STATUS_REPORTED = 701;
- ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
- ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
- ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
- ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
- ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
- ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
- ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
- ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
- ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
- ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
- ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
- ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
- ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
- ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
- ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
- ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
- ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
- ATOM_WEAR_POWER_MENU_OPENED = 731;
- ATOM_WEAR_ASSISTANT_OPENED = 755;
- ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
- ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
- ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
- ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
- ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
- ATOM_AUTOFILL_SESSION_COMMITTED = 607;
- ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
- ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
- ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
- ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
- ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
- ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
- ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
- ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
- ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
- ATOM_APF_SESSION_INFO_REPORTED = 777;
- ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
- ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
- ATOM_VPN_CONNECTION_REPORTED = 851;
- ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
- ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
- ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
- ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
- ATOM_ART_DATUM_REPORTED = 332;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
- ATOM_ART_DEX2OAT_REPORTED = 929;
- ATOM_ART_DEVICE_STATUS = 10205;
- ATOM_ODREFRESH_REPORTED = 366;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
- ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
- ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
- ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
- ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
- ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
- ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
- ATOM_EMERGENCY_STATE_CHANGED = 633;
- ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
- ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
- ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
- ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
- ATOM_HEALTH_CONNECT_API_CALLED = 616;
- ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
- ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
- ATOM_HEALTH_CONNECT_API_INVOKED = 643;
- ATOM_EXERCISE_ROUTE_API_CALLED = 654;
- ATOM_SELINUX_AUDIT_LOG = 799;
- ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
- ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
- ATOM_EMERGENCY_NUMBERS_INFO = 10180;
- ATOM_DATA_NETWORK_VALIDATION = 10207;
- ATOM_DATA_RAT_STATE_CHANGED = 854;
- ATOM_CONNECTED_CHANNEL_CHANGED = 882;
- ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
- ATOM_QNS_IMS_CALL_DROP_STATS = 635;
- ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
- ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
- ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
- ATOM_QNS_HANDOVER_PINGPONG = 10179;
- ATOM_SATELLITE_CONTROLLER = 10182;
- ATOM_SATELLITE_SESSION = 10183;
- ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
- ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
- ATOM_SATELLITE_PROVISION = 10186;
- ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
- ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
- ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
- ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
- ATOM_SATELLITE_ENTITLEMENT = 10214;
- ATOM_SATELLITE_CONFIG_UPDATER = 10215;
- ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
- ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
- ATOM_KEYBOARD_CONFIGURED = 682;
- ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
- ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
- ATOM_TOUCHPAD_USAGE = 10191;
- ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
- ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
- ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
- ATOM_CRONET_ENGINE_CREATED = 703;
- ATOM_CRONET_TRAFFIC_REPORTED = 704;
- ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
- ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
- ATOM_CRONET_INITIALIZED = 764;
- ATOM_WEAR_MODE_STATE_CHANGED = 715;
- ATOM_RENDERER_INITIALIZED = 736;
- ATOM_SCHEMA_VERSION_RECEIVED = 737;
- ATOM_LAYOUT_INSPECTED = 741;
- ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
- ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
- ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
- ATOM_TILE_REQUESTED = 745;
- ATOM_STATE_RESPONSE_RECEIVED = 746;
- ATOM_TILE_RESPONSE_RECEIVED = 747;
- ATOM_INFLATION_FINISHED = 748;
- ATOM_INFLATION_FAILED = 749;
- ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
- ATOM_DRAWABLE_RENDERED = 751;
- ATOM_MEDIA_ACTION_REPORTED = 608;
- ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
- ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
- ATOM_MEDIATOR_UPDATED = 721;
- ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
- ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
ATOM_ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED = 820;
- ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
- ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
- ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
- ATOM_RKPD_POOL_STATS = 664;
- ATOM_RKPD_CLIENT_OPERATION = 665;
- ATOM_CPU_POLICY = 10199;
- ATOM_ATOM_9999 = 9999;
- ATOM_ATOM_99999 = 99999;
- ATOM_SCREEN_OFF_REPORTED = 776;
- ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
- ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
- ATOM_SCREEN_DIM_REPORTED = 867;
- ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
- ATOM_BAL_ALLOWED = 632;
- ATOM_IN_TASK_ACTIVITY_STARTED = 685;
- ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
- ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
- ATOM_USER_RISK_EVENT_REPORTED = 725;
- ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
- ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
- ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
- ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
- ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
- ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
- ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
- ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
- ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
- ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
- ATOM_DREAM_SETTING_CHANGED = 705;
- ATOM_DREAM_SETTING_SNAPSHOT = 10192;
- ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
- ATOM_WIFI_AWARE_NDP_REPORTED = 638;
- ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
- ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
- ATOM_SOFT_AP_STARTED = 680;
- ATOM_SOFT_AP_STOPPED = 681;
- ATOM_WIFI_LOCK_RELEASED = 687;
- ATOM_WIFI_LOCK_DEACTIVATED = 688;
- ATOM_WIFI_CONFIG_SAVED = 689;
- ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
- ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
- ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
- ATOM_WIFI_STATE_CHANGED = 700;
- ATOM_PNO_SCAN_STARTED = 719;
- ATOM_PNO_SCAN_STOPPED = 720;
- ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
- ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
- ATOM_SOFT_AP_STATE_CHANGED = 805;
- ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
- ATOM_WIFI_AWARE_CAPABILITIES = 10190;
- ATOM_WIFI_MODULE_INFO = 10193;
- ATOM_WIFI_SETTING_INFO = 10194;
- ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
- ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
- ATOM_MTE_STATE = 10181;
- ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
- ATOM_SANDBOX_API_CALLED = 488;
- ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
- ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
- ATOM_SANDBOX_SDK_STORAGE = 10159;
- ATOM_EXPRESS_EVENT_REPORTED = 528;
- ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
- ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
- ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
- ATOM_IKE_SESSION_TERMINATED = 678;
- ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
- ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
- ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
- ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
- ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
- ATOM_DEVICE_POLICY_STATE = 10217;
- ATOM_DESKTOP_MODE_UI_CHANGED = 818;
- ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
- ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
- ATOM_MEDIA_CODEC_STARTED = 641;
- ATOM_MEDIA_CODEC_STOPPED = 642;
- ATOM_MEDIA_CODEC_RENDERED = 684;
- ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
- ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
- ATOM_PLUGIN_INITIALIZED = 655;
- ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
- ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
- ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
ATOM_THERMAL_STATUS_CALLED = 772;
ATOM_THERMAL_HEADROOM_CALLED = 773;
ATOM_THERMAL_HEADROOM_THRESHOLDS_CALLED = 774;
ATOM_ADPF_HINT_SESSION_TID_CLEANUP = 839;
ATOM_THERMAL_HEADROOM_THRESHOLDS = 10201;
ATOM_ADPF_SESSION_SNAPSHOT = 10218;
- ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
- ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
- ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
- ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
- ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
- ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
- ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
- ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
- ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
- ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
- ATOM_LE_RADIO_SCAN_STOPPED = 871;
- ATOM_LE_SCAN_RESULT_RECEIVED = 872;
- ATOM_LE_SCAN_ABUSED = 873;
- ATOM_LE_ADV_STATE_CHANGED = 874;
- ATOM_LE_ADV_ERROR_REPORTED = 875;
- ATOM_A2DP_SESSION_REPORTED = 904;
- ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
- ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
- ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
- ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
- ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
- ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
- ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
- ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
- ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
- ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
- ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
- ATOM_EMERGENCY_NUMBER_DIALED = 637;
ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
ATOM_AD_SERVICES_API_CALLED = 435;
ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
@@ -3061,27 +2800,68 @@
ATOM_SELECT_ADS_FROM_OUTCOMES_API_CALLED = 876;
ATOM_REPORT_IMPRESSION_API_CALLED = 877;
ATOM_AD_SERVICES_ENROLLMENT_TRANSACTION_STATS = 885;
- ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
- ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
- ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
- ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
- ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
- ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
- ATOM_NFC_FIELD_CHANGED = 856;
- ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
- ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
- ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
- ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
- ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
- ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
- ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
- ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
- ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
+ ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
+ ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
+ ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
+ ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
ATOM_APEX_INSTALLATION_REQUESTED = 732;
ATOM_APEX_INSTALLATION_STAGED = 733;
ATOM_APEX_INSTALLATION_ENDED = 734;
+ ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_ART_DEX2OAT_REPORTED = 929;
+ ATOM_ART_DEVICE_STATUS = 10205;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
+ ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
+ ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
+ ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
+ ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
+ ATOM_LE_RADIO_SCAN_STOPPED = 871;
+ ATOM_LE_SCAN_RESULT_RECEIVED = 872;
+ ATOM_LE_SCAN_ABUSED = 873;
+ ATOM_LE_ADV_STATE_CHANGED = 874;
+ ATOM_LE_ADV_ERROR_REPORTED = 875;
+ ATOM_A2DP_SESSION_REPORTED = 904;
+ ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
+ ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
+ ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_REPORTED_AT_CLOSE = 982;
+ ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
+ ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
+ ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
+ ATOM_CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED = 979;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
+ ATOM_VPN_CONNECTION_REPORTED = 851;
+ ATOM_CPU_POLICY = 10199;
ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
@@ -3091,8 +2871,232 @@
ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
- ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DESKTOP_MODE_UI_CHANGED = 818;
+ ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
+ ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
+ ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
+ ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
+ ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
+ ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
+ ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
+ ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
+ ATOM_DEVICE_POLICY_STATE = 10217;
+ ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
+ ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
ATOM_DND_STATE_CHANGED = 657;
+ ATOM_DREAM_SETTING_CHANGED = 705;
+ ATOM_DREAM_SETTING_SNAPSHOT = 10192;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
+ ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
+ ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
+ ATOM_USER_RISK_EVENT_REPORTED = 725;
+ ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
+ ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
+ ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
+ ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
+ ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
+ ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
+ ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
+ ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
+ ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
+ ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
+ ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
+ ATOM_HDMI_EARC_STATUS_REPORTED = 701;
+ ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
+ ATOM_KEYBOARD_CONFIGURED = 682;
+ ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
+ ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
+ ATOM_TOUCHPAD_USAGE = 10191;
+ ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
+ ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
+ ATOM_MTE_STATE = 10181;
+ ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
+ ATOM_NFC_FIELD_CHANGED = 856;
+ ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
+ ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
+ ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
+ ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
+ ATOM_PDF_LOAD_REPORTED = 859;
+ ATOM_PDF_API_USAGE_REPORTED = 860;
+ ATOM_PDF_SEARCH_REPORTED = 861;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
+ ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
+ ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
+ ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
+ ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
+ ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
+ ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
+ ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
+ ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
+ ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
+ ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
+ ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
+ ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
+ ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
+ ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
+ ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_SCREEN_OFF_REPORTED = 776;
+ ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
+ ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
+ ATOM_SCREEN_DIM_REPORTED = 867;
+ ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
+ ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_SELINUX_AUDIT_LOG = 799;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
+ ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
+ ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
+ ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_DATA_NETWORK_VALIDATION = 10207;
+ ATOM_DATA_RAT_STATE_CHANGED = 854;
+ ATOM_CONNECTED_CHANNEL_CHANGED = 882;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
+ ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
+ ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
+ ATOM_SATELLITE_ENTITLEMENT = 10214;
+ ATOM_SATELLITE_CONFIG_UPDATER = 10215;
+ ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
+ ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIATOR_UPDATED = 721;
+ ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
+ ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
+ ATOM_WEAR_MODE_STATE_CHANGED = 715;
+ ATOM_RENDERER_INITIALIZED = 736;
+ ATOM_SCHEMA_VERSION_RECEIVED = 737;
+ ATOM_LAYOUT_INSPECTED = 741;
+ ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
+ ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
+ ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
+ ATOM_TILE_REQUESTED = 745;
+ ATOM_STATE_RESPONSE_RECEIVED = 746;
+ ATOM_TILE_RESPONSE_RECEIVED = 747;
+ ATOM_INFLATION_FINISHED = 748;
+ ATOM_INFLATION_FAILED = 749;
+ ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
+ ATOM_DRAWABLE_RENDERED = 751;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_WS_ON_BODY_STATE_CHANGED = 787;
+ ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
+ ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
+ ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
+ ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
+ ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
+ ATOM_WEAR_POWER_MENU_OPENED = 731;
+ ATOM_WEAR_ASSISTANT_OPENED = 755;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_PNO_SCAN_STARTED = 719;
+ ATOM_PNO_SCAN_STOPPED = 720;
+ ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
+ ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
+ ATOM_SOFT_AP_STATE_CHANGED = 805;
+ ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_WIFI_SETTING_INFO = 10194;
+ ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
+ ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
}
// End of protos/perfetto/config/statsd/atom_ids.proto
diff --git a/protos/perfetto/config/profiling/perf_event_config.proto b/protos/perfetto/config/profiling/perf_event_config.proto
index b02aa07..d3cc51c 100644
--- a/protos/perfetto/config/profiling/perf_event_config.proto
+++ b/protos/perfetto/config/profiling/perf_event_config.proto
@@ -217,5 +217,7 @@
UNWIND_SKIP = 1;
// Use libunwindstack (default):
UNWIND_DWARF = 2;
+ // Use userspace frame pointer unwinder:
+ UNWIND_FRAME_POINTER = 3;
}
}
diff --git a/protos/perfetto/config/statsd/atom_ids.proto b/protos/perfetto/config/statsd/atom_ids.proto
index 0d0af24..c4da538 100644
--- a/protos/perfetto/config/statsd/atom_ids.proto
+++ b/protos/perfetto/config/statsd/atom_ids.proto
@@ -752,276 +752,13 @@
ATOM_NOTIFICATION_MEMORY_USE = 10174;
ATOM_HDR_CAPABILITIES = 10175;
ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
- ATOM_WS_WEAR_TIME_SESSION = 610;
- ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
- ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
- ATOM_WS_CALL_DURATION_REPORTED = 628;
- ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
- ATOM_WS_CALL_INTERACTION_REPORTED = 630;
- ATOM_WS_ON_BODY_STATE_CHANGED = 787;
- ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
- ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
- ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
- ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
- ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
- ATOM_SETTINGS_SPA_REPORTED = 622;
- ATOM_PDF_LOAD_REPORTED = 859;
- ATOM_PDF_API_USAGE_REPORTED = 860;
- ATOM_PDF_SEARCH_REPORTED = 861;
- ATOM_HDMI_EARC_STATUS_REPORTED = 701;
- ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
- ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
- ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
- ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
- ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
- ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
- ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
- ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
- ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
- ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
- ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
- ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
- ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
- ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
- ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
- ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
- ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
- ATOM_WEAR_POWER_MENU_OPENED = 731;
- ATOM_WEAR_ASSISTANT_OPENED = 755;
- ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
- ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
- ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
- ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
- ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
- ATOM_AUTOFILL_SESSION_COMMITTED = 607;
- ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
- ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
- ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
- ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
- ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
- ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
- ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
- ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
- ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
- ATOM_APF_SESSION_INFO_REPORTED = 777;
- ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
- ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
- ATOM_VPN_CONNECTION_REPORTED = 851;
- ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
- ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
- ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
- ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
- ATOM_ART_DATUM_REPORTED = 332;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
- ATOM_ART_DEX2OAT_REPORTED = 929;
- ATOM_ART_DEVICE_STATUS = 10205;
- ATOM_ODREFRESH_REPORTED = 366;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
- ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
- ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
- ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
- ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
- ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
- ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
- ATOM_EMERGENCY_STATE_CHANGED = 633;
- ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
- ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
- ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
- ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
- ATOM_HEALTH_CONNECT_API_CALLED = 616;
- ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
- ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
- ATOM_HEALTH_CONNECT_API_INVOKED = 643;
- ATOM_EXERCISE_ROUTE_API_CALLED = 654;
- ATOM_SELINUX_AUDIT_LOG = 799;
- ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
- ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
- ATOM_EMERGENCY_NUMBERS_INFO = 10180;
- ATOM_DATA_NETWORK_VALIDATION = 10207;
- ATOM_DATA_RAT_STATE_CHANGED = 854;
- ATOM_CONNECTED_CHANNEL_CHANGED = 882;
- ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
- ATOM_QNS_IMS_CALL_DROP_STATS = 635;
- ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
- ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
- ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
- ATOM_QNS_HANDOVER_PINGPONG = 10179;
- ATOM_SATELLITE_CONTROLLER = 10182;
- ATOM_SATELLITE_SESSION = 10183;
- ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
- ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
- ATOM_SATELLITE_PROVISION = 10186;
- ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
- ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
- ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
- ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
- ATOM_SATELLITE_ENTITLEMENT = 10214;
- ATOM_SATELLITE_CONFIG_UPDATER = 10215;
- ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
- ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
- ATOM_KEYBOARD_CONFIGURED = 682;
- ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
- ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
- ATOM_TOUCHPAD_USAGE = 10191;
- ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
- ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
- ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
- ATOM_CRONET_ENGINE_CREATED = 703;
- ATOM_CRONET_TRAFFIC_REPORTED = 704;
- ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
- ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
- ATOM_CRONET_INITIALIZED = 764;
- ATOM_WEAR_MODE_STATE_CHANGED = 715;
- ATOM_RENDERER_INITIALIZED = 736;
- ATOM_SCHEMA_VERSION_RECEIVED = 737;
- ATOM_LAYOUT_INSPECTED = 741;
- ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
- ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
- ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
- ATOM_TILE_REQUESTED = 745;
- ATOM_STATE_RESPONSE_RECEIVED = 746;
- ATOM_TILE_RESPONSE_RECEIVED = 747;
- ATOM_INFLATION_FINISHED = 748;
- ATOM_INFLATION_FAILED = 749;
- ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
- ATOM_DRAWABLE_RENDERED = 751;
- ATOM_MEDIA_ACTION_REPORTED = 608;
- ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
- ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
- ATOM_MEDIATOR_UPDATED = 721;
- ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
- ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
ATOM_ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED = 820;
- ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
- ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
- ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
- ATOM_RKPD_POOL_STATS = 664;
- ATOM_RKPD_CLIENT_OPERATION = 665;
- ATOM_CPU_POLICY = 10199;
- ATOM_ATOM_9999 = 9999;
- ATOM_ATOM_99999 = 99999;
- ATOM_SCREEN_OFF_REPORTED = 776;
- ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
- ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
- ATOM_SCREEN_DIM_REPORTED = 867;
- ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
- ATOM_BAL_ALLOWED = 632;
- ATOM_IN_TASK_ACTIVITY_STARTED = 685;
- ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
- ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
- ATOM_USER_RISK_EVENT_REPORTED = 725;
- ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
- ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
- ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
- ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
- ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
- ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
- ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
- ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
- ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
- ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
- ATOM_DREAM_SETTING_CHANGED = 705;
- ATOM_DREAM_SETTING_SNAPSHOT = 10192;
- ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
- ATOM_WIFI_AWARE_NDP_REPORTED = 638;
- ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
- ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
- ATOM_SOFT_AP_STARTED = 680;
- ATOM_SOFT_AP_STOPPED = 681;
- ATOM_WIFI_LOCK_RELEASED = 687;
- ATOM_WIFI_LOCK_DEACTIVATED = 688;
- ATOM_WIFI_CONFIG_SAVED = 689;
- ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
- ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
- ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
- ATOM_WIFI_STATE_CHANGED = 700;
- ATOM_PNO_SCAN_STARTED = 719;
- ATOM_PNO_SCAN_STOPPED = 720;
- ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
- ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
- ATOM_SOFT_AP_STATE_CHANGED = 805;
- ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
- ATOM_WIFI_AWARE_CAPABILITIES = 10190;
- ATOM_WIFI_MODULE_INFO = 10193;
- ATOM_WIFI_SETTING_INFO = 10194;
- ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
- ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
- ATOM_MTE_STATE = 10181;
- ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
- ATOM_SANDBOX_API_CALLED = 488;
- ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
- ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
- ATOM_SANDBOX_SDK_STORAGE = 10159;
- ATOM_EXPRESS_EVENT_REPORTED = 528;
- ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
- ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
- ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
- ATOM_IKE_SESSION_TERMINATED = 678;
- ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
- ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
- ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
- ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
- ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
- ATOM_DEVICE_POLICY_STATE = 10217;
- ATOM_DESKTOP_MODE_UI_CHANGED = 818;
- ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
- ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
- ATOM_MEDIA_CODEC_STARTED = 641;
- ATOM_MEDIA_CODEC_STOPPED = 642;
- ATOM_MEDIA_CODEC_RENDERED = 684;
- ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
- ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
- ATOM_PLUGIN_INITIALIZED = 655;
- ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
- ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
- ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
ATOM_THERMAL_STATUS_CALLED = 772;
ATOM_THERMAL_HEADROOM_CALLED = 773;
ATOM_THERMAL_HEADROOM_THRESHOLDS_CALLED = 774;
ATOM_ADPF_HINT_SESSION_TID_CLEANUP = 839;
ATOM_THERMAL_HEADROOM_THRESHOLDS = 10201;
ATOM_ADPF_SESSION_SNAPSHOT = 10218;
- ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
- ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
- ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
- ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
- ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
- ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
- ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
- ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
- ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
- ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
- ATOM_LE_RADIO_SCAN_STOPPED = 871;
- ATOM_LE_SCAN_RESULT_RECEIVED = 872;
- ATOM_LE_SCAN_ABUSED = 873;
- ATOM_LE_ADV_STATE_CHANGED = 874;
- ATOM_LE_ADV_ERROR_REPORTED = 875;
- ATOM_A2DP_SESSION_REPORTED = 904;
- ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
- ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
- ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
- ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
- ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
- ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
- ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
- ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
- ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
- ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
- ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
- ATOM_EMERGENCY_NUMBER_DIALED = 637;
ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
ATOM_AD_SERVICES_API_CALLED = 435;
ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
@@ -1089,27 +826,68 @@
ATOM_SELECT_ADS_FROM_OUTCOMES_API_CALLED = 876;
ATOM_REPORT_IMPRESSION_API_CALLED = 877;
ATOM_AD_SERVICES_ENROLLMENT_TRANSACTION_STATS = 885;
- ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
- ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
- ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
- ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
- ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
- ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
- ATOM_NFC_FIELD_CHANGED = 856;
- ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
- ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
- ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
- ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
- ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
- ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
- ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
- ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
- ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
+ ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
+ ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
+ ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
+ ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
ATOM_APEX_INSTALLATION_REQUESTED = 732;
ATOM_APEX_INSTALLATION_STAGED = 733;
ATOM_APEX_INSTALLATION_ENDED = 734;
+ ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_ART_DEX2OAT_REPORTED = 929;
+ ATOM_ART_DEVICE_STATUS = 10205;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
+ ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
+ ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
+ ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
+ ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
+ ATOM_LE_RADIO_SCAN_STOPPED = 871;
+ ATOM_LE_SCAN_RESULT_RECEIVED = 872;
+ ATOM_LE_SCAN_ABUSED = 873;
+ ATOM_LE_ADV_STATE_CHANGED = 874;
+ ATOM_LE_ADV_ERROR_REPORTED = 875;
+ ATOM_A2DP_SESSION_REPORTED = 904;
+ ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
+ ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
+ ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_REPORTED_AT_CLOSE = 982;
+ ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
+ ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
+ ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
+ ATOM_CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED = 979;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
+ ATOM_VPN_CONNECTION_REPORTED = 851;
+ ATOM_CPU_POLICY = 10199;
ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
@@ -1119,6 +897,230 @@
ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
- ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DESKTOP_MODE_UI_CHANGED = 818;
+ ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
+ ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
+ ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
+ ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
+ ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
+ ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
+ ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
+ ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
+ ATOM_DEVICE_POLICY_STATE = 10217;
+ ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
+ ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
ATOM_DND_STATE_CHANGED = 657;
+ ATOM_DREAM_SETTING_CHANGED = 705;
+ ATOM_DREAM_SETTING_SNAPSHOT = 10192;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
+ ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
+ ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
+ ATOM_USER_RISK_EVENT_REPORTED = 725;
+ ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
+ ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
+ ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
+ ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
+ ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
+ ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
+ ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
+ ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
+ ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
+ ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
+ ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
+ ATOM_HDMI_EARC_STATUS_REPORTED = 701;
+ ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
+ ATOM_KEYBOARD_CONFIGURED = 682;
+ ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
+ ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
+ ATOM_TOUCHPAD_USAGE = 10191;
+ ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
+ ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
+ ATOM_MTE_STATE = 10181;
+ ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
+ ATOM_NFC_FIELD_CHANGED = 856;
+ ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
+ ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
+ ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
+ ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
+ ATOM_PDF_LOAD_REPORTED = 859;
+ ATOM_PDF_API_USAGE_REPORTED = 860;
+ ATOM_PDF_SEARCH_REPORTED = 861;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
+ ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
+ ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
+ ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
+ ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
+ ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
+ ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
+ ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
+ ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
+ ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
+ ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
+ ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
+ ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
+ ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
+ ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
+ ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_SCREEN_OFF_REPORTED = 776;
+ ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
+ ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
+ ATOM_SCREEN_DIM_REPORTED = 867;
+ ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
+ ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_SELINUX_AUDIT_LOG = 799;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
+ ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
+ ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
+ ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_DATA_NETWORK_VALIDATION = 10207;
+ ATOM_DATA_RAT_STATE_CHANGED = 854;
+ ATOM_CONNECTED_CHANNEL_CHANGED = 882;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
+ ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
+ ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
+ ATOM_SATELLITE_ENTITLEMENT = 10214;
+ ATOM_SATELLITE_CONFIG_UPDATER = 10215;
+ ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
+ ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIATOR_UPDATED = 721;
+ ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
+ ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
+ ATOM_WEAR_MODE_STATE_CHANGED = 715;
+ ATOM_RENDERER_INITIALIZED = 736;
+ ATOM_SCHEMA_VERSION_RECEIVED = 737;
+ ATOM_LAYOUT_INSPECTED = 741;
+ ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
+ ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
+ ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
+ ATOM_TILE_REQUESTED = 745;
+ ATOM_STATE_RESPONSE_RECEIVED = 746;
+ ATOM_TILE_RESPONSE_RECEIVED = 747;
+ ATOM_INFLATION_FINISHED = 748;
+ ATOM_INFLATION_FAILED = 749;
+ ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
+ ATOM_DRAWABLE_RENDERED = 751;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_WS_ON_BODY_STATE_CHANGED = 787;
+ ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
+ ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
+ ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
+ ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
+ ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
+ ATOM_WEAR_POWER_MENU_OPENED = 731;
+ ATOM_WEAR_ASSISTANT_OPENED = 755;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_PNO_SCAN_STARTED = 719;
+ ATOM_PNO_SCAN_STOPPED = 720;
+ ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
+ ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
+ ATOM_SOFT_AP_STATE_CHANGED = 805;
+ ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_WIFI_SETTING_INFO = 10194;
+ ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
+ ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
}
\ No newline at end of file
diff --git a/protos/perfetto/metrics/android/batt_metric.proto b/protos/perfetto/metrics/android/batt_metric.proto
index a06a2d4..792dc0d 100644
--- a/protos/perfetto/metrics/android/batt_metric.proto
+++ b/protos/perfetto/metrics/android/batt_metric.proto
@@ -44,6 +44,8 @@
optional int64 sleep_screen_doze_ns = 8;
// Average power over the duration of the trace.
optional double avg_power_mw = 9;
+ // Energy usage estimate in joules.
+ optional double energy_usage_estimate = 10;
}
// Period of time during the trace that the device went to sleep completely.
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index 1de0b47..86c206c 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -303,8 +303,8 @@
// sorted by the duration in descending order.
// By checking out the top slices/threads, developers can identify specific
// slices or threads for further investigation.
- repeated TraceSliceSection trace_slice_sections = 7;
- repeated TraceThreadSection trace_thread_sections = 8;
+ optional TraceSliceSectionInfo trace_slice_sections = 7;
+ optional TraceThreadSectionInfo trace_thread_sections = 8;
// Details specific for a reason.
optional string additional_info = 9;
@@ -355,6 +355,13 @@
optional uint32 thread_tid = 6;
}
+ // Information for the SliceSections
+ message TraceSliceSectionInfo {
+ repeated TraceSliceSection slice_section = 1;
+ optional int64 start_timestamp = 2;
+ optional int64 end_timestamp = 3;
+ }
+
// Contains information for a section of a thread.
message TraceThreadSection {
optional int64 start_timestamp = 1;
@@ -371,6 +378,13 @@
optional uint32 thread_tid = 6;
}
+ // Information for the ThreadSections
+ message TraceThreadSectionInfo {
+ repeated TraceThreadSection thread_section = 1;
+ optional int64 start_timestamp = 2;
+ optional int64 end_timestamp = 3;
+ }
+
// Next id: 26
message Startup {
// Random id uniquely identifying an app startup in this trace.
diff --git a/protos/perfetto/metrics/android/wattson_tasks_attribution.proto b/protos/perfetto/metrics/android/wattson_tasks_attribution.proto
index 23bf5d0..d3b91f4 100644
--- a/protos/perfetto/metrics/android/wattson_tasks_attribution.proto
+++ b/protos/perfetto/metrics/android/wattson_tasks_attribution.proto
@@ -26,7 +26,13 @@
// different power model versions.
optional int32 power_model_version = 2;
// Lists tasks (e.g. threads, process, package) and associated estimates
- repeated AndroidWattsonTaskInfo task_info = 3;
+ repeated AndroidWattsonTaskPeriodInfo period_info = 3;
+}
+
+// Groups of power per task for each period
+message AndroidWattsonTaskPeriodInfo {
+ optional int32 period_id = 1;
+ repeated AndroidWattsonTaskInfo task_info = 2;
}
message AndroidWattsonTaskInfo {
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index bf47885..c0b0c36 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -27,6 +27,7 @@
"dropped_frames.proto",
"frame_times.proto",
"histogram_hashes.proto",
+ "histogram_summaries.proto",
"long_latency.proto",
"media_metric.proto",
"performance_mark_hashes.proto",
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index 596bc7f..ebc0ac5 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -23,6 +23,7 @@
import "protos/perfetto/metrics/chrome/dropped_frames.proto";
import "protos/perfetto/metrics/chrome/frame_times.proto";
import "protos/perfetto/metrics/chrome/histogram_hashes.proto";
+import "protos/perfetto/metrics/chrome/histogram_summaries.proto";
import "protos/perfetto/metrics/chrome/long_latency.proto";
import "protos/perfetto/metrics/chrome/media_metric.proto";
import "protos/perfetto/metrics/chrome/performance_mark_hashes.proto";
@@ -53,4 +54,5 @@
optional ChromeUnsymbolizedArgs chrome_unsymbolized_args = 1014;
optional ChromeArgsClassNames chrome_args_class_names = 1015;
optional ChromeScrollJankV3 chrome_scroll_jank_v3 = 1017;
+ optional ChromeHistogramSummaries chrome_histogram_summaries = 1018;
}
diff --git a/protos/perfetto/metrics/chrome/histogram_summaries.proto b/protos/perfetto/metrics/chrome/histogram_summaries.proto
new file mode 100644
index 0000000..57dad94
--- /dev/null
+++ b/protos/perfetto/metrics/chrome/histogram_summaries.proto
@@ -0,0 +1,43 @@
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message HistogramSummary {
+ // The name of the histogram event
+ optional string name = 1;
+ // The avarage value of the histogram event
+ optional int64 mean = 2;
+ // The number of the histogram event in the trace track
+ optional uint32 count = 3;
+ // The sum of value of the histogram event
+ optional int64 sum = 4;
+ // The maximum value of the histogram event
+ optional int64 max = 5;
+ // The 90 percentile value of the histogram event
+ optional int64 p90 = 6;
+ // The 50 percentile (median) value of the histogram event
+ optional int64 p50 = 7;
+}
+
+// The list of the summary of Chrome Histograms in trace track events.
+// This includes the statistic information of each histograms from Chrome.
+message ChromeHistogramSummaries {
+ repeated HistogramSummary histogram_summary = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index 47da583..9ab7eda 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -78,11 +78,13 @@
import "protos/perfetto/metrics/android/wattson_tasks_attribution.proto";
// Trace processor metadata
+// Next id: 17
message TraceMetadata {
reserved 1;
optional int64 trace_duration_ns = 2;
optional string trace_uuid = 3;
optional string android_build_fingerprint = 4;
+ optional string android_device_manufacturer = 16;
optional int64 statsd_triggering_subscription_id = 5;
optional int64 trace_size_bytes = 6;
repeated string trace_trigger = 7;
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 8267654..e2fc3a3 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -616,6 +616,8 @@
optional int64 sleep_screen_doze_ns = 8;
// Average power over the duration of the trace.
optional double avg_power_mw = 9;
+ // Energy usage estimate in joules.
+ optional double energy_usage_estimate = 10;
}
// Period of time during the trace that the device went to sleep completely.
@@ -2539,8 +2541,8 @@
// sorted by the duration in descending order.
// By checking out the top slices/threads, developers can identify specific
// slices or threads for further investigation.
- repeated TraceSliceSection trace_slice_sections = 7;
- repeated TraceThreadSection trace_thread_sections = 8;
+ optional TraceSliceSectionInfo trace_slice_sections = 7;
+ optional TraceThreadSectionInfo trace_thread_sections = 8;
// Details specific for a reason.
optional string additional_info = 9;
@@ -2591,6 +2593,13 @@
optional uint32 thread_tid = 6;
}
+ // Information for the SliceSections
+ message TraceSliceSectionInfo {
+ repeated TraceSliceSection slice_section = 1;
+ optional int64 start_timestamp = 2;
+ optional int64 end_timestamp = 3;
+ }
+
// Contains information for a section of a thread.
message TraceThreadSection {
optional int64 start_timestamp = 1;
@@ -2607,6 +2616,13 @@
optional uint32 thread_tid = 6;
}
+ // Information for the ThreadSections
+ message TraceThreadSectionInfo {
+ repeated TraceThreadSection thread_section = 1;
+ optional int64 start_timestamp = 2;
+ optional int64 end_timestamp = 3;
+ }
+
// Next id: 26
message Startup {
// Random id uniquely identifying an app startup in this trace.
@@ -2979,7 +2995,13 @@
// different power model versions.
optional int32 power_model_version = 2;
// Lists tasks (e.g. threads, process, package) and associated estimates
- repeated AndroidWattsonTaskInfo task_info = 3;
+ repeated AndroidWattsonTaskPeriodInfo period_info = 3;
+}
+
+// Groups of power per task for each period
+message AndroidWattsonTaskPeriodInfo {
+ optional int32 period_id = 1;
+ repeated AndroidWattsonTaskInfo task_info = 2;
}
message AndroidWattsonTaskInfo {
@@ -3001,11 +3023,13 @@
// Begin of protos/perfetto/metrics/metrics.proto
// Trace processor metadata
+// Next id: 17
message TraceMetadata {
reserved 1;
optional int64 trace_duration_ns = 2;
optional string trace_uuid = 3;
optional string android_build_fingerprint = 4;
+ optional string android_device_manufacturer = 16;
optional int64 statsd_triggering_subscription_id = 5;
optional int64 trace_size_bytes = 6;
repeated string trace_trigger = 7;
diff --git a/protos/perfetto/trace/android/server/windowmanagerservice.proto b/protos/perfetto/trace/android/server/windowmanagerservice.proto
index dcb4583..2c22522 100644
--- a/protos/perfetto/trace/android/server/windowmanagerservice.proto
+++ b/protos/perfetto/trace/android/server/windowmanagerservice.proto
@@ -454,6 +454,7 @@
repeated RectProto unrestricted_keep_clear_areas = 46;
repeated InsetsSourceProto mergedLocalInsetsSources = 47;
optional int32 requested_visible_types = 48;
+ optional RectProto dim_bounds = 49;
}
message IdentifierProto {
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index 59148eb..8565125 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -28,6 +28,7 @@
"clk.proto",
"cma.proto",
"compaction.proto",
+ "cpm_trace.proto",
"cpuhp.proto",
"cros_ec.proto",
"dcvsh.proto",
@@ -41,6 +42,7 @@
"fastrpc.proto",
"fence.proto",
"filemap.proto",
+ "fs.proto",
"ftrace.proto",
"g2d.proto",
"google_icc_trace.proto",
diff --git a/protos/perfetto/trace/ftrace/cpm_trace.proto b/protos/perfetto/trace/ftrace/cpm_trace.proto
new file mode 100644
index 0000000..f19f0f8
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/cpm_trace.proto
@@ -0,0 +1,12 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message ParamSetValueCpmFtraceEvent {
+ optional string body = 1;
+ optional uint32 value = 2;
+ optional int64 timestamp = 3;
+}
diff --git a/protos/perfetto/trace/ftrace/fs.proto b/protos/perfetto/trace/ftrace/fs.proto
new file mode 100644
index 0000000..4cc3531
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/fs.proto
@@ -0,0 +1,15 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message DoSysOpenFtraceEvent {
+ optional string filename = 1;
+ optional int32 flags = 2;
+ optional int32 mode = 3;
+}
+message OpenExecFtraceEvent {
+ optional string filename = 1;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index 7858418..40e7113 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -28,6 +28,7 @@
import "protos/perfetto/trace/ftrace/clk.proto";
import "protos/perfetto/trace/ftrace/cma.proto";
import "protos/perfetto/trace/ftrace/compaction.proto";
+import "protos/perfetto/trace/ftrace/cpm_trace.proto";
import "protos/perfetto/trace/ftrace/cpuhp.proto";
import "protos/perfetto/trace/ftrace/cros_ec.proto";
import "protos/perfetto/trace/ftrace/dcvsh.proto";
@@ -41,6 +42,7 @@
import "protos/perfetto/trace/ftrace/fastrpc.proto";
import "protos/perfetto/trace/ftrace/fence.proto";
import "protos/perfetto/trace/ftrace/filemap.proto";
+import "protos/perfetto/trace/ftrace/fs.proto";
import "protos/perfetto/trace/ftrace/ftrace.proto";
import "protos/perfetto/trace/ftrace/g2d.proto";
import "protos/perfetto/trace/ftrace/google_icc_trace.proto";
@@ -681,5 +683,8 @@
SchedWakeupTaskAttrFtraceEvent sched_wakeup_task_attr = 540;
DevfreqFrequencyFtraceEvent devfreq_frequency = 541;
KprobeEvent kprobe_event = 542;
+ ParamSetValueCpmFtraceEvent param_set_value_cpm = 543;
+ DoSysOpenFtraceEvent do_sys_open = 544;
+ OpenExecFtraceEvent open_exec = 545;
}
}
diff --git a/protos/perfetto/trace/ftrace/ftrace_stats.proto b/protos/perfetto/trace/ftrace/ftrace_stats.proto
index f921779..dd531d4 100644
--- a/protos/perfetto/trace/ftrace/ftrace_stats.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -61,6 +61,17 @@
optional uint64 read_events = 9;
}
+// Kprobe statistical data, gathered from /sys/kernel/tracing/kprobe_profile.
+message FtraceKprobeStats {
+ // Cumulative number of kprobe events generated for this function
+ optional int64 hits = 1;
+ // Cumulative number of kprobe events that could not be generated for this
+ // function and were missed. This happens when too much nesting
+ // happens between a kprobe and its kretprobe, overflowing the
+ // maxactives buffer.
+ optional int64 misses = 2;
+}
+
// Errors and kernel buffer stats for the ftrace data source.
message FtraceStats {
enum Phase {
@@ -112,6 +123,9 @@
// Any traces with entries in this field should be investigated, as they
// indicate a bug in perfetto or the kernel.
repeated FtraceParseStatus ftrace_parse_errors = 9;
+
+ // Kprobe profile stats for functions hits and misses
+ optional FtraceKprobeStats kprobe_stats = 10;
}
enum FtraceParseStatus {
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index c1508ef..131332d 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -1982,6 +1982,8 @@
UNWIND_SKIP = 1;
// Use libunwindstack (default):
UNWIND_DWARF = 2;
+ // Use userspace frame pointer unwinder:
+ UNWIND_FRAME_POINTER = 3;
}
}
@@ -2724,276 +2726,13 @@
ATOM_NOTIFICATION_MEMORY_USE = 10174;
ATOM_HDR_CAPABILITIES = 10175;
ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
- ATOM_WS_WEAR_TIME_SESSION = 610;
- ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
- ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
- ATOM_WS_CALL_DURATION_REPORTED = 628;
- ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
- ATOM_WS_CALL_INTERACTION_REPORTED = 630;
- ATOM_WS_ON_BODY_STATE_CHANGED = 787;
- ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
- ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
- ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
- ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
- ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
- ATOM_SETTINGS_SPA_REPORTED = 622;
- ATOM_PDF_LOAD_REPORTED = 859;
- ATOM_PDF_API_USAGE_REPORTED = 860;
- ATOM_PDF_SEARCH_REPORTED = 861;
- ATOM_HDMI_EARC_STATUS_REPORTED = 701;
- ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
- ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
- ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
- ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
- ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
- ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
- ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
- ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
- ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
- ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
- ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
- ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
- ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
- ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
- ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
- ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
- ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
- ATOM_WEAR_POWER_MENU_OPENED = 731;
- ATOM_WEAR_ASSISTANT_OPENED = 755;
- ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
- ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
- ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
- ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
- ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
- ATOM_AUTOFILL_SESSION_COMMITTED = 607;
- ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
- ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
- ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
- ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
- ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
- ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
- ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
- ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
- ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
- ATOM_APF_SESSION_INFO_REPORTED = 777;
- ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
- ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
- ATOM_VPN_CONNECTION_REPORTED = 851;
- ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
- ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
- ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
- ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
- ATOM_ART_DATUM_REPORTED = 332;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
- ATOM_ART_DEX2OAT_REPORTED = 929;
- ATOM_ART_DEVICE_STATUS = 10205;
- ATOM_ODREFRESH_REPORTED = 366;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
- ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
- ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
- ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
- ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
- ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
- ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
- ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
- ATOM_EMERGENCY_STATE_CHANGED = 633;
- ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
- ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
- ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
- ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
- ATOM_HEALTH_CONNECT_API_CALLED = 616;
- ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
- ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
- ATOM_HEALTH_CONNECT_API_INVOKED = 643;
- ATOM_EXERCISE_ROUTE_API_CALLED = 654;
- ATOM_SELINUX_AUDIT_LOG = 799;
- ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
- ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
- ATOM_EMERGENCY_NUMBERS_INFO = 10180;
- ATOM_DATA_NETWORK_VALIDATION = 10207;
- ATOM_DATA_RAT_STATE_CHANGED = 854;
- ATOM_CONNECTED_CHANNEL_CHANGED = 882;
- ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
- ATOM_QNS_IMS_CALL_DROP_STATS = 635;
- ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
- ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
- ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
- ATOM_QNS_HANDOVER_PINGPONG = 10179;
- ATOM_SATELLITE_CONTROLLER = 10182;
- ATOM_SATELLITE_SESSION = 10183;
- ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
- ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
- ATOM_SATELLITE_PROVISION = 10186;
- ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
- ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
- ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
- ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
- ATOM_SATELLITE_ENTITLEMENT = 10214;
- ATOM_SATELLITE_CONFIG_UPDATER = 10215;
- ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
- ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
- ATOM_KEYBOARD_CONFIGURED = 682;
- ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
- ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
- ATOM_TOUCHPAD_USAGE = 10191;
- ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
- ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
- ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
- ATOM_CRONET_ENGINE_CREATED = 703;
- ATOM_CRONET_TRAFFIC_REPORTED = 704;
- ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
- ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
- ATOM_CRONET_INITIALIZED = 764;
- ATOM_WEAR_MODE_STATE_CHANGED = 715;
- ATOM_RENDERER_INITIALIZED = 736;
- ATOM_SCHEMA_VERSION_RECEIVED = 737;
- ATOM_LAYOUT_INSPECTED = 741;
- ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
- ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
- ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
- ATOM_TILE_REQUESTED = 745;
- ATOM_STATE_RESPONSE_RECEIVED = 746;
- ATOM_TILE_RESPONSE_RECEIVED = 747;
- ATOM_INFLATION_FINISHED = 748;
- ATOM_INFLATION_FAILED = 749;
- ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
- ATOM_DRAWABLE_RENDERED = 751;
- ATOM_MEDIA_ACTION_REPORTED = 608;
- ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
- ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
- ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
- ATOM_MEDIATOR_UPDATED = 721;
- ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
- ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
ATOM_ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED = 820;
- ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
- ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
- ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
- ATOM_RKPD_POOL_STATS = 664;
- ATOM_RKPD_CLIENT_OPERATION = 665;
- ATOM_CPU_POLICY = 10199;
- ATOM_ATOM_9999 = 9999;
- ATOM_ATOM_99999 = 99999;
- ATOM_SCREEN_OFF_REPORTED = 776;
- ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
- ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
- ATOM_SCREEN_DIM_REPORTED = 867;
- ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
- ATOM_BAL_ALLOWED = 632;
- ATOM_IN_TASK_ACTIVITY_STARTED = 685;
- ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
- ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
- ATOM_USER_RISK_EVENT_REPORTED = 725;
- ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
- ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
- ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
- ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
- ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
- ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
- ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
- ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
- ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
- ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
- ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
- ATOM_DREAM_SETTING_CHANGED = 705;
- ATOM_DREAM_SETTING_SNAPSHOT = 10192;
- ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
- ATOM_WIFI_AWARE_NDP_REPORTED = 638;
- ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
- ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
- ATOM_SOFT_AP_STARTED = 680;
- ATOM_SOFT_AP_STOPPED = 681;
- ATOM_WIFI_LOCK_RELEASED = 687;
- ATOM_WIFI_LOCK_DEACTIVATED = 688;
- ATOM_WIFI_CONFIG_SAVED = 689;
- ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
- ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
- ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
- ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
- ATOM_WIFI_STATE_CHANGED = 700;
- ATOM_PNO_SCAN_STARTED = 719;
- ATOM_PNO_SCAN_STOPPED = 720;
- ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
- ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
- ATOM_SOFT_AP_STATE_CHANGED = 805;
- ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
- ATOM_WIFI_AWARE_CAPABILITIES = 10190;
- ATOM_WIFI_MODULE_INFO = 10193;
- ATOM_WIFI_SETTING_INFO = 10194;
- ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
- ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
- ATOM_MTE_STATE = 10181;
- ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
- ATOM_SANDBOX_API_CALLED = 488;
- ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
- ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
- ATOM_SANDBOX_SDK_STORAGE = 10159;
- ATOM_EXPRESS_EVENT_REPORTED = 528;
- ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
- ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
- ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
- ATOM_IKE_SESSION_TERMINATED = 678;
- ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
- ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
- ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
- ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
- ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
- ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
- ATOM_DEVICE_POLICY_STATE = 10217;
- ATOM_DESKTOP_MODE_UI_CHANGED = 818;
- ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
- ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
- ATOM_MEDIA_CODEC_STARTED = 641;
- ATOM_MEDIA_CODEC_STOPPED = 642;
- ATOM_MEDIA_CODEC_RENDERED = 684;
- ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
- ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
- ATOM_PLUGIN_INITIALIZED = 655;
- ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
- ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
- ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
ATOM_THERMAL_STATUS_CALLED = 772;
ATOM_THERMAL_HEADROOM_CALLED = 773;
ATOM_THERMAL_HEADROOM_THRESHOLDS_CALLED = 774;
ATOM_ADPF_HINT_SESSION_TID_CLEANUP = 839;
ATOM_THERMAL_HEADROOM_THRESHOLDS = 10201;
ATOM_ADPF_SESSION_SNAPSHOT = 10218;
- ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
- ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
- ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
- ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
- ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
- ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
- ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
- ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
- ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
- ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
- ATOM_LE_RADIO_SCAN_STOPPED = 871;
- ATOM_LE_SCAN_RESULT_RECEIVED = 872;
- ATOM_LE_SCAN_ABUSED = 873;
- ATOM_LE_ADV_STATE_CHANGED = 874;
- ATOM_LE_ADV_ERROR_REPORTED = 875;
- ATOM_A2DP_SESSION_REPORTED = 904;
- ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
- ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
- ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
- ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
- ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
- ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
- ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
- ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
- ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
- ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
- ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
- ATOM_EMERGENCY_NUMBER_DIALED = 637;
ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
ATOM_AD_SERVICES_API_CALLED = 435;
ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
@@ -3061,27 +2800,68 @@
ATOM_SELECT_ADS_FROM_OUTCOMES_API_CALLED = 876;
ATOM_REPORT_IMPRESSION_API_CALLED = 877;
ATOM_AD_SERVICES_ENROLLMENT_TRANSACTION_STATS = 885;
- ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
- ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
- ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
- ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
- ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
- ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
- ATOM_NFC_FIELD_CHANGED = 856;
- ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
- ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
- ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
- ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
- ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
- ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
- ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
- ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
- ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
- ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_AI_WALLPAPERS_BUTTON_PRESSED = 706;
+ ATOM_AI_WALLPAPERS_TEMPLATE_SELECTED = 707;
+ ATOM_AI_WALLPAPERS_TERM_SELECTED = 708;
+ ATOM_AI_WALLPAPERS_WALLPAPER_SET = 709;
+ ATOM_AI_WALLPAPERS_SESSION_SUMMARY = 710;
ATOM_APEX_INSTALLATION_REQUESTED = 732;
ATOM_APEX_INSTALLATION_STAGED = 733;
ATOM_APEX_INSTALLATION_ENDED = 734;
+ ATOM_APP_SEARCH_SET_SCHEMA_STATS_REPORTED = 385;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_STATS_REPORTED = 825;
+ ATOM_APP_SEARCH_USAGE_SEARCH_INTENT_RAW_QUERY_STATS_REPORTED = 826;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_ART_DEX2OAT_REPORTED = 929;
+ ATOM_ART_DEVICE_STATUS = 10205;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_PREREBOOT_DEXOPT_JOB_ENDED = 883;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_CAR_RECENTS_EVENT_REPORTED = 770;
+ ATOM_CAR_CALM_MODE_EVENT_REPORTED = 797;
+ ATOM_CAR_WAKEUP_FROM_SUSPEND_REPORTED = 852;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED = 781;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_ATTEMPTED = 782;
+ ATOM_REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID = 862;
+ ATOM_LE_APP_SCAN_STATE_CHANGED = 870;
+ ATOM_LE_RADIO_SCAN_STOPPED = 871;
+ ATOM_LE_SCAN_RESULT_RECEIVED = 872;
+ ATOM_LE_SCAN_ABUSED = 873;
+ ATOM_LE_ADV_STATE_CHANGED = 874;
+ ATOM_LE_ADV_ERROR_REPORTED = 875;
+ ATOM_A2DP_SESSION_REPORTED = 904;
+ ATOM_BLUETOOTH_CROSS_LAYER_EVENT_REPORTED = 916;
+ ATOM_BROADCAST_AUDIO_SESSION_REPORTED = 927;
+ ATOM_BROADCAST_AUDIO_SYNC_REPORTED = 928;
+ ATOM_BLUETOOTH_RFCOMM_CONNECTION_REPORTED_AT_CLOSE = 982;
+ ATOM_CAMERA_FEATURE_COMBINATION_QUERY_EVENT = 900;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_NETWORK_REQUEST_STATE_CHANGED = 779;
+ ATOM_TETHERING_ACTIVE_SESSIONS_REPORTED = 925;
+ ATOM_NETWORK_STATS_RECORDER_FILE_OPERATED = 783;
+ ATOM_CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED = 979;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_VPN_CONNECTION_STATE_CHANGED = 850;
+ ATOM_VPN_CONNECTION_REPORTED = 851;
+ ATOM_CPU_POLICY = 10199;
ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
@@ -3091,8 +2871,232 @@
ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
- ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DESKTOP_MODE_UI_CHANGED = 818;
+ ATOM_DESKTOP_MODE_SESSION_TASK_UPDATE = 819;
+ ATOM_DEVICE_LOCK_CHECK_IN_REQUEST_REPORTED = 726;
+ ATOM_DEVICE_LOCK_PROVISIONING_COMPLETE_REPORTED = 727;
+ ATOM_DEVICE_LOCK_KIOSK_APP_REQUEST_REPORTED = 728;
+ ATOM_DEVICE_LOCK_CHECK_IN_RETRY_REPORTED = 789;
+ ATOM_DEVICE_LOCK_PROVISION_FAILURE_REPORTED = 790;
+ ATOM_DEVICE_LOCK_LOCK_UNLOCK_DEVICE_FAILURE_REPORTED = 791;
+ ATOM_DEVICE_POLICY_MANAGEMENT_MODE = 10216;
+ ATOM_DEVICE_POLICY_STATE = 10217;
+ ATOM_DISPLAY_MODE_DIRECTOR_VOTE_CHANGED = 792;
+ ATOM_EXTERNAL_DISPLAY_STATE_CHANGED = 806;
ATOM_DND_STATE_CHANGED = 657;
+ ATOM_DREAM_SETTING_CHANGED = 705;
+ ATOM_DREAM_SETTING_SNAPSHOT = 10192;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_FEDERATED_COMPUTE_API_CALLED = 712;
+ ATOM_FEDERATED_COMPUTE_TRAINING_EVENT_REPORTED = 771;
+ ATOM_EXAMPLE_ITERATOR_NEXT_LATENCY_REPORTED = 838;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_STYLUS_PREDICTION_METRICS_REPORTED = 718;
+ ATOM_USER_RISK_EVENT_REPORTED = 725;
+ ATOM_MEDIA_PROJECTION_STATE_CHANGED = 729;
+ ATOM_MEDIA_PROJECTION_TARGET_CHANGED = 730;
+ ATOM_EXCESSIVE_BINDER_PROXY_COUNT_REPORTED = 853;
+ ATOM_PROXY_BYTES_TRANSFER_BY_FG_BG = 10200;
+ ATOM_MOBILE_BYTES_TRANSFER_BY_PROC_STATE = 10204;
+ ATOM_BIOMETRIC_FRR_NOTIFICATION = 817;
+ ATOM_SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION = 830;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_SESSION = 831;
+ ATOM_SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED = 832;
+ ATOM_SENSITIVE_NOTIFICATION_REDACTION = 833;
+ ATOM_SENSITIVE_CONTENT_APP_PROTECTION = 835;
+ ATOM_APP_RESTRICTION_STATE_CHANGED = 866;
+ ATOM_APPLICATION_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_SYSTEM_GRAMMATICAL_INFLECTION_CHANGED = 816;
+ ATOM_HDMI_EARC_STATUS_REPORTED = 701;
+ ATOM_HDMI_SOUNDBAR_MODE_STATUS_REPORTED = 724;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HOTWORD_EGRESS_SIZE_ATOM_REPORTED = 761;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_NEGOTIATED_SECURITY_ASSOCIATION = 821;
+ ATOM_KEYBOARD_CONFIGURED = 682;
+ ATOM_KEYBOARD_SYSTEMS_EVENT_REPORTED = 683;
+ ATOM_INPUTDEVICE_USAGE_REPORTED = 686;
+ ATOM_TOUCHPAD_USAGE = 10191;
+ ATOM_KERNEL_OOM_KILL_OCCURRED = 754;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_CHRE_SIGNIFICANT_MOTION_STATE_CHANGED = 868;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
+ ATOM_MEDIA_EDITING_ENDED_REPORTED = 798;
+ ATOM_MTE_STATE = 10181;
+ ATOM_NFC_OBSERVE_MODE_STATE_CHANGED = 855;
+ ATOM_NFC_FIELD_CHANGED = 856;
+ ATOM_NFC_POLLING_LOOP_NOTIFICATION_REPORTED = 857;
+ ATOM_NFC_PROPRIETARY_CAPABILITIES_REPORTED = 858;
+ ATOM_ONDEVICEPERSONALIZATION_API_CALLED = 711;
+ ATOM_COMPONENT_STATE_CHANGED_REPORTED = 863;
+ ATOM_PDF_LOAD_REPORTED = 859;
+ ATOM_PDF_API_USAGE_REPORTED = 860;
+ ATOM_PDF_SEARCH_REPORTED = 861;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED = 827;
+ ATOM_ENHANCED_CONFIRMATION_RESTRICTION_CLEARED = 828;
+ ATOM_PHOTOPICKER_SESSION_INFO_REPORTED = 886;
+ ATOM_PHOTOPICKER_API_INFO_REPORTED = 887;
+ ATOM_PHOTOPICKER_UI_EVENT_LOGGED = 888;
+ ATOM_PHOTOPICKER_MEDIA_ITEM_STATUS_REPORTED = 889;
+ ATOM_PHOTOPICKER_PREVIEW_INFO_LOGGED = 890;
+ ATOM_PHOTOPICKER_MENU_INTERACTION_LOGGED = 891;
+ ATOM_PHOTOPICKER_BANNER_INTERACTION_LOGGED = 892;
+ ATOM_PHOTOPICKER_MEDIA_LIBRARY_INFO_LOGGED = 893;
+ ATOM_PHOTOPICKER_PAGE_INFO_LOGGED = 894;
+ ATOM_PHOTOPICKER_MEDIA_GRID_SYNC_INFO_REPORTED = 895;
+ ATOM_PHOTOPICKER_ALBUM_SYNC_INFO_REPORTED = 896;
+ ATOM_PHOTOPICKER_SEARCH_INFO_REPORTED = 897;
+ ATOM_SEARCH_DATA_EXTRACTION_DETAILS_REPORTED = 898;
+ ATOM_EMBEDDED_PHOTOPICKER_INFO_REPORTED = 899;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_SCREEN_OFF_REPORTED = 776;
+ ATOM_SCREEN_TIMEOUT_OVERRIDE_REPORTED = 836;
+ ATOM_SCREEN_INTERACTIVE_SESSION_REPORTED = 837;
+ ATOM_SCREEN_DIM_REPORTED = 867;
+ ATOM_MEDIA_PROVIDER_DATABASE_ROLLBACK_REPORTED = 784;
+ ATOM_BACKUP_SETUP_STATUS_REPORTED = 785;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SDK_SANDBOX_RESTRICTED_ACCESS_IN_SESSION = 796;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_SELINUX_AUDIT_LOG = 799;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_LAUNCHER_IMPRESSION_EVENT_V2 = 716;
+ ATOM_DISPLAY_SWITCH_LATENCY_TRACKED = 753;
+ ATOM_NOTIFICATION_LISTENER_SERVICE = 829;
+ ATOM_NAV_HANDLE_TOUCH_POINTS = 869;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_CELLULAR_RADIO_POWER_STATE_CHANGED = 713;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_DATA_NETWORK_VALIDATION = 10207;
+ ATOM_DATA_RAT_STATE_CHANGED = 854;
+ ATOM_CONNECTED_CHANNEL_CHANGED = 882;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_CARRIER_ROAMING_SATELLITE_SESSION = 10211;
+ ATOM_CARRIER_ROAMING_SATELLITE_CONTROLLER_STATS = 10212;
+ ATOM_CONTROLLER_STATS_PER_PACKAGE = 10213;
+ ATOM_SATELLITE_ENTITLEMENT = 10214;
+ ATOM_SATELLITE_CONFIG_UPDATER = 10215;
+ ATOM_SATELLITE_ACCESS_CONTROLLER = 10219;
+ ATOM_CELLULAR_IDENTIFIER_DISCLOSED = 800;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_BOOT_INTEGRITY_INFO_REPORTED = 775;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_EXTERNAL_TV_INPUT_EVENT = 717;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIATOR_UPDATED = 721;
+ ATOM_SYSPROXY_BLUETOOTH_BYTES_TRANSFER = 10196;
+ ATOM_SYSPROXY_CONNECTION_UPDATED = 786;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_SESSION_STATE_CHANGED = 677;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_API_LATENCY = 757;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_SASS_DEVICE_UNAVAILABLE = 758;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FASTPAIR_API_TIMEOUT = 759;
+ ATOM_WEAR_MODE_STATE_CHANGED = 715;
+ ATOM_RENDERER_INITIALIZED = 736;
+ ATOM_SCHEMA_VERSION_RECEIVED = 737;
+ ATOM_LAYOUT_INSPECTED = 741;
+ ATOM_LAYOUT_EXPRESSION_INSPECTED = 742;
+ ATOM_LAYOUT_ANIMATIONS_INSPECTED = 743;
+ ATOM_MATERIAL_COMPONENTS_INSPECTED = 744;
+ ATOM_TILE_REQUESTED = 745;
+ ATOM_STATE_RESPONSE_RECEIVED = 746;
+ ATOM_TILE_RESPONSE_RECEIVED = 747;
+ ATOM_INFLATION_FINISHED = 748;
+ ATOM_INFLATION_FAILED = 749;
+ ATOM_IGNORED_INFLATION_FAILURES_REPORTED = 750;
+ ATOM_DRAWABLE_RENDERED = 751;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_WS_ON_BODY_STATE_CHANGED = 787;
+ ATOM_WS_WATCH_FACE_RESTRICTED_COMPLICATIONS_IMPACTED = 802;
+ ATOM_WS_WATCH_FACE_DEFAULT_RESTRICTED_COMPLICATIONS_REMOVED = 803;
+ ATOM_WS_COMPLICATIONS_IMPACTED_NOTIFICATION_EVENT_REPORTED = 804;
+ ATOM_WS_STANDALONE_MODE_SNAPSHOT = 10197;
+ ATOM_WS_FAVORITE_WATCH_FACE_SNAPSHOT = 10206;
+ ATOM_WEAR_POWER_MENU_OPENED = 731;
+ ATOM_WEAR_ASSISTANT_OPENED = 755;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_PNO_SCAN_STARTED = 719;
+ ATOM_PNO_SCAN_STOPPED = 720;
+ ATOM_WIFI_IS_UNUSABLE_REPORTED = 722;
+ ATOM_WIFI_AP_CAPABILITIES_REPORTED = 723;
+ ATOM_SOFT_AP_STATE_CHANGED = 805;
+ ATOM_SCORER_PREDICTION_RESULT_REPORTED = 884;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_WIFI_SETTING_INFO = 10194;
+ ATOM_WIFI_COMPLEX_SETTING_INFO = 10195;
+ ATOM_WIFI_CONFIGURED_NETWORK_INFO = 10198;
}
// End of protos/perfetto/config/statsd/atom_ids.proto
@@ -7536,6 +7540,16 @@
// End of protos/perfetto/trace/ftrace/compaction.proto
+// Begin of protos/perfetto/trace/ftrace/cpm_trace.proto
+
+message ParamSetValueCpmFtraceEvent {
+ optional string body = 1;
+ optional uint32 value = 2;
+ optional int64 timestamp = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/cpm_trace.proto
+
// Begin of protos/perfetto/trace/ftrace/cpuhp.proto
message CpuhpExitFtraceEvent {
@@ -8793,6 +8807,19 @@
// End of protos/perfetto/trace/ftrace/filemap.proto
+// Begin of protos/perfetto/trace/ftrace/fs.proto
+
+message DoSysOpenFtraceEvent {
+ optional string filename = 1;
+ optional int32 flags = 2;
+ optional int32 mode = 3;
+}
+message OpenExecFtraceEvent {
+ optional string filename = 1;
+}
+
+// End of protos/perfetto/trace/ftrace/fs.proto
+
// Begin of protos/perfetto/trace/ftrace/ftrace.proto
message PrintFtraceEvent {
@@ -11411,6 +11438,9 @@
SchedWakeupTaskAttrFtraceEvent sched_wakeup_task_attr = 540;
DevfreqFrequencyFtraceEvent devfreq_frequency = 541;
KprobeEvent kprobe_event = 542;
+ ParamSetValueCpmFtraceEvent param_set_value_cpm = 543;
+ DoSysOpenFtraceEvent do_sys_open = 544;
+ OpenExecFtraceEvent open_exec = 545;
}
}
@@ -11461,6 +11491,17 @@
optional uint64 read_events = 9;
}
+// Kprobe statistical data, gathered from /sys/kernel/tracing/kprobe_profile.
+message FtraceKprobeStats {
+ // Cumulative number of kprobe events generated for this function
+ optional int64 hits = 1;
+ // Cumulative number of kprobe events that could not be generated for this
+ // function and were missed. This happens when too much nesting
+ // happens between a kprobe and its kretprobe, overflowing the
+ // maxactives buffer.
+ optional int64 misses = 2;
+}
+
// Errors and kernel buffer stats for the ftrace data source.
message FtraceStats {
enum Phase {
@@ -11512,6 +11553,9 @@
// Any traces with entries in this field should be investigated, as they
// indicate a bug in perfetto or the kernel.
repeated FtraceParseStatus ftrace_parse_errors = 9;
+
+ // Kprobe profile stats for functions hits and misses
+ optional FtraceKprobeStats kprobe_stats = 10;
}
enum FtraceParseStatus {
@@ -15278,6 +15322,8 @@
THREAD_MEMORY_INFRA = 50;
THREAD_SAMPLING_PROFILER = 51;
+
+ THREAD_COMPOSITOR_GPU = 52;
};
optional ThreadType thread_type = 1;
diff --git a/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto b/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
index bd82b5a..5e2f119 100644
--- a/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
+++ b/protos/perfetto/trace/track_event/chrome_thread_descriptor.proto
@@ -77,6 +77,8 @@
THREAD_MEMORY_INFRA = 50;
THREAD_SAMPLING_PROFILER = 51;
+
+ THREAD_COMPOSITOR_GPU = 52;
};
optional ThreadType thread_type = 1;
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index bfb2a1f..86b4918 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -155,6 +155,8 @@
StatusResult status = 210;
// For TPM_REGISTER_SQL_PACKAGE.
RegisterSqlPackageResult register_sql_package_result = 211;
+ // For TPM_FINALIZE_TRACE_DATA.
+ FinalizeDataResult finalize_data_result = 212;
}
// Previously: RawQueryArgs for TPM_QUERY_RAW_DEPRECATED
@@ -356,4 +358,8 @@
message RegisterSqlPackageResult {
optional string error = 1;
-}
\ No newline at end of file
+}
+
+message FinalizeDataResult {
+ optional string error = 1;
+}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 7cf29f6..6d92cb6 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -2020,6 +2020,15 @@
// Timestamp in microseconds of the start of the task containing this slice.
optional uint64 task_start_time_us = 2;
+
+ // t1 - t0, where t1 is the start timestamp of this slice and t0 is the
+ // timestamp of the time when the task containing this slice
+ // was queued.
+ optional uint64 task_queueing_time_us = 3;
+
+ // Timestamp in microseconds of the time when the task containing
+ // this slice was queued.
+ optional uint64 task_queued_time_us = 4;
}
message ChromeLatencyInfo2 {
diff --git a/python/generators/diff_tests/runner.py b/python/generators/diff_tests/runner.py
index a6c49f0..49bd278 100644
--- a/python/generators/diff_tests/runner.py
+++ b/python/generators/diff_tests/runner.py
@@ -417,15 +417,12 @@
os.path.join(metrics_protos_path, 'webview',
'all_webview_metrics.descriptor')
]
- result_str = ""
-
result, run_str = self.__run(metrics_descriptor_paths,
extension_descriptor_paths, keep_input, rebase)
- result_str += run_str
if not result:
- return self.test.name, result_str, None
+ return self.test.name, run_str, None
- return self.test.name, result_str, result
+ return self.test.name, run_str, result
# Fetches and executes all diff viable tests.
@@ -435,12 +432,15 @@
trace_processor_path: str
trace_descriptor_path: str
test_runners: List[TestCaseRunner]
+ quiet: bool
def __init__(self, name_filter: str, trace_processor_path: str,
trace_descriptor: str, no_colors: bool,
- override_sql_module_paths: List[str], test_dir: str):
+ override_sql_module_paths: List[str], test_dir: str,
+ quiet: bool):
self.tests = read_all_tests(name_filter, test_dir)
self.trace_processor_path = trace_processor_path
+ self.quiet = quiet
out_path = os.path.dirname(self.trace_processor_path)
self.trace_descriptor_path = get_trace_descriptor_path(
@@ -461,6 +461,7 @@
failures = []
rebased = []
test_run_start = datetime.datetime.now()
+ completed_tests = 0
with concurrent.futures.ProcessPoolExecutor() as e:
fut = [
@@ -471,7 +472,16 @@
]
for res in concurrent.futures.as_completed(fut):
test_name, res_str, result = res.result()
- sys.stderr.write(res_str)
+
+ if self.quiet:
+ completed_tests += 1
+ sys.stderr.write(f"\rRan {completed_tests} tests")
+ if not result.passed:
+ sys.stderr.write(f"\r")
+ sys.stderr.write(res_str)
+ else:
+ sys.stderr.write(res_str)
+
if not result or not result.passed:
if rebase:
rebased.append(test_name)
@@ -480,4 +490,6 @@
perf_results.append(result.perf_result)
test_time_ms = int(
(datetime.datetime.now() - test_run_start).total_seconds() * 1000)
+ if self.quiet:
+ sys.stderr.write(f"\r")
return TestResults(failures, perf_results, rebased, test_time_ms)
diff --git a/python/generators/sql_processing/docs_extractor.py b/python/generators/sql_processing/docs_extractor.py
index d8cca0a..6da238c 100644
--- a/python/generators/sql_processing/docs_extractor.py
+++ b/python/generators/sql_processing/docs_extractor.py
@@ -30,18 +30,12 @@
sql: str
@dataclass
- class Annotation:
- key: str
- value: str
-
- @dataclass
class Extract:
"""Extracted documentation for a single view/table/function."""
obj_kind: ObjKind
obj_match: Match
description: str
- annotations: List['DocsExtractor.Annotation']
def __init__(self, path: str, module_name: str, sql: str):
self.path = path
@@ -72,27 +66,13 @@
def _extract_from_comment(self, kind: ObjKind, match: Match,
comment_lines: List[str]) -> Optional[Extract]:
- extract = DocsExtractor.Extract(kind, match, '', [])
+ extract = DocsExtractor.Extract(kind, match, '')
for line in comment_lines:
assert line.startswith('--')
# Remove the comment.
comment_stripped = line.lstrip('--')
stripped = comment_stripped.lstrip()
+ extract.description += comment_stripped + "\n"
- # Check if the line is an annotation.
- if not stripped.startswith('@'):
- # We are not in annotation: if we haven't seen an annotation yet, we
- # must be still be parsing the description. Just add to that
- if not extract.annotations:
- extract.description += comment_stripped + "\n"
- continue
-
- # Otherwise, add to the latest annotation.
- extract.annotations[-1].value += " " + stripped
- continue
-
- # This line is an annotation: find its name and add a new entry
- annotation, rest = stripped.split(' ', 1)
- extract.annotations.append(DocsExtractor.Annotation(annotation, rest))
return extract
diff --git a/python/generators/sql_processing/docs_parse.py b/python/generators/sql_processing/docs_parse.py
index 77d5938..b01933e 100644
--- a/python/generators/sql_processing/docs_parse.py
+++ b/python/generators/sql_processing/docs_parse.py
@@ -25,10 +25,8 @@
from python.generators.sql_processing.utils import ALLOWED_PREFIXES
from python.generators.sql_processing.utils import OBJECT_NAME_ALLOWLIST
-from python.generators.sql_processing.utils import COLUMN_ANNOTATION_PATTERN
from python.generators.sql_processing.utils import ANY_PATTERN
from python.generators.sql_processing.utils import ARG_DEFINITION_PATTERN
-from python.generators.sql_processing.utils import ARG_ANNOTATION_PATTERN
def _is_internal(name: str) -> bool:
@@ -102,88 +100,23 @@
self._error('Description of the table/view/function/macro is missing')
return desc.strip()
- def _validate_only_contains_annotations(self,
- ans: List[DocsExtractor.Annotation],
- ans_types: Set[str]):
- used_ans_types = set(a.key for a in ans)
- for type in used_ans_types.difference(ans_types):
- self._error(f'Unknown documentation annotation {type}')
-
- def _parse_columns(self, ans: List[DocsExtractor.Annotation],
- schema: Optional[str]) -> Dict[str, Arg]:
- column_annotations = {}
- for t in ans:
- if t.key != '@column':
- continue
- m = re.match(COLUMN_ANNOTATION_PATTERN, t.value)
- if not m:
- self._error(f'@column annotation value {t.value} does not match '
- f'pattern {COLUMN_ANNOTATION_PATTERN}')
- continue
- column_annotations[m.group(1)] = Arg(None, m.group(2).strip())
-
- if not schema:
- # If we don't have schema, we have to accept annotations as the source of
- # truth.
- return column_annotations
-
- columns = self._parse_args_definition(schema)
-
+ def _parse_columns(self, schema: str) -> Dict[str, Arg]:
+ columns = self._parse_args_definition(schema) if schema else {}
for column in columns:
- inline_comment = columns[column].description
- if not inline_comment and column not in column_annotations:
+ if not columns[column].description:
self._error(f'Column "{column}" is missing a description. Please add a '
'comment in front of the column definition')
continue
- if column not in column_annotations:
- continue
- annotation = column_annotations[column].description
- if inline_comment and annotation:
- self._error(f'Column "{column}" is documented twice. Please remove the '
- '@column annotation')
- if not inline_comment and annotation:
- # Absorb old-style annotations.
- columns[column] = Arg(columns[column].type, annotation)
-
- # Check that the annotations match existing columns.
- for annotation in column_annotations:
- if annotation not in columns:
- self._error(f'Column "{annotation}" is documented but does not exist '
- 'in table definition')
return columns
- def _parse_args(self, ans: List[DocsExtractor.Annotation],
- sql_args_str: str) -> Dict[str, Arg]:
+ def _parse_args(self, sql_args_str: str) -> Dict[str, Arg]:
args = self._parse_args_definition(sql_args_str)
- arg_annotations = {}
- for an in ans:
- if an.key != '@arg':
- continue
- m = re.match(ARG_ANNOTATION_PATTERN, an.value)
- if m is None:
- self._error(f'Expected arg documentation "{an.value}" to match pattern '
- f'{ARG_ANNOTATION_PATTERN}')
- continue
- arg_annotations[m.group(1)] = Arg(m.group(2), m.group(3).strip())
-
for arg in args:
- if not args[arg].description and arg not in arg_annotations:
+ if not args[arg].description:
self._error(f'Arg "{arg}" is missing a description. '
'Please add a comment in front of the arg definition.')
- if args[arg].description and arg in arg_annotations:
- self._error(f'Arg "{arg}" is documented twice. '
- 'Please remove the @arg annotation')
- if not args[arg].description and arg in arg_annotations:
- # Absorb old-style annotations.
- # TODO(b/307926059): Remove it once stdlib is migrated.
- args[arg] = Arg(args[arg].type, arg_annotations[arg].description)
-
- for arg in arg_annotations:
- if arg not in args:
- self._error(
- f'Arg "{arg}" is documented but not found in function definition.')
return args
# Parse function argument definition list or a table schema, e.g.
@@ -243,22 +176,33 @@
f'{type} "{self.name}": CREATE OR REPLACE is not allowed in stdlib '
f'as standard library modules can only included once. Please just '
f'use CREATE instead.')
+ return
+
if _is_internal(self.name):
return None
- is_perfetto_table_or_view = (
- perfetto_or_virtual and perfetto_or_virtual.lower() == 'perfetto')
- if not schema and is_perfetto_table_or_view:
+ if not schema and self.name.lower() != "window":
self._error(
f'{type} "{self.name}": schema is missing for a non-internal stdlib'
f' perfetto table or view')
+ return
- self._validate_only_contains_annotations(doc.annotations, {'@column'})
+ if type.lower() == "table" and not perfetto_or_virtual:
+ self._error(
+ f'{type} "{self.name}": Can only expose CREATE PERFETTO tables')
+ return
+
+ is_virtual_table = type.lower() == "table" and perfetto_or_virtual.lower(
+ ) == "virtual"
+ if is_virtual_table and self.name.lower() != "window":
+ self._error(f'{type} "{self.name}": Virtual tables cannot be exposed.')
+ return
+
return TableOrView(
name=self._parse_name(),
type=type,
desc=self._parse_desc_not_empty(doc.description),
- cols=self._parse_columns(doc.annotations, schema),
+ cols=self._parse_columns(schema),
)
@@ -309,7 +253,7 @@
return Function(
name=name,
desc=self._parse_desc_not_empty(doc.description),
- args=self._parse_args(doc.annotations, args),
+ args=self._parse_args(args),
return_type=ret_type,
return_desc=ret_desc,
)
@@ -342,13 +286,12 @@
f'Function "{self.name}": CREATE OR REPLACE is not allowed in stdlib '
f'as standard library modules can only included once. Please just '
f'use CREATE instead.')
+ return
# Ignore internal functions.
if _is_internal(self.name):
return None
- self._validate_only_contains_annotations(doc.annotations,
- {'@arg', '@column'})
name = self._parse_name()
if not _is_snake_case(name):
@@ -358,8 +301,8 @@
return TableFunction(
name=name,
desc=self._parse_desc_not_empty(doc.description),
- cols=self._parse_columns(doc.annotations, columns),
- args=self._parse_args(doc.annotations, args),
+ cols=self._parse_columns(columns),
+ args=self._parse_args(args),
)
@@ -398,7 +341,6 @@
if _is_internal(self.name):
return None
- self._validate_only_contains_annotations(doc.annotations, set())
name = self._parse_name()
if not _is_snake_case(name):
@@ -410,7 +352,7 @@
desc=self._parse_desc_not_empty(doc.description),
return_desc=parse_comment(return_desc),
return_type=return_type,
- args=self._parse_args(doc.annotations, args),
+ args=self._parse_args(args),
)
diff --git a/python/generators/sql_processing/utils.py b/python/generators/sql_processing/utils.py
index 9768619..edb6b95 100644
--- a/python/generators/sql_processing/utils.py
+++ b/python/generators/sql_processing/utils.py
@@ -82,12 +82,8 @@
INCLUDE_PATTERN = update_pattern(fr'^INCLUDE PERFETTO MODULE ([A-Za-z_.*]*);$')
-COLUMN_ANNOTATION_PATTERN = update_pattern(fr'^ ({NAME}) ({ANY_WORDS})')
-
NAME_AND_TYPE_PATTERN = update_pattern(fr' ({NAME})\s+({TYPE}) ')
-ARG_ANNOTATION_PATTERN = fr'\s*{NAME_AND_TYPE_PATTERN}\s+({ANY_WORDS})'
-
ARG_DEFINITION_PATTERN = update_pattern(ARG_PATTERN)
FUNCTION_RETURN_PATTERN = update_pattern(fr'^ ({TYPE})\s+({ANY_WORDS})')
@@ -117,7 +113,7 @@
'chrome/util': ['cr'],
'intervals': ['interval'],
'graphs': ['graph'],
- 'slices': ['slice'],
+ 'slices': ['slice', 'thread_slice', 'process_slice'],
'linux': ['cpu', 'memory'],
'stacks': ['cpu_profiling'],
}
@@ -125,8 +121,6 @@
# Allows for nonstandard object names.
OBJECT_NAME_ALLOWLIST = {
'graphs/partition.sql': ['tree_structural_partition_by_group'],
- 'slices/with_context.sql': ['process_slice', 'thread_slice'],
- 'slices/cpu_time.sql': ['thread_slice_cpu_time', 'thread_slice_cpu_cycles']
}
@@ -196,7 +190,7 @@
errors = []
for _, matches in match_pattern(CREATE_TABLE_AS_PATTERN, sql).items():
name = matches[0]
- if name != "trace_bounds":
+ if name != "_trace_bounds":
errors.append(
f"Table '{name}' uses CREATE TABLE which is deprecated "
"and this table is not allowlisted. Use CREATE PERFETTO TABLE.")
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 9c22fd3..ed15b47 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor b/python/perfetto/trace_processor/trace_processor.descriptor
index 976fb9b..40cec35 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor
+++ b/python/perfetto/trace_processor/trace_processor.descriptor
Binary files differ
diff --git a/python/test/stdlib_unittest.py b/python/test/stdlib_unittest.py
index 34725cc..9114e65 100644
--- a/python/test/stdlib_unittest.py
+++ b/python/test/stdlib_unittest.py
@@ -25,93 +25,6 @@
class TestStdlib(unittest.TestCase):
- def test_valid_table(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- First line.
--- Second line.
--- @column slice_id Id of slice.
--- @column slice_name Name of slice.
-CREATE TABLE foo_table AS
-SELECT 1;
- '''.strip())
- self.assertListEqual(res.errors, [])
-
- table = res.table_views[0]
- self.assertEqual(table.name, 'foo_table')
- self.assertEqual(table.desc, 'First line.\n Second line.')
- self.assertEqual(table.type, 'TABLE')
- self.assertEqual(
- table.cols, {
- 'slice_id': Arg(None, 'Id of slice.'),
- 'slice_name': Arg(None, 'Name of slice.'),
- })
-
- def test_valid_function(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- First line.
--- Second line.
--- @arg utid INT Utid of thread.
--- @arg name STRING String name.
-CREATE PERFETTO FUNCTION foo_fn(utid INT, name STRING)
--- Exists.
-RETURNS BOOL
-AS
-SELECT 1;
- '''.strip())
- self.assertListEqual(res.errors, [])
-
- fn = res.functions[0]
- self.assertEqual(fn.name, 'foo_fn')
- self.assertEqual(fn.desc, 'First line.\n Second line.')
- self.assertEqual(
- fn.args, {
- 'utid': Arg('INT', 'Utid of thread.'),
- 'name': Arg('STRING', 'String name.'),
- })
- self.assertEqual(fn.return_type, 'BOOL')
- self.assertEqual(fn.return_desc, 'Exists.')
-
- def test_valid_table_function(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Table comment.
--- @arg utid INT Utid of thread.
--- @arg name STRING String name.
--- @column slice_id Id of slice.
--- @column slice_name Name of slice.
-CREATE PERFETTO FUNCTION foo_view_fn(utid INT, name STRING)
-RETURNS TABLE(slice_id INT, slice_name STRING)
-AS SELECT 1;
- '''.strip())
- self.assertListEqual(res.errors, [])
-
- fn = res.table_functions[0]
- self.assertEqual(fn.name, 'foo_view_fn')
- self.assertEqual(fn.desc, 'Table comment.')
- self.assertEqual(
- fn.args, {
- 'utid': Arg('INT', 'Utid of thread.'),
- 'name': Arg('STRING', 'String name.'),
- })
- self.assertEqual(
- fn.cols, {
- 'slice_id': Arg('INT', 'Id of slice.'),
- 'slice_name': Arg('STRING', 'Name of slice.'),
- })
-
- def test_missing_module_name(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment
--- @column slice_id Id of slice.
-CREATE TABLE bar_table AS
-SELECT 1;
- '''.strip())
- # Expecting an error: function prefix (bar) not matching module name (foo).
- self.assertEqual(len(res.errors), 1)
-
# Checks that custom prefixes (cr for chrome/util) are allowed.
def test_custom_module_prefix(self):
res = parse_file(
@@ -185,79 +98,6 @@
# (allowed: foo).
self.assertEqual(len(res.errors), 1)
- def test_common_does_not_include_module_name(self):
- res = parse_file(
- 'common/bar.sql', f'''
--- Comment.
--- @column slice_id Id of slice.
-CREATE TABLE common_table AS
-SELECT 1;
- '''.strip())
- # Expecting an error: functions in common/ should not have a module prefix.
- self.assertEqual(len(res.errors), 1)
-
- def test_cols_typo(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment.
---
--- @column slice_id2 Foo.
--- @column slice_name Bar.
-CREATE TABLE bar_table AS
-SELECT 1;
- '''.strip())
- # Expecting an error: column slice_id2 not found in the table.
- self.assertEqual(len(res.errors), 1)
-
- def test_cols_no_desc(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment.
---
--- @column slice_id
--- @column slice_name Bar.
-CREATE TABLE bar_table AS
-SELECT 1;
- '''.strip())
- # Expecting an error: column slice_id is missing a description.
- self.assertEqual(len(res.errors), 1)
-
- def test_args_typo(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment.
---
--- @arg utid2 INT Uint.
--- @arg name STRING String name.
-CREATE PERFETTO FUNCTION foo_fn(utid INT, name STRING)
--- Exists.
-RETURNS BOOL
-AS
-SELECT 1;
- '''.strip())
- # Expecting 2 errors:
- # - arg utid2 not found in the function (should be utid);
- # - utid not documented.
- self.assertEqual(len(res.errors), 2)
-
- def test_args_no_desc(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment.
---
--- @arg utid INT
--- @arg name STRING String name.
-CREATE PERFETTO FUNCTION foo_fn(utid INT, name STRING)
--- Exists.
-RETURNS BOOL
-AS
-SELECT 1;
- '''.strip())
- # Expecting 2 errors:
- # - arg utid is missing a description;
- # - arg utid is not documented.
- self.assertEqual(len(res.errors), 2)
-
def test_ret_no_desc(self):
res = parse_file(
'foo/bar.sql', f'''
@@ -295,35 +135,6 @@
self.assertEqual(fn.desc,
'This\n is\n\n a\n very\n\n long\n\n description.')
- def test_multiline_arg_desc(self):
- res = parse_file(
- 'foo/bar.sql', f'''
--- Comment.
---
--- @arg utid INT Uint
--- spread
---
--- across lines.
--- @arg name STRING String name
--- which spans across multiple lines
--- inconsistently.
-CREATE PERFETTO FUNCTION foo_fn(utid INT, name STRING)
--- Exists.
-RETURNS BOOL
-AS
-SELECT 1;
- '''.strip())
-
- fn = res.functions[0]
- self.assertEqual(
- fn.args, {
- 'utid':
- Arg('INT', 'Uint spread across lines.'),
- 'name':
- Arg(
- 'STRING', 'String name which spans across multiple lines '
- 'inconsistently.'),
- })
def test_function_name_style(self):
res = parse_file(
diff --git a/python/tools/check_imports.py b/python/tools/check_imports.py
index ec08847..37c1a95 100755
--- a/python/tools/check_imports.py
+++ b/python/tools/check_imports.py
@@ -115,14 +115,9 @@
(['/public/lib/colorizer'], '/core/feature_flags'),
# TODO(primiano): Record page-related technical debt.
- ('/frontend/record_*', '/controller/*'),
- ('/common/*', '/controller/record_config_types'),
- ('/controller/index', '/common/recordingV2/target_factories/index'),
- ('/common/recordingV2/*', '/controller/*'),
- ('/controller/record_controller*', '*'),
- ('/controller/adb_*', '*'),
- ('/chrome_extension/chrome_tracing_controller', '/controller/*'),
- ('/chrome_extension/chrome_tracing_controller', '/core/trace_config_utils'),
+ ('/plugins/dev.perfetto.RecordTrace/*', '/frontend/globals'),
+ ('/chrome_extension/chrome_tracing_controller',
+ '/plugins/dev.perfetto.RecordTrace/*'),
# TODO(primiano): query-table tech debt.
(
@@ -130,8 +125,8 @@
['/frontend/*', '/core/app_impl', '/core/router'],
),
- # TODO(primiano): debug_tracks tech debt.
- ('/public/lib/debug_tracks/*', [
+ # TODO(primiano): tracks tech debt.
+ ('/public/lib/tracks/*', [
'/frontend/base_counter_track',
'/frontend/slice_args',
'/frontend/tracks/custom_sql_table_slice_track',
@@ -149,9 +144,6 @@
# Bigtrace deps.
('/bigtrace/*', ['/base/*', '/widgets/*', '/trace_processor/*']),
- # TODO(primiano): rationalize recordingv2. RecordingV2 is a mess of subdirs.
- ('/common/recordingV2/*', '/common/recordingV2/*'),
-
# TODO(primiano): misc tech debt.
('/public/lib/extensions', '/frontend/*'),
('/bigtrace/index', ['/core/live_reload', '/core/raf_scheduler']),
diff --git a/python/tools/check_ratchet.py b/python/tools/check_ratchet.py
index 0c845d2..a34ff08 100755
--- a/python/tools/check_ratchet.py
+++ b/python/tools/check_ratchet.py
@@ -36,7 +36,7 @@
from dataclasses import dataclass
-EXPECTED_ANY_COUNT = 59
+EXPECTED_ANY_COUNT = 51
EXPECTED_RUN_METRIC_COUNT = 4
ROOT_DIR = os.path.dirname(
diff --git a/src/base/time.cc b/src/base/time.cc
index e799542..ad971af 100644
--- a/src/base/time.cc
+++ b/src/base/time.cc
@@ -188,10 +188,15 @@
std::string GetTimeFmt(const std::string& fmt) {
time_t raw_time;
time(&raw_time);
- struct tm* local_tm;
- local_tm = localtime(&raw_time);
+ struct tm local_tm;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(localtime_s(&local_tm, &raw_time) == 0);
+#else
+ tzset();
+ PERFETTO_CHECK(localtime_r(&raw_time, &local_tm) != nullptr);
+#endif
char buf[128];
- PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), local_tm) > 0);
+ PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), &local_tm) > 0);
return buf;
}
diff --git a/src/profiling/memory/client_api_factory_android.cc b/src/profiling/memory/client_api_factory_android.cc
index 9c7b63c..fe5318f 100644
--- a/src/profiling/memory/client_api_factory_android.cc
+++ b/src/profiling/memory/client_api_factory_android.cc
@@ -46,7 +46,7 @@
std::optional<perfetto::base::UnixSocketRaw> sock =
Client::ConnectToHeapprofd(perfetto::profiling::kHeapprofdSocketFile);
if (!sock) {
- PERFETTO_ELOG("Failed to connect to %s. This is benign on user builds.",
+ PERFETTO_ELOG("Failed to connect to %s.",
perfetto::profiling::kHeapprofdSocketFile);
return nullptr;
}
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
index d77cb84..6465bff 100644
--- a/src/profiling/perf/BUILD.gn
+++ b/src/profiling/perf/BUILD.gn
@@ -32,6 +32,7 @@
":producer",
"../../../gn:default_deps",
"../../../src/base",
+ "../../../src/base:version",
"../../../src/tracing/ipc/producer",
]
sources = [
@@ -101,6 +102,8 @@
"../common:unwind_support",
]
sources = [
+ "frame_pointer_unwinder.cc",
+ "frame_pointer_unwinder.h",
"unwind_queue.h",
"unwinding.cc",
"unwinding.h",
@@ -147,6 +150,7 @@
]
sources = [
"event_config_unittest.cc",
+ "frame_pointer_unwinder_unittest.cc",
"perf_producer_unittest.cc",
"unwind_queue_unittest.cc",
]
diff --git a/src/profiling/perf/event_config.cc b/src/profiling/perf/event_config.cc
index 7409c18..94d563c 100644
--- a/src/profiling/perf/event_config.cc
+++ b/src/profiling/perf/event_config.cc
@@ -273,6 +273,20 @@
}
}
+bool IsSupportedUnwindMode(
+ protos::gen::PerfEventConfig::UnwindMode unwind_mode) {
+ using protos::gen::PerfEventConfig;
+ switch (static_cast<int>(unwind_mode)) { // cast to pacify -Wswitch-enum
+ case PerfEventConfig::UNWIND_UNKNOWN:
+ case PerfEventConfig::UNWIND_SKIP:
+ case PerfEventConfig::UNWIND_DWARF:
+ case PerfEventConfig::UNWIND_FRAME_POINTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace
// static
@@ -371,32 +385,18 @@
}
// Callstack sampling.
- bool user_frames = false;
bool kernel_frames = false;
+ // Disable user_frames by default.
+ auto unwind_mode = protos::gen::PerfEventConfig::UNWIND_SKIP;
+
TargetFilter target_filter;
bool legacy_config = pb_config.all_cpus(); // all_cpus was mandatory before
if (pb_config.has_callstack_sampling() || legacy_config) {
- user_frames = true;
-
// Userspace callstacks.
- using protos::gen::PerfEventConfig;
- switch (static_cast<int>(pb_config.callstack_sampling().user_frames())) {
- case PerfEventConfig::UNWIND_UNKNOWN:
- // default to true, both for backwards compatibility and because it's
- // almost always what the user wants.
- user_frames = true;
- break;
- case PerfEventConfig::UNWIND_SKIP:
- user_frames = false;
- break;
- case PerfEventConfig::UNWIND_DWARF:
- user_frames = true;
- break;
- default:
- // enum value from the future that we don't yet know, refuse the config
- // TODO(rsavitski): double-check that both pbzero and ::gen propagate
- // unknown enum values.
- return std::nullopt;
+ unwind_mode = pb_config.callstack_sampling().user_frames();
+ if (!IsSupportedUnwindMode(unwind_mode)) {
+ // enum value from the future that we don't yet know, refuse the config
+ return std::nullopt;
}
// Process scoping. Sharding parameter is supplied from outside as it is
@@ -482,7 +482,7 @@
pe.clockid = ToClockId(pb_config.timebase().timestamp_clock());
pe.use_clockid = true;
- if (user_frames) {
+ if (IsUserFramesEnabled(unwind_mode)) {
pe.sample_type |= PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER;
// PERF_SAMPLE_STACK_USER:
// Needs to be < ((u16)(~0u)), and have bottom 8 bits clear.
@@ -529,19 +529,35 @@
return EventConfig(
raw_ds_config, pe, std::move(pe_followers), timebase_event, followers,
- user_frames, kernel_frames, std::move(target_filter),
+ kernel_frames, unwind_mode, std::move(target_filter),
ring_buffer_pages.value(), read_tick_period_ms, samples_per_tick_limit,
remote_descriptor_timeout_ms, pb_config.unwind_state_clear_period_ms(),
max_enqueued_footprint_bytes, pb_config.target_installed_by());
}
+// static
+bool EventConfig::IsUserFramesEnabled(
+ const protos::gen::PerfEventConfig::UnwindMode unwind_mode) {
+ using protos::gen::PerfEventConfig;
+ switch (unwind_mode) {
+ case PerfEventConfig::UNWIND_UNKNOWN:
+ // default to true, both for backwards compatibility and because it's
+ // almost always what the user wants.
+ case PerfEventConfig::UNWIND_DWARF:
+ case PerfEventConfig::UNWIND_FRAME_POINTER:
+ return true;
+ case PerfEventConfig::UNWIND_SKIP:
+ return false;
+ }
+}
+
EventConfig::EventConfig(const DataSourceConfig& raw_ds_config,
const perf_event_attr& pe_timebase,
std::vector<perf_event_attr> pe_followers,
const PerfCounter& timebase_event,
std::vector<PerfCounter> follower_events,
- bool user_frames,
bool kernel_frames,
+ protos::gen::PerfEventConfig::UnwindMode unwind_mode,
TargetFilter target_filter,
uint32_t ring_buffer_pages,
uint32_t read_tick_period_ms,
@@ -554,8 +570,8 @@
perf_event_followers_(std::move(pe_followers)),
timebase_event_(timebase_event),
follower_events_(std::move(follower_events)),
- user_frames_(user_frames),
kernel_frames_(kernel_frames),
+ unwind_mode_(unwind_mode),
target_filter_(std::move(target_filter)),
ring_buffer_pages_(ring_buffer_pages),
read_tick_period_ms_(read_tick_period_ms),
diff --git a/src/profiling/perf/event_config.h b/src/profiling/perf/event_config.h
index fc498f7..a87429d 100644
--- a/src/profiling/perf/event_config.h
+++ b/src/profiling/perf/event_config.h
@@ -31,6 +31,7 @@
#include "perfetto/tracing/core/data_source_config.h"
#include "protos/perfetto/common/perf_events.gen.h"
+#include "protos/perfetto/config/profiling/perf_event_config.gen.h"
namespace perfetto {
namespace protos {
@@ -136,9 +137,12 @@
uint64_t max_enqueued_footprint_bytes() const {
return max_enqueued_footprint_bytes_;
}
- bool sample_callstacks() const { return user_frames_ || kernel_frames_; }
- bool user_frames() const { return user_frames_; }
+ bool sample_callstacks() const { return user_frames() || kernel_frames_; }
+ bool user_frames() const { return IsUserFramesEnabled(unwind_mode_); }
bool kernel_frames() const { return kernel_frames_; }
+ protos::gen::PerfEventConfig::UnwindMode unwind_mode() const {
+ return unwind_mode_;
+ }
const TargetFilter& filter() const { return target_filter_; }
perf_event_attr* perf_attr() const {
return const_cast<perf_event_attr*>(&perf_event_attr_);
@@ -158,13 +162,16 @@
const DataSourceConfig& raw_ds_config() const { return raw_ds_config_; }
private:
+ static bool IsUserFramesEnabled(
+ const protos::gen::PerfEventConfig::UnwindMode unwind_mode);
+
EventConfig(const DataSourceConfig& raw_ds_config,
const perf_event_attr& pe_timebase,
std::vector<perf_event_attr> pe_followers,
const PerfCounter& timebase_event,
std::vector<PerfCounter> follower_events,
- bool user_frames,
bool kernel_frames,
+ protos::gen::PerfEventConfig::UnwindMode unwind_mode,
TargetFilter target_filter,
uint32_t ring_buffer_pages,
uint32_t read_tick_period_ms,
@@ -187,12 +194,12 @@
// Timebase event, which are already described by |perf_event_followers_|.
std::vector<PerfCounter> follower_events_;
- // If true, include userspace frames in sampled callstacks.
- const bool user_frames_;
-
// If true, include kernel frames in sampled callstacks.
const bool kernel_frames_;
+ // Userspace unwinding mode.
+ const protos::gen::PerfEventConfig::UnwindMode unwind_mode_;
+
// Parsed allow/deny-list for filtering samples.
const TargetFilter target_filter_;
diff --git a/src/profiling/perf/frame_pointer_unwinder.cc b/src/profiling/perf/frame_pointer_unwinder.cc
new file mode 100644
index 0000000..6841dc6
--- /dev/null
+++ b/src/profiling/perf/frame_pointer_unwinder.cc
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "src/profiling/perf/frame_pointer_unwinder.h"
+
+#include <cinttypes>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace profiling {
+
+void FramePointerUnwinder::Unwind() {
+ if (!IsArchSupported()) {
+ PERFETTO_ELOG("Unsupported architecture: %d", arch_);
+ last_error_.code = unwindstack::ErrorCode::ERROR_UNSUPPORTED;
+ return;
+ }
+
+ if (maps_ == nullptr || maps_->Total() == 0) {
+ PERFETTO_ELOG("No maps provided");
+ last_error_.code = unwindstack::ErrorCode::ERROR_INVALID_MAP;
+ return;
+ }
+
+ PERFETTO_DCHECK(stack_size_ > 0u);
+
+ frames_.reserve(max_frames_);
+ ClearErrors();
+ TryUnwind();
+}
+
+void FramePointerUnwinder::TryUnwind() {
+ uint64_t fp = 0;
+ switch (arch_) {
+ case unwindstack::ARCH_ARM64:
+ fp = reinterpret_cast<uint64_t*>(
+ regs_->RawData())[unwindstack::Arm64Reg::ARM64_REG_R29];
+ break;
+ case unwindstack::ARCH_X86_64:
+ fp = reinterpret_cast<uint64_t*>(
+ regs_->RawData())[unwindstack::X86_64Reg::X86_64_REG_RBP];
+ break;
+ case unwindstack::ARCH_RISCV64:
+ fp = reinterpret_cast<uint64_t*>(
+ regs_->RawData())[unwindstack::Riscv64Reg::RISCV64_REG_S0];
+ break;
+ case unwindstack::ARCH_UNKNOWN:
+ case unwindstack::ARCH_ARM:
+ case unwindstack::ARCH_X86:
+ // not supported
+ ;
+ }
+ uint64_t sp = regs_->sp();
+ uint64_t pc = regs_->pc();
+ for (size_t i = 0; i < max_frames_; i++) {
+ if (!IsFrameValid(fp, sp))
+ return;
+
+ // retrive the map info and elf info
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps_->Find(pc);
+ if (map_info == nullptr) {
+ last_error_.code = unwindstack::ErrorCode::ERROR_INVALID_MAP;
+ return;
+ }
+
+ unwindstack::FrameData frame;
+ frame.num = i;
+ frame.rel_pc = pc;
+ frame.pc = pc;
+ frame.map_info = map_info;
+ unwindstack::Elf* elf = map_info->GetElf(process_memory_, arch_);
+ if (elf != nullptr) {
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info.get());
+ uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch_);
+ frame.rel_pc = relative_pc - pc_adjustment;
+ frame.pc = pc - pc_adjustment;
+ if (!resolve_names_ ||
+ !elf->GetFunctionName(frame.rel_pc, &frame.function_name,
+ &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ }
+ frames_.push_back(frame);
+ // move to the next frame
+ fp = DecodeFrame(fp, &pc, &sp);
+ }
+}
+
+uint64_t FramePointerUnwinder::DecodeFrame(uint64_t fp,
+ uint64_t* next_pc,
+ uint64_t* next_sp) {
+ uint64_t next_fp;
+ if (!process_memory_->ReadFully(static_cast<uint64_t>(fp), &next_fp,
+ sizeof(next_fp)))
+ return 0;
+
+ uint64_t pc;
+ if (!process_memory_->ReadFully(static_cast<uint64_t>(fp + sizeof(uint64_t)),
+ &pc, sizeof(pc)))
+ return 0;
+
+ // Ensure there's not a stack overflow.
+ if (__builtin_add_overflow(fp, sizeof(uint64_t) * 2, next_sp))
+ return 0;
+
+ *next_pc = static_cast<uint64_t>(pc);
+ return next_fp;
+}
+
+bool FramePointerUnwinder::IsFrameValid(uint64_t fp, uint64_t sp) {
+ uint64_t align_mask = 0;
+ switch (arch_) {
+ case unwindstack::ARCH_ARM64:
+ align_mask = 0x1;
+ break;
+ case unwindstack::ARCH_X86_64:
+ align_mask = 0xf;
+ break;
+ case unwindstack::ARCH_RISCV64:
+ align_mask = 0x7;
+ break;
+ case unwindstack::ARCH_UNKNOWN:
+ case unwindstack::ARCH_ARM:
+ case unwindstack::ARCH_X86:
+ // not supported
+ ;
+ }
+
+ if (fp == 0 || fp <= sp)
+ return false;
+
+ // Ensure there's space on the stack to read two values: the caller's
+ // frame pointer and the return address.
+ uint64_t result;
+ if (__builtin_add_overflow(fp, sizeof(uint64_t) * 2, &result))
+ return false;
+
+ return result <= stack_end_ && (fp & align_mask) == 0;
+}
+
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/perf/frame_pointer_unwinder.h b/src/profiling/perf/frame_pointer_unwinder.h
new file mode 100644
index 0000000..14534d9
--- /dev/null
+++ b/src/profiling/perf/frame_pointer_unwinder.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef SRC_PROFILING_PERF_FRAME_POINTER_UNWINDER_H_
+#define SRC_PROFILING_PERF_FRAME_POINTER_UNWINDER_H_
+
+#include <stdint.h>
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Error.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineRiscv64.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+namespace perfetto {
+namespace profiling {
+
+class FramePointerUnwinder {
+ public:
+ FramePointerUnwinder(size_t max_frames,
+ unwindstack::Maps* maps,
+ unwindstack::Regs* regs,
+ std::shared_ptr<unwindstack::Memory> process_memory,
+ size_t stack_size)
+ : max_frames_(max_frames),
+ maps_(maps),
+ regs_(regs),
+ process_memory_(process_memory),
+ stack_size_(stack_size),
+ arch_(regs->Arch()) {
+ stack_end_ = regs->sp() + stack_size;
+ }
+
+ FramePointerUnwinder(const FramePointerUnwinder&) = delete;
+ FramePointerUnwinder& operator=(const FramePointerUnwinder&) = delete;
+
+ void Unwind();
+
+ // Disabling the resolving of names results in the function name being
+ // set to an empty string and the function offset being set to zero.
+ void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
+
+ unwindstack::ErrorCode LastErrorCode() const { return last_error_.code; }
+ uint64_t warnings() const { return warnings_; }
+
+ std::vector<unwindstack::FrameData> ConsumeFrames() {
+ std::vector<unwindstack::FrameData> frames = std::move(frames_);
+ frames_.clear();
+ return frames;
+ }
+
+ bool IsArchSupported() const {
+ return arch_ == unwindstack::ARCH_ARM64 ||
+ arch_ == unwindstack::ARCH_X86_64;
+ }
+
+ void ClearErrors() {
+ warnings_ = unwindstack::WARNING_NONE;
+ last_error_.code = unwindstack::ERROR_NONE;
+ last_error_.address = 0;
+ }
+
+ protected:
+ const size_t max_frames_;
+ unwindstack::Maps* maps_;
+ unwindstack::Regs* regs_;
+ std::vector<unwindstack::FrameData> frames_;
+ std::shared_ptr<unwindstack::Memory> process_memory_;
+ const size_t stack_size_;
+ unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
+ bool resolve_names_ = false;
+ size_t stack_end_;
+
+ unwindstack::ErrorData last_error_;
+ uint64_t warnings_ = 0;
+
+ private:
+ void TryUnwind();
+ // Given a frame pointer, returns the frame pointer of the calling stack
+ // frame, places the return address of the calling stack frame into
+ // `ret_addr` and stack pointer into `sp`.
+ uint64_t DecodeFrame(uint64_t fp, uint64_t* ret_addr, uint64_t* sp);
+ bool IsFrameValid(uint64_t fp, uint64_t sp);
+};
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // SRC_PROFILING_PERF_FRAME_POINTER_UNWINDER_H_
diff --git a/src/profiling/perf/frame_pointer_unwinder_unittest.cc b/src/profiling/perf/frame_pointer_unwinder_unittest.cc
new file mode 100644
index 0000000..c494fd7
--- /dev/null
+++ b/src/profiling/perf/frame_pointer_unwinder_unittest.cc
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/perf/frame_pointer_unwinder.h"
+
+#include <sys/mman.h>
+#include <unwindstack/Unwinder.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+class RegsFake : public unwindstack::Regs {
+ public:
+ RegsFake(uint16_t total_regs)
+ : unwindstack::Regs(
+ total_regs,
+ unwindstack::Regs::Location(unwindstack::Regs::LOCATION_UNKNOWN,
+ 0)) {
+ fake_data_ = std::make_unique<uint64_t[]>(total_regs);
+ }
+ ~RegsFake() override = default;
+
+ unwindstack::ArchEnum Arch() override { return fake_arch_; }
+ void* RawData() override { return fake_data_.get(); }
+ uint64_t pc() override { return fake_pc_; }
+ uint64_t sp() override { return fake_sp_; }
+ void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+ void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+
+ void set_fp(uint64_t fp) {
+ switch (fake_arch_) {
+ case unwindstack::ARCH_ARM64:
+ fake_data_[unwindstack::Arm64Reg::ARM64_REG_R29] = fp;
+ break;
+ case unwindstack::ARCH_X86_64:
+ fake_data_[unwindstack::X86_64Reg::X86_64_REG_RBP] = fp;
+ break;
+ case unwindstack::ARCH_RISCV64:
+ fake_data_[unwindstack::Riscv64Reg::RISCV64_REG_S0] = fp;
+ break;
+ case unwindstack::ARCH_UNKNOWN:
+ case unwindstack::ARCH_ARM:
+ case unwindstack::ARCH_X86:
+ // not supported
+ ;
+ }
+ }
+
+ bool SetPcFromReturnAddress(unwindstack::Memory*) override { return false; }
+
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
+
+ bool StepIfSignalHandler(uint64_t,
+ unwindstack::Elf*,
+ unwindstack::Memory*) override {
+ return false;
+ }
+
+ void FakeSetArch(unwindstack::ArchEnum arch) { fake_arch_ = arch; }
+
+ Regs* Clone() override { return nullptr; }
+
+ private:
+ unwindstack::ArchEnum fake_arch_ = unwindstack::ARCH_UNKNOWN;
+ uint64_t fake_pc_ = 0;
+ uint64_t fake_sp_ = 0;
+ std::unique_ptr<uint64_t[]> fake_data_;
+};
+
+class MemoryFake : public unwindstack::Memory {
+ public:
+ MemoryFake() = default;
+ ~MemoryFake() override = default;
+
+ size_t Read(uint64_t addr, void* memory, size_t size) override {
+ uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
+ for (size_t i = 0; i < size; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value == data_.end()) {
+ return i;
+ }
+ dst[i] = value->second;
+ }
+ return size;
+ }
+
+ void SetMemory(uint64_t addr, const void* memory, size_t length) {
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
+ for (size_t i = 0; i < length; i++, addr++) {
+ auto value = data_.find(addr);
+ if (value != data_.end()) {
+ value->second = src[i];
+ } else {
+ data_.insert({addr, src[i]});
+ }
+ }
+ }
+
+ void SetData8(uint64_t addr, uint8_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData16(uint64_t addr, uint16_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData32(uint64_t addr, uint32_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetData64(uint64_t addr, uint64_t value) {
+ SetMemory(addr, &value, sizeof(value));
+ }
+
+ void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
+ SetMemory(addr, values.data(), values.size());
+ }
+
+ void SetMemory(uint64_t addr, std::string string) {
+ SetMemory(addr, string.c_str(), string.size() + 1);
+ }
+
+ void Clear() override { data_.clear(); }
+
+ private:
+ std::unordered_map<uint64_t, uint8_t> data_;
+};
+
+constexpr static uint64_t kMaxFrames = 64;
+constexpr static uint64_t kStackSize = 0xFFFFFFF;
+
+class FramePointerUnwinderTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_fake_ = new MemoryFake;
+ maps_.reset(new unwindstack::Maps);
+ regs_fake_ = std::make_unique<RegsFake>(64);
+ regs_fake_->FakeSetArch(unwindstack::ARCH_X86_64);
+ process_memory_.reset(memory_fake_);
+
+ unwinder_ = std::make_unique<FramePointerUnwinder>(
+ kMaxFrames, maps_.get(), regs_fake_.get(), process_memory_, kStackSize);
+ }
+
+ MemoryFake* memory_fake_;
+ std::unique_ptr<unwindstack::Maps> maps_;
+ std::unique_ptr<RegsFake> regs_fake_;
+ std::shared_ptr<unwindstack::Memory> process_memory_;
+
+ std::unique_ptr<FramePointerUnwinder> unwinder_;
+};
+
+TEST_F(FramePointerUnwinderTest, UnwindUnsupportedArch) {
+ regs_fake_->FakeSetArch(unwindstack::ARCH_UNKNOWN);
+ unwinder_.reset(new FramePointerUnwinder(
+ kMaxFrames, maps_.get(), regs_fake_.get(), process_memory_, kStackSize));
+ unwinder_->Unwind();
+ EXPECT_EQ(unwinder_->LastErrorCode(),
+ unwindstack::ErrorCode::ERROR_UNSUPPORTED);
+
+ regs_fake_->FakeSetArch(unwindstack::ARCH_X86);
+ unwinder_.reset(new FramePointerUnwinder(
+ kMaxFrames, maps_.get(), regs_fake_.get(), process_memory_, kStackSize));
+ unwinder_->Unwind();
+ EXPECT_EQ(unwinder_->LastErrorCode(),
+ unwindstack::ErrorCode::ERROR_UNSUPPORTED);
+
+ regs_fake_->FakeSetArch(unwindstack::ARCH_ARM);
+ unwinder_.reset(new FramePointerUnwinder(
+ kMaxFrames, maps_.get(), regs_fake_.get(), process_memory_, kStackSize));
+ unwinder_->Unwind();
+ EXPECT_EQ(unwinder_->LastErrorCode(),
+ unwindstack::ErrorCode::ERROR_UNSUPPORTED);
+}
+
+TEST_F(FramePointerUnwinderTest, UnwindInvalidMaps) {
+ // Set up a valid stack frame
+ regs_fake_->set_pc(0x1000);
+ regs_fake_->set_sp(0x2000);
+ memory_fake_->SetData64(0x2000, 0x3000);
+ memory_fake_->SetData64(0x2008, 0x2000);
+ unwinder_->Unwind();
+ EXPECT_EQ(unwinder_->LastErrorCode(),
+ unwindstack::ErrorCode::ERROR_INVALID_MAP);
+ EXPECT_EQ(unwinder_->ConsumeFrames().size(), 0UL);
+}
+
+TEST_F(FramePointerUnwinderTest, UnwindValidStack) {
+ regs_fake_->set_pc(0x1900);
+ regs_fake_->set_sp(0x1800);
+ regs_fake_->set_fp(0x2000);
+
+ memory_fake_->SetData64(0x2000, 0x2200); // mock next_fp
+ memory_fake_->SetData64(0x2000 + sizeof(uint64_t),
+ 0x2100); // mock return_address(next_pc)
+
+ memory_fake_->SetData64(0x2200, 0);
+
+ maps_->Add(0x1000, 0x12000, 0, PROT_READ | PROT_WRITE, "libmock.so");
+
+ unwinder_.reset(new FramePointerUnwinder(
+ kMaxFrames, maps_.get(), regs_fake_.get(), process_memory_, kStackSize));
+ unwinder_->Unwind();
+ EXPECT_EQ(unwinder_->LastErrorCode(), unwindstack::ErrorCode::ERROR_NONE);
+ EXPECT_EQ(unwinder_->ConsumeFrames().size(), 2UL);
+}
+
+} // namespace
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 492907e..86d7b15 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -500,8 +500,12 @@
// Inform unwinder of the new data source instance, and optionally start a
// periodic task to clear its cached state.
- unwinding_worker_->PostStartDataSource(ds_id,
- ds.event_config.kernel_frames());
+ auto unwind_mode = (ds.event_config.unwind_mode() ==
+ protos::gen::PerfEventConfig::UNWIND_FRAME_POINTER)
+ ? Unwinder::UnwindMode::kFramePointer
+ : Unwinder::UnwindMode::kUnwindStack;
+ unwinding_worker_->PostStartDataSource(ds_id, ds.event_config.kernel_frames(),
+ unwind_mode);
if (ds.event_config.unwind_state_clear_period_ms()) {
unwinding_worker_->PostClearCachedStatePeriodic(
ds_id, ds.event_config.unwind_state_clear_period_ms());
diff --git a/src/profiling/perf/traced_perf.cc b/src/profiling/perf/traced_perf.cc
index 7664053..f08d866 100644
--- a/src/profiling/perf/traced_perf.cc
+++ b/src/profiling/perf/traced_perf.cc
@@ -15,8 +15,14 @@
*/
#include "src/profiling/perf/traced_perf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "perfetto/ext/base/getopt.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/base/version.h"
#include "perfetto/tracing/default_socket.h"
#include "src/profiling/perf/perf_producer.h"
#include "src/profiling/perf/proc_descriptors.h"
@@ -41,7 +47,40 @@
} // namespace
// TODO(rsavitski): watchdog.
-int TracedPerfMain(int, char**) {
+int TracedPerfMain(int argc, char** argv) {
+ enum LongOption {
+ OPT_BACKGROUND = 1000,
+ OPT_VERSION,
+ };
+
+ bool background = false;
+
+ static const option long_options[] = {
+ {"background", no_argument, nullptr, OPT_BACKGROUND},
+ {"version", no_argument, nullptr, OPT_VERSION},
+ {nullptr, 0, nullptr, 0}};
+
+ for (;;) {
+ int option = getopt_long(argc, argv, "", long_options, nullptr);
+ if (option == -1)
+ break;
+ switch (option) {
+ case OPT_BACKGROUND:
+ background = true;
+ break;
+ case OPT_VERSION:
+ printf("%s\n", base::GetVersionString());
+ return 0;
+ default:
+ fprintf(stderr, "Usage: %s [--background] [--version]\n", argv[0]);
+ return 1;
+ }
+ }
+
+ if (background) {
+ base::Daemonize([] { return 0; });
+ }
+
base::UnixTaskRunner task_runner;
// TODO(rsavitski): support standalone --root or similar on android.
diff --git a/src/profiling/perf/unwinding.cc b/src/profiling/perf/unwinding.cc
index 53023a8..7ead30f 100644
--- a/src/profiling/perf/unwinding.cc
+++ b/src/profiling/perf/unwinding.cc
@@ -25,6 +25,7 @@
#include "perfetto/ext/base/no_destructor.h"
#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/ext/base/utils.h"
+#include "src/profiling/perf/frame_pointer_unwinder.h"
namespace {
constexpr size_t kUnwindingMaxFrames = 1000;
@@ -43,18 +44,23 @@
}
void Unwinder::PostStartDataSource(DataSourceInstanceID ds_id,
- bool kernel_frames) {
+ bool kernel_frames,
+ UnwindMode unwind_mode) {
// No need for a weak pointer as the associated task runner quits (stops
// running tasks) strictly before the Unwinder's destruction.
- task_runner_->PostTask(
- [this, ds_id, kernel_frames] { StartDataSource(ds_id, kernel_frames); });
+ task_runner_->PostTask([this, ds_id, kernel_frames, unwind_mode] {
+ StartDataSource(ds_id, kernel_frames, unwind_mode);
+ });
}
-void Unwinder::StartDataSource(DataSourceInstanceID ds_id, bool kernel_frames) {
+void Unwinder::StartDataSource(DataSourceInstanceID ds_id,
+ bool kernel_frames,
+ UnwindMode unwind_mode) {
PERFETTO_DCHECK_THREAD(thread_checker_);
PERFETTO_DLOG("Unwinder::StartDataSource(%zu)", static_cast<size_t>(ds_id));
- auto it_and_inserted = data_sources_.emplace(ds_id, DataSourceState{});
+ auto it_and_inserted =
+ data_sources_.emplace(ds_id, DataSourceState{unwind_mode});
PERFETTO_DCHECK(it_and_inserted.second);
if (kernel_frames) {
@@ -297,8 +303,9 @@
(proc_state.unwind_state.has_value()
? &proc_state.unwind_state.value()
: nullptr);
- CompletedSample unwound_sample = UnwindSample(
- entry.sample, opt_user_state, proc_state.attempted_unwinding);
+ CompletedSample unwound_sample =
+ UnwindSample(entry.sample, opt_user_state,
+ proc_state.attempted_unwinding, ds.unwind_mode);
proc_state.attempted_unwinding = true;
PERFETTO_METATRACE_COUNTER(TAG_PRODUCER, PROFILER_UNWIND_CURRENT_PID, 0);
@@ -334,7 +341,8 @@
CompletedSample Unwinder::UnwindSample(const ParsedSample& sample,
UnwindingMetadata* opt_user_state,
- bool pid_unwound_before) {
+ bool pid_unwound_before,
+ UnwindMode unwind_mode) {
PERFETTO_DCHECK_THREAD(thread_checker_);
CompletedSample ret;
@@ -375,7 +383,7 @@
UnwindResult& operator=(UnwindResult&&) = default;
};
auto attempt_unwind = [&sample, unwind_state, pid_unwound_before,
- &overlay_memory]() -> UnwindResult {
+ &overlay_memory, unwind_mode]() -> UnwindResult {
metatrace::ScopedEvent m(metatrace::TAG_PRODUCER,
pid_unwound_before
? metatrace::PROFILER_UNWIND_ATTEMPT
@@ -384,16 +392,29 @@
// Unwindstack clobbers registers, so make a copy in case of retries.
auto regs_copy = std::unique_ptr<unwindstack::Regs>{sample.regs->Clone()};
- unwindstack::Unwinder unwinder(kUnwindingMaxFrames, &unwind_state->fd_maps,
- regs_copy.get(), overlay_memory);
+ switch (unwind_mode) {
+ case UnwindMode::kFramePointer: {
+ FramePointerUnwinder unwinder(kUnwindingMaxFrames,
+ &unwind_state->fd_maps, regs_copy.get(),
+ overlay_memory, sample.stack.size());
+ unwinder.Unwind();
+ return {unwinder.LastErrorCode(), unwinder.warnings(),
+ unwinder.ConsumeFrames()};
+ }
+ case UnwindMode::kUnwindStack: {
+ unwindstack::Unwinder unwinder(kUnwindingMaxFrames,
+ &unwind_state->fd_maps, regs_copy.get(),
+ overlay_memory);
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
- unwinder.SetJitDebug(unwind_state->GetJitDebug(regs_copy->Arch()));
- unwinder.SetDexFiles(unwind_state->GetDexFiles(regs_copy->Arch()));
+ unwinder.SetJitDebug(unwind_state->GetJitDebug(regs_copy->Arch()));
+ unwinder.SetDexFiles(unwind_state->GetDexFiles(regs_copy->Arch()));
#endif
- unwinder.Unwind(/*initial_map_names_to_skip=*/nullptr,
- /*map_suffixes_to_ignore=*/nullptr);
- return {unwinder.LastErrorCode(), unwinder.warnings(),
- unwinder.ConsumeFrames()};
+ unwinder.Unwind(/*initial_map_names_to_skip=*/nullptr,
+ /*map_suffixes_to_ignore=*/nullptr);
+ return {unwinder.LastErrorCode(), unwinder.warnings(),
+ unwinder.ConsumeFrames()};
+ }
+ }
};
// first unwind attempt
diff --git a/src/profiling/perf/unwinding.h b/src/profiling/perf/unwinding.h
index 8295fb8..13a7d6d 100644
--- a/src/profiling/perf/unwinding.h
+++ b/src/profiling/perf/unwinding.h
@@ -75,6 +75,8 @@
public:
friend class UnwinderHandle;
+ enum class UnwindMode { kUnwindStack, kFramePointer };
+
// Callbacks from the unwinder to the primary producer thread.
class Delegate {
public:
@@ -89,7 +91,9 @@
~Unwinder() { PERFETTO_DCHECK_THREAD(thread_checker_); }
- void PostStartDataSource(DataSourceInstanceID ds_id, bool kernel_frames);
+ void PostStartDataSource(DataSourceInstanceID ds_id,
+ bool kernel_frames,
+ UnwindMode unwind_mode);
void PostAdoptProcDescriptors(DataSourceInstanceID ds_id,
pid_t pid,
base::ScopedFile maps_fd,
@@ -144,8 +148,11 @@
struct DataSourceState {
enum class Status { kActive, kShuttingDown };
+ explicit DataSourceState(UnwindMode _unwind_mode)
+ : unwind_mode(_unwind_mode) {}
Status status = Status::kActive;
+ const UnwindMode unwind_mode;
std::map<pid_t, ProcessState> process_states;
};
@@ -163,7 +170,9 @@
// Marks the data source as valid and active at the unwinding stage.
// Initializes kernel address symbolization if needed.
- void StartDataSource(DataSourceInstanceID ds_id, bool kernel_frames);
+ void StartDataSource(DataSourceInstanceID ds_id,
+ bool kernel_frames,
+ UnwindMode unwind_mode);
void AdoptProcDescriptors(DataSourceInstanceID ds_id,
pid_t pid,
@@ -184,7 +193,8 @@
CompletedSample UnwindSample(const ParsedSample& sample,
UnwindingMetadata* opt_user_state,
- bool pid_unwound_before);
+ bool pid_unwound_before,
+ UnwindMode unwind_mode);
// Returns a list of symbolized kernel frames in the sample (if any).
std::vector<unwindstack::FrameData> SymbolizeKernelCallchain(
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index dc6d6b9..7eeadfb 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -536,3 +536,6 @@
pixel_mm/pixel_mm_kswapd_done
sched/sched_wakeup_task_attr
devfreq/devfreq_frequency
+cpm_trace/param_set_value_cpm
+fs/do_sys_open
+fs/open_exec
diff --git a/src/trace_processor/importers/common/track_tracker.cc b/src/trace_processor/importers/common/track_tracker.cc
index 8b3b00c..6b02ff8 100644
--- a/src/trace_processor/importers/common/track_tracker.cc
+++ b/src/trace_processor/importers/common/track_tracker.cc
@@ -24,7 +24,6 @@
#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_view.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/common/cpu_tracker.h"
#include "src/trace_processor/importers/common/process_track_translation_table.h"
@@ -74,33 +73,27 @@
bool IsLegacyCharArrayNameAllowed(tracks::TrackClassification classification) {
// **DO NOT** add new values here. Use TrackTracker::AutoName instead.
- return classification == tracks::triggers ||
- classification == tracks::interconnect_events ||
- classification == tracks::linux_rpm ||
- classification == tracks::cpu_irq ||
- classification == tracks::cpu_softirq ||
- classification == tracks::cpu_napi_gro ||
- classification == tracks::cpu_funcgraph ||
- classification == tracks::cpu_mali_irq ||
- classification == tracks::pkvm_hypervisor ||
+ return classification == tracks::cpu_capacity ||
classification == tracks::cpu_frequency ||
classification == tracks::cpu_frequency_throttle ||
+ classification == tracks::cpu_funcgraph ||
classification == tracks::cpu_idle ||
- classification == tracks::cpu_user_time ||
- classification == tracks::cpu_nice_user_time ||
- classification == tracks::cpu_system_mode_time ||
- classification == tracks::cpu_idle_time ||
- classification == tracks::cpu_io_wait_time ||
- classification == tracks::cpu_irq_time ||
- classification == tracks::cpu_softirq_time ||
- classification == tracks::irq_counter ||
- classification == tracks::softirq_counter ||
- classification == tracks::cpu_utilization ||
- classification == tracks::cpu_capacity ||
- classification == tracks::cpu_nr_running ||
+ classification == tracks::cpu_irq ||
+ classification == tracks::cpu_mali_irq ||
classification == tracks::cpu_max_frequency_limit ||
classification == tracks::cpu_min_frequency_limit ||
- classification == tracks::gpu_frequency;
+ classification == tracks::cpu_napi_gro ||
+ classification == tracks::cpu_nr_running ||
+ classification == tracks::cpu_stat ||
+ classification == tracks::cpu_softirq ||
+ classification == tracks::cpu_utilization ||
+ classification == tracks::gpu_frequency ||
+ classification == tracks::interconnect_events ||
+ classification == tracks::irq_counter ||
+ classification == tracks::linux_rpm ||
+ classification == tracks::pkvm_hypervisor ||
+ classification == tracks::softirq_counter ||
+ classification == tracks::triggers;
}
} // namespace
diff --git a/src/trace_processor/importers/common/tracks.h b/src/trace_processor/importers/common/tracks.h
index dc42c2c..ce4fbcd 100644
--- a/src/trace_processor/importers/common/tracks.h
+++ b/src/trace_processor/importers/common/tracks.h
@@ -41,20 +41,14 @@
F(cpu_funcgraph) \
F(cpu_idle_state) \
F(cpu_idle) \
- F(cpu_idle_time) \
- F(cpu_io_wait_time) \
- F(cpu_irq_time) \
F(cpu_irq) \
+ F(cpu_nr_running) \
F(cpu_mali_irq) \
F(cpu_max_frequency_limit) \
F(cpu_min_frequency_limit) \
F(cpu_napi_gro) \
- F(cpu_nice_user_time) \
- F(cpu_nr_running) \
- F(cpu_softirq_time) \
F(cpu_softirq) \
- F(cpu_system_mode_time) \
- F(cpu_user_time) \
+ F(cpu_stat) \
F(cpu_utilization) \
F(gpu_frequency) \
F(interconnect_events) \
@@ -62,6 +56,7 @@
F(legacy_chrome_global_instants) \
F(linux_device_frequency) \
F(linux_rpm) \
+ F(pixel_cpm_trace) \
F(pkvm_hypervisor) \
F(softirq_counter) \
F(thread) \
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 1d31d7d..c9d3970 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
namespace trace_processor {
namespace {
-std::array<FtraceMessageDescriptor, 543> descriptors{{
+std::array<FtraceMessageDescriptor, 546> descriptors{{
{nullptr, 0, {}},
{nullptr, 0, {}},
{nullptr, 0, {}},
@@ -6006,6 +6006,34 @@
{"type", ProtoSchemaType::kInt32},
},
},
+ {
+ "param_set_value_cpm",
+ 3,
+ {
+ {},
+ {"body", ProtoSchemaType::kString},
+ {"value", ProtoSchemaType::kUint32},
+ {"timestamp", ProtoSchemaType::kInt64},
+ },
+ },
+ {
+ "do_sys_open",
+ 3,
+ {
+ {},
+ {"filename", ProtoSchemaType::kString},
+ {"flags", ProtoSchemaType::kInt32},
+ {"mode", ProtoSchemaType::kInt32},
+ },
+ },
+ {
+ "open_exec",
+ 1,
+ {
+ {},
+ {"filename", ProtoSchemaType::kString},
+ },
+ },
}};
} // namespace
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 07dbe31..59b2446 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -71,6 +71,7 @@
#include "protos/perfetto/trace/ftrace/bcl_exynos.pbzero.h"
#include "protos/perfetto/trace/ftrace/binder.pbzero.h"
#include "protos/perfetto/trace/ftrace/cma.pbzero.h"
+#include "protos/perfetto/trace/ftrace/cpm_trace.pbzero.h"
#include "protos/perfetto/trace/ftrace/cpuhp.pbzero.h"
#include "protos/perfetto/trace/ftrace/cros_ec.pbzero.h"
#include "protos/perfetto/trace/ftrace/dcvsh.pbzero.h"
@@ -660,6 +661,28 @@
}
}
+ protos::pbzero::FtraceKprobeStats::Decoder kprobe_stats(evt.kprobe_stats());
+ storage->SetStats(stats::ftrace_kprobe_hits_begin + phase,
+ kprobe_stats.hits());
+ storage->SetStats(stats::ftrace_kprobe_misses_begin + phase,
+ kprobe_stats.misses());
+ if (is_end) {
+ auto kprobe_hits_begin = storage->GetStats(stats::ftrace_kprobe_hits_begin);
+ auto kprobe_hits_end = kprobe_stats.hits();
+ if (kprobe_hits_begin) {
+ int64_t delta_hits = kprobe_hits_end - kprobe_hits_begin;
+ storage->SetStats(stats::ftrace_kprobe_hits_delta, delta_hits);
+ }
+
+ auto kprobe_misses_begin =
+ storage->GetStats(stats::ftrace_kprobe_misses_begin);
+ auto kprobe_misses_end = kprobe_stats.misses();
+ if (kprobe_misses_begin) {
+ int64_t delta_misses = kprobe_misses_end - kprobe_misses_begin;
+ storage->SetStats(stats::ftrace_kprobe_misses_delta, delta_misses);
+ }
+ }
+
// Compute atrace + ftrace setup errors. We do two things here:
// 1. We add up all the errors and put the counter in the stats table (which
// can hold only numerals).
@@ -1350,6 +1373,10 @@
ParseKprobe(ts, pid, fld_bytes);
break;
}
+ case FtraceEvent::kParamSetValueCpmFieldNumber: {
+ ParseParamSetValueCpm(fld_bytes);
+ break;
+ }
default:
break;
}
@@ -2241,6 +2268,22 @@
// family) and thread creation (clone(CLONE_THREAD, ...)).
static const uint32_t kCloneThread = 0x00010000; // From kernel's sched.h.
+ if (PERFETTO_UNLIKELY(new_tid == 0)) {
+ // In the case of boot-time tracing (kernel is started with tracing
+ // enabled), the ftrace buffer will see /bin/init creating swapper/0 tasks:
+ // event {
+ // pid: 1
+ // task_newtask {
+ // pid: 0
+ // comm: "swapper/0"
+ // }
+ // }
+ // Skip these task_newtask events since they are kernel idle tasks.
+ PERFETTO_DCHECK(source_tid == 1);
+ PERFETTO_DCHECK(base::StartsWith(evt.comm().ToStdString(), "swapper"));
+ return;
+ }
+
// If the process is a fork, start a new process.
if ((clone_flags & kCloneThread) == 0) {
// This is a plain-old fork() or equivalent.
@@ -3814,4 +3857,16 @@
track_id);
}
+void FtraceParser::ParseParamSetValueCpm(protozero::ConstBytes blob) {
+ protos::pbzero::ParamSetValueCpmFtraceEvent::Decoder event(blob);
+ TrackTracker::DimensionsBuilder dims_builder =
+ context_->track_tracker->CreateDimensionsBuilder();
+ // Store event body which denotes the name of the track.
+ dims_builder.AppendName(context_->storage->InternString(event.body()));
+ TrackId track_id = context_->track_tracker->InternTrack(
+ tracks::pixel_cpm_trace, std::move(dims_builder).Build());
+ context_->event_tracker->PushCounter(static_cast<int64_t>(event.timestamp()),
+ event.value(), track_id);
+}
+
} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 335649d..6a08f65 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -317,6 +317,7 @@
void ParseGoogleIccEvent(int64_t timestamp, protozero::ConstBytes);
void ParseGoogleIrmEvent(int64_t timestamp, protozero::ConstBytes);
void ParseDeviceFrequency(int64_t ts, protozero::ConstBytes blob);
+ void ParseParamSetValueCpm(protozero::ConstBytes blob);
TraceProcessorContext* context_;
RssStatTracker rss_stat_tracker_;
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 5c6c2e3..50a85da 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -43,6 +43,7 @@
#include "src/trace_processor/util/status_macros.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "protos/perfetto/trace/ftrace/cpm_trace.pbzero.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
#include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
#include "protos/perfetto/trace/ftrace/power.pbzero.h"
@@ -279,6 +280,11 @@
TokenizeFtraceThermalExynosAcpmBulk(cpu, std::move(event),
std::move(state));
return;
+ } else if (PERFETTO_UNLIKELY(event_id ==
+ protos::pbzero::FtraceEvent::
+ kParamSetValueCpmFieldNumber)) {
+ TokenizeFtraceParamSetValueCpm(cpu, std::move(event), std::move(state));
+ return;
}
auto timestamp = context_->clock_tracker->ToTraceTime(
@@ -448,19 +454,12 @@
RefPtr<PacketSequenceStateGeneration> state) {
// Special handling of valid gpu_work_period tracepoint events which contain
// timestamp values for the GPU time period nested inside the event data.
- const uint8_t* data = event.data();
- const size_t length = event.length();
-
- ProtoDecoder decoder(data, length);
- auto ts_field =
- decoder.FindField(protos::pbzero::FtraceEvent::kGpuWorkPeriodFieldNumber);
- if (!ts_field.valid()) {
- context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
- return;
- }
+ auto ts_field = GetFtraceEventField(
+ protos::pbzero::FtraceEvent::kGpuWorkPeriodFieldNumber, event);
+ if (!ts_field.has_value()) return;
protos::pbzero::GpuWorkPeriodFtraceEvent::Decoder gpu_work_event(
- ts_field.data(), ts_field.size());
+ ts_field.value().data(), ts_field.value().size());
if (!gpu_work_event.has_start_time_ns()) {
context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
return;
@@ -490,19 +489,13 @@
RefPtr<PacketSequenceStateGeneration> state) {
// Special handling of valid thermal_exynos_acpm_bulk tracepoint events which
// contains the right timestamp value nested inside the event data.
- const uint8_t* data = event.data();
- const size_t length = event.length();
-
- ProtoDecoder decoder(data, length);
- auto ts_field = decoder.FindField(
- protos::pbzero::FtraceEvent::kThermalExynosAcpmBulkFieldNumber);
- if (!ts_field.valid()) {
- context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
- return;
- }
+ auto ts_field = GetFtraceEventField(
+ protos::pbzero::FtraceEvent::kThermalExynosAcpmBulkFieldNumber, event);
+ if (!ts_field.has_value()) return;
protos::pbzero::ThermalExynosAcpmBulkFtraceEvent::Decoder
- thermal_exynos_acpm_bulk_event(ts_field.data(), ts_field.size());
+ thermal_exynos_acpm_bulk_event(ts_field.value().data(),
+ ts_field.value().size());
if (!thermal_exynos_acpm_bulk_event.has_timestamp()) {
context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
return;
@@ -513,5 +506,42 @@
std::move(state), context_->machine_id());
}
+void FtraceTokenizer::TokenizeFtraceParamSetValueCpm(
+ uint32_t cpu, TraceBlobView event,
+ RefPtr<PacketSequenceStateGeneration> state) {
+ // Special handling of valid param_set_value_cpm tracepoint events which
+ // contains the right timestamp value nested inside the event data.
+ auto ts_field = GetFtraceEventField(
+ protos::pbzero::FtraceEvent::kParamSetValueCpmFieldNumber, event);
+ if (!ts_field.has_value()) return;
+
+ protos::pbzero::ParamSetValueCpmFtraceEvent::Decoder
+ param_set_value_cpm_event(ts_field.value().data(),
+ ts_field.value().size());
+ if (!param_set_value_cpm_event.has_timestamp()) {
+ context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+ return;
+ }
+ int64_t timestamp =
+ static_cast<int64_t>(param_set_value_cpm_event.timestamp());
+ context_->sorter->PushFtraceEvent(cpu, timestamp, std::move(event),
+ std::move(state), context_->machine_id());
+}
+
+std::optional<protozero::Field> FtraceTokenizer::GetFtraceEventField(
+ uint32_t event_id, const TraceBlobView& event) {
+ // Extract ftrace event field by decoding event trace blob.
+ const uint8_t* data = event.data();
+ const size_t length = event.length();
+
+ ProtoDecoder decoder(data, length);
+ auto ts_field = decoder.FindField(event_id);
+ if (!ts_field.valid()) {
+ context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+ return std::nullopt;
+ }
+ return ts_field;
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
index 6aff47d..d8780b1 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.h
@@ -17,6 +17,7 @@
#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_TOKENIZER_H_
+#include <optional>
#include <vector>
#include "perfetto/trace_processor/trace_blob_view.h"
@@ -68,6 +69,11 @@
uint32_t cpu,
TraceBlobView event,
RefPtr<PacketSequenceStateGeneration> state);
+ void TokenizeFtraceParamSetValueCpm(
+ uint32_t cpu, TraceBlobView event,
+ RefPtr<PacketSequenceStateGeneration> state);
+ std::optional<protozero::Field> GetFtraceEventField(
+ uint32_t event_id, const TraceBlobView& event);
void DlogWithLimit(const base::Status& status) {
static std::atomic<uint32_t> dlog_count(0);
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index 705fcfb..8ec36af 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -748,7 +748,9 @@
}
base::Status JsonTraceTokenizer::NotifyEndOfFile() {
- return position_ == TracePosition::kEof
+ return position_ == TracePosition::kEof ||
+ (position_ == TracePosition::kInsideTraceEventsArray &&
+ format_ == TraceFormat::kOnlyTraceEvents)
? base::OkStatus()
: base::ErrStatus("JSON trace file is incomplete");
}
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 2da16c9..7c3e9f9 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -147,8 +147,10 @@
TrackTracker::Group::kPower, batt_power_id);
auto current = evt.current_ua();
auto voltage = evt.voltage_uv();
- context_->event_tracker->PushCounter(
- ts, static_cast<double>(current * voltage / 1000000000), track);
+ // Current is negative when discharging, but we want the power counter to
+ // always be positive, so take the absolute value.
+ auto power = std::abs(static_cast<double>(current * voltage / 1000000000));
+ context_->event_tracker->PushCounter(ts, power, track);
}
}
diff --git a/src/trace_processor/importers/proto/atoms.descriptor b/src/trace_processor/importers/proto/atoms.descriptor
index b318814..85c6ca8 100644
--- a/src/trace_processor/importers/proto/atoms.descriptor
+++ b/src/trace_processor/importers/proto/atoms.descriptor
Binary files differ
diff --git a/src/trace_processor/importers/proto/chrome_string_lookup.cc b/src/trace_processor/importers/proto/chrome_string_lookup.cc
index f3e1125..f688510 100644
--- a/src/trace_processor/importers/proto/chrome_string_lookup.cc
+++ b/src/trace_processor/importers/proto/chrome_string_lookup.cc
@@ -170,6 +170,7 @@
"NetworkConfigWatcher"},
{ChromeThreadDescriptor::THREAD_WASAPI_RENDER, "wasapi_render_thread"},
{ChromeThreadDescriptor::THREAD_LOADER_LOCK_SAMPLER, "LoaderLockSampler"},
+ {ChromeThreadDescriptor::THREAD_COMPOSITOR_GPU, "CompositorGpuThread"},
};
} // namespace
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 5d4c236..08212a8 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -165,6 +165,7 @@
thermal_unit_id_(context->storage->InternString("C")),
gpufreq_id(context->storage->InternString("gpufreq")),
gpufreq_unit_id(context->storage->InternString("MHz")),
+ cpu_stat_counter_name_id_(context->storage->InternString("counter_name")),
arm_cpu_implementer(
context->storage->InternString("arm_cpu_implementer")),
arm_cpu_architecture(
@@ -376,47 +377,42 @@
continue;
}
- TrackId track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_user_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.user_ns"});
- context_->event_tracker->PushCounter(ts, static_cast<double>(ct.user_ns()),
- track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_nice_user_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.user_nice_ns"});
+ auto ucpu = context_->cpu_tracker->GetOrCreateCpu(ct.cpu_id());
+ auto intern_track =
+ [&, this](TrackTracker::LegacyCharArrayName name) -> TrackId {
+ auto builder = context_->track_tracker->CreateDimensionsBuilder();
+ builder.AppendDimension(
+ cpu_stat_counter_name_id_,
+ Variadic::String(context_->storage->InternString(name.name)));
+ builder.AppendUcpu(ucpu);
+ return context_->track_tracker->InternCounterTrack(
+ tracks::cpu_stat, std::move(builder).Build(), name);
+ };
context_->event_tracker->PushCounter(
- ts, static_cast<double>(ct.user_nice_ns()), track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_system_mode_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.system_mode_ns"});
+ ts, static_cast<double>(ct.user_ns()),
+ intern_track(TrackTracker::LegacyCharArrayName{"cpu.times.user_ns"}));
context_->event_tracker->PushCounter(
- ts, static_cast<double>(ct.system_mode_ns()), track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_idle_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.idle_ns"});
- context_->event_tracker->PushCounter(ts, static_cast<double>(ct.idle_ns()),
- track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_io_wait_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.io_wait_ns"});
+ ts, static_cast<double>(ct.user_nice_ns()),
+ intern_track(
+ TrackTracker::LegacyCharArrayName{"cpu.times.user_nice_ns"}));
context_->event_tracker->PushCounter(
- ts, static_cast<double>(ct.io_wait_ns()), track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_irq_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.irq_ns"});
- context_->event_tracker->PushCounter(ts, static_cast<double>(ct.irq_ns()),
- track);
-
- track = context_->track_tracker->InternCpuCounterTrack(
- tracks::cpu_softirq_time, ct.cpu_id(),
- TrackTracker::LegacyCharArrayName{"cpu.times.softirq_ns"});
+ ts, static_cast<double>(ct.system_mode_ns()),
+ intern_track(
+ TrackTracker::LegacyCharArrayName{"cpu.times.system_mode_ns"}));
context_->event_tracker->PushCounter(
- ts, static_cast<double>(ct.softirq_ns()), track);
+ ts, static_cast<double>(ct.idle_ns()),
+ intern_track(TrackTracker::LegacyCharArrayName{"cpu.times.idle_ns"}));
+ context_->event_tracker->PushCounter(
+ ts, static_cast<double>(ct.io_wait_ns()),
+ intern_track(
+ TrackTracker::LegacyCharArrayName{"cpu.times.io_wait_ns"}));
+ context_->event_tracker->PushCounter(
+ ts, static_cast<double>(ct.irq_ns()),
+ intern_track(TrackTracker::LegacyCharArrayName{"cpu.times.irq_ns"}));
+ context_->event_tracker->PushCounter(
+ ts, static_cast<double>(ct.softirq_ns()),
+ intern_track(
+ TrackTracker::LegacyCharArrayName{"cpu.times.softirq_ns"}));
}
for (auto it = sys_stats.num_irq(); it; ++it) {
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index 1aa1ba1..87b5734 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -18,14 +18,15 @@
#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_SYSTEM_PROBES_PARSER_H_
#include <array>
+#include <cstddef>
+#include <cstdint>
#include <vector>
#include "perfetto/protozero/field.h"
#include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
#include "src/trace_processor/storage/trace_storage.h"
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
class TraceProcessorContext;
@@ -37,7 +38,7 @@
explicit SystemProbesParser(TraceProcessorContext*);
void ParseProcessTree(ConstBytes);
- void ParseProcessStats(int64_t timestamp, ConstBytes);
+ void ParseProcessStats(int64_t ts, ConstBytes);
void ParseSysStats(int64_t ts, ConstBytes);
void ParseSystemInfo(ConstBytes);
void ParseCpuInfo(ConstBytes);
@@ -64,6 +65,8 @@
const StringId gpufreq_id;
const StringId gpufreq_unit_id;
+ const StringId cpu_stat_counter_name_id_;
+
// Arm CPU identifier string IDs
const StringId arm_cpu_implementer;
const StringId arm_cpu_architecture;
@@ -97,7 +100,6 @@
int64_t prev_flush_time = -1;
};
-} // namespace trace_processor
-} // namespace perfetto
+} // namespace perfetto::trace_processor
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_SYSTEM_PROBES_PARSER_H_
diff --git a/src/trace_processor/importers/proto/winscope/BUILD.gn b/src/trace_processor/importers/proto/winscope/BUILD.gn
index e04c4f0..ba4452c 100644
--- a/src/trace_processor/importers/proto/winscope/BUILD.gn
+++ b/src/trace_processor/importers/proto/winscope/BUILD.gn
@@ -55,6 +55,7 @@
"../../common:parser_types",
"../../proto:minimal",
"../../proto:packet_sequence_state_generation_hdr",
+ "../../../util:winscope_proto_mapping"
]
}
diff --git a/src/trace_processor/importers/proto/winscope/android_input_event_parser.cc b/src/trace_processor/importers/proto/winscope/android_input_event_parser.cc
index 03bf5ad..e3b2553 100644
--- a/src/trace_processor/importers/proto/winscope/android_input_event_parser.cc
+++ b/src/trace_processor/importers/proto/winscope/android_input_event_parser.cc
@@ -16,13 +16,14 @@
#include "src/trace_processor/importers/proto/winscope/android_input_event_parser.h"
+#include "perfetto/ext/base/base64.h"
#include "protos/perfetto/trace/android/android_input_event.pbzero.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/proto/args_parser.h"
-#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/android_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto::trace_processor {
@@ -33,10 +34,7 @@
using perfetto::protos::pbzero::TracePacket;
AndroidInputEventParser::AndroidInputEventParser(TraceProcessorContext* context)
- : context_(*context), args_parser_{pool_} {
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
-}
+ : context_(*context), args_parser_{*context->descriptor_pool_} {}
void AndroidInputEventParser::ParseAndroidInputEvent(
int64_t packet_ts,
@@ -80,6 +78,10 @@
tables::AndroidMotionEventsTable::Row event_row;
event_row.event_id = event_proto.event_id();
event_row.ts = packet_ts;
+ event_row.base64_proto =
+ context_.storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(bytes.data, bytes.size)));
+ event_row.base64_proto_id = event_row.base64_proto.raw_id();
auto event_row_id = context_.storage->mutable_android_motion_events_table()
->Insert(event_row)
@@ -88,7 +90,9 @@
ArgsParser writer{packet_ts, inserter, *context_.storage};
base::Status status =
- args_parser_.ParseMessage(bytes, ".perfetto.protos.AndroidMotionEvent",
+ args_parser_.ParseMessage(bytes,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::AndroidMotionEventsTable::Name()),
nullptr /*parse all fields*/, writer);
if (!status.ok())
context_.storage->IncrementStats(stats::android_input_event_parse_errors);
@@ -101,6 +105,10 @@
tables::AndroidKeyEventsTable::Row event_row;
event_row.event_id = event_proto.event_id();
event_row.ts = packet_ts;
+ event_row.base64_proto =
+ context_.storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(bytes.data, bytes.size)));
+ event_row.base64_proto_id = event_row.base64_proto.raw_id();
auto event_row_id = context_.storage->mutable_android_key_events_table()
->Insert(event_row)
@@ -109,7 +117,9 @@
ArgsParser writer{packet_ts, inserter, *context_.storage};
base::Status status =
- args_parser_.ParseMessage(bytes, ".perfetto.protos.AndroidKeyEvent",
+ args_parser_.ParseMessage(bytes,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::AndroidKeyEventsTable::Name()),
nullptr /*parse all fields*/, writer);
if (!status.ok())
context_.storage->IncrementStats(stats::android_input_event_parse_errors);
@@ -123,16 +133,23 @@
event_row.event_id = event_proto.event_id();
event_row.vsync_id = event_proto.vsync_id();
event_row.window_id = event_proto.window_id();
+ event_row.base64_proto =
+ context_.storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(bytes.data, bytes.size)));
+ event_row.base64_proto_id = event_row.base64_proto.raw_id();
auto event_row_id =
context_.storage->mutable_android_input_event_dispatch_table()
->Insert(event_row)
.id;
+
auto inserter = context_.args_tracker->AddArgsTo(event_row_id);
ArgsParser writer{packet_ts, inserter, *context_.storage};
base::Status status = args_parser_.ParseMessage(
- bytes, ".perfetto.protos.AndroidWindowInputDispatchEvent",
+ bytes,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::AndroidInputEventDispatchTable::Name()),
nullptr /*parse all fields*/, writer);
if (!status.ok())
context_.storage->IncrementStats(stats::android_input_event_parse_errors);
diff --git a/src/trace_processor/importers/proto/winscope/android_input_event_parser.h b/src/trace_processor/importers/proto/winscope/android_input_event_parser.h
index 96f523b..12ce63b 100644
--- a/src/trace_processor/importers/proto/winscope/android_input_event_parser.h
+++ b/src/trace_processor/importers/proto/winscope/android_input_event_parser.h
@@ -36,7 +36,6 @@
private:
TraceProcessorContext& context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
void ParseMotionEvent(int64_t packet_ts, const protozero::ConstBytes& bytes);
diff --git a/src/trace_processor/importers/proto/winscope/protolog_parser.cc b/src/trace_processor/importers/proto/winscope/protolog_parser.cc
index 8c3b4a4..e9e0c83 100644
--- a/src/trace_processor/importers/proto/winscope/protolog_parser.cc
+++ b/src/trace_processor/importers/proto/winscope/protolog_parser.cc
@@ -35,7 +35,6 @@
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/importers/proto/winscope/protolog_message_decoder.h"
-#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/winscope_tables_py.h"
@@ -45,7 +44,7 @@
ProtoLogParser::ProtoLogParser(TraceProcessorContext* context)
: context_(context),
- args_parser_{pool_},
+ args_parser_{*context_->descriptor_pool_},
log_level_debug_string_id_(context->storage->InternString("DEBUG")),
log_level_verbose_string_id_(context->storage->InternString("VERBOSE")),
log_level_info_string_id_(context->storage->InternString("INFO")),
@@ -53,8 +52,6 @@
log_level_error_string_id_(context->storage->InternString("ERROR")),
log_level_wtf_string_id_(context->storage->InternString("WTF")),
log_level_unknown_string_id_(context_->storage->InternString("UNKNOWN")) {
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
}
void ProtoLogParser::ParseProtoLogMessage(
diff --git a/src/trace_processor/importers/proto/winscope/protolog_parser.h b/src/trace_processor/importers/proto/winscope/protolog_parser.h
index 13348d6..98b71c8 100644
--- a/src/trace_processor/importers/proto/winscope/protolog_parser.h
+++ b/src/trace_processor/importers/proto/winscope/protolog_parser.h
@@ -48,7 +48,6 @@
std::optional<std::string>& location);
TraceProcessorContext* const context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
const StringId log_level_debug_string_id_;
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
index 12618ac..07fef5e 100644
--- a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
@@ -17,21 +17,19 @@
#include "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h"
#include "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h"
+#include "perfetto/ext/base/base64.h"
#include "protos/perfetto/trace/android/shell_transition.pbzero.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/proto/args_parser.h"
-#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
ShellTransitionsParser::ShellTransitionsParser(TraceProcessorContext* context)
- : context_(context), args_parser_{pool_} {
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
-}
+ : context_(context), args_parser_{*context->descriptor_pool_} {}
void ShellTransitionsParser::ParseTransition(protozero::ConstBytes blob) {
protos::pbzero::ShellTransition::Decoder transition(blob);
@@ -48,10 +46,17 @@
row.set_ts(transition.dispatch_time_ns());
}
+ auto base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.set_base64_proto(base64_proto);
+ row.set_base64_proto_id(base64_proto.raw_id());
auto inserter = context_->args_tracker->AddArgsTo(row_id);
ArgsParser writer(/*timestamp=*/0, inserter, *context_->storage.get());
base::Status status = args_parser_.ParseMessage(
- blob, kShellTransitionsProtoName, nullptr /* parse all fields */, writer);
+ blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::WindowManagerShellTransitionsTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
@@ -72,6 +77,9 @@
row.handler_id = mapping.id();
row.handler_name = context_->storage->InternString(
base::StringView(mapping.name().ToStdString()));
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
shell_handlers_table->Insert(row);
}
}
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
index 44b86d1..3dfe529 100644
--- a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
@@ -33,11 +33,7 @@
void ParseHandlerMappings(protozero::ConstBytes);
private:
- static constexpr auto* kShellTransitionsProtoName =
- ".perfetto.protos.ShellTransition";
-
TraceProcessorContext* const context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
index 6025d91..3526750 100644
--- a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
@@ -19,6 +19,7 @@
#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/storage/metadata.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
index 07ef736..5218a7b 100644
--- a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
@@ -20,6 +20,7 @@
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc
index 20687e4..1975f5d 100644
--- a/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc
+++ b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc
@@ -16,21 +16,19 @@
#include "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h"
+#include "perfetto/ext/base/base64.h"
#include "protos/perfetto/trace/android/surfaceflinger_layers.pbzero.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/proto/args_parser.h"
-#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
SurfaceFlingerLayersParser::SurfaceFlingerLayersParser(
TraceProcessorContext* context)
- : context_{context}, args_parser_{pool_} {
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
-}
+ : context_{context}, args_parser_{*context->descriptor_pool_} {}
void SurfaceFlingerLayersParser::Parse(int64_t timestamp,
protozero::ConstBytes blob) {
@@ -38,6 +36,10 @@
blob.size);
tables::SurfaceFlingerLayersSnapshotTable::Row snapshot;
snapshot.ts = timestamp;
+ snapshot.base64_proto =
+ context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ snapshot.base64_proto_id = snapshot.base64_proto.raw_id();
auto snapshot_id =
context_->storage->mutable_surfaceflinger_layers_snapshot_table()
->Insert(snapshot)
@@ -45,9 +47,12 @@
auto inserter = context_->args_tracker->AddArgsTo(snapshot_id);
ArgsParser writer(timestamp, inserter, *context_->storage);
- base::Status status =
- args_parser_.ParseMessage(blob, kLayersSnapshotProtoName,
- &kLayersSnapshotFieldsToArgsParse, writer);
+ const auto table_name = tables::SurfaceFlingerLayersSnapshotTable::Name();
+ auto allowed_fields =
+ util::winscope_proto_mapping::GetAllowedFields(table_name);
+ base::Status status = args_parser_.ParseMessage(
+ blob, *util::winscope_proto_mapping::GetProtoName(table_name),
+ &allowed_fields.value(), writer);
if (!status.ok()) {
context_->storage->IncrementStats(stats::winscope_sf_layers_parse_errors);
}
@@ -65,14 +70,20 @@
tables::SurfaceFlingerLayersSnapshotTable::Id snapshot_id) {
tables::SurfaceFlingerLayerTable::Row layer;
layer.snapshot_id = snapshot_id;
+ layer.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ layer.base64_proto_id = layer.base64_proto.raw_id();
auto layerId =
context_->storage->mutable_surfaceflinger_layer_table()->Insert(layer).id;
ArgsTracker tracker(context_);
auto inserter = tracker.AddArgsTo(layerId);
ArgsParser writer(timestamp, inserter, *context_->storage);
- base::Status status = args_parser_.ParseMessage(
- blob, kLayerProtoName, nullptr /* parse all fields */, writer);
+ base::Status status =
+ args_parser_.ParseMessage(blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::SurfaceFlingerLayerTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(stats::winscope_sf_layers_parse_errors);
}
diff --git a/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h
index f615a8e..987eb81 100644
--- a/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h
+++ b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h
@@ -33,18 +33,11 @@
void Parse(int64_t timestamp, protozero::ConstBytes);
private:
- const std::vector<std::uint32_t> kLayersSnapshotFieldsToArgsParse{1, 2, 4, 5,
- 6, 7, 8};
- static constexpr auto* kLayersSnapshotProtoName =
- ".perfetto.protos.LayersSnapshotProto";
- static constexpr auto* kLayerProtoName = ".perfetto.protos.LayerProto";
-
void ParseLayer(int64_t timestamp,
protozero::ConstBytes blob,
tables::SurfaceFlingerLayersSnapshotTable::Id);
TraceProcessorContext* const context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc b/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc
index c2c5638..2fa14b6 100644
--- a/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc
+++ b/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc
@@ -16,27 +16,28 @@
#include "src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h"
+#include "perfetto/ext/base/base64.h"
#include "protos/perfetto/trace/android/surfaceflinger_transactions.pbzero.h"
#include "src/trace_processor/importers/common/args_tracker.h"
#include "src/trace_processor/importers/proto/args_parser.h"
-#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
SurfaceFlingerTransactionsParser::SurfaceFlingerTransactionsParser(
TraceProcessorContext* context)
- : context_{context}, args_parser_{pool_} {
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
-}
+ : context_{context}, args_parser_{*context->descriptor_pool_} {}
void SurfaceFlingerTransactionsParser::Parse(int64_t timestamp,
protozero::ConstBytes blob) {
tables::SurfaceFlingerTransactionsTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId = context_->storage->mutable_surfaceflinger_transactions_table()
->Insert(row)
.id;
@@ -44,9 +45,11 @@
ArgsTracker tracker(context_);
auto inserter = tracker.AddArgsTo(rowId);
ArgsParser writer(timestamp, inserter, *context_->storage.get());
- base::Status status =
- args_parser_.ParseMessage(blob, kTransactionTraceEntryProtoName,
- nullptr /* parse all fields */, writer);
+ base::Status status = args_parser_.ParseMessage(
+ blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::SurfaceFlingerTransactionsTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
stats::winscope_sf_transactions_parse_errors);
diff --git a/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h b/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h
index f9d45cc..b696408 100644
--- a/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h
+++ b/src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h
@@ -32,11 +32,7 @@
void Parse(int64_t timestamp, protozero::ConstBytes);
private:
- static constexpr auto* kTransactionTraceEntryProtoName =
- ".perfetto.protos.TransactionTraceEntry";
-
TraceProcessorContext* const context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.cc b/src/trace_processor/importers/proto/winscope/winscope_module.cc
index ac81193..a25a05d 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.cc
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.cc
@@ -15,11 +15,13 @@
*/
#include "src/trace_processor/importers/proto/winscope/winscope_module.h"
+#include "perfetto/ext/base/base64.h"
#include "protos/perfetto/trace/android/winscope_extensions.pbzero.h"
#include "protos/perfetto/trace/android/winscope_extensions_impl.pbzero.h"
#include "src/trace_processor/importers/proto/args_parser.h"
#include "src/trace_processor/importers/proto/winscope/viewcapture_args_parser.h"
#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
namespace perfetto {
namespace trace_processor {
@@ -29,12 +31,14 @@
WinscopeModule::WinscopeModule(TraceProcessorContext* context)
: context_{context},
- args_parser_{pool_},
+ args_parser_{*context->descriptor_pool_.get()},
surfaceflinger_layers_parser_(context),
surfaceflinger_transactions_parser_(context),
shell_transitions_parser_(context),
protolog_parser_(context),
android_input_event_parser_(context) {
+ context->descriptor_pool_->AddFromFileDescriptorSet(
+ kWinscopeDescriptor.data(), kWinscopeDescriptor.size());
RegisterForField(TracePacket::kSurfaceflingerLayersSnapshotFieldNumber,
context);
RegisterForField(TracePacket::kSurfaceflingerTransactionsFieldNumber,
@@ -44,9 +48,6 @@
RegisterForField(TracePacket::kProtologMessageFieldNumber, context);
RegisterForField(TracePacket::kProtologViewerConfigFieldNumber, context);
RegisterForField(TracePacket::kWinscopeExtensionsFieldNumber, context);
-
- pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
- kWinscopeDescriptor.size());
}
ModuleResult WinscopeModule::TokenizePacket(
@@ -135,6 +136,9 @@
protozero::ConstBytes blob) {
tables::InputMethodClientsTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId =
context_->storage->mutable_inputmethod_clients_table()->Insert(row).id;
@@ -142,7 +146,9 @@
auto inserter = tracker.AddArgsTo(rowId);
ArgsParser writer(timestamp, inserter, *context_->storage.get());
base::Status status =
- args_parser_.ParseMessage(blob, kInputMethodClientsProtoName,
+ args_parser_.ParseMessage(blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::InputMethodClientsTable::Name()),
nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
@@ -155,6 +161,9 @@
protozero::ConstBytes blob) {
tables::InputMethodManagerServiceTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId = context_->storage->mutable_inputmethod_manager_service_table()
->Insert(row)
.id;
@@ -162,9 +171,11 @@
ArgsTracker tracker(context_);
auto inserter = tracker.AddArgsTo(rowId);
ArgsParser writer(timestamp, inserter, *context_->storage.get());
- base::Status status =
- args_parser_.ParseMessage(blob, kInputMethodManagerServiceProtoName,
- nullptr /* parse all fields */, writer);
+ base::Status status = args_parser_.ParseMessage(
+ blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::InputMethodManagerServiceTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
stats::winscope_inputmethod_manager_service_parse_errors);
@@ -175,6 +186,9 @@
protozero::ConstBytes blob) {
tables::InputMethodServiceTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId =
context_->storage->mutable_inputmethod_service_table()->Insert(row).id;
@@ -182,7 +196,9 @@
auto inserter = tracker.AddArgsTo(rowId);
ArgsParser writer(timestamp, inserter, *context_->storage.get());
base::Status status =
- args_parser_.ParseMessage(blob, kInputMethodServiceProtoName,
+ args_parser_.ParseMessage(blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::InputMethodServiceTable::Name()),
nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
@@ -196,14 +212,20 @@
PacketSequenceStateGeneration* sequence_state) {
tables::ViewCaptureTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId = context_->storage->mutable_viewcapture_table()->Insert(row).id;
ArgsTracker tracker(context_);
auto inserter = tracker.AddArgsTo(rowId);
ViewCaptureArgsParser writer(timestamp, inserter, *context_->storage.get(),
sequence_state);
- base::Status status = args_parser_.ParseMessage(
- blob, kViewCaptureProtoName, nullptr /* parse all fields */, writer);
+ base::Status status =
+ args_parser_.ParseMessage(blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::ViewCaptureTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(stats::winscope_viewcapture_parse_errors);
}
@@ -213,13 +235,19 @@
protozero::ConstBytes blob) {
tables::WindowManagerTable::Row row;
row.ts = timestamp;
+ row.base64_proto = context_->storage->mutable_string_pool()->InternString(
+ base::StringView(base::Base64Encode(blob.data, blob.size)));
+ row.base64_proto_id = row.base64_proto.raw_id();
auto rowId = context_->storage->mutable_windowmanager_table()->Insert(row).id;
ArgsTracker tracker(context_);
auto inserter = tracker.AddArgsTo(rowId);
ArgsParser writer(timestamp, inserter, *context_->storage.get());
- base::Status status = args_parser_.ParseMessage(
- blob, kWindowManagerProtoName, nullptr /* parse all fields */, writer);
+ base::Status status =
+ args_parser_.ParseMessage(blob,
+ *util::winscope_proto_mapping::GetProtoName(
+ tables::WindowManagerTable::Name()),
+ nullptr /* parse all fields */, writer);
if (!status.ok()) {
context_->storage->IncrementStats(
stats::winscope_windowmanager_parse_errors);
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.h b/src/trace_processor/importers/proto/winscope/winscope_module.h
index e14be59..b6e876f 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.h
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.h
@@ -63,18 +63,7 @@
PacketSequenceStateGeneration* sequence_state);
void ParseWindowManagerData(int64_t timestamp, protozero::ConstBytes blob);
- static constexpr auto* kInputMethodClientsProtoName =
- ".perfetto.protos.InputMethodClientsTraceProto";
- static constexpr auto* kInputMethodManagerServiceProtoName =
- ".perfetto.protos.InputMethodManagerServiceTraceProto";
- static constexpr auto* kInputMethodServiceProtoName =
- ".perfetto.protos.InputMethodServiceTraceProto";
- static constexpr auto* kViewCaptureProtoName = ".perfetto.protos.ViewCapture";
- static constexpr auto* kWindowManagerProtoName =
- ".perfetto.protos.WindowManagerTraceEntry";
-
TraceProcessorContext* const context_;
- DescriptorPool pool_;
util::ProtoToArgsParser args_parser_;
SurfaceFlingerLayersParser surfaceflinger_layers_parser_;
diff --git a/src/trace_processor/metrics/metrics_unittest.cc b/src/trace_processor/metrics/metrics_unittest.cc
index 4779251..7b5a050 100644
--- a/src/trace_processor/metrics/metrics_unittest.cc
+++ b/src/trace_processor/metrics/metrics_unittest.cc
@@ -88,9 +88,9 @@
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- descriptor.AddField(FieldDescriptor("int_value", 1,
- FieldDescriptorProto::TYPE_INT64, "",
- std::vector<uint8_t>(), false, false));
+ descriptor.AddField(
+ FieldDescriptor("int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
+ std::vector<uint8_t>(), std::nullopt, false, false));
ProtoBuilder builder(&pool, &descriptor);
ASSERT_OK(builder.AppendSqlValue("int_value", SqlValue::Long(12345)));
@@ -112,9 +112,9 @@
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- descriptor.AddField(FieldDescriptor("double_value", 1,
- FieldDescriptorProto::TYPE_DOUBLE, "",
- std::vector<uint8_t>(), false, false));
+ descriptor.AddField(
+ FieldDescriptor("double_value", 1, FieldDescriptorProto::TYPE_DOUBLE, "",
+ std::vector<uint8_t>(), std::nullopt, false, false));
ProtoBuilder builder(&pool, &descriptor);
ASSERT_OK(builder.AppendSqlValue("double_value", SqlValue::Double(1.2345)));
@@ -136,9 +136,9 @@
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- descriptor.AddField(FieldDescriptor("string_value", 1,
- FieldDescriptorProto::TYPE_STRING, "",
- std::vector<uint8_t>(), false, false));
+ descriptor.AddField(
+ FieldDescriptor("string_value", 1, FieldDescriptorProto::TYPE_STRING, "",
+ std::vector<uint8_t>(), std::nullopt, false, false));
ProtoBuilder builder(&pool, &descriptor);
ASSERT_OK(
@@ -164,9 +164,9 @@
ProtoDescriptor nested("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto.NestedProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- nested.AddField(FieldDescriptor("nested_int_value", 1,
- FieldDescriptorProto::TYPE_INT64, "",
- std::vector<uint8_t>(), false, false));
+ nested.AddField(
+ FieldDescriptor("nested_int_value", 1, FieldDescriptorProto::TYPE_INT64,
+ "", std::vector<uint8_t>(), std::nullopt, false, false));
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
@@ -174,7 +174,7 @@
auto field =
FieldDescriptor("nested_value", 1, FieldDescriptorProto::TYPE_MESSAGE,
".perfetto.protos.TestProto.NestedProto",
- std::vector<uint8_t>(), false, false);
+ std::vector<uint8_t>(), std::nullopt, false, false);
field.set_resolved_type_name(".perfetto.protos.TestProto.NestedProto");
descriptor.AddField(field);
@@ -214,9 +214,9 @@
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- descriptor.AddField(FieldDescriptor("rep_int_value", 1,
- FieldDescriptorProto::TYPE_INT64, "",
- std::vector<uint8_t>(), true, false));
+ descriptor.AddField(
+ FieldDescriptor("rep_int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
+ std::vector<uint8_t>(), std::nullopt, true, false));
ASSERT_THAT(RepeatedFieldBuilder().SerializeToProtoBuilderResult(),
IsEmpty());
@@ -241,9 +241,9 @@
ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
".perfetto.protos.TestProto",
ProtoDescriptor::Type::kMessage, std::nullopt);
- descriptor.AddField(FieldDescriptor("rep_int_value", 1,
- FieldDescriptorProto::TYPE_INT64, "",
- std::vector<uint8_t>(), true, false));
+ descriptor.AddField(
+ FieldDescriptor("rep_int_value", 1, FieldDescriptorProto::TYPE_INT64, "",
+ std::vector<uint8_t>(), std::nullopt, true, false));
RepeatedFieldBuilder rep_builder;
rep_builder.AddSqlValue(SqlValue::Long(1234));
@@ -289,7 +289,8 @@
ProtoDescriptor::Type::kMessage, std::nullopt);
FieldDescriptor enum_field("enum_value", 1, FieldDescriptorProto::TYPE_ENUM,
".perfetto.protos.TestEnum",
- std::vector<uint8_t>(), false, false);
+ std::vector<uint8_t>(), std::nullopt, false,
+ false);
enum_field.set_resolved_type_name(".perfetto.protos.TestEnum");
descriptor.AddField(enum_field);
pool.AddProtoDescriptorForTesting(descriptor);
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index 6600841..0e12c1d 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -81,6 +81,33 @@
)
SELECT * FROM counter_leading_intervals!(power_mw_counter);
+DROP TABLE IF EXISTS energy_usage_estimate;
+CREATE PERFETTO TABLE energy_usage_estimate AS
+with energy_counters as (
+select
+ ts,
+ CASE
+ WHEN energy_counter_uwh IS NOT NULL THEN energy_counter_uwh
+ ELSE charge_uah * voltage_uv / 1e12 END as energy
+ from android_battery_charge
+), start_energy as (
+ select
+ min(ts),
+ energy
+ from energy_counters
+), end_energy as (
+ select
+ max(ts),
+ energy
+ from energy_counters
+)
+select
+ -- If the battery is discharging, the start value will be greater than the end
+ -- and the estimate will report a positive value.
+ -- Battery energy is in watt hours, so multiply by 3600 to convert to joules.
+ (s.energy - e.energy) * 3600 as estimate
+from start_energy s, end_energy e;
+
DROP VIEW IF EXISTS android_batt_output;
CREATE PERFETTO VIEW android_batt_output AS
SELECT AndroidBatteryMetric(
@@ -115,7 +142,9 @@
'total_wakelock_ns',
(SELECT SUM(ts_end - ts) FROM android_batt_wakelocks_merged),
'avg_power_mw',
- (SELECT SUM(value * dur) / SUM(dur) FROM power_mw_intervals)
+ (SELECT SUM(value * dur) / SUM(dur) FROM power_mw_intervals),
+ 'energy_usage_estimate',
+ (select estimate FROM energy_usage_estimate)
))
FROM (
SELECT dur, value AS state, 'total' AS tbl
diff --git a/src/trace_processor/metrics/sql/android/jank/frames.sql b/src/trace_processor/metrics/sql/android/jank/frames.sql
index 8f72e96..481bb4b 100644
--- a/src/trace_processor/metrics/sql/android/jank/frames.sql
+++ b/src/trace_processor/metrics/sql/android/jank/frames.sql
@@ -122,30 +122,55 @@
-- the commit/composite slices on the main thread.
DROP TABLE IF EXISTS android_jank_cuj_sf_frame;
CREATE PERFETTO TABLE android_jank_cuj_sf_frame AS
+WITH android_jank_cuj_timeline_sf_frame AS (
+ SELECT DISTINCT
+ cuj_id,
+ CAST(timeline.name AS INTEGER) AS vsync,
+ timeline.display_frame_token
+ FROM android_jank_cuj_vsync_boundary boundary
+ JOIN actual_frame_timeline_slice timeline
+ ON
+ boundary.upid = timeline.upid
+ AND CAST(timeline.name AS INTEGER) >= vsync_min
+ AND CAST(timeline.name AS INTEGER) <= vsync_max
+ WHERE
+ boundary.layer_id IS NULL
+ OR (
+ timeline.layer_name GLOB '*#*'
+ AND boundary.layer_id = CAST(STR_SPLIT(timeline.layer_name, '#', 1) AS INTEGER))
+),
+android_jank_cuj_sf_frame_base AS (
+ SELECT DISTINCT
+ boundary.cuj_id,
+ boundary.vsync,
+ boundary.ts,
+ boundary.ts_main_thread_start,
+ boundary.ts_end,
+ boundary.dur,
+ actual_timeline.jank_tag = 'Self Jank' AS sf_missed,
+ NULL AS app_missed, -- for simplicity align schema with android_jank_cuj_frame
+ jank_tag,
+ jank_type,
+ prediction_type,
+ present_type,
+ gpu_composition,
+ -- In case expected timeline is missing, as a fallback we use the typical frame deadline
+ -- for 60Hz.
+ -- See similar expression in android_jank_cuj_frame_timeline.
+ COALESCE(expected_timeline.dur, 16600000) AS dur_expected
+ FROM android_jank_cuj_sf_main_thread_frame_boundary boundary
+ JOIN android_jank_cuj_sf_process sf_process
+ JOIN actual_frame_timeline_slice actual_timeline
+ ON actual_timeline.upid = sf_process.upid
+ AND boundary.vsync = CAST(actual_timeline.name AS INTEGER)
+ JOIN android_jank_cuj_timeline_sf_frame ft
+ ON CAST(actual_timeline.name AS INTEGER) = ft.display_frame_token
+ AND boundary.cuj_id = ft.cuj_id
+ LEFT JOIN expected_frame_timeline_slice expected_timeline
+ ON expected_timeline.upid = actual_timeline.upid
+ AND expected_timeline.name = actual_timeline.name
+)
SELECT
- cuj_id,
- ROW_NUMBER() OVER (PARTITION BY cuj_id ORDER BY vsync ASC) AS frame_number,
- vsync,
- boundary.ts,
- boundary.ts_main_thread_start,
- boundary.ts_end,
- boundary.dur,
- actual_timeline.jank_tag = 'Self Jank' AS sf_missed,
- NULL AS app_missed, -- for simplicity align schema with android_jank_cuj_frame
- jank_tag,
- jank_type,
- prediction_type,
- present_type,
- gpu_composition,
- -- In case expected timeline is missing, as a fallback we use the typical frame deadline
- -- for 60Hz.
- -- See similar expression in android_jank_cuj_frame_timeline.
- COALESCE(expected_timeline.dur, 16600000) AS dur_expected
-FROM android_jank_cuj_sf_main_thread_frame_boundary boundary
-JOIN android_jank_cuj_sf_process sf_process
-JOIN actual_frame_timeline_slice actual_timeline
- ON actual_timeline.upid = sf_process.upid
- AND boundary.vsync = CAST(actual_timeline.name AS INTEGER)
-LEFT JOIN expected_frame_timeline_slice expected_timeline
- ON expected_timeline.upid = actual_timeline.upid
- AND expected_timeline.name = actual_timeline.name;
+ *,
+ ROW_NUMBER() OVER (PARTITION BY cuj_id ORDER BY vsync ASC) AS frame_number
+FROM android_jank_cuj_sf_frame_base;
diff --git a/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql b/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
index 9c43318..536dea7 100644
--- a/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
+++ b/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
@@ -67,6 +67,7 @@
-- Ignore child slice e.g. "Choreographer#doFrame - resynced to 1234 in 20.0ms"
AND slice.name not GLOB '*resynced*'
AND slice.dur > 0
+ AND vsync > 0
AND (vsync >= begin_vsync OR begin_vsync is NULL)
AND (vsync <= end_vsync OR end_vsync is NULL)
-- In some malformed traces we see nested doFrame slices.
diff --git a/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql b/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
index 2d5b646..8a4a7dd 100644
--- a/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
+++ b/src/trace_processor/metrics/sql/android/startup/slow_start_reasons.sql
@@ -46,10 +46,13 @@
CREATE OR REPLACE PERFETTO FUNCTION get_main_thread_time_for_launch_in_runnable_state(
startup_id LONG, num_threads INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceThreadSection(
- 'start_timestamp', ts, 'end_timestamp', ts + dur,
- 'thread_tid', tid, 'process_pid', pid,
- 'thread_name', thread_name))
+ SELECT AndroidStartupMetric_TraceThreadSectionInfo(
+ 'start_timestamp', MIN(ts),
+ 'end_timestamp', MAX(ts + dur),
+ 'thread_section', RepeatedField(AndroidStartupMetric_TraceThreadSection(
+ 'start_timestamp', ts, 'end_timestamp', ts + dur,
+ 'thread_tid', tid, 'process_pid', pid,
+ 'thread_name', thread_name)))
FROM (
SELECT p.pid, ts, dur, thread.tid, thread_name
FROM launch_threads_by_thread_state l, android_startup_processes p
@@ -62,10 +65,13 @@
CREATE OR REPLACE PERFETTO FUNCTION get_main_thread_time_for_launch_and_state(
startup_id LONG, state STRING, num_threads INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceThreadSection(
- 'start_timestamp', ts, 'end_timestamp', ts + dur,
- 'thread_tid', tid, 'process_pid', pid,
- 'thread_name', thread_name))
+ SELECT AndroidStartupMetric_TraceThreadSectionInfo(
+ 'start_timestamp', MIN(ts),
+ 'end_timestamp', MAX(ts + dur),
+ 'thread_section', RepeatedField(AndroidStartupMetric_TraceThreadSection(
+ 'start_timestamp', ts, 'end_timestamp', ts + dur,
+ 'thread_tid', tid, 'process_pid', pid,
+ 'thread_name', thread_name)))
FROM (
SELECT p.pid, ts, dur, thread.tid, thread_name
FROM launch_threads_by_thread_state l, android_startup_processes p
@@ -78,10 +84,13 @@
CREATE OR REPLACE PERFETTO FUNCTION get_main_thread_time_for_launch_state_and_io_wait(
startup_id INT, state STRING, io_wait BOOL, num_threads INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceThreadSection(
- 'start_timestamp', ts, 'end_timestamp', ts + dur,
- 'thread_tid', tid, 'process_pid', pid,
- 'thread_name', thread_name))
+ SELECT AndroidStartupMetric_TraceThreadSectionInfo(
+ 'start_timestamp', MIN(ts),
+ 'end_timestamp', MAX(ts + dur),
+ 'thread_section', RepeatedField(AndroidStartupMetric_TraceThreadSection(
+ 'start_timestamp', ts, 'end_timestamp', ts + dur,
+ 'thread_tid', tid, 'process_pid', pid,
+ 'thread_name', thread_name)))
FROM (
SELECT p.pid, ts, dur, thread.tid, thread_name
FROM launch_threads_by_thread_state l, android_startup_processes p
@@ -95,10 +104,13 @@
CREATE OR REPLACE PERFETTO FUNCTION get_thread_time_for_launch_state_and_thread(
startup_id INT, state STRING, thread_name STRING, num_threads INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceThreadSection(
- 'start_timestamp', ts, 'end_timestamp', ts + dur,
- 'thread_tid', tid, 'process_pid', pid,
- 'thread_name', thread_name))
+ SELECT AndroidStartupMetric_TraceThreadSectionInfo(
+ 'start_timestamp', MIN(ts),
+ 'end_timestamp', MAX(ts + dur),
+ 'thread_section', RepeatedField(AndroidStartupMetric_TraceThreadSection(
+ 'start_timestamp', ts, 'end_timestamp', ts + dur,
+ 'thread_tid', tid, 'process_pid', pid,
+ 'thread_name', thread_name)))
FROM (
SELECT p.pid, ts, dur, thread.tid, thread_name
FROM launch_threads_by_thread_state l, android_startup_processes p
@@ -111,13 +123,16 @@
CREATE OR REPLACE PERFETTO FUNCTION get_missing_baseline_profile_for_launch(
startup_id LONG, pkg_name STRING)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', pid,
- 'start_timestamp', slice_ts,
- 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id,
- 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', pid,
+ 'start_timestamp', slice_ts,
+ 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id,
+ 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT p.pid, tid, slice_ts, slice_dur, slice_id, slice_name
FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME($startup_id,
@@ -135,13 +150,16 @@
CREATE OR REPLACE PERFETTO FUNCTION get_run_from_apk(startup_id LONG)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', pid,
- 'start_timestamp', slice_ts,
- 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id,
- 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', pid,
+ 'start_timestamp', slice_ts,
+ 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id,
+ 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT p.pid, tid, slice_ts, slice_dur, slice_id, slice_name
FROM android_thread_slices_for_all_startups l, android_startup_processes p
@@ -157,13 +175,16 @@
CREATE OR REPLACE PERFETTO FUNCTION get_unlock_running_during_launch_slice(startup_id LONG,
pid INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', $pid,
- 'start_timestamp', slice_ts,
- 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id,
- 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', $pid,
+ 'start_timestamp', slice_ts,
+ 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id,
+ 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT tid, slice.ts as slice_ts, slice.dur as slice_dur,
slice.id as slice_id, slice.name as slice_name
@@ -180,13 +201,16 @@
CREATE OR REPLACE PERFETTO FUNCTION get_gc_activity(startup_id LONG, num_slices INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', pid,
- 'start_timestamp', slice_ts,
- 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id,
- 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', pid,
+ 'start_timestamp', slice_ts,
+ 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id,
+ 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT p.pid, tid, slice_ts, slice_dur, slice_id, slice_name
FROM android_thread_slices_for_all_startups slice, android_startup_processes p
@@ -204,13 +228,16 @@
CREATE OR REPLACE PERFETTO FUNCTION get_dur_on_main_thread_for_startup_and_slice(
startup_id LONG, slice_name STRING, num_slices INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', pid,
- 'start_timestamp', slice_ts,
- 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id,
- 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', pid,
+ 'start_timestamp', slice_ts,
+ 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id,
+ 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT p.pid, tid, slice_ts, slice_dur, slice_id, slice_name
FROM android_thread_slices_for_all_startups l,
@@ -223,11 +250,14 @@
CREATE OR REPLACE PERFETTO FUNCTION get_main_thread_binder_transactions_blocked(
startup_id LONG, threshold DOUBLE, num_slices INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', pid,
- 'start_timestamp', slice_ts, 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id, 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', pid,
+ 'start_timestamp', slice_ts, 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id, 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT pid, request.tid as tid, request.slice_ts as slice_ts, request.slice_dur as slice_dur,
request.id as slice_id, request.slice_name as slice_name
@@ -253,11 +283,14 @@
CREATE OR REPLACE PERFETTO FUNCTION get_slices_concurrent_to_launch(
startup_id INT, slice_glob STRING, num_slices INT, pid INT)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', $pid,
- 'start_timestamp', ts, 'end_timestamp', ts + dur,
- 'slice_id', id, 'slice_name', name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', $pid,
+ 'start_timestamp', ts, 'end_timestamp', ts + dur,
+ 'slice_id', id, 'slice_name', name)),
+ 'start_timestamp', MIN(ts),
+ 'end_timestamp', MAX(ts + dur))
FROM (
SELECT thread.tid, s.ts as ts, dur, s.id, s.name FROM slice s
JOIN thread_track t ON s.track_id = t.id
@@ -275,11 +308,14 @@
CREATE OR REPLACE PERFETTO FUNCTION get_slices_for_startup_and_slice_name(
startup_id INT, slice_name STRING, num_slices INT, pid int)
RETURNS PROTO AS
- SELECT RepeatedField(AndroidStartupMetric_TraceSliceSection(
- 'thread_tid', tid,
- 'process_pid', $pid,
- 'start_timestamp', slice_ts, 'end_timestamp', slice_ts + slice_dur,
- 'slice_id', slice_id, 'slice_name', slice_name))
+ SELECT AndroidStartupMetric_TraceSliceSectionInfo(
+ 'slice_section', RepeatedField(AndroidStartupMetric_TraceSliceSection(
+ 'thread_tid', tid,
+ 'process_pid', $pid,
+ 'start_timestamp', slice_ts, 'end_timestamp', slice_ts + slice_dur,
+ 'slice_id', slice_id, 'slice_name', slice_name)),
+ 'start_timestamp', MIN(slice_ts),
+ 'end_timestamp', MAX(slice_ts + slice_dur))
FROM (
SELECT tid, slice_ts, slice_dur, slice_id, slice_name
FROM android_thread_slices_for_all_startups
diff --git a/src/trace_processor/metrics/sql/android/wattson_markers_threads.sql b/src/trace_processor/metrics/sql/android/wattson_markers_threads.sql
index 96d5335..4074a20 100644
--- a/src/trace_processor/metrics/sql/android/wattson_markers_threads.sql
+++ b/src/trace_processor/metrics/sql/android/wattson_markers_threads.sql
@@ -22,7 +22,8 @@
-- Requirement is there is exactly one pair of start/stop
(SELECT ts FROM slice WHERE name == 'wattson_start') as ts,
(SELECT ts FROM slice WHERE name == 'wattson_stop')
- - (SELECT ts FROM slice WHERE name == 'wattson_start') as dur;
+ - (SELECT ts FROM slice WHERE name == 'wattson_start') as dur,
+ 1 as period_id;
SELECT RUN_METRIC(
'android/wattson_tasks_attribution.sql',
@@ -30,43 +31,18 @@
'_wattson_period_window'
);
--- Group by unique thread ID and disregard CPUs, summing of power over all CPUs
--- and all instances of the thread
-DROP VIEW IF EXISTS _wattson_thread_attribution;
-CREATE PERFETTO VIEW _wattson_thread_attribution AS
-SELECT
- -- active time of thread divided by total time where Wattson is defined
- SUM(estimated_mw * dur) / 1000000000 as estimated_mws,
- (
- SUM(estimated_mw * dur) / (SELECT SUM(dur) from _windowed_wattson)
- ) as estimated_mw,
- idle_cost_mws,
- thread_name,
- process_name,
- tid,
- pid
-FROM _windowed_threads_system_state
-LEFT JOIN _per_thread_idle_attribution USING (utid)
-GROUP BY utid
-ORDER BY estimated_mw DESC;
-
DROP VIEW IF EXISTS wattson_markers_threads_output;
CREATE PERFETTO VIEW wattson_markers_threads_output AS
SELECT AndroidWattsonTasksAttributionMetric(
- 'metric_version', 3,
+ 'metric_version', 4,
'power_model_version', 1,
- 'task_info', (
+ 'period_info', (
SELECT RepeatedField(
- AndroidWattsonTaskInfo(
- 'estimated_mws', ROUND(estimated_mws, 6),
- 'estimated_mw', ROUND(estimated_mw, 6),
- 'idle_transitions_mws', ROUND(idle_cost_mws, 6),
- 'thread_name', thread_name,
- 'process_name', process_name,
- 'thread_id', tid,
- 'process_id', pid
+ AndroidWattsonTaskPeriodInfo(
+ 'period_id', period_id,
+ 'task_info', proto
)
)
- FROM _wattson_thread_attribution
+ FROM _wattson_per_task
)
);
diff --git a/src/trace_processor/metrics/sql/android/wattson_tasks_attribution.sql b/src/trace_processor/metrics/sql/android/wattson_tasks_attribution.sql
index 41f7730..d023354 100644
--- a/src/trace_processor/metrics/sql/android/wattson_tasks_attribution.sql
+++ b/src/trace_processor/metrics/sql/android/wattson_tasks_attribution.sql
@@ -20,50 +20,64 @@
-- Take only the Wattson estimations that are in the window of interest
DROP VIEW IF EXISTS _windowed_wattson;
CREATE PERFETTO VIEW _windowed_wattson AS
-SELECT ii.*, ss.*
-FROM _interval_intersect_single!(
- (SELECT ts FROM {{window_table}}),
- (SELECT dur FROM {{window_table}}),
- _ii_subquery!(_system_state_mw)
+SELECT
+ ii.ts,
+ ii.dur,
+ ii.id_1 as period_id,
+ ss.cpu0_mw,
+ ss.cpu1_mw,
+ ss.cpu2_mw,
+ ss.cpu3_mw,
+ ss.cpu4_mw,
+ ss.cpu5_mw,
+ ss.cpu6_mw,
+ ss.cpu7_mw,
+ ss.dsu_scu_mw
+FROM _interval_intersect!(
+ (
+ _ii_subquery!(_system_state_mw),
+ (SELECT ts, dur, period_id as id FROM {{window_table}})
+ ),
+ ()
) ii
-JOIN _system_state_mw AS ss ON ss._auto_id = id;
+JOIN _system_state_mw AS ss ON ss._auto_id = id_0;
-- "Unpivot" the table so that table can by PARTITIONED BY cpu
DROP TABLE IF EXISTS _unioned_windowed_wattson;
CREATE PERFETTO TABLE _unioned_windowed_wattson AS
- SELECT ts, dur, 0 as cpu, cpu0_mw as estimated_mw
+ SELECT ts, dur, 0 as cpu, cpu0_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 0 = cpu)
UNION ALL
- SELECT ts, dur, 1 as cpu, cpu1_mw as estimated_mw
+ SELECT ts, dur, 1 as cpu, cpu1_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 1 = cpu)
UNION ALL
- SELECT ts, dur, 2 as cpu, cpu2_mw as estimated_mw
+ SELECT ts, dur, 2 as cpu, cpu2_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 2 = cpu)
UNION ALL
- SELECT ts, dur, 3 as cpu, cpu3_mw as estimated_mw
+ SELECT ts, dur, 3 as cpu, cpu3_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 3 = cpu)
UNION ALL
- SELECT ts, dur, 4 as cpu, cpu4_mw as estimated_mw
+ SELECT ts, dur, 4 as cpu, cpu4_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 4 = cpu)
UNION ALL
- SELECT ts, dur, 5 as cpu, cpu5_mw as estimated_mw
+ SELECT ts, dur, 5 as cpu, cpu5_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 5 = cpu)
UNION ALL
- SELECT ts, dur, 6 as cpu, cpu6_mw as estimated_mw
+ SELECT ts, dur, 6 as cpu, cpu6_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 6 = cpu)
UNION ALL
- SELECT ts, dur, 7 as cpu, cpu7_mw as estimated_mw
+ SELECT ts, dur, 7 as cpu, cpu7_mw as estimated_mw, period_id
FROM _windowed_wattson
WHERE EXISTS (SELECT cpu FROM _dev_cpu_policy_map WHERE 7 = cpu)
UNION ALL
- SELECT ts, dur, -1 as cpu, dsu_scu_mw as estimated_mw
+ SELECT ts, dur, -1 as cpu, dsu_scu_mw as estimated_mw, period_id
FROM _windowed_wattson;
DROP TABLE IF EXISTS _windowed_threads_system_state;
@@ -77,7 +91,8 @@
s.process_name,
s.tid,
s.pid,
- s.utid
+ s.utid,
+ uw.period_id
FROM _interval_intersect!(
(
_ii_subquery!(_unioned_windowed_wattson),
@@ -92,10 +107,58 @@
DROP VIEW IF EXISTS _per_thread_idle_attribution;
CREATE PERFETTO VIEW _per_thread_idle_attribution AS
SELECT
- SUM(idle_cost_mws) as idle_cost_mws,
- utid
-FROM _filter_idle_attribution(
- (SELECT ts FROM {{window_table}}),
- (SELECT dur FROM {{window_table}})
-)
-GROUP BY utid;
+ SUM(cost.estimated_mw * cost.dur) / 1e9 as idle_cost_mws,
+ cost.utid,
+ ii.id_1 as period_id
+FROM _interval_intersect!(
+ (
+ _ii_subquery!(_idle_transition_cost),
+ (SELECT ts, dur, period_id as id FROM {{window_table}})
+ ),
+ ()
+) ii
+JOIN _idle_transition_cost as cost ON cost._auto_id = id_0
+GROUP BY utid, period_id;
+
+-- Group by unique thread ID and disregard CPUs, summing of power over all CPUs
+-- and all instances of the thread
+DROP VIEW IF EXISTS _wattson_thread_attribution;
+CREATE PERFETTO VIEW _wattson_thread_attribution AS
+SELECT
+ -- active time of thread divided by total time where Wattson is defined
+ SUM(estimated_mw * dur) / 1000000000 as estimated_mws,
+ (
+ SUM(estimated_mw * dur) / (SELECT SUM(dur) from _windowed_wattson)
+ ) as estimated_mw,
+ idle_cost_mws,
+ thread_name,
+ process_name,
+ tid,
+ pid,
+ period_id
+FROM _windowed_threads_system_state
+LEFT JOIN _per_thread_idle_attribution USING (utid, period_id)
+GROUP BY utid, period_id
+ORDER BY estimated_mw DESC;
+
+-- Create proto format task attribution for each period
+DROP VIEW IF EXISTS _wattson_per_task;
+CREATE PERFETTO VIEW _wattson_per_task AS
+SELECT
+ period_id,
+ (
+ SELECT RepeatedField(
+ AndroidWattsonTaskInfo(
+ 'estimated_mws', ROUND(estimated_mws, 6),
+ 'estimated_mw', ROUND(estimated_mw, 6),
+ 'idle_transitions_mws', ROUND(idle_cost_mws, 6),
+ 'thread_name', thread_name,
+ 'process_name', process_name,
+ 'thread_id', tid,
+ 'process_id', pid
+ )
+ )
+ ) as proto
+FROM _wattson_thread_attribution
+GROUP BY period_id;
+
diff --git a/src/trace_processor/metrics/sql/android/wattson_trace_threads.sql b/src/trace_processor/metrics/sql/android/wattson_trace_threads.sql
index 308ca72..fb8071f 100644
--- a/src/trace_processor/metrics/sql/android/wattson_trace_threads.sql
+++ b/src/trace_processor/metrics/sql/android/wattson_trace_threads.sql
@@ -21,7 +21,8 @@
CREATE PERFETTO VIEW _wattson_period_window AS
SELECT
trace_start() as ts,
- trace_dur() as dur;
+ trace_dur() as dur,
+ 1 as period_id;
SELECT RUN_METRIC(
'android/wattson_tasks_attribution.sql',
@@ -29,43 +30,18 @@
'_wattson_period_window'
);
--- Group by unique thread ID and disregard CPUs, summing of power over all CPUs
--- and all instances of the thread
-DROP VIEW IF EXISTS _wattson_thread_attribution;
-CREATE PERFETTO VIEW _wattson_thread_attribution AS
-SELECT
- -- active time of thread divided by total time of trace
- SUM(estimated_mw * dur) / 1000000000 as estimated_mws,
- (
- SUM(estimated_mw * dur) / (SELECT SUM(dur) from _windowed_wattson)
- ) as estimated_mw,
- idle_cost_mws,
- thread_name,
- process_name,
- tid,
- pid
-FROM _windowed_threads_system_state
-LEFT JOIN _per_thread_idle_attribution USING (utid)
-GROUP BY utid
-ORDER BY estimated_mw DESC;
-
DROP VIEW IF EXISTS wattson_trace_threads_output;
CREATE PERFETTO VIEW wattson_trace_threads_output AS
SELECT AndroidWattsonTasksAttributionMetric(
- 'metric_version', 3,
+ 'metric_version', 4,
'power_model_version', 1,
- 'task_info', (
+ 'period_info', (
SELECT RepeatedField(
- AndroidWattsonTaskInfo(
- 'estimated_mws', ROUND(estimated_mws, 6),
- 'estimated_mw', ROUND(estimated_mw, 6),
- 'idle_transitions_mws', ROUND(idle_cost_mws, 6),
- 'thread_name', thread_name,
- 'process_name', process_name,
- 'thread_id', tid,
- 'process_id', pid
+ AndroidWattsonTaskPeriodInfo(
+ 'period_id', period_id,
+ 'task_info', proto
)
)
- FROM _wattson_thread_attribution
+ FROM _wattson_per_task
)
);
diff --git a/src/trace_processor/metrics/sql/chrome/BUILD.gn b/src/trace_processor/metrics/sql/chrome/BUILD.gn
index 30962b8..7d10aeb 100644
--- a/src/trace_processor/metrics/sql/chrome/BUILD.gn
+++ b/src/trace_processor/metrics/sql/chrome/BUILD.gn
@@ -26,6 +26,7 @@
"chrome_args_class_names.sql",
"chrome_event_metadata.sql",
"chrome_histogram_hashes.sql",
+ "chrome_histogram_summaries.sql",
"chrome_input_to_browser_intervals.sql",
"chrome_input_to_browser_intervals_base.sql",
"chrome_input_to_browser_intervals_template.sql",
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql b/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql
new file mode 100644
index 0000000..7946491
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql
@@ -0,0 +1,49 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+INCLUDE PERFETTO MODULE chrome.histograms;
+
+DROP VIEW IF EXISTS HistogramSummaryTable;
+CREATE PERFETTO VIEW HistogramSummaryTable AS
+SELECT
+ hist.name AS histname,
+ CAST(AVG(hist.value) AS INTEGER) AS mean_histval,
+ COUNT(*) AS hist_count,
+ CAST(SUM(hist.value) AS INTEGER) AS sum_histval,
+ CAST(MAX(hist.value) AS INTEGER) AS max_histval,
+ CAST(PERCENTILE(hist.value, 90) AS INTEGER) AS p90_histval,
+ CAST(PERCENTILE(hist.value, 50) AS INTEGER) AS p50_histval
+FROM chrome_histograms hist
+GROUP BY hist.name;
+
+DROP VIEW IF EXISTS chrome_histogram_summaries_output;
+CREATE PERFETTO VIEW chrome_histogram_summaries_output AS
+SELECT ChromeHistogramSummaries(
+ 'histogram_summary', (
+ SELECT RepeatedField(
+ HistogramSummary(
+ 'name', histname,
+ 'mean', mean_histval,
+ 'count', hist_count,
+ 'sum', sum_histval,
+ 'max', max_histval,
+ 'p90', p90_histval,
+ 'p50', p50_histval
+ )
+ )
+ FROM HistogramSummaryTable
+ )
+);
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql
index 788ff3b..3545234 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql
@@ -55,22 +55,6 @@
ELSE "unknown" END)
ELSE "regular" END AS delay_type;
--- Checks if slice has a descendant with provided name.
-CREATE OR REPLACE PERFETTO FUNCTION _has_descendant_slice_with_name(
- -- Id of the slice to check descendants of.
- id INT,
- -- Name of potential descendant slice.
- descendant_name STRING
-)
--- Whether `descendant_name` is a name of an descendant slice.
-RETURNS BOOL AS
-SELECT EXISTS(
- SELECT 1
- FROM descendant_slice($id)
- WHERE name = $descendant_name
- LIMIT 1
-);
-
-- Get all EventLatency events for scroll updates to use their
-- flows later on to decide how much time we waited from queueing the event
-- until we started processing it.
@@ -87,10 +71,16 @@
{{slice_table_name}} AS s JOIN args USING(arg_set_id)
WHERE
NAME = "EventLatency"
- AND (args.string_value GLOB "*GESTURE_SCROLL_UPDATE"
- OR args.string_value = "GESTURE_SCROLL_END")
- AND _has_descendant_slice_with_name(
- s.id, "SubmitCompositorFrameToPresentationCompositorFrame")
+ AND EXISTS(
+ SELECT 1
+ FROM descendant_slice(s.id)
+ WHERE name = "SubmitCompositorFrameToPresentationCompositorFrame"
+ LIMIT 1
+ )
+ AND (
+ args.string_value GLOB "*GESTURE_SCROLL_UPDATE"
+ OR args.string_value = "GESTURE_SCROLL_END"
+ )
ORDER BY trace_id;
-- Get all chrome_latency_info_for_gesture_slices where trace_ids are not -1,
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
index 94d2794..62d84af 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
@@ -21,7 +21,22 @@
-- The numbers mentioned above are estimates in the ideal case scenario.
INCLUDE PERFETTO MODULE chrome.scroll_jank.utils;
-INCLUDE PERFETTO MODULE common.slices;
+
+-- Checks if slice has a descendant with provided name.
+CREATE OR REPLACE PERFETTO FUNCTION _has_descendant_slice_with_name(
+ -- Id of the slice to check descendants of.
+ id INT,
+ -- Name of potential descendant slice.
+ descendant_name STRING
+)
+-- Whether `descendant_name` is a name of an descendant slice.
+RETURNS BOOL AS
+SELECT EXISTS(
+ SELECT 1
+ FROM descendant_slice($id)
+ WHERE name = $descendant_name
+ LIMIT 1
+);
-- Grab all GestureScrollUpdate slices.
DROP VIEW IF EXISTS chrome_all_scroll_updates;
@@ -29,7 +44,7 @@
SELECT
S.id,
chrome_get_most_recent_scroll_begin_id(ts) AS scroll_id,
- has_descendant_slice_with_name(S.id, "SubmitCompositorFrameToPresentationCompositorFrame")
+ _has_descendant_slice_with_name(S.id, "SubmitCompositorFrameToPresentationCompositorFrame")
AS is_presented,
ts,
dur,
diff --git a/src/trace_processor/metrics/sql/common/parent_slice.sql b/src/trace_processor/metrics/sql/common/parent_slice.sql
index d5c6f24..5478d93 100644
--- a/src/trace_processor/metrics/sql/common/parent_slice.sql
+++ b/src/trace_processor/metrics/sql/common/parent_slice.sql
@@ -13,5 +13,3 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-
-INCLUDE PERFETTO MODULE deprecated.v42.common.slices;
diff --git a/src/trace_processor/metrics/sql/trace_metadata.sql b/src/trace_processor/metrics/sql/trace_metadata.sql
index 90c112d..1e45573 100644
--- a/src/trace_processor/metrics/sql/trace_metadata.sql
+++ b/src/trace_processor/metrics/sql/trace_metadata.sql
@@ -23,6 +23,9 @@
'android_build_fingerprint', (
SELECT str_value FROM metadata WHERE name = 'android_build_fingerprint'
),
+ 'android_device_manufacturer', (
+ SELECT str_value FROM metadata WHERE name = 'android_device_manufacturer'
+ ),
'statsd_triggering_subscription_id', (
SELECT int_value FROM metadata
WHERE name = 'statsd_triggering_subscription_id'
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index 21216b6..36bd391 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -704,8 +704,18 @@
}
std::string package_name = sql_modules::GetPackageName(key);
+
auto* package = FindPackage(package_name);
if (!package) {
+ if (package_name == "common") {
+ return base::ErrStatus(
+ "INCLUDE: Package `common` has been removed and most of the "
+ "functionality has been moved to other packages. Check "
+ "`slices.with_context` for replacement for `common.slices` and "
+ "`time.conversion` for replacement for `common.timestamps`. The "
+ "documentation for Perfetto standard library can be found at "
+ "https://perfetto.dev/docs/analysis/stdlib-docs.");
+ }
return base::ErrStatus("INCLUDE: Package '%s' not found", key.c_str());
}
return IncludePackageImpl(*package, key, parser);
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
index 7be5b26..a73c7d9 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/BUILD.gn
@@ -41,6 +41,8 @@
"experimental_slice_layout.h",
"flamegraph_construction_algorithms.cc",
"flamegraph_construction_algorithms.h",
+ "winscope_proto_to_args_with_defaults.cc",
+ "winscope_proto_to_args_with_defaults.h",
"table_info.cc",
"table_info.h",
]
@@ -62,6 +64,9 @@
"../../../tables",
"../../../types",
"../../../util",
+ "../../../util:descriptors",
+ "../../../util:proto_to_args_parser",
+ "../../../util:winscope_proto_mapping",
"../../engine",
]
public_deps = [ ":interface" ]
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
index aa882dd..22999ee 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -80,6 +80,21 @@
],
parent=FLOW_TABLE)
+ARGS_WITH_DEFAULTS_TABLE = Table(
+ python_module=__file__,
+ class_name='WinscopeArgsWithDefaultsTable',
+ sql_name='__intrinsic_winscope_proto_to_args_with_defaults',
+ columns=[
+ C("table_name", CppString(), flags=ColumnFlag.HIDDEN),
+ C('base64_proto_id', CppUint32()),
+ C('flat_key', CppString()),
+ C('key', CppString()),
+ C('int_value', CppOptional(CppInt64())),
+ C('string_value', CppOptional(CppString())),
+ C('real_value', CppOptional(CppDouble())),
+ C('value_type', CppString()),
+ ])
+
DESCENDANT_SLICE_TABLE = Table(
python_module=__file__,
class_name="DescendantSliceTable",
@@ -169,6 +184,7 @@
ANCESTOR_SLICE_TABLE,
ANCESTOR_STACK_PROFILE_CALLSITE_TABLE,
CONNECTED_FLOW_TABLE,
+ ARGS_WITH_DEFAULTS_TABLE,
DESCENDANT_SLICE_BY_STACK_TABLE,
DESCENDANT_SLICE_TABLE,
DFS_WEIGHT_BOUNDED_TABLE,
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc
new file mode 100644
index 0000000..ee8e600
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.cc
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/winscope_proto_mapping.h"
+
+namespace perfetto::trace_processor {
+namespace tables {
+WinscopeArgsWithDefaultsTable::~WinscopeArgsWithDefaultsTable() = default;
+} // namespace tables
+
+namespace {
+using Row = tables::WinscopeArgsWithDefaultsTable::Row;
+
+class Delegate : public util::ProtoToArgsParser::Delegate {
+ public:
+ using Key = util::ProtoToArgsParser::Key;
+ explicit Delegate(StringPool* pool,
+ const uint32_t base64_proto_id,
+ tables::WinscopeArgsWithDefaultsTable* table)
+ : pool_(pool), base64_proto_id_(base64_proto_id), table_(table) {}
+
+ void AddInteger(const Key& key, int64_t res) override {
+ Row r;
+ r.int_value = res;
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddUnsignedInteger(const Key& key, uint64_t res) override {
+ Row r;
+ r.int_value = res;
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddString(const Key& key, const protozero::ConstChars& res) override {
+ Row r;
+ r.string_value = pool_->InternString(base::StringView((res.ToStdString())));
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddString(const Key& key, const std::string& res) override {
+ Row r;
+ r.string_value = pool_->InternString(base::StringView(res));
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddDouble(const Key& key, double res) override {
+ Row r;
+ r.real_value = res;
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddBoolean(const Key& key, bool res) override {
+ Row r;
+ r.int_value = res;
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddBytes(const Key& key, const protozero::ConstBytes& res) override {
+ Row r;
+ r.string_value = pool_->InternString(base::StringView((res.ToStdString())));
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddNull(const Key& key) override {
+ Row r;
+ SetColumnsAndInsertRow(key, r);
+ }
+ void AddPointer(const Key&, const void*) override {
+ PERFETTO_FATAL("Unsupported");
+ }
+ bool AddJson(const Key&, const protozero::ConstChars&) override {
+ PERFETTO_FATAL("Unsupported");
+ }
+ size_t GetArrayEntryIndex(const std::string&) override {
+ PERFETTO_FATAL("Unsupported");
+ }
+ size_t IncrementArrayEntryIndex(const std::string&) override {
+ PERFETTO_FATAL("Unsupported");
+ }
+ PacketSequenceStateGeneration* seq_state() override { return nullptr; }
+
+ private:
+ InternedMessageView* GetInternedMessageView(uint32_t, uint64_t) override {
+ return nullptr;
+ }
+
+ void SetColumnsAndInsertRow(const Key& key, Row& row) {
+ row.key = pool_->InternString(base::StringView(key.key));
+ row.flat_key = pool_->InternString(base::StringView(key.flat_key));
+ row.base64_proto_id = base64_proto_id_;
+ table_->Insert(row);
+ }
+
+ StringPool* pool_;
+ const uint32_t base64_proto_id_;
+ tables::WinscopeArgsWithDefaultsTable* table_;
+};
+
+base::Status InsertRows(
+ const Table& static_table,
+ tables::WinscopeArgsWithDefaultsTable* inflated_args_table,
+ const std::string& proto_name,
+ const std::vector<uint32_t>* allowed_fields,
+ DescriptorPool& descriptor_pool,
+ StringPool* string_pool) {
+ util::ProtoToArgsParser args_parser{descriptor_pool};
+ const auto base64_proto_id_col_idx =
+ static_table.ColumnIdxFromName("base64_proto_id").value();
+ const auto base_64_proto_col_idx =
+ static_table.ColumnIdxFromName("base64_proto").value();
+
+ std::unordered_set<uint32_t> inflated_protos;
+ for (auto it = static_table.IterateRows(); it; ++it) {
+ const auto base64_proto_id =
+ static_cast<uint32_t>(it.Get(base64_proto_id_col_idx).AsLong());
+ if (inflated_protos.count(base64_proto_id) > 0) {
+ continue;
+ }
+ inflated_protos.insert(base64_proto_id);
+ const auto* raw_proto = it.Get(base_64_proto_col_idx).AsString();
+ const auto blob = *base::Base64Decode(raw_proto);
+ const auto cb = protozero::ConstBytes{
+ reinterpret_cast<const uint8_t*>(blob.data()), blob.size()};
+ Delegate delegate(string_pool, base64_proto_id, inflated_args_table);
+ RETURN_IF_ERROR(args_parser.ParseMessage(cb, proto_name, allowed_fields,
+ delegate, nullptr, true));
+ }
+ return base::OkStatus();
+}
+} // namespace
+
+WinscopeProtoToArgsWithDefaults::WinscopeProtoToArgsWithDefaults(
+ StringPool* string_pool,
+ PerfettoSqlEngine* engine,
+ TraceProcessorContext* context)
+ : string_pool_(string_pool), engine_(engine), context_(context) {}
+
+base::StatusOr<std::unique_ptr<Table>>
+WinscopeProtoToArgsWithDefaults::ComputeTable(
+ const std::vector<SqlValue>& arguments) {
+ PERFETTO_CHECK(arguments.size() == 1);
+ if (arguments[0].type != SqlValue::kString) {
+ return base::ErrStatus(
+ "__intrinsic_winscope_proto_to_args_with_defaults takes table name as "
+ "a string.");
+ }
+ std::string table_name = arguments[0].AsString();
+
+ const Table* static_table = engine_->GetStaticTableOrNull(table_name);
+ if (!static_table) {
+ return base::ErrStatus("Failed to find %s table.", table_name.c_str());
+ }
+
+ std::string proto_name;
+ ASSIGN_OR_RETURN(proto_name,
+ util::winscope_proto_mapping::GetProtoName(table_name));
+
+ auto table =
+ std::make_unique<tables::WinscopeArgsWithDefaultsTable>(string_pool_);
+
+ auto allowed_fields =
+ util::winscope_proto_mapping::GetAllowedFields(table_name);
+ RETURN_IF_ERROR(InsertRows(*static_table, table.get(), proto_name,
+ allowed_fields ? &allowed_fields.value() : nullptr,
+ *context_->descriptor_pool_, string_pool_));
+
+ return std::unique_ptr<Table>(std::move(table));
+}
+
+Table::Schema WinscopeProtoToArgsWithDefaults::CreateSchema() {
+ return tables::WinscopeArgsWithDefaultsTable::ComputeStaticSchema();
+}
+
+std::string WinscopeProtoToArgsWithDefaults::TableName() {
+ return tables::WinscopeArgsWithDefaultsTable::Name();
+}
+
+uint32_t WinscopeProtoToArgsWithDefaults::EstimateRowCount() {
+ // 100 inflated args per 100 elements per 100 entries
+ return 1000000;
+}
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h
new file mode 100644
index 0000000..91ab8c8
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
+#define SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/static_table_function.h"
+
+namespace perfetto::trace_processor {
+
+class TraceProcessorContext;
+
+class WinscopeProtoToArgsWithDefaults : public StaticTableFunction {
+ public:
+ explicit WinscopeProtoToArgsWithDefaults(StringPool*,
+ PerfettoSqlEngine*,
+ TraceProcessorContext* context);
+
+ Table::Schema CreateSchema() override;
+ std::string TableName() override;
+ uint32_t EstimateRowCount() override;
+ base::StatusOr<std::unique_ptr<Table>> ComputeTable(
+ const std::vector<SqlValue>& arguments) override;
+
+ private:
+ StringPool* string_pool_ = nullptr;
+ PerfettoSqlEngine* engine_ = nullptr;
+ TraceProcessorContext* context_ = nullptr;
+};
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_PERFETTO_SQL_INTRINSICS_TABLE_FUNCTIONS_WINSCOPE_PROTO_TO_ARGS_WITH_DEFAULTS_H_
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index bf51d9e..adaeb73 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -22,9 +22,7 @@
"android",
"callstacks",
"chrome:chrome_sql",
- "common",
"counters",
- "deprecated/v42/common",
"export",
"graphs",
"intervals",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/battery.sql b/src/trace_processor/perfetto_sql/stdlib/android/battery.sql
index 8d21767..c3723d8 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/battery.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/battery.sql
@@ -24,14 +24,20 @@
-- Current charge in micro ampers.
charge_uah DOUBLE,
-- Current micro ampers.
- current_ua DOUBLE
+ current_ua DOUBLE,
+ -- Current voltage in micro volts.
+ voltage_uv DOUBLE,
+ -- Current energy counter in microwatt-hours(µWh).
+ energy_counter_uwh DOUBLE
) AS
SELECT
all_ts.ts,
current_avg_ua,
capacity_percent,
charge_uah,
- current_ua
+ current_ua,
+ voltage_uv,
+ energy_counter_uwh
FROM (
SELECT DISTINCT(ts) AS ts
FROM counter c
@@ -62,4 +68,16 @@
JOIN counter_track t ON c.track_id = t.id
WHERE name = 'batt.current_ua'
) USING(ts)
+LEFT JOIN (
+ SELECT ts, value AS voltage_uv
+ FROM counter c
+ JOIN counter_track t ON c.track_id = t.id
+ WHERE name = 'batt.voltage_uv'
+) USING(ts)
+LEFT JOIN (
+ SELECT ts, value AS energy_counter_uwh
+ FROM counter c
+ JOIN counter_track t ON c.track_id = t.id
+ WHERE name = 'batt.energy_counter_uwh'
+) USING(ts)
ORDER BY ts;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql b/src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql
index 90697fa..66d2d01 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/critical_blocking_calls.sql
@@ -39,6 +39,8 @@
OR $name GLOB 'NotificationStackScrollLayout#onMeasure'
OR $name GLOB 'ExpNotRow#*'
OR $name GLOB 'GC: Wait For*'
+ OR $name GLOB 'Recomposer:*'
+ OR $name GLOB 'Compose:*'
OR (
-- Some top level handler slices
$depth = 0
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql b/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql
index 496e4ee..29c0b84 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/desktop_mode.sql
@@ -51,12 +51,21 @@
dw_statsd_events_update_by_instance AS (
SELECT instance_id, session_id, min(uid) AS uid FROM atoms
WHERE type = 'TASK_INFO_CHANGED' GROUP BY instance_id, session_id),
+ dw_statsd_reset_event AS (
+ SELECT ts FROM atoms
+ WHERE type = 'TASK_INIT_STATSD'
+ UNION
+ SELECT trace_end()),
dw_windows AS (
SELECT
a.ts AS raw_add_ts,
r.ts AS raw_remove_ts,
ifnull(a.ts, trace_start()) AS ts, -- Assume trace_start() if no add event found.
- ifnull(r.ts, trace_end()) - ifnull(a.ts, trace_start()) AS dur, -- Assume trace_end() if no remove event found.
+ ifnull(r.ts,
+ (
+ SELECT MIN(ts) FROM dw_statsd_reset_event
+ WHERE ts > ifnull(a.ts, trace_start())
+ )) - ifnull(a.ts, trace_start()) AS dur, -- Assume next reset event or trace_end() if no remove event found.
ifnull(a.instance_id, r.instance_id) AS instance_id,
ifnull(a.uid, r.uid) AS uid
FROM dw_statsd_events_add a
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/input.sql b/src/trace_processor/perfetto_sql/stdlib/android/input.sql
index 39c39fe..217132d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/input.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/input.sql
@@ -300,13 +300,19 @@
-- The timestamp of when the input event was processed by the system
ts INT,
-- Details of the input event parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
event_id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_android_key_events;
-- Motion events processed by the Android framework (from android.input.inputevent data source).
@@ -319,13 +325,19 @@
-- The timestamp of when the input event was processed by the system
ts INT,
-- Details of the input event parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
event_id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_android_motion_events;
-- Input event dispatching information in Android (from android.input.inputevent data source).
@@ -334,8 +346,12 @@
id INT,
-- Event ID of the input event that was dispatched
event_id INT,
- -- Extra args parsed from the proto message
+ -- Details of the input event parsed from the proto message
arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT,
-- Vsync ID that identifies the state of the windows during which the dispatch decision was made
vsync_id INT,
-- Window ID of the window receiving the event
@@ -345,6 +361,8 @@
id,
event_id,
arg_set_id,
+ base64_proto,
+ base64_proto_id,
vsync_id,
window_id
FROM __intrinsic_android_input_event_dispatch;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql b/src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql
index ea3f8cb..4407660 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/job_scheduler_states.sql
@@ -62,6 +62,7 @@
s.ts,
s.id AS slice_id,
extract_arg(arg_set_id, 'scheduled_job_state_changed.job_name') AS job_name,
+ extract_arg(arg_set_id, 'scheduled_job_state_changed.attribution_node[0].uid') AS uid,
extract_arg(arg_set_id, 'scheduled_job_state_changed.state') AS state,
extract_arg(arg_set_id, 'scheduled_job_state_changed.internal_stop_reason')
AS internal_stop_reason,
@@ -126,21 +127,21 @@
SELECT
*,
LEAD(state, 1)
- OVER (PARTITION BY job_name, job_id ORDER BY job_name, job_id, ts) AS lead_state,
+ OVER (PARTITION BY uid, job_name, job_id ORDER BY uid, job_name, job_id, ts) AS lead_state,
LEAD(ts, 1, TRACE_END())
- OVER (PARTITION BY job_name, job_id ORDER BY job_name, job_id, ts) AS ts_lead,
+ OVER (PARTITION BY uid, job_name, job_id ORDER BY uid, job_name, job_id, ts) AS ts_lead,
--- Filter out statsd lossy issue.
LEAD(ts, 1)
- OVER (PARTITION BY job_name, job_id ORDER BY job_name, job_id, ts) IS NULL AS is_end_slice,
+ OVER (PARTITION BY uid, job_name, job_id ORDER BY uid, job_name, job_id, ts) IS NULL AS is_end_slice,
LEAD(internal_stop_reason, 1, 'INTERNAL_STOP_REASON_UNKNOWN')
OVER (
- PARTITION BY job_name, job_id
- ORDER BY job_name, job_id, ts
+ PARTITION BY uid, job_name, job_id
+ ORDER BY uid, job_name, job_id, ts
) AS lead_internal_stop_reason,
LEAD(public_stop_reason, 1, 'PUBLIC_STOP_REASON_UNKNOWN')
OVER (
- PARTITION BY job_name, job_id
- ORDER BY job_name, job_id, ts
+ PARTITION BY uid, job_name, job_id
+ ORDER BY uid, job_name, job_id, ts
) AS lead_public_stop_reason
FROM _job_states
WHERE state != 'CANCELLED'
@@ -209,6 +210,8 @@
slice_id INT,
-- Name of the job (as named by the app).
job_name STRING,
+ -- Uid associated with job.
+ uid INT,
-- Id of job (assigned by app for T- builds and system generated in U+
-- builds).
job_id INT,
@@ -264,7 +267,12 @@
-- Number of uncompleted job work items.
num_uncompleted_work_items INT,
-- Process state of the process responsible for running the job.
- proc_state STRING
+ proc_state STRING,
+ -- Internal stop reason for a job.
+ internal_stop_reason STRING,
+ -- Public stop reason for a job.
+ public_stop_reason STRING
+
) AS
SELECT
ROW_NUMBER() OVER (ORDER BY ts) AS id,
@@ -272,6 +280,7 @@
dur,
slice_id,
job_name,
+ uid,
job_id,
package_name,
job_namespace,
@@ -297,7 +306,9 @@
deadline_ms,
job_start_latency_ms,
num_uncompleted_work_items,
- proc_state
+ proc_state,
+ lead_internal_stop_reason AS internal_stop_reason,
+ lead_public_stop_reason AS public_stop_reason
FROM _job_started;
-- This table returns the constraint, charging,
@@ -324,7 +335,7 @@
CREATE PERFETTO TABLE android_job_scheduler_with_screen_charging_states(
-- Timestamp of job.
ts INT,
- -- Duration of job in ns.
+ -- Duration of slice in ns.
dur INT,
-- Id of the slice.
slice_id INT,
@@ -333,7 +344,9 @@
-- Id of job (assigned by app for T- builds and system generated in U+
-- builds).
job_id INT,
- -- Duration of job in ns.
+ -- Uid associated with job.
+ uid INT,
+ -- Duration of entire job in ns.
job_dur INT,
-- Package that the job belongs (ex: associated app).
package_name STRING,
@@ -393,13 +406,18 @@
-- Number of uncompleted job work items.
num_uncompleted_work_items INT,
-- Process state of the process responsible for running the job.
- proc_state STRING
+ proc_state STRING,
+ -- Internal stop reason for a job.
+ internal_stop_reason STRING,
+ -- Public stop reason for a job.
+ public_stop_reason STRING
) AS
SELECT
ii.ts,
ii.dur,
js.slice_id,
js.job_name || '_' || js.job_id AS job_name,
+ js.uid,
js.job_id,
js.dur AS job_dur,
js.package_name,
@@ -428,7 +446,9 @@
js.deadline_ms,
js.job_start_latency_ms,
js.num_uncompleted_work_items,
- js.proc_state
+ js.proc_state,
+ js.internal_stop_reason,
+ js.public_stop_reason
FROM _interval_intersect!(
(_charging_screen_states,
android_job_scheduler_states),
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/BUILD.gn
index daaf9e0..aa317e4 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/BUILD.gn
@@ -15,5 +15,8 @@
import("../../../../../../../gn/perfetto_sql.gni")
perfetto_sql_source_set("heap_profile") {
- sources = [ "callstacks.sql" ]
+ sources = [
+ "callstacks.sql",
+ "summary_tree.sql",
+ ]
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/summary_tree.sql b/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/summary_tree.sql
new file mode 100644
index 0000000..c9e89c3
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/android/memory/heap_profile/summary_tree.sql
@@ -0,0 +1,123 @@
+
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the 'License');
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an 'AS IS' BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE callstacks.stack_profile;
+
+CREATE PERFETTO TABLE _android_heap_profile_raw_callstacks AS
+WITH metrics AS MATERIALIZED (
+ SELECT
+ callsite_id,
+ SUM(size) AS self_size,
+ SUM(MAX(size, 0)) AS self_alloc_size
+ FROM heap_profile_allocation
+ GROUP BY callsite_id
+)
+SELECT
+ c.id,
+ c.parent_id,
+ c.name,
+ c.mapping_name,
+ c.source_file,
+ c.line_number,
+ IFNULL(m.self_size, 0) AS self_size,
+ IFNULL(m.self_alloc_size, 0) AS self_alloc_size
+FROM _callstacks_for_stack_profile_samples!(metrics) c
+LEFT JOIN metrics m USING (callsite_id);
+
+CREATE PERFETTO TABLE _android_heap_profile_cumulatives AS
+SELECT a.*
+FROM _graph_aggregating_scan!(
+ (
+ SELECT id AS source_node_id, parent_id AS dest_node_id
+ FROM _android_heap_profile_raw_callstacks
+ WHERE parent_id IS NOT NULL
+ ),
+ (
+ SELECT
+ p.id,
+ p.self_size AS cumulative_size,
+ p.self_alloc_size AS cumulative_alloc_size
+ FROM _android_heap_profile_raw_callstacks p
+ LEFT JOIN _android_heap_profile_raw_callstacks c ON c.parent_id = p.id
+ WHERE c.id IS NULL
+ ),
+ (cumulative_size, cumulative_alloc_size),
+ (
+ WITH agg AS (
+ SELECT
+ t.id,
+ SUM(t.cumulative_size) AS child_size,
+ SUM(t.cumulative_alloc_size) AS child_alloc_size
+ FROM $table t
+ GROUP BY t.id
+ )
+ SELECT
+ a.id,
+ a.child_size + r.self_size as cumulative_size,
+ a.child_alloc_size + r.self_alloc_size AS cumulative_alloc_size
+ FROM agg a
+ JOIN _android_heap_profile_raw_callstacks r USING (id)
+ )
+) a;
+
+-- Table summarising the amount of memory allocated by each
+-- callstack as seen by Android native heap profiling (i.e.
+-- profiling information collected by heapprofd).
+--
+-- Note: this table collapses data from all processes together
+-- into a single table.
+CREATE PERFETTO TABLE android_heap_profile_summary_tree(
+ -- The id of the callstack. A callstack in this context
+ -- is a unique set of frames up to the root.
+ id INT,
+ -- The id of the parent callstack for this callstack.
+ parent_id INT,
+ -- The function name of the frame for this callstack.
+ name STRING,
+ -- The name of the mapping containing the frame. This
+ -- can be a native binary, library, JAR or APK.
+ mapping_name STRING,
+ -- The name of the file containing the function.
+ source_file STRING,
+ -- The line number in the file the function is located at.
+ line_number INT,
+ -- The amount of memory allocated and *not freed* with this
+ -- function as the leaf frame.
+ self_size INT,
+ -- The amount of memory allocated and *not freed* with this
+ -- function appearing anywhere on the callstack.
+ cumulative_size INT,
+ -- The amount of memory allocated with this function as the leaf
+ -- frame. This may include memory which was later freed.
+ self_alloc_size INT,
+ -- The amount of memory allocated with this function appearing
+ -- anywhere on the callstack. This may include memory which was
+ -- later freed.
+ cumulative_alloc_size INT
+) AS
+SELECT
+ id,
+ parent_id,
+ name,
+ mapping_name,
+ source_file,
+ line_number,
+ self_size,
+ cumulative_size,
+ self_alloc_size,
+ cumulative_alloc_size
+FROM _android_heap_profile_raw_callstacks r
+JOIN _android_heap_profile_cumulatives a USING (id);
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_minsdk33.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_minsdk33.sql
index 2378473..b96b53a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_minsdk33.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_minsdk33.sql
@@ -45,6 +45,7 @@
-- Originally completed was unqualified, but at some point we introduced
-- the startup type as well
AND name GLOB 'launchingActivity#*:completed*:*'
+ AND NOT name GLOB '*:completed-same-process:*'
)
GROUP BY 1, 2, 3;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql b/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql
index 15a4a8e..afd234b 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/winscope/inputmethod.sql
@@ -20,12 +20,18 @@
-- Timestamp when the dump was triggered
ts INT,
-- Extra args parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_inputmethod_clients;
-- Android inputmethod manager service state dumps (from android.inputmethod data source).
@@ -35,12 +41,18 @@
-- Timestamp when the dump was triggered
ts INT,
-- Extra args parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_inputmethod_manager_service;
-- Android inputmethod service state dumps (from android.inputmethod data source).
@@ -50,10 +62,16 @@
-- Timestamp when the dump was triggered
ts INT,
-- Extra args parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_inputmethod_service;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/winscope/viewcapture.sql b/src/trace_processor/perfetto_sql/stdlib/android/winscope/viewcapture.sql
index 77e68b5..11fb341 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/winscope/viewcapture.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/winscope/viewcapture.sql
@@ -20,10 +20,16 @@
-- Timestamp when the snapshot was triggered
ts INT,
-- Extra args parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_viewcapture;
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/winscope/windowmanager.sql b/src/trace_processor/perfetto_sql/stdlib/android/winscope/windowmanager.sql
index e602125..f8d42da 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/winscope/windowmanager.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/winscope/windowmanager.sql
@@ -20,10 +20,16 @@
-- Timestamp when the snapshot was triggered
ts INT,
-- Extra args parsed from the proto message
- arg_set_id INT
+ arg_set_id INT,
+ -- Raw proto message encoded in base64
+ base64_proto STRING,
+ -- String id for raw proto message
+ base64_proto_id INT
) AS
SELECT
id,
ts,
- arg_set_id
+ arg_set_id,
+ base64_proto,
+ base64_proto_id
FROM __intrinsic_windowmanager;
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
index 8657ca5..a653903 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/cpu_powerups.sql
@@ -149,23 +149,33 @@
WHERE s.depth = 0 -- Top-level slices only.
ORDER BY ts ASC;
--- A table holding the slices that executed within the scheduler
--- slice that ran on a CPU immediately after power-up.
---
--- @column ts Timestamp of the resulting slice
--- @column dur Duration of the slice.
--- @column cpu The CPU the sched slice ran on.
--- @column utid Unique thread id for the slice.
--- @column sched_id 'id' field from the sched_slice table.
--- @column type From the sched_slice table, always 'sched_slice'.
--- @column end_state The ending state for the sched_slice
--- @column priority The kernel thread priority
--- @column slice_id Id of the top-level slice for this (sched) slice.
-CREATE VIRTUAL TABLE chrome_cpu_power_post_powerup_slice
+CREATE VIRTUAL TABLE _chrome_cpu_power_post_powerup_slice_sj
USING
SPAN_JOIN(chrome_cpu_power_first_sched_slice_after_powerup PARTITIONED utid,
_cpu_power_thread_and_toplevel_slice PARTITIONED utid);
+-- A table holding the slices that executed within the scheduler
+-- slice that ran on a CPU immediately after power-up.
+CREATE PERFETTO TABLE chrome_cpu_power_post_powerup_slice(
+-- Timestamp of the resulting slice
+ts INT,
+-- Duration of the slice.
+dur INT,
+-- The CPU the sched slice ran on.
+cpu INT,
+-- Unique thread id for the slice.
+utid INT,
+-- 'id' field from the sched_slice table.
+sched_id INT,
+-- Id of the top-level slice for this (sched) slice.
+slice_id INT,
+-- Previous power state.
+previous_power_state LONG,
+-- Id of the powerup.
+powerup_id INT
+) AS
+SELECT * FROM _chrome_cpu_power_post_powerup_slice_sj;
+
-- The first top-level slice that ran after a CPU power-up.
CREATE PERFETTO VIEW chrome_cpu_power_first_toplevel_slice_after_powerup(
-- ID of the slice in the slice table.
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/event_latency.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/event_latency.sql
index 62b1d88..0d704db 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/event_latency.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/event_latency.sql
@@ -2,8 +2,6 @@
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
-INCLUDE PERFETTO MODULE deprecated.v42.common.slices;
-
-- Finds the start timestamp for a given slice's descendant with a given name.
-- If there are multiple descendants with a given name, the function will return
-- the first one, so it's most useful when working with a timeline broken down
@@ -42,6 +40,22 @@
WHERE s.name GLOB $child_name
LIMIT 1;
+-- Checks if slice has a descendant with provided name.
+CREATE PERFETTO FUNCTION _has_descendant_slice_with_name(
+ -- Id of the slice to check descendants of.
+ id INT,
+ -- Name of potential descendant slice.
+ descendant_name STRING
+)
+-- Whether `descendant_name` is a name of an descendant slice.
+RETURNS BOOL AS
+SELECT EXISTS(
+ SELECT 1
+ FROM descendant_slice($id)
+ WHERE name = $descendant_name
+ LIMIT 1
+);
+
-- Returns the presentation timestamp for a given EventLatency slice.
-- This is either the end of
-- SwapEndToPresentationCompositorFrame (if it exists),
@@ -101,7 +115,7 @@
slice.ts,
slice.dur,
EXTRACT_arg(arg_set_id, 'event_latency.event_latency_id') AS scroll_update_id,
- has_descendant_slice_with_name(
+ _has_descendant_slice_with_name(
slice.id,
'SubmitCompositorFrameToPresentationCompositorFrame')
AS is_presented,
@@ -142,7 +156,7 @@
event_type GLOB '*GESTURE_SCROLL*'
-- Pinches are only relevant if the frame was presented.
OR (event_type GLOB '*GESTURE_PINCH_UPDATE'
- AND has_descendant_slice_with_name(
+ AND _has_descendant_slice_with_name(
id,
'SubmitCompositorFrameToPresentationCompositorFrame')
)
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/graphics_pipeline.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/graphics_pipeline.sql
index 909619c..eae5bd5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/graphics_pipeline.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/graphics_pipeline.sql
@@ -2,7 +2,7 @@
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
-INCLUDE PERFETTO MODULE deprecated.v42.common.slices;
+INCLUDE PERFETTO MODULE slices.with_context;
-- `Graphics.Pipeline` steps corresponding to work done by a Viz client to
-- produce a frame (i.e. before surface aggregation). Covers steps:
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/input.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/input.sql
index b882627..ebc8f8b 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/input.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/input.sql
@@ -19,7 +19,9 @@
-- Step name (ChromeLatencyInfo.step).
step STRING,
-- Input type.
- input_type STRING
+ input_type STRING,
+ -- Start time of the parent Chrome scheduler task (if any) of this step.
+ task_start_time_ts INT
) AS
SELECT
EXTRACT_ARG(thread_slice.arg_set_id, 'chrome_latency_info.trace_id') AS latency_id,
@@ -28,7 +30,8 @@
dur,
utid,
EXTRACT_ARG(thread_slice.arg_set_id, 'chrome_latency_info.step') AS step,
- EXTRACT_ARG(thread_slice.arg_set_id, 'chrome_latency_info.input_type') AS input_type
+ EXTRACT_ARG(thread_slice.arg_set_id, 'chrome_latency_info.input_type') AS input_type,
+ ts - (EXTRACT_ARG(thread_slice.arg_set_id, 'current_task.event_offset_from_task_start_time_us') * 1000) AS task_start_time_ts
FROM
thread_slice
WHERE
@@ -69,7 +72,9 @@
-- Step name (ChromeLatencyInfo.step).
step STRING,
-- Input type.
- input_type STRING
+ input_type STRING,
+ -- Start time of the parent Chrome scheduler task (if any) of this step.
+ task_start_time_ts INT
) AS
SELECT
latency_id,
@@ -78,7 +83,8 @@
dur,
utid,
step,
- chrome_inputs.input_type AS input_type
+ chrome_inputs.input_type AS input_type,
+ task_start_time_ts
FROM
chrome_inputs
LEFT JOIN
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
deleted file mode 100644
index 73db0a7..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import("../../../../../gn/perfetto_sql.gni")
-
-perfetto_sql_source_set("common") {
- sources = [
- "args.sql",
- "counters.sql",
- "metadata.sql",
- "percentiles.sql",
- "slices.sql",
- "timestamps.sql",
- ]
-}
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/OWNERS b/src/trace_processor/perfetto_sql/stdlib/common/OWNERS
deleted file mode 100644
index 0a16b3f..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-set noparent
-
-# Please prefer sending to one of the following people
-mayzner@google.com
-lalitm@google.com
-
-# For emergency reviews
-primiano@google.com
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/args.sql b/src/trace_processor/perfetto_sql/stdlib/common/args.sql
deleted file mode 100644
index 3d1e793..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/args.sql
+++ /dev/null
@@ -1,20 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/counters.sql b/src/trace_processor/perfetto_sql/stdlib/common/counters.sql
deleted file mode 100644
index f0c1ce6..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/counters.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
-INCLUDE PERFETTO MODULE deprecated.v42.common.counters;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/metadata.sql b/src/trace_processor/perfetto_sql/stdlib/common/metadata.sql
deleted file mode 100644
index bd1a0fd..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/metadata.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
-INCLUDE PERFETTO MODULE deprecated.v42.common.metadata;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql b/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql
deleted file mode 100644
index 525c95c..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
-INCLUDE PERFETTO MODULE deprecated.v42.common.percentiles;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/slices.sql b/src/trace_processor/perfetto_sql/stdlib/common/slices.sql
deleted file mode 100644
index d5d70c9..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/slices.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
-INCLUDE PERFETTO MODULE deprecated.v42.common.slices;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql b/src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql
deleted file mode 100644
index 8f91d3b..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- No new changes allowed. Will be removed after v45 of Perfetto.
---
--- We decided to move away from the generalised `common` module and migrate the
--- most useful functionality into specialised modules.
-INCLUDE PERFETTO MODULE deprecated.v42.common.args;
-INCLUDE PERFETTO MODULE deprecated.v42.common.timestamps;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/BUILD.gn
deleted file mode 100644
index a99b51b..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/BUILD.gn
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2022 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import("../../../../../../../gn/perfetto_sql.gni")
-
-perfetto_sql_source_set("common") {
- sources = [
- "args.sql",
- "counters.sql",
- "metadata.sql",
- "percentiles.sql",
- "slices.sql",
- "timestamps.sql",
- ]
-}
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql
deleted file mode 100644
index df0615a..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- Returns the formatted value of a given argument.
--- Similar to EXTRACT_ARG, but instead of returning the raw value, it returns
--- the value formatted according to the 'value_type' column (e.g. for booleans,
--- EXTRACT_ARG will return 0 or 1, while FORMATTED_ARG will return 'true' or
--- 'false').
-CREATE PERFETTO FUNCTION formatted_arg(
- -- Id of the arg set.
- arg_set_id INT,
- -- Key of the argument.
- arg_key STRING
-)
--- Formatted value of the argument.
-RETURNS STRING AS
-SELECT display_value
-FROM args
-WHERE arg_set_id = $arg_set_id AND key = $arg_key;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql
deleted file mode 100644
index 7923c52..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql
+++ /dev/null
@@ -1,101 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
-INCLUDE PERFETTO MODULE deprecated.v42.common.timestamps;
-
--- Timestamp of first counter value in a counter.
-CREATE PERFETTO FUNCTION earliest_timestamp_for_counter_track(
- -- Id of a counter track with a counter.
- counter_track_id INT)
--- Timestamp of first counter value. Null if doesn't exist.
-RETURNS LONG AS
-SELECT MIN(ts) FROM counter WHERE counter.track_id = $counter_track_id;
-
--- Counter values with details of counter track with calculated duration of each counter value.
--- Duration is calculated as time from counter to the next counter.
-CREATE PERFETTO FUNCTION counter_with_dur_for_track(
- -- Id of track counter track.
- counter_track_id INT)
-RETURNS TABLE(
- -- Timestamp of the counter value.
- ts LONG,
- -- Duration of the counter value.
- dur LONG,
- -- Counter value.
- value DOUBLE,
- -- Id of the counter track.
- track_id INT,
- -- Name of the counter track.
- track_name STRING,
- -- Counter track set id.
- track_arg_set_id INT,
- -- Counter arg set id.
- arg_set_id INT
-) AS
-SELECT
- ts,
- LEAD(ts, 1, trace_end()) OVER(ORDER BY ts) - ts AS dur,
- value,
- track.id AS track_id,
- track.name AS track_name,
- track.source_arg_set_id AS track_arg_set_id,
- counter.arg_set_id AS arg_set_id
-FROM counter
-JOIN counter_track track ON track.id = counter.track_id
-WHERE track.id = $counter_track_id;
-
--- COUNTER_WITH_DUR_FOR_TRACK but in a specified time.
--- Does calculation over the table ends - creates an artificial counter value at
--- the start if needed and chops the duration of the last timestamps in range.
-CREATE PERFETTO FUNCTION counter_for_time_range(
- -- Id of track counter track.
- counter_track_id INT,
- -- Timestamp of the timerange start.
- -- Can be earlier than the first counter value.
- start_ts LONG,
- -- Timestamp of the timerange end.
- end_ts LONG)
-RETURNS TABLE(
- -- Timestamp of the counter value.
- ts LONG,
- -- Duration of the counter value.
- dur LONG,
- -- Counter value.
- value DOUBLE,
- -- If of the counter track.
- track_id INT,
- -- Name of the counter track.
- track_name STRING,
- -- Counter track set id.
- track_arg_set_id INT,
- -- Counter arg set id.
- arg_set_id INT
-) AS
-SELECT
- IIF(ts < $start_ts, $start_ts, ts) AS ts,
- IIF(
- ts < $start_ts,
- dur - ($start_ts - ts),
- IIF(ts + dur > $end_ts, $end_ts - ts, dur)) AS dur,
- value,
- track_id,
- track_name,
- track_arg_set_id,
- arg_set_id
-FROM counter_with_dur_for_track($counter_track_id)
-WHERE TRUE
- AND ts + dur >= $start_ts
- AND ts < $end_ts
-ORDER BY ts ASC;
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/metadata.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/metadata.sql
deleted file mode 100644
index e667477..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/metadata.sql
+++ /dev/null
@@ -1,22 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
--- Extracts an int value with the given name from the metadata table.
-CREATE PERFETTO FUNCTION extract_int_metadata(
- -- The name of the metadata entry.
- name STRING)
--- int_value for the given name. NULL if there's no such entry.
-RETURNS LONG AS
-SELECT int_value FROM metadata WHERE name = ($name);
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql
deleted file mode 100644
index e807a78..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql
+++ /dev/null
@@ -1,169 +0,0 @@
---
--- Copyright 2023 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
-INCLUDE PERFETTO MODULE deprecated.v42.common.counters;
-INCLUDE PERFETTO MODULE deprecated.v42.common.timestamps;
-
-CREATE PERFETTO FUNCTION _number_generator(upper_limit INT)
-RETURNS TABLE(num INT) AS
-WITH nums AS
- (SELECT 1 num UNION SELECT num + 1
- from NUMS
- WHERE num < $upper_limit)
-SELECT num FROM nums;
-
-CREATE PERFETTO FUNCTION _earliest_timestamp_for_counter_track(
- -- Id of a counter track with a counter.
- counter_track_id INT)
--- Timestamp of first counter value. Null if doesn't exist.
-RETURNS LONG AS
-SELECT MIN(ts) FROM counter WHERE counter.track_id = $counter_track_id;
-
--- COUNTER_WITH_DUR_FOR_TRACK but in a specified time.
--- Does calculation over the table ends - creates an artificial counter value at
--- the start if needed and chops the duration of the last timestamps in range.
-CREATE PERFETTO FUNCTION _counter_for_time_range(
- -- Id of track counter track.
- counter_track_id INT,
- -- Timestamp of the timerange start.
- -- Can be earlier than the first counter value.
- start_ts LONG,
- -- Timestamp of the timerange end.
- end_ts LONG)
-RETURNS TABLE(
- -- Timestamp of the counter value.
- ts LONG,
- -- Duration of the counter value.
- dur LONG,
- -- Counter value.
- value DOUBLE,
- -- If of the counter track.
- track_id INT,
- -- Name of the counter track.
- track_name STRING,
- -- Counter track set id.
- track_arg_set_id INT,
- -- Counter arg set id.
- arg_set_id INT
-) AS
-SELECT
- IIF(ts < $start_ts, $start_ts, ts) AS ts,
- IIF(
- ts < $start_ts,
- dur - ($start_ts - ts),
- IIF(ts + dur > $end_ts, $end_ts - ts, dur)) AS dur,
- value,
- track_id,
- track_name,
- track_arg_set_id,
- arg_set_id
-FROM counter_with_dur_for_track($counter_track_id)
-WHERE TRUE
- AND ts + dur >= $start_ts
- AND ts < $end_ts
-ORDER BY ts ASC;
-
---
--- Get durations for percentile
---
-
--- All percentiles (range 1-100) for counter track ID in a given time range.
---
--- Percentiles are calculated by:
--- 1. Dividing the sum of duration in time range for each value in the counter
--- by duration of the counter in range. This gives us `percentile_for)value` (DOUBLE).
--- 2. Fetching each percentile by taking floor of each `percentile_for_value`, grouping by
--- resulting `percentile` and MIN from value for each grouping. As we are rounding down,
--- taking MIN assures most reliable data.
--- 3. Filling the possible gaps in percentiles by getting the minimal value from higher
--- percentiles for each gap.
-CREATE PERFETTO FUNCTION counter_percentiles_for_time_range(
- -- Id of the counter track.
- counter_track_id INT,
- -- Timestamp of start of time range.
- start_ts LONG,
- -- Timestamp of end of time range.
- end_ts LONG)
-RETURNS TABLE(
- -- All of the numbers from 1 to 100.
- percentile INT,
- -- Value for the percentile.
- value DOUBLE
-) AS
-WITH percentiles_for_value AS (
- SELECT
- value,
- (CAST(SUM(dur) OVER(ORDER BY value ASC) AS DOUBLE) /
- ($end_ts - MAX($start_ts, _earliest_timestamp_for_counter_track($counter_track_id)))) * 100
- AS percentile_for_value
- FROM _COUNTER_FOR_TIME_RANGE($counter_track_id, $start_ts, $end_ts)
- ORDER BY value ASC
-),
-with_gaps AS (
- SELECT
- CAST(percentile_for_value AS INT) AS percentile,
- MIN(value) AS value
- FROM percentiles_for_value
- GROUP BY percentile
- ORDER BY percentile ASC)
-SELECT
- num AS percentile,
- IFNULL(value, MIN(value) OVER (ORDER BY percentile DESC)) AS value
-FROM _NUMBER_GENERATOR(100) AS nums
-LEFT JOIN with_gaps ON with_gaps.percentile = nums.num
-ORDER BY percentile DESC;
-
--- All percentiles (range 1-100) for counter track ID.
-CREATE PERFETTO FUNCTION counter_percentiles_for_track(
- -- Id of the counter track.
- counter_track_id INT)
-RETURNS TABLE(
- -- All of the numbers from 1 to 100.
- percentile INT,
- -- Value for the percentile.
- value DOUBLE
-) AS
-SELECT *
-FROM counter_percentiles_for_time_range(
- $counter_track_id, trace_start(), trace_end());
-
--- Value for specific percentile (range 1-100) for counter track ID in time range.
-CREATE PERFETTO FUNCTION counter_track_percentile_for_time(
- -- Id of the counter track.
- counter_track_id INT,
- -- Any of the numbers from 1 to 100.
- percentile INT,
- -- Timestamp of start of time range.
- start_ts LONG,
- -- Timestamp of end of time range.
- end_ts LONG)
--- Value for the percentile.
-RETURNS DOUBLE AS
-SELECT value
-FROM counter_percentiles_for_time_range($counter_track_id, $start_ts, $end_ts)
-WHERE percentile = $percentile;
-
--- Value for specific percentile (range 1-100) for counter track ID.
-CREATE PERFETTO FUNCTION counter_track_percentile(
- -- Id of the counter track.
- counter_track_id INT,
- -- Any of the numbers from 1 to 100.
- percentile INT)
--- Value for the percentile.
-RETURNS DOUBLE AS
-SELECT counter_track_percentile_for_time($counter_track_id,
- $percentile,
- trace_start(),
- trace_end());
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql
deleted file mode 100644
index 05b6b21..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql
+++ /dev/null
@@ -1,133 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
-INCLUDE PERFETTO MODULE slices.with_context;
-
--- Checks if slice has an ancestor with provided name.
-CREATE PERFETTO FUNCTION has_parent_slice_with_name(
- -- Id of the slice to check parents of.
- id INT,
- -- Name of potential ancestor slice.
- parent_name STRING)
--- Whether `parent_name` is a name of an ancestor slice.
-RETURNS BOOL AS
-SELECT EXISTS(
- SELECT 1
- FROM ancestor_slice($id)
- WHERE name = $parent_name
- LIMIT 1
-);
-
--- Checks if slice has a descendant with provided name.
-CREATE PERFETTO FUNCTION has_descendant_slice_with_name(
- -- Id of the slice to check descendants of.
- id INT,
- -- Name of potential descendant slice.
- descendant_name STRING
-)
--- Whether `descendant_name` is a name of an descendant slice.
-RETURNS BOOL AS
-SELECT EXISTS(
- SELECT 1
- FROM descendant_slice($id)
- WHERE name = $descendant_name
- LIMIT 1
-);
-
--- Finds the end timestamp for a given slice's descendant with a given name.
--- If there are multiple descendants with a given name, the function will return the
--- first one, so it's most useful when working with a timeline broken down into phases,
--- where each subphase can happen only once.
-CREATE PERFETTO FUNCTION descendant_slice_end(
- -- Id of the parent slice.
- parent_id INT,
- -- Name of the child with the desired end TS.
- child_name STRING
-)
--- End timestamp of the child or NULL if it doesn't exist.
-RETURNS INT AS
-SELECT
- CASE WHEN s.dur
- IS NOT -1 THEN s.ts + s.dur
- ELSE NULL
- END
-FROM descendant_slice($parent_id) s
-WHERE s.name = $child_name
-LIMIT 1;
-
--- Finds all slices with a direct parent with the given parent_id.
-CREATE PERFETTO FUNCTION direct_children_slice(
- -- Id of the parent slice.
- parent_id LONG)
-RETURNS TABLE(
- -- Alias for `slice.id`.
- id LONG,
- -- Alias for `slice.type`.
- type STRING,
- -- Alias for `slice.ts`.
- ts LONG,
- -- Alias for `slice.dur`.
- dur LONG,
- -- Alias for `slice.category`.
- category LONG,
- -- Alias for `slice.name`.
- name STRING,
- -- Alias for `slice.track_id`.
- track_id LONG,
- -- Alias for `slice.depth`.
- depth LONG,
- -- Alias for `slice.parent_id`.
- parent_id LONG,
- -- Alias for `slice.arg_set_id`.
- arg_set_id LONG,
- -- Alias for `slice.thread_ts`.
- thread_ts LONG,
- -- Alias for `slice.thread_dur`.
- thread_dur LONG
-) AS
-SELECT
- slice.id,
- slice.type,
- slice.ts,
- slice.dur,
- slice.category,
- slice.name,
- slice.track_id,
- slice.depth,
- slice.parent_id,
- slice.arg_set_id,
- slice.thread_ts,
- slice.thread_dur
-FROM slice
-WHERE parent_id = $parent_id;
-
--- Given a slice id, returns the name of the slice.
-CREATE PERFETTO FUNCTION slice_name_from_id(
- -- The slice id which we need the name for.
- id LONG
-)
--- The name of slice with the given id.
-RETURNS STRING AS
-SELECT
- name
-FROM slice
-WHERE $id = id;
-
-CREATE PERFETTO FUNCTION slice_count(
- -- Name of the slices to counted.
- slice_glob STRING)
--- Number of slices with the name.
-RETURNS INT AS
-SELECT COUNT(1) FROM slice WHERE name GLOB $slice_glob;
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql
deleted file mode 100644
index bff333f..0000000
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql
+++ /dev/null
@@ -1,72 +0,0 @@
---
--- Copyright 2022 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
-
-INCLUDE PERFETTO MODULE time.conversion;
-
-CREATE PERFETTO FUNCTION is_spans_overlapping(
- ts1 LONG,
- ts_end1 LONG,
- ts2 LONG,
- ts_end2 LONG)
-RETURNS BOOL AS
-SELECT (IIF($ts1 < $ts2, $ts2, $ts1)
- < IIF($ts_end1 < $ts_end2, $ts_end1, $ts_end2));
-
-CREATE PERFETTO FUNCTION spans_overlapping_dur(
- ts1 LONG,
- dur1 LONG,
- ts2 LONG,
- dur2 LONG
-)
-RETURNS INT AS
-SELECT
- CASE
- WHEN $dur1 = -1 OR $dur2 = -1 THEN 0
- WHEN $ts1 + $dur1 < $ts2 OR $ts2 + $dur2 < $ts1 THEN 0
- WHEN ($ts1 >= $ts2) AND ($ts1 + $dur1 <= $ts2 + $dur2) THEN $dur1
- WHEN ($ts1 < $ts2) AND ($ts1 + $dur1 < $ts2 + $dur2) THEN $ts1 + $dur1 - $ts2
- WHEN ($ts1 > $ts2) AND ($ts1 + $dur1 > $ts2 + $dur2) THEN $ts2 + $dur2 - $ts1
- ELSE $dur2
- END;
-
--- Renames
-
-CREATE PERFETTO FUNCTION ns(nanos INT)
-RETURNS INT AS
-SELECT time_from_ns($nanos);
-
-CREATE PERFETTO FUNCTION us(micros INT)
-RETURNS INT AS
-SELECT time_from_us($micros);
-
-CREATE PERFETTO FUNCTION ms(millis INT)
-RETURNS INT AS
-SELECT time_from_ms($millis);
-
-CREATE PERFETTO FUNCTION seconds(seconds INT)
-RETURNS INT AS
-SELECT time_from_s($seconds);
-
-CREATE PERFETTO FUNCTION minutes(minutes INT)
-RETURNS INT AS
-SELECT time_from_min($minutes);
-
-CREATE PERFETTO FUNCTION hours(hours INT)
-RETURNS INT AS
-SELECT time_from_hours($hours);
-
-CREATE PERFETTO FUNCTION days(days INT)
-RETURNS INT AS
-SELECT time_from_days($days);
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/tables_views.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/tables_views.sql
index acd9cdb..e074220 100644
--- a/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/tables_views.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/tables_views.sql
@@ -15,6 +15,25 @@
INCLUDE PERFETTO MODULE prelude.after_eof.views;
+-- Lists all metrics built-into trace processor.
+CREATE PERFETTO VIEW trace_metrics(
+ -- The name of the metric.
+ name STRING
+) AS
+SELECT name FROM _trace_metrics;
+
+-- Definition of `trace_bounds` table. The values are being filled by Trace
+-- Processor when parsing the trace.
+-- It is recommended to depend on the `trace_start()` and `trace_end()`
+-- functions rather than directly on `trace_bounds`.
+CREATE PERFETTO VIEW trace_bounds(
+ -- First ts in the trace.
+ start_ts INT,
+ -- End of the trace.
+ end_ts INT
+) AS
+SELECT start_ts, end_ts FROM _trace_bounds;
+
-- Tracks are a fundamental concept in trace processor and represent a
-- "timeline" for events of the same type and with the same context. See
-- https://perfetto.dev/docs/analysis/trace-processor#tracks for a more
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/tables.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/tables.sql
index e8b5db0..2689491 100644
--- a/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/tables.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/tables.sql
@@ -14,7 +14,7 @@
-- limitations under the License.
-- Lists all metrics built-into trace processor.
-CREATE TABLE trace_metrics(
+CREATE TABLE _trace_metrics(
-- The name of the metric.
name STRING
);
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/trace_bounds.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/trace_bounds.sql
index 51db48d..ece2ecd 100644
--- a/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/trace_bounds.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/before_eof/trace_bounds.sql
@@ -13,25 +13,22 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
--- Definition of `trace_bounds` table. The values are being filled by Trace
--- Processor when parsing the trace. Can't be a Perfetto table because it has
--- to be mutable.
--- It is recommended to depend on the `trace_start()` and `trace_end()`
--- functions rather than directly on `trace_bounds`.
-CREATE TABLE trace_bounds AS
+-- The values are being filled by Trace Processor when parsing the trace.
+-- Exposed with `trace_bounds`.
+CREATE TABLE _trace_bounds AS
SELECT 0 AS start_ts, 0 AS end_ts;
-- Fetch start of the trace.
CREATE PERFETTO FUNCTION trace_start()
-- Start of the trace in nanoseconds.
RETURNS LONG AS
-SELECT start_ts FROM trace_bounds;
+SELECT start_ts FROM _trace_bounds;
-- Fetch end of the trace.
CREATE PERFETTO FUNCTION trace_end()
-- End of the trace in nanoseconds.
RETURNS LONG AS
-SELECT end_ts FROM trace_bounds;
+SELECT end_ts FROM _trace_bounds;
-- Fetch duration of the trace.
CREATE PERFETTO FUNCTION trace_dur()
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
index d7e9608..3a2ccbe 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
@@ -16,6 +16,7 @@
perfetto_sql_source_set("sched") {
sources = [
+ "latency.sql",
"runnable.sql",
"states.sql",
"thread_executing_span.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/latency.sql b/src/trace_processor/perfetto_sql/stdlib/sched/latency.sql
new file mode 100644
index 0000000..50dd7e7
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/latency.sql
@@ -0,0 +1,51 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE sched.runnable;
+
+CREATE PERFETTO VIEW _sched_with_thread_state_join AS
+SELECT
+ thread_state.id AS thread_state_id,
+ sched.id AS sched_id
+FROM sched
+JOIN thread_state USING (utid, ts, dur);
+
+-- Scheduling latency of running thread states.
+-- For each time the thread was running, returns the duration of the runnable
+-- state directly before.
+CREATE PERFETTO TABLE sched_latency_for_running_interval(
+ -- Running state of the thread. Alias of `thread_state.id`.
+ thread_state_id INT,
+ -- Id of a corresponding slice in a `sched` table. Alias of `sched.id`.
+ sched_id INT,
+ -- Thread with running state. Alias of `thread.id`.
+ utid INT,
+ -- Runnable state before thread is "running". Duration of this thread state
+ -- is `latency_dur`. One of `thread_state.id`.
+ runnable_latency_id INT,
+ -- Scheduling latency of thread state. Duration of thread state with
+ -- `runnable_latency_id`.
+ latency_dur INT
+) AS
+SELECT
+ r.id AS thread_state_id,
+ sched_id,
+ utid,
+ prev_runnable_id AS runnable_latency_id,
+ dur AS latency_dur
+FROM sched_previous_runnable_on_thread r
+JOIN thread_state prev_ts ON prev_runnable_id = prev_ts.id
+JOIN _sched_with_thread_state_join ON thread_state_id = r.id
+
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql b/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql
index 38956b1..ffe8b55 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql
@@ -19,12 +19,12 @@
-- - previous "Runnable" (or runnable preempted) state.
-- - previous uninterrupted "Runnable" state with a valid waker thread.
CREATE PERFETTO TABLE sched_previous_runnable_on_thread(
- -- `thread_state.id` id.
+ -- Alias of `thread_state.id`.
id INT,
- -- Previous runnable `thread_state.id`.
+ -- Previous runnable thread state. Alias of `thread_state.id`.
prev_runnable_id INT,
- -- Previous runnable `thread_state.id` with valid waker
- -- thread.
+ -- Previous runnable thread state with valid waker thread. Alias of
+ -- `thread_state.id`.
prev_wakeup_runnable_id INT
) AS
WITH running_and_runnable AS (
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql b/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
index e7081ee..50331e6 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
@@ -37,6 +37,24 @@
FROM intervals_overlap_count!(runnable, ts, dur)
ORDER BY ts;
+-- The count of threads in uninterruptible sleep over time.
+CREATE PERFETTO TABLE sched_uninterruptible_sleep_thread_count(
+ -- Timestamp when the thread count changed to the current value.
+ ts INT,
+ -- Number of threads in uninterrutible sleep, covering the range from this timestamp to the
+ -- next row's timestamp.
+ uninterruptible_sleep_thread_count INT
+) AS
+WITH
+uninterruptible_sleep AS (
+ SELECT ts, dur FROM thread_state
+ where state = 'D'
+)
+SELECT
+ ts, value as uninterruptible_sleep_thread_count
+FROM intervals_overlap_count!(uninterruptible_sleep, ts, dur)
+ORDER BY ts;
+
-- The count of active CPUs over time.
CREATE PERFETTO TABLE sched_active_cpu_count(
-- Timestamp when the number of active CPU changed.
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql b/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
index e627991..80696a1 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/time_in_state.sql
@@ -93,36 +93,58 @@
GROUP BY utid;
-- Time the thread spent each state in a given interval.
+--
+-- This function is only designed to run over a small number of intervals
+-- (10-100 at most). It will be *very slow* for large sets of intervals.
+--
+-- Specifically for any non-trivial subset of thread slices, prefer using
+-- `thread_slice_time_in_state` in the `slices.time_in_state` module for this
+-- purpose instead.
CREATE PERFETTO FUNCTION sched_time_in_state_for_thread_in_interval(
-- The start of the interval.
ts INT,
-- The duration of the interval.
dur INT,
-- The utid of the thread.
- utid INT)
+ utid INT
+)
RETURNS TABLE(
- -- Thread state (from the `thread_state` table).
- -- Use `sched_state_to_human_readable_string` function to get full name.
- state INT,
+ -- The scheduling state (from the `thread_state` table).
+ --
+ -- Use the `sched_state_to_human_readable_string` function in the `sched`
+ -- package to get full name.
+ state STRING,
-- A (posssibly NULL) boolean indicating, if the device was in uninterruptible
-- sleep, if it was an IO sleep.
io_wait BOOL,
- -- Some states can specify the blocked function. Usually NULL.
+ -- If the `state` is uninterruptible sleep, `io_wait` indicates if it was
+ -- an IO sleep. Will be null if `state` is *not* uninterruptible sleep or if
+ -- we cannot tell if it was an IO sleep or not.
+ --
+ -- Only available on Android when
+ -- `sched/sched_blocked_reason` ftrace tracepoint is enabled.
blocked_function INT,
- -- Total time spent with this state, cpu and blocked function.
- dur INT) AS
+ -- The duration of time the threads slice spent for each
+ -- (state, io_wait, blocked_function) tuple.
+ dur INT
+) AS
SELECT
state,
io_wait,
blocked_function,
sum(ii.dur) as dur
FROM thread_state
-JOIN
- (SELECT * FROM _interval_intersect_single!(
+JOIN (
+ SELECT *
+ FROM _interval_intersect_single!(
$ts, $dur,
- (SELECT id, ts, dur
- FROM thread_state
- WHERE utid = $utid AND dur > 0))) ii USING (id)
+ (
+ SELECT id, ts, dur
+ FROM thread_state
+ WHERE utid = $utid AND dur > 0
+ )
+ )
+) ii USING (id)
GROUP BY 1, 2, 3
ORDER BY 4 DESC;
@@ -137,7 +159,7 @@
RETURNS TABLE(
-- Thread state (from the `thread_state` table).
-- Use `sched_state_to_human_readable_string` function to get full name.
- state INT,
+ state STRING,
-- A (posssibly NULL) boolean indicating, if the device was in uninterruptible
-- sleep, if it was an IO sleep.
io_wait BOOL,
diff --git a/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
index 2e58615..4d616ea 100644
--- a/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/slices/BUILD.gn
@@ -21,6 +21,7 @@
"flow.sql",
"hierarchy.sql",
"slices.sql",
+ "time_in_state.sql",
"with_context.sql",
]
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/slices/time_in_state.sql b/src/trace_processor/perfetto_sql/stdlib/slices/time_in_state.sql
new file mode 100644
index 0000000..142a664
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/slices/time_in_state.sql
@@ -0,0 +1,77 @@
+--
+-- Copyright 2024 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+INCLUDE PERFETTO MODULE intervals.intersect;
+INCLUDE PERFETTO MODULE slices.with_context;
+
+-- For each thread slice, returns the sum of the time it spent in various
+-- scheduling states.
+--
+-- Requires scheduling data to be available in the trace.
+CREATE PERFETTO TABLE thread_slice_time_in_state(
+ -- Id of a slice. Alias of `slice.id`.
+ id INT,
+ -- Name of the slice.
+ name STRING,
+ -- Id of the thread the slice is running on. Alias of `thread.id`.
+ utid INT,
+ -- Name of the thread.
+ thread_name STRING,
+ -- Id of the process the slice is running on. Alias of `process.id`.
+ upid INT,
+ -- Name of the process.
+ process_name STRING,
+ -- The scheduling state (from the `thread_state` table).
+ --
+ -- Use the `sched_state_to_human_readable_string` function in the `sched`
+ -- package to get full name.
+ state STRING,
+ -- If the `state` is uninterruptible sleep, `io_wait` indicates if it was
+ -- an IO sleep. Will be null if `state` is *not* uninterruptible sleep or if
+ -- we cannot tell if it was an IO sleep or not.
+ --
+ -- Only available on Android when
+ -- `sched/sched_blocked_reason` ftrace tracepoint is enabled.
+ io_wait BOOL,
+ -- If in uninterruptible sleep (D), the kernel function on which was blocked.
+ -- Only available on userdebug Android builds when
+ -- `sched/sched_blocked_reason` ftrace tracepoint is enabled.
+ blocked_function INT,
+ -- The duration of time the threads slice spent for each
+ -- (state, io_wait, blocked_function) tuple.
+ dur INT
+) AS
+SELECT
+ ii.id_0 AS id,
+ ts.name,
+ ts.utid,
+ ts.thread_name,
+ ts.upid,
+ ts.process_name,
+ tstate.state,
+ tstate.io_wait,
+ tstate.blocked_function,
+ SUM(ii.dur) AS dur
+FROM _interval_intersect!(
+ (
+ (SELECT * FROM thread_slice WHERE utid > 0 AND dur > 0),
+ (SELECT * FROM thread_state WHERE dur > 0)
+ ),
+ (utid)
+) ii
+JOIN thread_slice ts ON ts.id = ii.id_0
+JOIN thread_state tstate ON tstate.id = ii.id_1
+GROUP BY ii.id_0, tstate.state, tstate.io_wait, tstate.blocked_function
+ORDER BY ii.id_0;
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_freq.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_freq.sql
index 94f8fd7..1256f3b 100644
--- a/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_freq.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_freq.sql
@@ -51,4 +51,16 @@
freq,
cpu,
policy
-FROM _cpu_freq;
+FROM _cpu_freq
+UNION ALL
+-- Add empty cpu freq counters for CPUs that are physically present, but did not
+-- have a single freq event register. The time region needs to be defined so
+-- that interval_intersect doesn't remove the undefined time region.
+SELECT
+ trace_start() as ts,
+ trace_dur() as dur,
+ NULL as freq,
+ cpu,
+ NULL as policy
+FROM _dev_cpu_policy_map
+WHERE cpu NOT IN (SELECT cpu FROM first_cpu_freq_slices);
diff --git a/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_idle.sql b/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_idle.sql
index 5c8c6b5..c2170b7 100644
--- a/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_idle.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/wattson/cpu_idle.sql
@@ -91,4 +91,15 @@
idle
FROM _cpu_idle
-- Some durations are 0 post-adjustment and won't work with interval intersect
-WHERE dur > 0;
+WHERE dur > 0
+UNION ALL
+-- Add empty cpu idle counters for CPUs that are physically present, but did not
+-- have a single idle event register. The time region needs to be defined so
+-- that interval_intersect doesn't remove the undefined time region.
+SELECT
+ trace_start() as ts,
+ trace_dur() as dur,
+ cpu,
+ NULL as idle
+FROM _dev_cpu_policy_map
+WHERE cpu NOT IN (SELECT cpu FROM first_cpu_idle_slices);
diff --git a/src/trace_processor/rpc/rpc.cc b/src/trace_processor/rpc/rpc.cc
index 49feef3..d26cdc1 100644
--- a/src/trace_processor/rpc/rpc.cc
+++ b/src/trace_processor/rpc/rpc.cc
@@ -212,7 +212,11 @@
}
case RpcProto::TPM_FINALIZE_TRACE_DATA: {
Response resp(tx_seq_id_++, req_type);
- NotifyEndOfFile();
+ auto* result = resp->set_finalize_data_result();
+ base::Status res = NotifyEndOfFile();
+ if (!res.ok()) {
+ result->set_error(res.message());
+ }
resp.Send(rpc_response_fn_);
break;
}
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index a7ca41d..cb24633 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -67,6 +67,21 @@
"unreliable. The kernel buffer overwrote events between our reads " \
"in userspace. Try re-recording the trace with a bigger buffer " \
"(ftrace_config.buffer_size_kb), or with fewer enabled ftrace events."),\
+ F(ftrace_kprobe_hits_begin, kSingle, kInfo, kTrace, \
+ "The number of kretprobe hits at the beginning of the trace."), \
+ F(ftrace_kprobe_hits_end, kSingle, kInfo, kTrace, \
+ "The number of kretprobe hits at the end of the trace."), \
+ F(ftrace_kprobe_hits_delta, kSingle, kInfo, kTrace, \
+ "The number of kprobe hits encountered during the collection of the" \
+ "trace."), \
+ F(ftrace_kprobe_misses_begin, kSingle, kInfo, kTrace, \
+ "The number of kretprobe missed events at the beginning of the trace."),\
+ F(ftrace_kprobe_misses_end, kSingle, kInfo, kTrace, \
+ "The number of kretprobe missed events at the end of the trace."), \
+ F(ftrace_kprobe_misses_delta, kSingle, kDataLoss, kTrace, \
+ "The number of kretprobe missed events encountered during the " \
+ "collection of the trace. A value greater than zero is due to the " \
+ "maxactive parameter for the kretprobe being too small"), \
F(ftrace_setup_errors, kSingle, kInfo, kTrace, \
"One or more atrace/ftrace categories were not found or failed to " \
"enable. See ftrace_setup_errors in the metadata table for details."), \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 2e350ae..f590be0 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -275,6 +275,12 @@
return std::nullopt;
}
+ int64_t GetStats(size_t key) {
+ PERFETTO_DCHECK(key < stats::kNumKeys);
+ PERFETTO_DCHECK(stats::kTypes[key] == stats::kSingle);
+ return stats_[key].value;
+ }
+
class ScopedStatsTracer {
public:
ScopedStatsTracer(TraceStorage* storage, size_t key)
diff --git a/src/trace_processor/tables/android_tables.py b/src/trace_processor/tables/android_tables.py
index 8f5e591..47fe3ea 100644
--- a/src/trace_processor/tables/android_tables.py
+++ b/src/trace_processor/tables/android_tables.py
@@ -165,6 +165,8 @@
C('event_id', CppUint32()),
C('ts', CppInt64()),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='Contains Android MotionEvents processed by the system',
@@ -181,17 +183,20 @@
ColumnDoc(
doc='Details of the motion event parsed from the proto message.',
joinable='args.arg_set_id'),
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
ANDROID_KEY_EVENTS_TABLE = Table(
python_module=__file__,
class_name='AndroidKeyEventsTable',
sql_name='__intrinsic_android_key_events',
- wrapping_sql_view=WrappingSqlView('android_key_events'),
columns=[
C('event_id', CppUint32()),
C('ts', CppInt64()),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='Contains Android KeyEvents processed by the system',
@@ -208,18 +213,21 @@
ColumnDoc(
doc='Details of the key event parsed from the proto message.',
joinable='args.arg_set_id'),
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
ANDROID_INPUT_EVENT_DISPATCH_TABLE = Table(
python_module=__file__,
class_name='AndroidInputEventDispatchTable',
sql_name='__intrinsic_android_input_event_dispatch',
- wrapping_sql_view=WrappingSqlView('android_input_event_dispath'),
columns=[
C('event_id', CppUint32()),
C('arg_set_id', CppUint32()),
C('vsync_id', CppInt64()),
C('window_id', CppInt32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='''
@@ -244,6 +252,8 @@
''',
'window_id':
'The id of the window to which the event was dispatched.',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
# Keep this list sorted.
diff --git a/src/trace_processor/tables/jit_tables.py b/src/trace_processor/tables/jit_tables.py
index d780250..6f0637f 100644
--- a/src/trace_processor/tables/jit_tables.py
+++ b/src/trace_processor/tables/jit_tables.py
@@ -44,7 +44,6 @@
C('native_code_base64', CppOptional(CppString())),
C('jit_code_id', Alias('id')),
],
- wrapping_sql_view=WrappingSqlView('jit_code'),
tabledoc=TableDoc(
doc="""
Represents a jitted code snippet
@@ -75,7 +74,6 @@
C('jit_code_id', CppTableId(JIT_CODE_TABLE)),
C('frame_id', CppTableId(STACK_PROFILE_FRAME_TABLE)),
],
- wrapping_sql_view=WrappingSqlView('jit_frame'),
tabledoc=TableDoc(
doc="""
Represents a jitted frame
diff --git a/src/trace_processor/tables/v8_tables.py b/src/trace_processor/tables/v8_tables.py
index 28729e4..25592f0 100644
--- a/src/trace_processor/tables/v8_tables.py
+++ b/src/trace_processor/tables/v8_tables.py
@@ -47,7 +47,6 @@
C('shared_code_range', CppOptional(CppBool())),
C('embedded_blob_code_copy_start_address', CppOptional(CppInt64())),
],
- wrapping_sql_view=WrappingSqlView('v8_isolate'),
tabledoc=TableDoc(
doc='Represents one Isolate instance',
group='v8',
@@ -88,7 +87,6 @@
C('name', CppString()),
C('source', CppOptional(CppString())),
],
- wrapping_sql_view=WrappingSqlView('v8_js_script'),
tabledoc=TableDoc(
doc='Represents one Javascript script',
group='v8',
@@ -112,7 +110,6 @@
C('url', CppString()),
C('source', CppOptional(CppString())),
],
- wrapping_sql_view=WrappingSqlView('v8_wasm_script'),
tabledoc=TableDoc(
doc='Represents one WASM script',
group='v8',
@@ -137,7 +134,6 @@
C('line', CppOptional(CppUint32())),
C('col', CppOptional(CppUint32())),
],
- wrapping_sql_view=WrappingSqlView('v8_js_function'),
tabledoc=TableDoc(
doc='Represents a v8 Javascript function',
group='v8',
@@ -171,7 +167,6 @@
C('tier', CppString()),
C('bytecode_base64', CppOptional(CppString())),
],
- wrapping_sql_view=WrappingSqlView('v8_js_code'),
tabledoc=TableDoc(
doc="""
Represents a v8 code snippet for a Javascript function. A given
@@ -210,7 +205,6 @@
C('function_name', CppString()),
C('code_type', CppString()),
],
- wrapping_sql_view=WrappingSqlView('v8_internal_code'),
tabledoc=TableDoc(
doc="""
Represents a v8 code snippet for a v8 internal function.
@@ -248,7 +242,6 @@
C('tier', CppString()),
C('code_offset_in_module', CppInt32()),
],
- wrapping_sql_view=WrappingSqlView('v8_wasm_code'),
tabledoc=TableDoc(
doc="""
Represents the code associated to a WASM function
@@ -292,7 +285,6 @@
C('v8_isolate_id', CppTableId(V8_ISOLATE)),
C('pattern', CppString()),
],
- wrapping_sql_view=WrappingSqlView('v8_regexp_code'),
tabledoc=TableDoc(
doc="""
Represents the code associated to a regular expression
diff --git a/src/trace_processor/tables/winscope_tables.py b/src/trace_processor/tables/winscope_tables.py
index f498958..a218e6b 100644
--- a/src/trace_processor/tables/winscope_tables.py
+++ b/src/trace_processor/tables/winscope_tables.py
@@ -17,6 +17,7 @@
from python.generators.trace_processor_table.public import ColumnFlag
from python.generators.trace_processor_table.public import Table
from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppOptional
from python.generators.trace_processor_table.public import TableDoc
from python.generators.trace_processor_table.public import CppUint32
from python.generators.trace_processor_table.public import CppString
@@ -29,14 +30,17 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
- wrapping_sql_view=WrappingSqlView('inputmethod_clients'),
tabledoc=TableDoc(
doc='InputMethod clients',
group='Winscope',
columns={
'ts': 'The timestamp the dump was triggered',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
INPUTMETHOD_MANAGER_SERVICE_TABLE = Table(
@@ -46,14 +50,17 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
- wrapping_sql_view=WrappingSqlView('inputmethod_manager_service'),
tabledoc=TableDoc(
doc='InputMethod manager service',
group='Winscope',
columns={
'ts': 'The timestamp the dump was triggered',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
INPUTMETHOD_SERVICE_TABLE = Table(
@@ -63,14 +70,17 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
- wrapping_sql_view=WrappingSqlView('inputmethod_service'),
tabledoc=TableDoc(
doc='InputMethod service',
group='Winscope',
columns={
'ts': 'The timestamp the dump was triggered',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE = Table(
@@ -80,6 +90,8 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='SurfaceFlinger layers snapshot',
@@ -87,6 +99,8 @@
columns={
'ts': 'Timestamp of the snapshot',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
SURFACE_FLINGER_LAYER_TABLE = Table(
@@ -96,6 +110,8 @@
columns=[
C('snapshot_id', CppTableId(SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE)),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='SurfaceFlinger layer',
@@ -103,6 +119,8 @@
columns={
'snapshot_id': 'The snapshot that generated this layer',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
SURFACE_FLINGER_TRANSACTIONS_TABLE = Table(
@@ -112,6 +130,8 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='SurfaceFlinger transactions. Each row contains a set of ' +
@@ -120,6 +140,8 @@
columns={
'ts': 'Timestamp of the transactions commit',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
VIEWCAPTURE_TABLE = Table(
@@ -129,14 +151,17 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
- wrapping_sql_view=WrappingSqlView('viewcapture'),
tabledoc=TableDoc(
doc='ViewCapture',
group='Winscope',
columns={
'ts': 'The timestamp the views were captured',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
WINDOW_MANAGER_SHELL_TRANSITIONS_TABLE = Table(
@@ -147,6 +172,8 @@
C('ts', CppInt64()),
C('transition_id', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='Window Manager Shell Transitions',
@@ -155,6 +182,8 @@
'ts': 'The timestamp the transition started playing',
'transition_id': 'The id of the transition',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
WINDOW_MANAGER_SHELL_TRANSITION_HANDLERS_TABLE = Table(
@@ -164,6 +193,8 @@
columns=[
C('handler_id', CppInt64()),
C('handler_name', CppString()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
tabledoc=TableDoc(
doc='Window Manager Shell Transition Handlers',
@@ -171,6 +202,8 @@
columns={
'handler_id': 'The id of the handler',
'handler_name': 'The name of the handler',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
WINDOW_MANAGER_TABLE = Table(
@@ -180,6 +213,8 @@
columns=[
C('ts', CppInt64(), ColumnFlag.SORTED),
C('arg_set_id', CppUint32()),
+ C('base64_proto', CppString()),
+ C('base64_proto_id', CppOptional(CppUint32())),
],
wrapping_sql_view=WrappingSqlView('windowmanager'),
tabledoc=TableDoc(
@@ -188,6 +223,8 @@
columns={
'ts': 'The timestamp the state snapshot was captured',
'arg_set_id': 'Extra args parsed from the proto message',
+ 'base64_proto': 'Raw proto message encoded in base64',
+ 'base64_proto_id': 'String id for raw proto message',
}))
PROTOLOG_TABLE = Table(
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index 8397d8d..b9d4c47 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -501,7 +501,7 @@
for (int repeat = 0; repeat < 3; repeat++) {
ASSERT_EQ(RestoreInitialTables(), 0u);
{
- auto it = Query("INCLUDE PERFETTO MODULE common.timestamps;");
+ auto it = Query("INCLUDE PERFETTO MODULE time.conversion;");
it.Next();
ASSERT_TRUE(it.Status().ok());
}
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 296dc99..cd04da5 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -115,6 +115,7 @@
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_sched_upid.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/experimental_slice_layout.h"
#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/table_info.h"
+#include "src/trace_processor/perfetto_sql/intrinsics/table_functions/winscope_proto_to_args_with_defaults.h"
#include "src/trace_processor/perfetto_sql/stdlib/stdlib.h"
#include "src/trace_processor/sqlite/bindings/sqlite_aggregate_function.h"
#include "src/trace_processor/sqlite/bindings/sqlite_result.h"
@@ -192,14 +193,14 @@
void BuildBoundsTable(sqlite3* db, std::pair<int64_t, int64_t> bounds) {
char* error = nullptr;
- sqlite3_exec(db, "DELETE FROM trace_bounds", nullptr, nullptr, &error);
+ sqlite3_exec(db, "DELETE FROM _trace_bounds", nullptr, nullptr, &error);
if (error) {
PERFETTO_ELOG("Error deleting from bounds table: %s", error);
sqlite3_free(error);
return;
}
- base::StackString<1024> sql("INSERT INTO trace_bounds VALUES(%" PRId64
+ base::StackString<1024> sql("INSERT INTO _trace_bounds VALUES(%" PRId64
", %" PRId64 ")",
bounds.first, bounds.second);
sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &error);
@@ -311,7 +312,7 @@
void InsertIntoTraceMetricsTable(sqlite3* db, const std::string& metric_name) {
char* insert_sql = sqlite3_mprintf(
- "INSERT INTO trace_metrics(name) VALUES('%q')", metric_name.c_str());
+ "INSERT INTO _trace_metrics(name) VALUES('%q')", metric_name.c_str());
char* insert_error = nullptr;
sqlite3_exec(db, insert_sql, nullptr, nullptr, &insert_error);
sqlite3_free(insert_sql);
@@ -1061,6 +1062,9 @@
std::make_unique<ExperimentalFlatSlice>(&context_));
engine_->RegisterStaticTableFunction(std::make_unique<DfsWeightBounded>(
context_.storage->mutable_string_pool()));
+ engine_->RegisterStaticTableFunction(
+ std::make_unique<WinscopeProtoToArgsWithDefaults>(
+ context_.storage->mutable_string_pool(), engine_.get(), &context_));
// Value table aggregate functions.
engine_->RegisterSqliteAggregateFunction<DominatorTree>(
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 1fe24c0..d409320 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -52,6 +52,7 @@
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/trace_reader_registry.h"
#include "src/trace_processor/types/variadic.h"
+#include "src/trace_processor/util/descriptors.h"
#include "src/trace_processor/util/status_macros.h"
#include "src/trace_processor/util/trace_type.h"
@@ -152,6 +153,9 @@
// kernel version (inside system_info_tracker) to know how to textualise
// sched_switch.prev_state bitflags.
context.system_info_tracker = std::move(context_.system_info_tracker);
+ // "__intrinsic_winscope_proto_to_args_with_defaults" requires proto
+ // descriptors.
+ context.descriptor_pool_ = std::move(context_.descriptor_pool_);
context_ = std::move(context);
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index fc6ee4b..73bdbcb 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -284,6 +284,15 @@
]
}
+source_set("winscope_proto_mapping") {
+ sources = ["winscope_proto_mapping.h"]
+ deps = [
+ "../../../gn:default_deps",
+ "../../../include/perfetto/ext/base:base",
+ "../tables",
+ ]
+}
+
source_set("unittests") {
sources = [
"bump_allocator_unittest.cc",
diff --git a/src/trace_processor/util/descriptors.cc b/src/trace_processor/util/descriptors.cc
index 96833ed..8a1d35f 100644
--- a/src/trace_processor/util/descriptors.cc
+++ b/src/trace_processor/util/descriptors.cc
@@ -48,13 +48,17 @@
? static_cast<uint32_t>(f_decoder.type())
: static_cast<uint32_t>(FieldDescriptorProto::TYPE_MESSAGE);
protos::pbzero::FieldOptions::Decoder opt(f_decoder.options());
+ std::optional<std::string> default_value;
+ if (f_decoder.has_default_value()) {
+ default_value = f_decoder.default_value().ToStdString();
+ }
return FieldDescriptor(
base::StringView(f_decoder.name()).ToStdString(),
static_cast<uint32_t>(f_decoder.number()), type, std::move(type_name),
std::vector<uint8_t>(f_decoder.options().data,
f_decoder.options().data + f_decoder.options().size),
- f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED, opt.packed(),
- is_extension);
+ default_value, f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED,
+ opt.packed(), is_extension);
}
base::Status CheckExtensionField(const ProtoDescriptor& proto_descriptor,
@@ -443,6 +447,7 @@
uint32_t type,
std::string raw_type_name,
std::vector<uint8_t> options,
+ std::optional<std::string> default_value,
bool is_repeated,
bool is_packed,
bool is_extension)
@@ -451,6 +456,7 @@
type_(type),
raw_type_name_(std::move(raw_type_name)),
options_(std::move(options)),
+ default_value_(std::move(default_value)),
is_repeated_(is_repeated),
is_packed_(is_packed),
is_extension_(is_extension) {}
diff --git a/src/trace_processor/util/descriptors.h b/src/trace_processor/util/descriptors.h
index c99a72b..075455c 100644
--- a/src/trace_processor/util/descriptors.h
+++ b/src/trace_processor/util/descriptors.h
@@ -41,7 +41,8 @@
uint32_t number,
uint32_t type,
std::string raw_type_name,
- std::vector<uint8_t>,
+ std::vector<uint8_t> options,
+ std::optional<std::string> default_value,
bool is_repeated,
bool is_packed,
bool is_extension = false);
@@ -57,6 +58,9 @@
const std::vector<uint8_t>& options() const { return options_; }
std::vector<uint8_t>* mutable_options() { return &options_; }
+ const std::optional<std::string>& default_value() const {
+ return default_value_;
+ }
void set_resolved_type_name(const std::string& resolved_type_name) {
resolved_type_name_ = resolved_type_name;
@@ -69,6 +73,7 @@
std::string raw_type_name_;
std::string resolved_type_name_;
std::vector<uint8_t> options_;
+ std::optional<std::string> default_value_;
bool is_repeated_;
bool is_packed_;
bool is_extension_;
diff --git a/src/trace_processor/util/proto_to_args_parser.cc b/src/trace_processor/util/proto_to_args_parser.cc
index e09359c..da2f15c 100644
--- a/src/trace_processor/util/proto_to_args_parser.cc
+++ b/src/trace_processor/util/proto_to_args_parser.cc
@@ -16,9 +16,11 @@
#include "src/trace_processor/util/proto_to_args_parser.h"
-#include <stdint.h>
+#include <unordered_set>
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/protozero/field.h"
#include "perfetto/protozero/proto_decoder.h"
#include "perfetto/protozero/proto_utils.h"
#include "protos/perfetto/common/descriptor.pbzero.h"
@@ -40,6 +42,15 @@
target += value;
}
+bool IsFieldAllowed(const FieldDescriptor& field,
+ const std::vector<uint32_t>* allowed_fields) {
+ // If allowlist is not provided, reflect all fields. Otherwise, check if the
+ // current field either an extension or is in allowlist.
+ return field.is_extension() || !allowed_fields ||
+ std::find(allowed_fields->begin(), allowed_fields->end(),
+ field.number()) != allowed_fields->end();
+}
+
} // namespace
ProtoToArgsParser::Key::Key() = default;
@@ -88,10 +99,11 @@
const std::string& type,
const std::vector<uint32_t>* allowed_fields,
Delegate& delegate,
- int* unknown_extensions) {
+ int* unknown_extensions,
+ bool add_defaults) {
ScopedNestedKeyContext key_context(key_prefix_);
return ParseMessageInternal(key_context, cb, type, allowed_fields, delegate,
- unknown_extensions);
+ unknown_extensions, add_defaults);
}
base::Status ProtoToArgsParser::ParseMessageInternal(
@@ -100,7 +112,8 @@
const std::string& type,
const std::vector<uint32_t>* allowed_fields,
Delegate& delegate,
- int* unknown_extensions) {
+ int* unknown_extensions,
+ bool add_defaults) {
if (auto override_result =
MaybeApplyOverrideForType(type, key_context, cb, delegate)) {
return override_result.value();
@@ -116,6 +129,7 @@
std::unordered_map<size_t, int> repeated_field_index;
bool empty_message = true;
protozero::ProtoDecoder decoder(cb);
+ std::unordered_set<std::string_view> existing_fields;
for (protozero::Field f = decoder.ReadField(); f.valid();
f = decoder.ReadField()) {
empty_message = false;
@@ -128,13 +142,11 @@
continue;
}
- // If allowlist is not provided, reflect all fields. Otherwise, check if the
- // current field either an extension or is in allowlist.
- bool is_allowed = field->is_extension() || !allowed_fields ||
- std::find(allowed_fields->begin(), allowed_fields->end(),
- f.id()) != allowed_fields->end();
+ if (add_defaults) {
+ existing_fields.insert(field->name());
+ }
- if (!is_allowed) {
+ if (!IsFieldAllowed(*field, allowed_fields)) {
// Field is neither an extension, nor is allowed to be
// reflected.
continue;
@@ -143,12 +155,13 @@
// Packed fields need to be handled specially because
if (field->is_packed()) {
RETURN_IF_ERROR(ParsePackedField(*field, repeated_field_index, f,
- delegate, unknown_extensions));
+ delegate, unknown_extensions,
+ add_defaults));
continue;
}
RETURN_IF_ERROR(ParseField(*field, repeated_field_index[f.id()], f,
- delegate, unknown_extensions));
+ delegate, unknown_extensions, add_defaults));
if (field->is_repeated()) {
repeated_field_index[f.id()]++;
}
@@ -156,6 +169,22 @@
if (empty_message) {
delegate.AddNull(key_prefix_);
+ } else if (add_defaults) {
+ for (const auto& [id, field] : descriptor.fields()) {
+ if (!IsFieldAllowed(field, allowed_fields)) {
+ continue;
+ }
+ const std::string& field_name = field.name();
+ bool field_exists =
+ existing_fields.find(field_name) != existing_fields.cend();
+ if (field_exists) {
+ continue;
+ }
+ ScopedNestedKeyContext key_context_default(key_prefix_);
+ AppendProtoType(key_prefix_.flat_key, field_name);
+ AppendProtoType(key_prefix_.key, field_name);
+ RETURN_IF_ERROR(AddDefault(field, delegate));
+ }
}
return base::OkStatus();
@@ -166,7 +195,8 @@
int repeated_field_number,
protozero::Field field,
Delegate& delegate,
- int* unknown_extensions) {
+ int* unknown_extensions,
+ bool add_defaults) {
std::string prefix_part = field_descriptor.name();
if (field_descriptor.is_repeated()) {
std::string number = std::to_string(repeated_field_number);
@@ -197,7 +227,7 @@
protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
return ParseMessageInternal(key_context, field.as_bytes(),
field_descriptor.resolved_type_name(), nullptr,
- delegate, unknown_extensions);
+ delegate, unknown_extensions, add_defaults);
}
return ParseSimpleField(field_descriptor, field, delegate);
}
@@ -207,7 +237,8 @@
std::unordered_map<size_t, int>& repeated_field_index,
protozero::Field field,
Delegate& delegate,
- int* unknown_extensions) {
+ int* unknown_extensions,
+ bool add_defaults) {
using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
using PWT = protozero::proto_utils::ProtoWireType;
@@ -225,7 +256,7 @@
protozero::Field f;
f.initialize(field.id(), static_cast<uint8_t>(wire_type), new_value, 0);
return ParseField(field_descriptor, repeated_field_index[field.id()]++, f,
- delegate, unknown_extensions);
+ delegate, unknown_extensions, add_defaults);
};
const uint8_t* data = field.as_bytes().data;
@@ -335,30 +366,12 @@
case FieldDescriptorProto::TYPE_STRING:
delegate.AddString(key_prefix_, field.as_string());
return base::OkStatus();
- case FieldDescriptorProto::TYPE_ENUM: {
- auto opt_enum_descriptor_idx =
- pool_.FindDescriptorIdx(descriptor.resolved_type_name());
- if (!opt_enum_descriptor_idx) {
- delegate.AddInteger(key_prefix_, field.as_int32());
- return base::OkStatus();
- }
- auto opt_enum_string =
- pool_.descriptors()[*opt_enum_descriptor_idx].FindEnumString(
- field.as_int32());
- if (!opt_enum_string) {
- // Fall back to the integer representation of the field.
- delegate.AddInteger(key_prefix_, field.as_int32());
- return base::OkStatus();
- }
- delegate.AddString(key_prefix_,
- protozero::ConstChars{opt_enum_string->data(),
- opt_enum_string->size()});
- return base::OkStatus();
- }
+ case FieldDescriptorProto::TYPE_ENUM:
+ return AddEnum(descriptor, field.as_int32(), delegate);
default:
return base::ErrStatus(
"Tried to write value of type field %s (in proto type "
- "%s) which has type enum %d",
+ "%s) which has type enum %u",
descriptor.name().c_str(), descriptor.resolved_type_name().c_str(),
descriptor.type());
}
@@ -379,6 +392,108 @@
return context;
}
+base::Status ProtoToArgsParser::AddDefault(const FieldDescriptor& descriptor,
+ Delegate& delegate) {
+ using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
+ if (descriptor.is_repeated()) {
+ delegate.AddNull(key_prefix_);
+ return base::OkStatus();
+ }
+ const auto& default_value = descriptor.default_value();
+ const auto& default_value_if_number =
+ default_value ? default_value.value() : "0";
+ switch (descriptor.type()) {
+ case FieldDescriptorProto::TYPE_INT32:
+ case FieldDescriptorProto::TYPE_SFIXED32:
+ delegate.AddInteger(key_prefix_,
+ base::StringToInt32(default_value_if_number).value());
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_SINT32:
+ delegate.AddInteger(
+ key_prefix_,
+ protozero::proto_utils::ZigZagDecode(
+ base::StringToInt64(default_value_if_number).value()));
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_INT64:
+ case FieldDescriptorProto::TYPE_SFIXED64:
+ delegate.AddInteger(key_prefix_,
+ base::StringToInt64(default_value_if_number).value());
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_SINT64:
+ delegate.AddInteger(
+ key_prefix_,
+ protozero::proto_utils::ZigZagDecode(
+ base::StringToInt64(default_value_if_number).value()));
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_UINT32:
+ case FieldDescriptorProto::TYPE_FIXED32:
+ delegate.AddUnsignedInteger(
+ key_prefix_, base::StringToUInt32(default_value_if_number).value());
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_UINT64:
+ case FieldDescriptorProto::TYPE_FIXED64:
+ delegate.AddUnsignedInteger(
+ key_prefix_, base::StringToUInt64(default_value_if_number).value());
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_BOOL:
+ delegate.AddBoolean(key_prefix_, default_value == "true");
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_DOUBLE:
+ case FieldDescriptorProto::TYPE_FLOAT:
+ delegate.AddDouble(key_prefix_,
+ base::StringToDouble(default_value_if_number).value());
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_BYTES:
+ delegate.AddBytes(key_prefix_, protozero::ConstBytes{});
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_STRING:
+ if (default_value) {
+ delegate.AddString(key_prefix_, default_value.value());
+ } else {
+ delegate.AddNull(key_prefix_);
+ }
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_MESSAGE:
+ delegate.AddNull(key_prefix_);
+ return base::OkStatus();
+ case FieldDescriptorProto::TYPE_ENUM:
+ return AddEnum(descriptor,
+ base::StringToInt32(default_value_if_number).value(),
+ delegate);
+ default:
+ return base::ErrStatus(
+ "Tried to write default value of type field %s (in proto type "
+ "%s) which has type enum %u",
+ descriptor.name().c_str(), descriptor.resolved_type_name().c_str(),
+ descriptor.type());
+ }
+}
+
+base::Status ProtoToArgsParser::AddEnum(const FieldDescriptor& descriptor,
+ int32_t value,
+ Delegate& delegate) {
+ auto opt_enum_descriptor_idx =
+ pool_.FindDescriptorIdx(descriptor.resolved_type_name());
+ if (!opt_enum_descriptor_idx) {
+ delegate.AddInteger(key_prefix_, value);
+ return base::OkStatus();
+ }
+ auto opt_enum_string =
+ pool_.descriptors()[*opt_enum_descriptor_idx].FindEnumString(value);
+ if (!opt_enum_string) {
+ // Fall back to the integer representation of the field.
+ // We add the string representation of the int value here in order that
+ // EXTRACT_ARG() should return consistent types under error conditions and
+ // that CREATE PERFETTO TABLE AS EXTRACT_ARG(...) should be generally safe
+ // to use.
+ delegate.AddString(key_prefix_, std::to_string(value));
+ return base::OkStatus();
+ }
+ delegate.AddString(
+ key_prefix_,
+ protozero::ConstChars{opt_enum_string->data(), opt_enum_string->size()});
+ return base::OkStatus();
+}
} // namespace util
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/util/proto_to_args_parser.h b/src/trace_processor/util/proto_to_args_parser.h
index b709a40..427ed41 100644
--- a/src/trace_processor/util/proto_to_args_parser.h
+++ b/src/trace_processor/util/proto_to_args_parser.h
@@ -147,7 +147,8 @@
const std::string& type,
const std::vector<uint32_t>* allowed_fields,
Delegate& delegate,
- int* unknown_extensions = nullptr);
+ int* unknown_extensions = nullptr,
+ bool add_defaults = false);
// This class is responsible for resetting the current key prefix to the old
// value when deleted or reset.
@@ -249,14 +250,16 @@
int repeated_field_number,
protozero::Field field,
Delegate& delegate,
- int* unknown_extensions);
+ int* unknown_extensions,
+ bool add_defaults);
base::Status ParsePackedField(
const FieldDescriptor& field_descriptor,
std::unordered_map<size_t, int>& repeated_field_index,
protozero::Field field,
Delegate& delegate,
- int* unknown_extensions);
+ int* unknown_extensions,
+ bool add_defaults);
std::optional<base::Status> MaybeApplyOverrideForField(
const protozero::Field&,
@@ -275,12 +278,19 @@
const std::string& type,
const std::vector<uint32_t>* fields,
Delegate& delegate,
- int* unknown_extensions);
+ int* unknown_extensions,
+ bool add_defaults = false);
base::Status ParseSimpleField(const FieldDescriptor& desciptor,
const protozero::Field& field,
Delegate& delegate);
+ base::Status AddDefault(const FieldDescriptor& desciptor, Delegate& delegate);
+
+ base::Status AddEnum(const FieldDescriptor& descriptor,
+ int32_t value,
+ Delegate& delegate);
+
std::unordered_map<std::string, ParsingOverrideForField> field_overrides_;
std::unordered_map<std::string, ParsingOverrideForType> type_overrides_;
const DescriptorPool& pool_;
diff --git a/src/trace_processor/util/proto_to_args_parser_unittest.cc b/src/trace_processor/util/proto_to_args_parser_unittest.cc
index 5baf8f0..3dae057 100644
--- a/src/trace_processor/util/proto_to_args_parser_unittest.cc
+++ b/src/trace_processor/util/proto_to_args_parser_unittest.cc
@@ -662,6 +662,54 @@
"field_double field_double[3] 1.79769e+308"));
}
+TEST_F(ProtoToArgsParserTest, AddsDefaults) {
+ using namespace protozero::test::protos::pbzero;
+ protozero::HeapBuffered<EveryField> msg{kChunkSize, kChunkSize};
+ msg->set_field_int32(-1);
+ msg->add_repeated_string("test");
+ msg->add_repeated_sfixed32(1);
+ msg->add_repeated_fixed64(1);
+ msg->set_nested_enum(EveryField::PONG);
+
+ auto binary_proto = msg.SerializeAsArray();
+
+ DescriptorPool pool;
+ auto status = pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
+ kTestMessagesDescriptor.size());
+ ProtoToArgsParser parser(pool);
+ ASSERT_TRUE(status.ok()) << "Failed to parse kTestMessagesDescriptor: "
+ << status.message();
+
+ status = parser.ParseMessage(
+ protozero::ConstBytes{binary_proto.data(), binary_proto.size()},
+ ".protozero.test.protos.EveryField", nullptr, *this, nullptr, true);
+
+ EXPECT_TRUE(status.ok()) << "AddsDefaults failed with error: "
+ << status.message();
+
+ EXPECT_THAT(
+ args(),
+ testing::UnorderedElementsAre(
+ "field_int32 field_int32 -1", // exists in message
+ "repeated_string repeated_string[0] test",
+ "repeated_sfixed32 repeated_sfixed32[0] 1",
+ "repeated_fixed64 repeated_fixed64[0] 1",
+ "nested_enum nested_enum PONG",
+ "field_bytes field_bytes <bytes size=0>",
+ "field_string field_string [NULL]", // null if no string default
+ "field_nested field_nested [NULL]", // no defaults for inner fields
+ "field_bool field_bool false",
+ "repeated_int32 repeated_int32 [NULL]", // null for repeated fields
+ "field_double field_double 0", "field_float field_float 0",
+ "field_sfixed64 field_sfixed64 0", "field_sfixed32 field_sfixed32 0",
+ "field_fixed64 field_fixed64 0", "field_sint64 field_sint64 0",
+ "big_enum big_enum 0", "field_fixed32 field_fixed32 0",
+ "field_sint32 field_sint32 0",
+ "signed_enum signed_enum NEUTRAL", // translates default enum
+ "small_enum small_enum NOT_TO_BE", "field_uint64 field_uint64 0",
+ "field_uint32 field_uint32 0", "field_int64 field_int64 0"));
+}
+
} // namespace
} // namespace util
} // namespace trace_processor
diff --git a/src/trace_processor/util/winscope_proto_mapping.h b/src/trace_processor/util/winscope_proto_mapping.h
new file mode 100644
index 0000000..b9bd320
--- /dev/null
+++ b/src/trace_processor/util/winscope_proto_mapping.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_UTIL_WINSCOPE_PROTO_MAPPING_H_
+#define SRC_TRACE_PROCESSOR_UTIL_WINSCOPE_PROTO_MAPPING_H_
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/tables/android_tables_py.h"
+#include "src/trace_processor/tables/winscope_tables_py.h"
+
+namespace perfetto::trace_processor {
+namespace util {
+namespace winscope_proto_mapping {
+inline base::StatusOr<const char* const> GetProtoName(
+ const std::string& table_name) {
+ if (table_name == tables::SurfaceFlingerLayerTable::Name()) {
+ return ".perfetto.protos.LayerProto";
+ }
+ if (table_name == tables::SurfaceFlingerLayersSnapshotTable::Name()) {
+ return ".perfetto.protos.LayersSnapshotProto";
+ }
+ if (table_name == tables::SurfaceFlingerTransactionsTable::Name()) {
+ return ".perfetto.protos.TransactionTraceEntry";
+ }
+ if (table_name == tables::WindowManagerShellTransitionsTable::Name()) {
+ return ".perfetto.protos.ShellTransition";
+ }
+ if (table_name == tables::InputMethodClientsTable::Name()) {
+ return ".perfetto.protos.InputMethodClientsTraceProto";
+ }
+ if (table_name == tables::InputMethodManagerServiceTable::Name()) {
+ return ".perfetto.protos.InputMethodManagerServiceTraceProto";
+ }
+ if (table_name == tables::InputMethodServiceTable::Name()) {
+ return ".perfetto.protos.InputMethodServiceTraceProto";
+ }
+ if (table_name == tables::ViewCaptureTable::Name()) {
+ return ".perfetto.protos.ViewCapture";
+ }
+ if (table_name == tables::WindowManagerTable::Name()) {
+ return ".perfetto.protos.WindowManagerTraceEntry";
+ }
+ if (table_name == tables::AndroidKeyEventsTable::Name()) {
+ return ".perfetto.protos.AndroidKeyEvent";
+ }
+ if (table_name == tables::AndroidMotionEventsTable::Name()) {
+ return ".perfetto.protos.AndroidMotionEvent";
+ }
+ if (table_name == tables::AndroidInputEventDispatchTable::Name()) {
+ return ".perfetto.protos.AndroidWindowInputDispatchEvent";
+ }
+ return base::ErrStatus("%s table does not have proto descriptor.",
+ table_name.c_str());
+}
+
+inline std::optional<const std::vector<uint32_t>> GetAllowedFields(
+ const std::string& table_name) {
+ if (table_name == tables::SurfaceFlingerLayersSnapshotTable::Name()) {
+ return std::vector<uint32_t>({1, 2, 4, 5, 6, 7, 8});
+ }
+ return std::nullopt;
+}
+} // namespace winscope_proto_mapping
+} // namespace util
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_UTIL_WINSCOPE_PROTO_MAPPING_H_
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index b152d02..f93e6e4 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -1294,6 +1294,22 @@
kUnsetFtraceId,
112,
kUnsetSize},
+ {"param_set_value_cpm",
+ "cpm_trace",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "body", 1, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "value", 2, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "timestamp", 3, ProtoSchemaType::kInt64,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 543,
+ kUnsetSize},
{"cpuhp_exit",
"cpuhp",
{
@@ -5029,6 +5045,32 @@
kUnsetFtraceId,
98,
kUnsetSize},
+ {"do_sys_open",
+ "fs",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "filename", 1, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "flags", 2, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "mode", 3, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 544,
+ kUnsetSize},
+ {"open_exec",
+ "fs",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "filename", 1, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 545,
+ kUnsetSize},
{"print",
"ftrace",
{
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 9696c0b..39c3ea7 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -1406,9 +1406,10 @@
"p:perfetto_kprobes/fuse_file_write_iter fuse_file_write_iter"));
EXPECT_CALL(
ftrace_,
- AppendToFile(
- "/root/kprobe_events",
- "r:perfetto_kretprobes/fuse_file_write_iter fuse_file_write_iter"));
+ AppendToFile("/root/kprobe_events",
+ std::string("r") + std::string(kKretprobeDefaultMaxactives) +
+ ":perfetto_kretprobes/fuse_file_write_iter "
+ "fuse_file_write_iter"));
std::string g1(kKprobeGroup);
static constexpr int kExpectedEventId = 77;
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index a09f13d..837a5e1 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -39,6 +39,7 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/metatrace.h"
#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "src/kallsyms/kernel_symbol_map.h"
@@ -630,6 +631,33 @@
StopIfNeeded(instance);
}
+bool DumpKprobeStats(const std::string& text, FtraceStats* ftrace_stats) {
+ int64_t hits = 0;
+ int64_t misses = 0;
+
+ base::StringSplitter line(std::move(text), '\n');
+ while (line.Next()) {
+ base::StringSplitter tok(line.cur_token(), line.cur_token_size() + 1, ' ');
+
+ if (!tok.Next())
+ return false;
+ // Skip the event name field
+
+ if (!tok.Next())
+ return false;
+ hits += static_cast<int64_t>(std::strtoll(tok.cur_token(), nullptr, 10));
+
+ if (!tok.Next())
+ return false;
+ misses += static_cast<int64_t>(std::strtoll(tok.cur_token(), nullptr, 10));
+ }
+
+ ftrace_stats->kprobe_stats.hits = hits;
+ ftrace_stats->kprobe_stats.misses = misses;
+
+ return true;
+}
+
void FtraceController::DumpFtraceStats(FtraceDataSource* data_source,
FtraceStats* stats_out) {
FtraceInstanceState* instance =
@@ -646,6 +674,11 @@
stats_out->kernel_symbols_mem_kb =
static_cast<uint32_t>(symbol_map->size_bytes() / 1024);
}
+
+ if (data_source->parsing_config()->kprobes.size() > 0) {
+ DumpKprobeStats(instance->ftrace_procfs.get()->ReadKprobeStats(),
+ stats_out);
+ }
}
void FtraceController::MaybeSnapshotFtraceClock() {
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index a858dfe..41bb59c 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -189,6 +189,8 @@
base::WeakPtrFactory<FtraceController> weak_factory_; // Keep last.
};
+bool DumpKprobeStats(const std::string& text, FtraceStats* ftrace_stats);
+
} // namespace perfetto
#endif // SRC_TRACED_PROBES_FTRACE_FTRACE_CONTROLLER_H_
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 723df01..549f81a 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -656,6 +656,72 @@
EXPECT_EQ(result.cpu(), 0u);
EXPECT_EQ(result.entries(), 1u);
EXPECT_EQ(result.overrun(), 2u);
+ auto kprobe_stats = result_packet.ftrace_stats().kprobe_stats();
+ EXPECT_EQ(kprobe_stats.hits(), 0u);
+ EXPECT_EQ(kprobe_stats.misses(), 0u);
+}
+
+TEST(FtraceStatsTest, WriteKprobeStats) {
+ FtraceStats stats{};
+ FtraceKprobeStats kprobe_stats{};
+ kprobe_stats.hits = 1;
+ kprobe_stats.misses = 2;
+ stats.kprobe_stats = kprobe_stats;
+
+ std::unique_ptr<TraceWriterForTesting> writer =
+ std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+ {
+ auto packet = writer->NewTracePacket();
+ auto* out = packet->set_ftrace_stats();
+ stats.Write(out);
+ }
+
+ protos::gen::TracePacket result_packet = writer->GetOnlyTracePacket();
+ auto result = result_packet.ftrace_stats();
+ EXPECT_EQ(result.kprobe_stats().hits(), 1u);
+ EXPECT_EQ(result.kprobe_stats().misses(), 2u);
+}
+
+TEST(FtraceStatsTest, KprobeProfileParseEmpty) {
+ std::string text = "";
+
+ FtraceStats stats{};
+ EXPECT_TRUE(DumpKprobeStats(text, &stats));
+}
+
+TEST(FtraceStatsTest, KprobeProfileParseEmptyLines) {
+ std::string text = R"(
+
+)";
+
+ FtraceStats stats{};
+ EXPECT_TRUE(DumpKprobeStats(text, &stats));
+}
+
+TEST(FtraceStatsTest, KprobeProfileParseValid) {
+ std::string text = R"( _binder_inner_proc_lock 1 8
+ _binder_inner_proc_unlock 2 9
+ _binder_node_inner_unlock 3 10
+ _binder_node_unlock 4 11
+)";
+
+ FtraceStats stats{};
+ EXPECT_TRUE(DumpKprobeStats(text, &stats));
+
+ EXPECT_EQ(stats.kprobe_stats.hits, 10u);
+ EXPECT_EQ(stats.kprobe_stats.misses, 38u);
+}
+
+TEST(FtraceStatsTest, KprobeProfileMissingValuesParseInvalid) {
+ std::string text = R"( _binder_inner_proc_lock 1 8
+ _binder_inner_proc_unlock 2
+)";
+
+ FtraceStats stats{};
+ EXPECT_FALSE(DumpKprobeStats(text, &stats));
+
+ EXPECT_EQ(stats.kprobe_stats.hits, 0u);
+ EXPECT_EQ(stats.kprobe_stats.misses, 0u);
}
TEST(FtraceControllerTest, OnlySecondaryInstance) {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 696cb36..fd2c948 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -149,7 +149,9 @@
bool is_retprobe) {
std::string path = root_ + "kprobe_events";
std::string probe =
- (is_retprobe ? "r:" : "p:") + group + "/" + name + " " + name;
+ (is_retprobe ? std::string("r") + std::string(kKretprobeDefaultMaxactives)
+ : "p") +
+ std::string(":") + group + "/" + name + " " + name;
PERFETTO_DLOG("Writing \"%s >> %s\"", probe.c_str(), path.c_str());
@@ -177,6 +179,11 @@
return AppendToFile(path, "-:" + group + "/" + name);
}
+std::string FtraceProcfs::ReadKprobeStats() const {
+ std::string path = root_ + "/kprobe_profile";
+ return ReadFileIntoString(path);
+}
+
bool FtraceProcfs::DisableEvent(const std::string& group,
const std::string& name) {
std::string path = root_ + "events/" + group + "/" + name + "/enable";
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index 50e4390..2a0593f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -26,6 +26,8 @@
namespace perfetto {
+constexpr std::string_view kKretprobeDefaultMaxactives = "1024";
+
class FtraceProcfs {
public:
static const char* const kTracingPaths[];
@@ -59,6 +61,9 @@
// Remove kprobe event from the system
bool RemoveKprobeEvent(const std::string& group, const std::string& name);
+ // Read the "kprobe_profile" file.
+ std::string ReadKprobeStats() const;
+
// Disable the event under with the given |group| and |name|.
bool DisableEvent(const std::string& group, const std::string& name);
diff --git a/src/traced/probes/ftrace/ftrace_stats.cc b/src/traced/probes/ftrace/ftrace_stats.cc
index 01c5890..1f7b053 100644
--- a/src/traced/probes/ftrace/ftrace_stats.cc
+++ b/src/traced/probes/ftrace/ftrace_stats.cc
@@ -32,6 +32,12 @@
writer->add_unknown_ftrace_events(err);
for (const std::string& err : setup_errors.failed_ftrace_events)
writer->add_failed_ftrace_events(err);
+
+ if (kprobe_stats.hits || kprobe_stats.misses) {
+ auto* kprobe_stats_pb = writer->set_kprobe_stats();
+ kprobe_stats_pb->set_hits(kprobe_stats.hits);
+ kprobe_stats_pb->set_misses(kprobe_stats.misses);
+ }
}
void FtraceCpuStats::Write(protos::pbzero::FtraceCpuStats* writer) const {
diff --git a/src/traced/probes/ftrace/ftrace_stats.h b/src/traced/probes/ftrace/ftrace_stats.h
index 127b4f4..ccabe4a 100644
--- a/src/traced/probes/ftrace/ftrace_stats.h
+++ b/src/traced/probes/ftrace/ftrace_stats.h
@@ -27,6 +27,7 @@
namespace pbzero {
class FtraceStats;
class FtraceCpuStats;
+class FtraceKprobeStats;
} // namespace pbzero
} // namespace protos
@@ -44,6 +45,11 @@
void Write(protos::pbzero::FtraceCpuStats*) const;
};
+struct FtraceKprobeStats {
+ int64_t hits;
+ int64_t misses;
+};
+
struct FtraceSetupErrors {
std::string atrace_errors;
std::vector<std::string> unknown_ftrace_events;
@@ -55,6 +61,7 @@
FtraceSetupErrors setup_errors;
uint32_t kernel_symbols_parsed = 0;
uint32_t kernel_symbols_mem_kb = 0;
+ FtraceKprobeStats kprobe_stats = {};
void Write(protos::pbzero::FtraceStats*) const;
};
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/cpm_trace/param_set_value_cpm/format b/src/traced/probes/ftrace/test/data/synthetic/events/cpm_trace/param_set_value_cpm/format
new file mode 100644
index 0000000..a7a2876
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/cpm_trace/param_set_value_cpm/format
@@ -0,0 +1,13 @@
+name: param_set_value_cpm
+ID: 1125
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:__data_loc char[] body; offset:8; size:4; signed:0;
+ field:unsigned int value; offset:12; size:4; signed:0;
+ field:long long timestamp; offset:16; size:8; signed:1;
+
+print fmt: "%s state=%u timestamp=%lld", __get_str(body), REC->value, REC->timestamp
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format b/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format
new file mode 100644
index 0000000..d6bac92
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format
@@ -0,0 +1,13 @@
+name: do_sys_open
+ID: 685
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:__data_loc char[] filename; offset:8; size:4; signed:1;
+ field:int flags; offset:12; size:4; signed:1;
+ field:int mode; offset:16; size:4; signed:1;
+
+print fmt: ""%s" %x %o", __get_str(filename), REC->flags, REC->mode
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format b/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format
new file mode 100644
index 0000000..9f6fe3d
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format
@@ -0,0 +1,11 @@
+name: open_exec
+ID: 686
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:__data_loc char[] filename; offset:8; size:4; signed:1;
+
+print fmt: ""%s"", __get_str(filename)
diff --git a/src/tracing/service/tracing_service_impl.cc b/src/tracing/service/tracing_service_impl.cc
index b5ca411..68b5ba6 100644
--- a/src/tracing/service/tracing_service_impl.cc
+++ b/src/tracing/service/tracing_service_impl.cc
@@ -3780,7 +3780,7 @@
// guest_soc model is not always present
std::string guest_soc_model_value =
- base::GetAndroidProp("ro.guest_soc.model");
+ base::GetAndroidProp("ro.boot.guest_soc.model");
if (!guest_soc_model_value.empty()) {
info->set_android_guest_soc_model(guest_soc_model_value);
}
diff --git a/test/cmdline_integrationtest.cc b/test/cmdline_integrationtest.cc
index 60cce1f..c4a6fc7 100644
--- a/test/cmdline_integrationtest.cc
+++ b/test/cmdline_integrationtest.cc
@@ -47,6 +47,7 @@
using ::testing::ContainsRegex;
using ::testing::Each;
+using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::HasSubstr;
@@ -99,6 +100,52 @@
return trace_config;
}
+// For the regular tests.
+TraceConfig CreateTraceConfigForTest(uint32_t test_msg_count = 11,
+ uint32_t test_msg_size = 32) {
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(test_msg_count);
+ ds_config->mutable_for_testing()->set_message_size(test_msg_size);
+ return trace_config;
+}
+
+void ExpectTraceContainsTestMessages(const protos::gen::Trace& trace,
+ uint32_t count) {
+ ssize_t actual_test_packets_count = std::count_if(
+ trace.packet().begin(), trace.packet().end(),
+ [](const protos::gen::TracePacket& tp) { return tp.has_for_testing(); });
+ EXPECT_EQ(count, static_cast<uint32_t>(actual_test_packets_count));
+}
+
+void ExpectTraceContainsTestMessagesWithSize(const protos::gen::Trace& trace,
+ uint32_t message_size) {
+ for (const auto& packet : trace.packet()) {
+ if (packet.has_for_testing()) {
+ EXPECT_EQ(message_size, packet.for_testing().str().size());
+ }
+ }
+}
+
+void ExpectTraceContainsConfigWithTriggerMode(
+ const protos::gen::Trace& trace,
+ protos::gen::TraceConfig::TriggerConfig::TriggerMode trigger_mode) {
+ // GTest three level nested Property matcher is hard to read, so we use
+ // 'find_if' with lambda to ensure the trace config properly includes the
+ // trigger mode we set.
+ auto found =
+ std::find_if(trace.packet().begin(), trace.packet().end(),
+ [trigger_mode](const protos::gen::TracePacket& tp) {
+ return tp.has_trace_config() &&
+ tp.trace_config().trigger_config().trigger_mode() ==
+ trigger_mode;
+ });
+ EXPECT_NE(found, trace.packet().end())
+ << "Trace config doesn't include expected trigger mode.";
+}
+
class ScopedFileRemove {
public:
explicit ScopedFileRemove(const std::string& path) : path_(path) {}
@@ -106,6 +153,27 @@
std::string path_;
};
+bool ParseNotEmptyTraceFromFile(const std::string& trace_path,
+ protos::gen::Trace& out) {
+ std::string trace_str;
+ if (!base::ReadFile(trace_path, &trace_str))
+ return false;
+ if (trace_str.empty())
+ return false;
+ return out.ParseFromString(trace_str);
+}
+
+std::vector<std::string> GetReceivedTriggerNames(
+ const protos::gen::Trace& trace) {
+ std::vector<std::string> triggers;
+ for (const protos::gen::TracePacket& packet : trace.packet()) {
+ if (packet.has_trigger()) {
+ triggers.push_back(packet.trigger().trigger_name());
+ }
+ }
+ return triggers;
+}
+
class PerfettoCmdlineTest : public ::testing::Test {
public:
void StartServiceIfRequiredNoNewExecsAfterThis() {
@@ -190,11 +258,8 @@
// Read the trace written in the fixed location
// (/data/misc/perfetto-traces/ on Android, /tmp/ on Linux/Mac) and make
// sure it has the right contents.
- std::string trace_str;
- base::ReadFile(trace_path, &trace_str);
- ASSERT_FALSE(trace_str.empty());
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(trace_path, trace));
uint32_t test_packets = 0;
for (const auto& p : trace.packet())
test_packets += p.has_for_testing() ? 1 : 0;
@@ -212,6 +277,11 @@
std::string stderr_;
base::TestTaskRunner task_runner_;
+ // We use these two constants to set test data payload parameters and assert
+ // it was correctly written to the trace.
+ static constexpr size_t kTestMessageCount = 11;
+ static constexpr size_t kTestMessageSize = 32;
+
private:
bool exec_allowed_ = true;
TestHelper test_helper_{&task_runner_};
@@ -350,15 +420,8 @@
}
TEST_F(PerfettoCmdlineTest, StartTracingTrigger) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 11;
- constexpr size_t kMessageSize = 32;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::START_TRACING);
@@ -404,53 +467,25 @@
test_helper().WaitForProducerSetup();
EXPECT_EQ(0, trigger_proc.Run(&stderr_));
- // Wait for the producer to start, and then write out 11 packets.
+ // Wait for the producer to start, and then write out some test packets.
test_helper().WaitForProducerEnabled();
auto on_data_written = task_runner_.CreateCheckpoint("data_written");
fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written));
task_runner_.RunUntilCheckpoint("data_written");
background_trace.join();
- std::string trace_str;
- base::ReadFile(path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- size_t for_testing_packets = 0;
- size_t trigger_packets = 0;
- size_t trace_config_packets = 0;
- for (const auto& packet : trace.packet()) {
- if (packet.has_trace_config()) {
- // Ensure the trace config properly includes the trigger mode we set.
- auto kStartTrig = protos::gen::TraceConfig::TriggerConfig::START_TRACING;
- EXPECT_EQ(kStartTrig,
- packet.trace_config().trigger_config().trigger_mode());
- ++trace_config_packets;
- } else if (packet.has_trigger()) {
- // validate that the triggers are properly added to the trace.
- EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
- ++trigger_packets;
- } else if (packet.has_for_testing()) {
- // Make sure that the data size is correctly set based on what we
- // requested.
- EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
- ++for_testing_packets;
- }
- }
- EXPECT_EQ(trace_config_packets, 1u);
- EXPECT_EQ(trigger_packets, 1u);
- EXPECT_EQ(for_testing_packets, kMessageCount);
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace));
+ ExpectTraceContainsConfigWithTriggerMode(
+ trace, protos::gen::TraceConfig::TriggerConfig::START_TRACING);
+ EXPECT_THAT(GetReceivedTriggerNames(trace), ElementsAre("trigger_name"));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
}
TEST_F(PerfettoCmdlineTest, StopTracingTrigger) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 11;
- constexpr size_t kMessageSize = 32;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
@@ -497,8 +532,8 @@
});
test_helper().WaitForProducerEnabled();
- // Wait for the producer to start, and then write out 11 packets, before the
- // trace actually starts (the trigger is seen).
+ // Wait for the producer to start, and then write out some test packets,
+ // before the trace actually starts (the trigger is seen).
auto on_data_written = task_runner_.CreateCheckpoint("data_written_1");
fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written));
task_runner_.RunUntilCheckpoint("data_written_1");
@@ -507,56 +542,23 @@
background_trace.join();
- std::string trace_str;
- base::ReadFile(path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- bool seen_first_trigger = false;
- size_t for_testing_packets = 0;
- size_t trigger_packets = 0;
- size_t trace_config_packets = 0;
- for (const auto& packet : trace.packet()) {
- if (packet.has_trace_config()) {
- // Ensure the trace config properly includes the trigger mode we set.
- auto kStopTrig = protos::gen::TraceConfig::TriggerConfig::STOP_TRACING;
- EXPECT_EQ(kStopTrig,
- packet.trace_config().trigger_config().trigger_mode());
- ++trace_config_packets;
- } else if (packet.has_trigger()) {
- // validate that the triggers are properly added to the trace.
- if (!seen_first_trigger) {
- EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
- seen_first_trigger = true;
- } else {
- EXPECT_EQ("trigger_name_3", packet.trigger().trigger_name());
- }
- ++trigger_packets;
- } else if (packet.has_for_testing()) {
- // Make sure that the data size is correctly set based on what we
- // requested.
- EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
- ++for_testing_packets;
- }
- }
- EXPECT_EQ(trace_config_packets, 1u);
- EXPECT_EQ(trigger_packets, 2u);
- EXPECT_EQ(for_testing_packets, kMessageCount);
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace));
+ ExpectTraceContainsConfigWithTriggerMode(
+ trace, protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
+ EXPECT_THAT(GetReceivedTriggerNames(trace),
+ ElementsAre("trigger_name", "trigger_name_3"));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
}
// Dropbox on the commandline client only works on android builds. So disable
// this test on all other builds.
TEST_F(PerfettoCmdlineTest, AndroidOnly(NoDataNoFileWithoutTrigger)) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 11;
- constexpr size_t kMessageSize = 32;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* incident_config = trace_config.mutable_incident_report_config();
incident_config->set_destination_package("foo.bar.baz");
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
@@ -601,15 +603,8 @@
}
TEST_F(PerfettoCmdlineTest, StopTracingTriggerFromConfig) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 11;
- constexpr size_t kMessageSize = 32;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
@@ -666,8 +661,8 @@
});
test_helper().WaitForProducerEnabled();
- // Wait for the producer to start, and then write out 11 packets, before the
- // trace actually starts (the trigger is seen).
+ // Wait for the producer to start, and then write out some test packets,
+ // before the trace actually starts (the trigger is seen).
auto on_data_written = task_runner_.CreateCheckpoint("data_written_1");
fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written));
task_runner_.RunUntilCheckpoint("data_written_1");
@@ -676,44 +671,20 @@
background_trace.join();
- std::string trace_str;
- base::ReadFile(path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size());
- bool seen_first_trigger = false;
- for (const auto& packet : trace.packet()) {
- if (packet.has_trace_config()) {
- // Ensure the trace config properly includes the trigger mode we set.
- auto kStopTrig = protos::gen::TraceConfig::TriggerConfig::STOP_TRACING;
- EXPECT_EQ(kStopTrig,
- packet.trace_config().trigger_config().trigger_mode());
- } else if (packet.has_trigger()) {
- // validate that the triggers are properly added to the trace.
- if (!seen_first_trigger) {
- EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
- seen_first_trigger = true;
- } else {
- EXPECT_EQ("trigger_name_3", packet.trigger().trigger_name());
- }
- } else if (packet.has_for_testing()) {
- // Make sure that the data size is correctly set based on what we
- // requested.
- EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
- }
- }
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace));
+ EXPECT_LT(static_cast<int>(kTestMessageCount), trace.packet_size());
+ ExpectTraceContainsConfigWithTriggerMode(
+ trace, protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
+ EXPECT_THAT(GetReceivedTriggerNames(trace),
+ ElementsAre("trigger_name", "trigger_name_3"));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
}
TEST_F(PerfettoCmdlineTest, TriggerFromConfigStopsFileOpening) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 11;
- constexpr size_t kMessageSize = 32;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
@@ -772,15 +743,8 @@
}
TEST_F(PerfettoCmdlineTest, AndroidOnly(CmdTriggerWithUploadFlag)) {
- // See |message_count| and |message_size| in the TraceConfig above.
- constexpr size_t kMessageCount = 2;
- constexpr size_t kMessageSize = 2;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::STOP_TRACING);
@@ -831,8 +795,8 @@
});
test_helper().WaitForProducerEnabled();
- // Wait for the producer to start, and then write out 11 packets, before the
- // trace actually starts (the trigger is seen).
+ // Wait for the producer to start, and then write out some test packets,
+ // before the trace actually starts (the trigger is seen).
auto on_data_written = task_runner_.CreateCheckpoint("data_written_1");
fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written));
task_runner_.RunUntilCheckpoint("data_written_1");
@@ -841,11 +805,11 @@
background_trace.join();
- std::string trace_str;
- base::ReadFile(path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size());
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
+ EXPECT_LT(static_cast<int>(kTestMessageCount), trace.packet_size());
EXPECT_THAT(trace.packet(),
Contains(Property(&protos::gen::TracePacket::trigger,
Property(&protos::gen::Trigger::trigger_name,
@@ -853,14 +817,8 @@
}
TEST_F(PerfettoCmdlineTest, TriggerCloneSnapshot) {
- constexpr size_t kMessageCount = 2;
- constexpr size_t kMessageSize = 2;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
auto* trigger_cfg = trace_config.mutable_trigger_config();
trigger_cfg->set_trigger_mode(
protos::gen::TraceConfig::TriggerConfig::CLONE_SNAPSHOT);
@@ -910,8 +868,8 @@
});
test_helper().WaitForProducerEnabled();
- // Wait for the producer to start, and then write out 11 packets, before the
- // trace actually starts (the trigger is seen).
+ // Wait for the producer to start, and then write out some test packets,
+ // before the trace actually starts (the trigger is seen).
auto on_data_written = task_runner_.CreateCheckpoint("data_written_1");
fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written));
task_runner_.RunUntilCheckpoint("data_written_1");
@@ -931,11 +889,11 @@
perfetto_proc.SendSigterm();
background_trace.join();
- std::string trace_str;
- base::ReadFile(snapshot_path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str));
- EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size());
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(snapshot_path, trace));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
+ EXPECT_LT(static_cast<int>(kTestMessageCount), trace.packet_size());
EXPECT_THAT(trace.packet(),
Contains(Property(&protos::gen::TracePacket::trigger,
Property(&protos::gen::Trigger::trigger_name,
@@ -961,14 +919,9 @@
}
TEST_F(PerfettoCmdlineTest, CloneByName) {
- constexpr size_t kMessageCount = 2;
- protos::gen::TraceConfig trace_config;
- trace_config.add_buffers()->set_size_kb(1024);
+ protos::gen::TraceConfig trace_config =
+ CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize);
trace_config.set_unique_session_name("my_unique_session_name");
- auto* ds_config = trace_config.add_data_sources()->mutable_config();
- ds_config->set_name("android.perfetto.FakeProducer");
- ds_config->mutable_for_testing()->set_message_count(kMessageCount);
- ds_config->mutable_for_testing()->set_message_size(2);
// We have to construct all the processes we want to fork before we start the
// service with |StartServiceIfRequired()|. this is because it is unsafe
@@ -1026,26 +979,18 @@
EXPECT_EQ(0, perfetto_proc_clone_2.Run(&stderr_)) << "stderr: " << stderr_;
EXPECT_FALSE(base::FileExists(path_cloned_2));
- std::string cloned_trace_str;
- base::ReadFile(path_cloned, &cloned_trace_str);
protos::gen::Trace cloned_trace;
- ASSERT_TRUE(cloned_trace.ParseFromString(cloned_trace_str));
- ssize_t cloned_num_test_packets = std::count_if(
- cloned_trace.packet().begin(), cloned_trace.packet().end(),
- [](const protos::gen::TracePacket& tp) { return tp.has_for_testing(); });
- EXPECT_EQ(cloned_num_test_packets, static_cast<ssize_t>(kMessageCount));
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path_cloned, cloned_trace));
+ ExpectTraceContainsTestMessages(cloned_trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(cloned_trace, kTestMessageSize);
perfetto_proc.SendSigterm();
background_trace.join();
- std::string trace_str;
- base::ReadFile(path, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(cloned_trace_str));
- ssize_t num_test_packets = std::count_if(
- trace.packet().begin(), trace.packet().end(),
- [](const protos::gen::TracePacket& tp) { return tp.has_for_testing(); });
- EXPECT_EQ(num_test_packets, static_cast<ssize_t>(kMessageCount));
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace));
+ ExpectTraceContainsTestMessages(trace, kTestMessageCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize);
}
// Regression test for b/279753347: --save-for-bugreport would create an empty
@@ -1185,10 +1130,8 @@
auto check_trace = [&](std::string fname, int expected_score) {
std::string fpath = GetBugreportTraceDir() + "/" + fname;
ASSERT_TRUE(base::FileExists(fpath)) << fpath;
- std::string trace_str;
- base::ReadFile(fpath, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str)) << fpath;
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(fpath, trace)) << fpath;
EXPECT_THAT(
trace.packet(),
Contains(Property(&protos::gen::TracePacket::trace_config,
@@ -1211,8 +1154,9 @@
auto remove_on_exit = base::OnScopeExit(remove_br_files);
const uint32_t kMsgCount = 10000;
+ const uint32_t kMsgSize = 1024;
TraceConfig cfg = CreateTraceConfigForBugreportTest(
- /*score=*/1, /*add_filter=*/false, kMsgCount, /*msg_size=*/1024);
+ /*score=*/1, /*add_filter=*/false, kMsgCount, kMsgSize);
auto session_name = "bugreport_test_" +
std::to_string(base::GetWallTimeNs().count() % 1000000);
@@ -1263,14 +1207,10 @@
std::string fpath = GetBugreportTraceDir() + "/systrace.pftrace";
ASSERT_TRUE(base::FileExists(fpath)) << fpath;
- std::string trace_str;
- base::ReadFile(fpath, &trace_str);
protos::gen::Trace trace;
- ASSERT_TRUE(trace.ParseFromString(trace_str)) << fpath;
- ssize_t num_test_packets = std::count_if(
- trace.packet().begin(), trace.packet().end(),
- [](const protos::gen::TracePacket& tp) { return tp.has_for_testing(); });
- EXPECT_EQ(num_test_packets, static_cast<ssize_t>(kMsgCount));
+ ASSERT_TRUE(ParseNotEmptyTraceFromFile(fpath, trace)) << fpath;
+ ExpectTraceContainsTestMessages(trace, kMsgCount);
+ ExpectTraceContainsTestMessagesWithSize(trace, kMsgSize);
}
} // namespace perfetto
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index ff0e759..1a492b7 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -72,7 +72,7 @@
StopApp(app_name, "old.app.stopped", &task_runner);
task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
}
- StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
+ StartAppActivity(app_name, "NoopActivity", "target.app.running", &task_runner,
/*delay_ms=*/100);
task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
// If we try to dump too early in app initialization, we sometimes deadlock.
@@ -219,7 +219,7 @@
StopApp(app_name, "old.app.stopped", &task_runner);
task_runner.RunUntilCheckpoint("old.app.stopped", 10000 /*ms*/);
}
- StartAppActivity(app_name, "MainActivity", "target.app.running", &task_runner,
+ StartAppActivity(app_name, "NoopActivity", "target.app.running", &task_runner,
/*delay_ms=*/100);
task_runner.RunUntilCheckpoint("target.app.running", 10000 /*ms*/);
// If we try to dump too early in app initialization, we sometimes deadlock.
diff --git a/test/cts/test_apps/AndroidManifest_debuggable.xml b/test/cts/test_apps/AndroidManifest_debuggable.xml
index 79993f8..e2fe258 100755
--- a/test/cts/test_apps/AndroidManifest_debuggable.xml
+++ b/test/cts/test_apps/AndroidManifest_debuggable.xml
@@ -18,7 +18,20 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.perfetto.cts.app.debuggable">
- <application android:debuggable="true">
+ <!-- vmSafeMode="true" disables the JIT.
+
+ HeapprofdJavaCtsTest cover Java heap dumps.
+
+ Java heap dumps are not 100% reliable because they fork the app process,
+ which is multithreaded. If another thread is holding a lock, the forked
+ process can get stuck. This is a known limitation of java heap dumps.
+
+ debuggable apps are not AOT-compiled, so there's a high chance that the
+ JIT is in use. The JIT runs on another thread and can hold locks. To
+ reduce the chance of running into the fork deadlock described earlier,
+ we simply disable the JIT for debuggable apps in tests.
+ -->
+ <application android:debuggable="true" android:vmSafeMode="true">
<activity
android:name="android.perfetto.cts.app.MainActivity"
android:exported="true">
@@ -71,6 +84,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.debuggable.NoopActivity"
+ android:targetActivity="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
<provider
android:name="android.perfetto.cts.app.FileContentProvider"
android:authorities="android.perfetto.cts.app.debuggable"
diff --git a/test/cts/test_apps/AndroidManifest_nonprofileable.xml b/test/cts/test_apps/AndroidManifest_nonprofileable.xml
index 8322daf..da3210d 100755
--- a/test/cts/test_apps/AndroidManifest_nonprofileable.xml
+++ b/test/cts/test_apps/AndroidManifest_nonprofileable.xml
@@ -59,6 +59,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.nonprofileable.NoopActivity"
+ android:targetActivity="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
<provider
android:name="android.perfetto.cts.app.FileContentProvider"
android:authorities="android.perfetto.cts.app.nonprofileable"
diff --git a/test/cts/test_apps/AndroidManifest_profileable.xml b/test/cts/test_apps/AndroidManifest_profileable.xml
index cd434d4..aebb810 100755
--- a/test/cts/test_apps/AndroidManifest_profileable.xml
+++ b/test/cts/test_apps/AndroidManifest_profileable.xml
@@ -72,6 +72,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.profileable.NoopActivity"
+ android:targetActivity="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
<provider
android:name="android.perfetto.cts.app.FileContentProvider"
android:authorities="android.perfetto.cts.app.profileable"
diff --git a/test/cts/test_apps/AndroidManifest_release.xml b/test/cts/test_apps/AndroidManifest_release.xml
index 1795a59..8a87e23 100755
--- a/test/cts/test_apps/AndroidManifest_release.xml
+++ b/test/cts/test_apps/AndroidManifest_release.xml
@@ -71,6 +71,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+ <activity
+ android:name="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ </activity>
+ <activity-alias
+ android:name="android.perfetto.cts.app.release.NoopActivity"
+ android:targetActivity="android.perfetto.cts.app.NoopActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
<provider
android:name="android.perfetto.cts.app.FileContentProvider"
android:authorities="android.perfetto.cts.app.release"
diff --git a/test/cts/test_apps/src/android/perfetto/cts/app/NoopActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/NoopActivity.java
new file mode 100644
index 0000000..296e692
--- /dev/null
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/NoopActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perfetto.cts.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class NoopActivity extends Activity {
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+ }
+}
diff --git a/test/data/android_desktop_mode/task_update_reset_events.pb.sha256 b/test/data/android_desktop_mode/task_update_reset_events.pb.sha256
new file mode 100644
index 0000000..f047a6a
--- /dev/null
+++ b/test/data/android_desktop_mode/task_update_reset_events.pb.sha256
@@ -0,0 +1 @@
+1aca7da3154a8893d214444d10559d88e689d090e6d39206e30959aa2a2cd306
\ No newline at end of file
diff --git a/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-slice-clicked.png.sha256 b/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-slice-clicked.png.sha256
index 297135d..f18a143 100644
--- a/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-slice-clicked.png.sha256
+++ b/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-slice-clicked.png.sha256
@@ -1 +1 @@
-c64f0b6618bde8a55462ffd03ae99b880a8cd88b2afdebdc11ef03f78448b99d
\ No newline at end of file
+f89e12d7bc8ba6c45d35be4c157bff221e9659465e2106dc22c9960cfd6b6de8
\ No newline at end of file
diff --git a/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-track-removed.png.sha256 b/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-track-removed.png.sha256
index 514cdb7..e5bab38 100644
--- a/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-track-removed.png.sha256
+++ b/test/data/ui-screenshots/debug_tracks.test.ts/debug-tracks/debug-track-removed.png.sha256
@@ -1 +1 @@
-9a3b3836dc1fc66ea78f19d2b8f0a47dacd37d2fc170e4f08e07dc062969cab4
\ No newline at end of file
+fda4f777d5a613c3c7622f5825031610441bf924af8c7920bf63840526a87819
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ftrace_tracks_and_tab.test.ts/ftrace-tracks/ftrace-events.png.sha256 b/test/data/ui-screenshots/ftrace_tracks_and_tab.test.ts/ftrace-tracks/ftrace-events.png.sha256
index b97e598..66f89fe 100644
--- a/test/data/ui-screenshots/ftrace_tracks_and_tab.test.ts/ftrace-tracks/ftrace-events.png.sha256
+++ b/test/data/ui-screenshots/ftrace_tracks_and_tab.test.ts/ftrace-tracks/ftrace-events.png.sha256
@@ -1 +1 @@
-ef239fe1e10e3b830f7adf9b5b8f96a52c69a50e6d879cfb5a873cd26caab769
\ No newline at end of file
+ae6623f3d00536a815ad76c40c5933845a436e6e8acee6349d0e925c06d0b6b0
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index a51fac7..69c1bc7 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -112,12 +112,11 @@
from diff_tests.stdlib.android.frames_tests import Frames
from diff_tests.stdlib.android.gpu import AndroidGpu
from diff_tests.stdlib.android.heap_graph_tests import HeapGraph
+from diff_tests.stdlib.android.heap_profile_tests import HeapProfile
from diff_tests.stdlib.android.memory import AndroidMemory
from diff_tests.stdlib.android.startups_tests import Startups
from diff_tests.stdlib.android.tests import AndroidStdlib
from diff_tests.stdlib.chrome.chrome_stdlib_testsuites import CHROME_STDLIB_TESTSUITES
-from diff_tests.stdlib.common.tests import StdlibCommon
-from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.counters.tests import StdlibCounterIntervals
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
from diff_tests.stdlib.export.tests import ExportTests
@@ -325,7 +324,6 @@
*Pkvm(index_path, 'stdlib/pkvm', 'Pkvm').fetch(),
*PreludeSlices(index_path, 'stdlib/prelude', 'PreludeSlices').fetch(),
*StdlibSmoke(index_path, 'stdlib', 'StdlibSmoke').fetch(),
- *StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*Slices(index_path, 'stdlib/slices', 'Slices').fetch(),
*SpanJoinLeftJoin(index_path, 'stdlib/span_join',
'SpanJoinLeftJoin').fetch(),
@@ -334,7 +332,6 @@
*SpanJoinRegression(index_path, 'stdlib/span_join',
'SpanJoinRegression').fetch(),
*SpanJoinSmoke(index_path, 'stdlib/span_join', 'SpanJoinSmoke').fetch(),
- *StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*StdlibIntervals(index_path, 'stdlib/intervals',
'StdlibIntervals').fetch(),
*IntervalsIntersect(index_path, 'stdlib/intervals',
@@ -343,6 +340,7 @@
*Timestamps(index_path, 'stdlib/timestamps', 'Timestamps').fetch(),
*Viz(index_path, 'stdlib/viz', 'Viz').fetch(),
*WattsonStdlib(index_path, 'stdlib/wattson', 'WattsonStdlib').fetch(),
+ *HeapProfile(index_path, 'stdlib/android', 'HeapProfile').fetch(),
] + chrome_stdlib_tests
syntax_tests = [
diff --git a/test/trace_processor/diff_tests/metrics/android/wattson_markers_threads.out b/test/trace_processor/diff_tests/metrics/android/wattson_markers_threads.out
index ff082fd..4168034 100644
--- a/test/trace_processor/diff_tests/metrics/android/wattson_markers_threads.out
+++ b/test/trace_processor/diff_tests/metrics/android/wattson_markers_threads.out
@@ -1,719 +1,722 @@
wattson_markers_threads {
- metric_version: 3
+ metric_version: 4
power_model_version: 1
- task_info {
- estimated_mws: 15.333553
- estimated_mw: 7.546518
- thread_name: "swapper"
- thread_id: 0
- process_id: 0
- }
- task_info {
- estimated_mws: 11.805121
- estimated_mw: 5.809974
- idle_transitions_mws: 0.300579
- thread_name: "RenderThread"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3099
- process_id: 2710
- }
- task_info {
- estimated_mws: 9.112684
- estimated_mw: 4.484872
- idle_transitions_mws: 0.013483
- thread_name: "binder:683_3"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
- thread_id: 816
- process_id: 683
- }
- task_info {
- estimated_mws: 8.802570
- estimated_mw: 4.332248
- idle_transitions_mws: 0.223591
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 742
- process_id: 742
- }
- task_info {
- estimated_mws: 4.007993
- estimated_mw: 1.972562
- idle_transitions_mws: 0.449704
- thread_name: ".wearable.sysui"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2710
- process_id: 2710
- }
- task_info {
- estimated_mws: 1.779128
- estimated_mw: 0.875610
- idle_transitions_mws: 0.686579
- thread_name: "crtc_commit:80"
- process_name: "crtc_commit:80"
- thread_id: 300
- process_id: 300
- }
- task_info {
- estimated_mws: 1.436499
- estimated_mw: 0.706983
- idle_transitions_mws: 0.113163
- thread_name: "binder:2710_E"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 6515
- process_id: 2710
- }
- task_info {
- estimated_mws: 1.262685
- estimated_mw: 0.621440
- idle_transitions_mws: 0.337691
- thread_name: "TimerDispatch"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 819
- process_id: 742
- }
- task_info {
- estimated_mws: 1.242906
- estimated_mw: 0.611705
- idle_transitions_mws: 0.318923
- thread_name: "kworker/u8:4"
- process_name: "kworker/u8:4"
- thread_id: 11407
- process_id: 11407
- }
- task_info {
- estimated_mws: 1.231494
- estimated_mw: 0.606089
- idle_transitions_mws: 0.311600
- thread_name: "BckgrndExec HP"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 837
- process_id: 742
- }
- task_info {
- estimated_mws: 1.194067
- estimated_mw: 0.587669
- idle_transitions_mws: 0.427458
- thread_name: "kworker/u8:5"
- process_name: "kworker/u8:5"
- thread_id: 10610
- process_id: 10610
- }
- task_info {
- estimated_mws: 1.132809
- estimated_mw: 0.557520
- idle_transitions_mws: 0.095612
- thread_name: "binder:742_2"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 791
- process_id: 742
- }
- task_info {
- estimated_mws: 1.065350
- estimated_mw: 0.524320
- idle_transitions_mws: 1.154817
- thread_name: "rcu_preempt"
- process_name: "rcu_preempt"
- thread_id: 14
- process_id: 14
- }
- task_info {
- estimated_mws: 0.872391
- estimated_mw: 0.429353
- idle_transitions_mws: 0.072071
- thread_name: "binder:2710_7"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 5691
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.865098
- estimated_mw: 0.425764
- idle_transitions_mws: 0.010689
- thread_name: "traced_probes"
- process_name: "/system/bin/traced_probes"
- thread_id: 916
- process_id: 916
- }
- task_info {
- estimated_mws: 0.844506
- estimated_mw: 0.415630
- idle_transitions_mws: 0.000684
- thread_name: "sleep"
- process_name: "sleep"
- thread_id: 11474
- process_id: 11474
- }
- task_info {
- estimated_mws: 0.795564
- estimated_mw: 0.391542
- idle_transitions_mws: 0.252256
- thread_name: "kgsl_dispatcher"
- process_name: "kgsl_dispatcher"
- thread_id: 122
- process_id: 122
- }
- task_info {
- estimated_mws: 0.715993
- estimated_mw: 0.352381
- idle_transitions_mws: 0.426966
- thread_name: "irq/33-4520300."
- process_name: "irq/33-4520300."
- thread_id: 307
- process_id: 307
- }
- task_info {
- estimated_mws: 0.715212
- estimated_mw: 0.351997
- idle_transitions_mws: 0.062089
- thread_name: "binder:742_1"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 786
- process_id: 742
- }
- task_info {
- estimated_mws: 0.659174
- estimated_mw: 0.324417
- idle_transitions_mws: 0.153809
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 788
- process_id: 742
- }
- task_info {
- estimated_mws: 0.653970
- estimated_mw: 0.321856
- idle_transitions_mws: 0.111764
- thread_name: "app"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 820
- process_id: 742
- }
- task_info {
- estimated_mws: 0.463974
- estimated_mw: 0.228348
- idle_transitions_mws: 0.175385
- thread_name: "rcuog/0"
- process_name: "rcuog/0"
- thread_id: 15
- process_id: 15
- }
- task_info {
- estimated_mws: 0.434532
- estimated_mw: 0.213858
- idle_transitions_mws: 0.095329
- thread_name: "Primes-Jank"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 5094
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.356684
- estimated_mw: 0.175544
- idle_transitions_mws: 0.276803
- thread_name: "crtc_event:80"
- process_name: "crtc_event:80"
- thread_id: 301
- process_id: 301
- }
- task_info {
- estimated_mws: 0.271999
- estimated_mw: 0.133866
- idle_transitions_mws: 0.151016
- thread_name: "rcuog/2"
- process_name: "rcuog/2"
- thread_id: 40
- process_id: 40
- }
- task_info {
- estimated_mws: 0.204649
- estimated_mw: 0.100719
- idle_transitions_mws: 0.067710
- thread_name: "binder:2710_2"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3100
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.197450
- estimated_mw: 0.097176
- thread_name: "FileWatcherThre"
- process_name: "/vendor/bin/hw/android.hardware.thermal-service.pixel"
- thread_id: 1544
- process_id: 1529
- }
- task_info {
- estimated_mws: 0.165350
- estimated_mw: 0.081378
- idle_transitions_mws: 0.030527
- thread_name: "rcuop/0"
- process_name: "rcuop/0"
- thread_id: 16
- process_id: 16
- }
- task_info {
- estimated_mws: 0.123135
- estimated_mw: 0.060602
- thread_name: "msm_irqbalance"
- process_name: "/vendor/bin/msm_irqbalance"
- thread_id: 3230
- process_id: 3230
- }
- task_info {
- estimated_mws: 0.122703
- estimated_mw: 0.060389
- idle_transitions_mws: 0.026041
- thread_name: "kgsl-events"
- process_name: "kgsl-events"
- thread_id: 120
- process_id: 120
- }
- task_info {
- estimated_mws: 0.106642
- estimated_mw: 0.052485
- thread_name: "traced"
- process_name: "/system/bin/traced"
- thread_id: 919
- process_id: 919
- }
- task_info {
- estimated_mws: 0.104195
- estimated_mw: 0.051280
- idle_transitions_mws: 0.301336
- thread_name: "kworker/2:0"
- process_name: "kworker/2:0"
- thread_id: 11444
- process_id: 11444
- }
- task_info {
- estimated_mws: 0.095284
- estimated_mw: 0.046894
- idle_transitions_mws: 0.039497
- thread_name: "rcuop/2"
- process_name: "rcuop/2"
- thread_id: 41
- process_id: 41
- }
- task_info {
- estimated_mws: 0.084534
- estimated_mw: 0.041604
- idle_transitions_mws: 0.025241
- thread_name: "RegSampIdle"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 826
- process_id: 742
- }
- task_info {
- estimated_mws: 0.076505
- estimated_mw: 0.037652
- idle_transitions_mws: 0.075963
- thread_name: "rcuop/1"
- process_name: "rcuop/1"
- thread_id: 32
- process_id: 32
- }
- task_info {
- estimated_mws: 0.067736
- estimated_mw: 0.033337
- thread_name: "sh"
- process_name: "/system/bin/sh"
- thread_id: 11472
- process_id: 11472
- }
- task_info {
- estimated_mws: 0.065940
- estimated_mw: 0.032453
- idle_transitions_mws: 0.002859
- thread_name: "BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3524
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.053141
- estimated_mw: 0.026154
- idle_transitions_mws: 0.000744
- thread_name: "StateService"
- process_name: "com.google.android.apps.scone"
- thread_id: 3621
- process_id: 3505
- }
- task_info {
- estimated_mws: 0.052200
- estimated_mw: 0.025691
- idle_transitions_mws: 0.000544
- thread_name: "Blocking Thread"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 11310
- process_id: 11279
- }
- task_info {
- estimated_mws: 0.040767
- estimated_mw: 0.020064
- idle_transitions_mws: 0.003269
- thread_name: "kworker/0:1"
- process_name: "kworker/0:1"
- thread_id: 11436
- process_id: 11436
- }
- task_info {
- estimated_mws: 0.040587
- estimated_mw: 0.019975
- thread_name: "binder:1629_7"
- process_name: "system_server"
- thread_id: 2635
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.040484
- estimated_mw: 0.019924
- idle_transitions_mws: 0.020651
- thread_name: "rcuop/3"
- process_name: "rcuop/3"
- thread_id: 49
- process_id: 49
- }
- task_info {
- estimated_mws: 0.038016
- estimated_mw: 0.018710
- idle_transitions_mws: 0.002906
- thread_name: "atchdog.monitor"
- process_name: "system_server"
- thread_id: 1669
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.036888
- estimated_mw: 0.018155
- thread_name: "logd.writer"
- process_name: "/system/bin/logd"
- thread_id: 228
- process_id: 213
- }
- task_info {
- estimated_mws: 0.032972
- estimated_mw: 0.016227
- idle_transitions_mws: 0.025051
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 828
- process_id: 742
- }
- task_info {
- estimated_mws: 0.032239
- estimated_mw: 0.015867
- idle_transitions_mws: 0.001840
- thread_name: "it.FitbitMobile"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 11279
- process_id: 11279
- }
- task_info {
- estimated_mws: 0.031160
- estimated_mw: 0.015336
- idle_transitions_mws: 0.000559
- thread_name: "binder:11279_4"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 11426
- process_id: 11279
- }
- task_info {
- estimated_mws: 0.028389
- estimated_mw: 0.013972
- thread_name: "irq/207-dwc3"
- process_name: "irq/207-dwc3"
- thread_id: 9733
- process_id: 9733
- }
- task_info {
- estimated_mws: 0.027208
- estimated_mw: 0.013391
- idle_transitions_mws: 0.000951
- thread_name: "UsbFfs-worker"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 9734
- process_id: 5154
- }
- task_info {
- estimated_mws: 0.024832
- estimated_mw: 0.012221
- thread_name: "logcat"
- process_name: "logcat"
- thread_id: 1199
- process_id: 1199
- }
- task_info {
- estimated_mws: 0.023707
- estimated_mw: 0.011668
- idle_transitions_mws: 0.000854
- thread_name: "logd.reader.per"
- process_name: "/system/bin/logd"
- thread_id: 1227
- process_id: 213
- }
- task_info {
- estimated_mws: 0.022160
- estimated_mw: 0.010906
- idle_transitions_mws: 0.006160
- thread_name: "kworker/u8:2"
- process_name: "kworker/u8:2"
- thread_id: 11458
- process_id: 11458
- }
- task_info {
- estimated_mws: 0.019052
- estimated_mw: 0.009376
- idle_transitions_mws: 0.008121
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3575
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.018414
- estimated_mw: 0.009063
- idle_transitions_mws: 0.040061
- thread_name: "RegionSampling"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 825
- process_id: 742
- }
- task_info {
- estimated_mws: 0.016701
- estimated_mw: 0.008220
- idle_transitions_mws: 0.006448
- thread_name: "halt_drain_rqs"
- process_name: "halt_drain_rqs"
- thread_id: 108
- process_id: 108
- }
- task_info {
- estimated_mws: 0.011023
- estimated_mw: 0.005425
- idle_transitions_mws: 0.001553
- thread_name: "irq/26-4744000."
- process_name: "irq/26-4744000."
- thread_id: 112
- process_id: 112
- }
- task_info {
- estimated_mws: 0.010004
- estimated_mw: 0.004924
- thread_name: "migration/2"
- process_name: "migration/2"
- thread_id: 35
- process_id: 35
- }
- task_info {
- estimated_mws: 0.008819
- estimated_mw: 0.004341
- thread_name: "ksoftirqd/0"
- process_name: "ksoftirqd/0"
- thread_id: 13
- process_id: 13
- }
- task_info {
- estimated_mws: 0.007911
- estimated_mw: 0.003894
- idle_transitions_mws: 0.001205
- thread_name: "watchdog"
- process_name: "system_server"
- thread_id: 1676
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.007796
- estimated_mw: 0.003837
- idle_transitions_mws: 0.000627
- thread_name: "pool-283-thread"
- process_name: "system_server"
- thread_id: 4427
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.007628
- estimated_mw: 0.003754
- idle_transitions_mws: 0.001280
- thread_name: "adbd"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5154
- process_id: 5154
- }
- task_info {
- estimated_mws: 0.006796
- estimated_mw: 0.003344
- idle_transitions_mws: 0.001484
- thread_name: "pool-1-thread-1"
- process_name: "system_server"
- thread_id: 2655
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.005691
- estimated_mw: 0.002801
- idle_transitions_mws: 0.001832
- thread_name: "pool-1-thread-1"
- process_name: "com.google.android.apps.scone"
- thread_id: 3625
- process_id: 3505
- }
- task_info {
- estimated_mws: 0.005476
- estimated_mw: 0.002695
- thread_name: "binder:237_2"
- process_name: "/system/bin/vold"
- thread_id: 237
- process_id: 237
- }
- task_info {
- estimated_mws: 0.004537
- estimated_mw: 0.002233
- thread_name: "shell svc 11472"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 11473
- process_id: 5154
- }
- task_info {
- estimated_mws: 0.003924
- estimated_mw: 0.001931
- thread_name: "ksoftirqd/1"
- process_name: "ksoftirqd/1"
- thread_id: 29
- process_id: 29
- }
- task_info {
- estimated_mws: 0.002908
- estimated_mw: 0.001431
- thread_name: "BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 5230
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.002492
- estimated_mw: 0.001226
- idle_transitions_mws: 0.028896
- thread_name: "kworker/3:2"
- process_name: "kworker/3:2"
- thread_id: 9832
- process_id: 9832
- }
- task_info {
- estimated_mws: 0.002333
- estimated_mw: 0.001148
- idle_transitions_mws: 0.000780
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3577
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.002293
- estimated_mw: 0.001128
- idle_transitions_mws: 0.000781
- thread_name: "InputReader"
- process_name: "system_server"
- thread_id: 2560
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.002261
- estimated_mw: 0.001113
- thread_name: "DefaultDispatch"
- process_name: "com.google.android.wearable.media.sessions"
- thread_id: 3618
- process_id: 3553
- }
- task_info {
- estimated_mws: 0.002226
- estimated_mw: 0.001095
- thread_name: "migration/3"
- process_name: "migration/3"
- thread_id: 44
- process_id: 44
- }
- task_info {
- estimated_mws: 0.002100
- estimated_mw: 0.001034
- idle_transitions_mws: 0.000657
- thread_name: "InputDispatcher"
- process_name: "system_server"
- thread_id: 2559
- process_id: 1629
- }
- task_info {
- estimated_mws: 0.001973
- estimated_mw: 0.000971
- idle_transitions_mws: 0.000764
- thread_name: "kworker/1:0"
- process_name: "kworker/1:0"
- thread_id: 10984
- process_id: 10984
- }
- task_info {
- estimated_mws: 0.001966
- estimated_mw: 0.000967
- thread_name: "irq/193-wdog-ba"
- process_name: "irq/193-wdog-ba"
- thread_id: 344
- process_id: 344
- }
- task_info {
- estimated_mws: 0.001867
- estimated_mw: 0.000919
- thread_name: "DefaultDispatch"
- process_name: "com.google.android.wearable.media.sessions"
- thread_id: 3615
- process_id: 3553
- }
- task_info {
- estimated_mws: 0.001867
- estimated_mw: 0.000919
- idle_transitions_mws: 0.000705
- thread_name: "irq/25-mmc0"
- process_name: "irq/25-mmc0"
- thread_id: 115
- process_id: 115
- }
- task_info {
- estimated_mws: 0.001728
- estimated_mw: 0.000851
- thread_name: "iou-wrk-214"
- process_name: "/system/bin/lmkd"
- thread_id: 11440
- process_id: 214
- }
- task_info {
- estimated_mws: 0.001600
- estimated_mw: 0.000787
- idle_transitions_mws: 0.001911
- thread_name: "DefaultDispatch"
- process_name: "com.google.android.wearable.media.sessions"
- thread_id: 3616
- process_id: 3553
- }
- task_info {
- estimated_mws: 0.001393
- estimated_mw: 0.000686
- thread_name: "kworker/u8:1"
- process_name: "kworker/u8:1"
- thread_id: 11185
- process_id: 11185
- }
- task_info {
- estimated_mws: 0.001373
- estimated_mw: 0.000676
- idle_transitions_mws: 0.007386
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3576
- process_id: 2710
- }
- task_info {
- estimated_mws: 0.000811
- estimated_mw: 0.000399
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3622
- process_id: 2710
+ period_info {
+ period_id: 1
+ task_info {
+ estimated_mws: 15.333553
+ estimated_mw: 7.546518
+ thread_name: "swapper"
+ thread_id: 0
+ process_id: 0
+ }
+ task_info {
+ estimated_mws: 11.805121
+ estimated_mw: 5.809974
+ idle_transitions_mws: 0.300579
+ thread_name: "RenderThread"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3099
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 9.112684
+ estimated_mw: 4.484872
+ idle_transitions_mws: 0.013483
+ thread_name: "binder:683_3"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
+ thread_id: 816
+ process_id: 683
+ }
+ task_info {
+ estimated_mws: 8.802570
+ estimated_mw: 4.332248
+ idle_transitions_mws: 0.223591
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 742
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 4.007993
+ estimated_mw: 1.972562
+ idle_transitions_mws: 0.449704
+ thread_name: ".wearable.sysui"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2710
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 1.779128
+ estimated_mw: 0.875610
+ idle_transitions_mws: 0.686579
+ thread_name: "crtc_commit:80"
+ process_name: "crtc_commit:80"
+ thread_id: 300
+ process_id: 300
+ }
+ task_info {
+ estimated_mws: 1.436499
+ estimated_mw: 0.706983
+ idle_transitions_mws: 0.113163
+ thread_name: "binder:2710_E"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 6515
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 1.262685
+ estimated_mw: 0.621440
+ idle_transitions_mws: 0.337691
+ thread_name: "TimerDispatch"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 819
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 1.242906
+ estimated_mw: 0.611705
+ idle_transitions_mws: 0.318923
+ thread_name: "kworker/u8:4"
+ process_name: "kworker/u8:4"
+ thread_id: 11407
+ process_id: 11407
+ }
+ task_info {
+ estimated_mws: 1.231494
+ estimated_mw: 0.606089
+ idle_transitions_mws: 0.311600
+ thread_name: "BckgrndExec HP"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 837
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 1.194067
+ estimated_mw: 0.587669
+ idle_transitions_mws: 0.427458
+ thread_name: "kworker/u8:5"
+ process_name: "kworker/u8:5"
+ thread_id: 10610
+ process_id: 10610
+ }
+ task_info {
+ estimated_mws: 1.132809
+ estimated_mw: 0.557520
+ idle_transitions_mws: 0.095612
+ thread_name: "binder:742_2"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 791
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 1.065350
+ estimated_mw: 0.524320
+ idle_transitions_mws: 1.154817
+ thread_name: "rcu_preempt"
+ process_name: "rcu_preempt"
+ thread_id: 14
+ process_id: 14
+ }
+ task_info {
+ estimated_mws: 0.872391
+ estimated_mw: 0.429353
+ idle_transitions_mws: 0.072071
+ thread_name: "binder:2710_7"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 5691
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.865098
+ estimated_mw: 0.425764
+ idle_transitions_mws: 0.010689
+ thread_name: "traced_probes"
+ process_name: "/system/bin/traced_probes"
+ thread_id: 916
+ process_id: 916
+ }
+ task_info {
+ estimated_mws: 0.844506
+ estimated_mw: 0.415630
+ idle_transitions_mws: 0.000684
+ thread_name: "sleep"
+ process_name: "sleep"
+ thread_id: 11474
+ process_id: 11474
+ }
+ task_info {
+ estimated_mws: 0.795564
+ estimated_mw: 0.391542
+ idle_transitions_mws: 0.252256
+ thread_name: "kgsl_dispatcher"
+ process_name: "kgsl_dispatcher"
+ thread_id: 122
+ process_id: 122
+ }
+ task_info {
+ estimated_mws: 0.715993
+ estimated_mw: 0.352381
+ idle_transitions_mws: 0.426966
+ thread_name: "irq/33-4520300."
+ process_name: "irq/33-4520300."
+ thread_id: 307
+ process_id: 307
+ }
+ task_info {
+ estimated_mws: 0.715212
+ estimated_mw: 0.351997
+ idle_transitions_mws: 0.062089
+ thread_name: "binder:742_1"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 786
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.659174
+ estimated_mw: 0.324417
+ idle_transitions_mws: 0.153809
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 788
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.653970
+ estimated_mw: 0.321856
+ idle_transitions_mws: 0.111764
+ thread_name: "app"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 820
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.463974
+ estimated_mw: 0.228348
+ idle_transitions_mws: 0.175385
+ thread_name: "rcuog/0"
+ process_name: "rcuog/0"
+ thread_id: 15
+ process_id: 15
+ }
+ task_info {
+ estimated_mws: 0.434532
+ estimated_mw: 0.213858
+ idle_transitions_mws: 0.095329
+ thread_name: "Primes-Jank"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 5094
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.356684
+ estimated_mw: 0.175544
+ idle_transitions_mws: 0.276803
+ thread_name: "crtc_event:80"
+ process_name: "crtc_event:80"
+ thread_id: 301
+ process_id: 301
+ }
+ task_info {
+ estimated_mws: 0.271999
+ estimated_mw: 0.133866
+ idle_transitions_mws: 0.151016
+ thread_name: "rcuog/2"
+ process_name: "rcuog/2"
+ thread_id: 40
+ process_id: 40
+ }
+ task_info {
+ estimated_mws: 0.204649
+ estimated_mw: 0.100719
+ idle_transitions_mws: 0.067710
+ thread_name: "binder:2710_2"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3100
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.197450
+ estimated_mw: 0.097176
+ thread_name: "FileWatcherThre"
+ process_name: "/vendor/bin/hw/android.hardware.thermal-service.pixel"
+ thread_id: 1544
+ process_id: 1529
+ }
+ task_info {
+ estimated_mws: 0.165350
+ estimated_mw: 0.081378
+ idle_transitions_mws: 0.030527
+ thread_name: "rcuop/0"
+ process_name: "rcuop/0"
+ thread_id: 16
+ process_id: 16
+ }
+ task_info {
+ estimated_mws: 0.123135
+ estimated_mw: 0.060602
+ thread_name: "msm_irqbalance"
+ process_name: "/vendor/bin/msm_irqbalance"
+ thread_id: 3230
+ process_id: 3230
+ }
+ task_info {
+ estimated_mws: 0.122703
+ estimated_mw: 0.060389
+ idle_transitions_mws: 0.026041
+ thread_name: "kgsl-events"
+ process_name: "kgsl-events"
+ thread_id: 120
+ process_id: 120
+ }
+ task_info {
+ estimated_mws: 0.106642
+ estimated_mw: 0.052485
+ thread_name: "traced"
+ process_name: "/system/bin/traced"
+ thread_id: 919
+ process_id: 919
+ }
+ task_info {
+ estimated_mws: 0.104195
+ estimated_mw: 0.051280
+ idle_transitions_mws: 0.301336
+ thread_name: "kworker/2:0"
+ process_name: "kworker/2:0"
+ thread_id: 11444
+ process_id: 11444
+ }
+ task_info {
+ estimated_mws: 0.095284
+ estimated_mw: 0.046894
+ idle_transitions_mws: 0.039497
+ thread_name: "rcuop/2"
+ process_name: "rcuop/2"
+ thread_id: 41
+ process_id: 41
+ }
+ task_info {
+ estimated_mws: 0.084534
+ estimated_mw: 0.041604
+ idle_transitions_mws: 0.025241
+ thread_name: "RegSampIdle"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 826
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.076505
+ estimated_mw: 0.037652
+ idle_transitions_mws: 0.075963
+ thread_name: "rcuop/1"
+ process_name: "rcuop/1"
+ thread_id: 32
+ process_id: 32
+ }
+ task_info {
+ estimated_mws: 0.067736
+ estimated_mw: 0.033337
+ thread_name: "sh"
+ process_name: "/system/bin/sh"
+ thread_id: 11472
+ process_id: 11472
+ }
+ task_info {
+ estimated_mws: 0.065940
+ estimated_mw: 0.032453
+ idle_transitions_mws: 0.002859
+ thread_name: "BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3524
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.053141
+ estimated_mw: 0.026154
+ idle_transitions_mws: 0.000744
+ thread_name: "StateService"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 3621
+ process_id: 3505
+ }
+ task_info {
+ estimated_mws: 0.052200
+ estimated_mw: 0.025691
+ idle_transitions_mws: 0.000544
+ thread_name: "Blocking Thread"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 11310
+ process_id: 11279
+ }
+ task_info {
+ estimated_mws: 0.040767
+ estimated_mw: 0.020064
+ idle_transitions_mws: 0.003269
+ thread_name: "kworker/0:1"
+ process_name: "kworker/0:1"
+ thread_id: 11436
+ process_id: 11436
+ }
+ task_info {
+ estimated_mws: 0.040587
+ estimated_mw: 0.019975
+ thread_name: "binder:1629_7"
+ process_name: "system_server"
+ thread_id: 2635
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.040484
+ estimated_mw: 0.019924
+ idle_transitions_mws: 0.020651
+ thread_name: "rcuop/3"
+ process_name: "rcuop/3"
+ thread_id: 49
+ process_id: 49
+ }
+ task_info {
+ estimated_mws: 0.038016
+ estimated_mw: 0.018710
+ idle_transitions_mws: 0.002906
+ thread_name: "atchdog.monitor"
+ process_name: "system_server"
+ thread_id: 1669
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.036888
+ estimated_mw: 0.018155
+ thread_name: "logd.writer"
+ process_name: "/system/bin/logd"
+ thread_id: 228
+ process_id: 213
+ }
+ task_info {
+ estimated_mws: 0.032972
+ estimated_mw: 0.016227
+ idle_transitions_mws: 0.025051
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 828
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.032239
+ estimated_mw: 0.015867
+ idle_transitions_mws: 0.001840
+ thread_name: "it.FitbitMobile"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 11279
+ process_id: 11279
+ }
+ task_info {
+ estimated_mws: 0.031160
+ estimated_mw: 0.015336
+ idle_transitions_mws: 0.000559
+ thread_name: "binder:11279_4"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 11426
+ process_id: 11279
+ }
+ task_info {
+ estimated_mws: 0.028389
+ estimated_mw: 0.013972
+ thread_name: "irq/207-dwc3"
+ process_name: "irq/207-dwc3"
+ thread_id: 9733
+ process_id: 9733
+ }
+ task_info {
+ estimated_mws: 0.027208
+ estimated_mw: 0.013391
+ idle_transitions_mws: 0.000951
+ thread_name: "UsbFfs-worker"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 9734
+ process_id: 5154
+ }
+ task_info {
+ estimated_mws: 0.024832
+ estimated_mw: 0.012221
+ thread_name: "logcat"
+ process_name: "logcat"
+ thread_id: 1199
+ process_id: 1199
+ }
+ task_info {
+ estimated_mws: 0.023707
+ estimated_mw: 0.011668
+ idle_transitions_mws: 0.000854
+ thread_name: "logd.reader.per"
+ process_name: "/system/bin/logd"
+ thread_id: 1227
+ process_id: 213
+ }
+ task_info {
+ estimated_mws: 0.022160
+ estimated_mw: 0.010906
+ idle_transitions_mws: 0.006160
+ thread_name: "kworker/u8:2"
+ process_name: "kworker/u8:2"
+ thread_id: 11458
+ process_id: 11458
+ }
+ task_info {
+ estimated_mws: 0.019052
+ estimated_mw: 0.009376
+ idle_transitions_mws: 0.008121
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3575
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.018414
+ estimated_mw: 0.009063
+ idle_transitions_mws: 0.040061
+ thread_name: "RegionSampling"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 825
+ process_id: 742
+ }
+ task_info {
+ estimated_mws: 0.016701
+ estimated_mw: 0.008220
+ idle_transitions_mws: 0.006448
+ thread_name: "halt_drain_rqs"
+ process_name: "halt_drain_rqs"
+ thread_id: 108
+ process_id: 108
+ }
+ task_info {
+ estimated_mws: 0.011023
+ estimated_mw: 0.005425
+ idle_transitions_mws: 0.001553
+ thread_name: "irq/26-4744000."
+ process_name: "irq/26-4744000."
+ thread_id: 112
+ process_id: 112
+ }
+ task_info {
+ estimated_mws: 0.010004
+ estimated_mw: 0.004924
+ thread_name: "migration/2"
+ process_name: "migration/2"
+ thread_id: 35
+ process_id: 35
+ }
+ task_info {
+ estimated_mws: 0.008819
+ estimated_mw: 0.004341
+ thread_name: "ksoftirqd/0"
+ process_name: "ksoftirqd/0"
+ thread_id: 13
+ process_id: 13
+ }
+ task_info {
+ estimated_mws: 0.007911
+ estimated_mw: 0.003894
+ idle_transitions_mws: 0.001205
+ thread_name: "watchdog"
+ process_name: "system_server"
+ thread_id: 1676
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.007796
+ estimated_mw: 0.003837
+ idle_transitions_mws: 0.000627
+ thread_name: "pool-283-thread"
+ process_name: "system_server"
+ thread_id: 4427
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.007628
+ estimated_mw: 0.003754
+ idle_transitions_mws: 0.001280
+ thread_name: "adbd"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5154
+ process_id: 5154
+ }
+ task_info {
+ estimated_mws: 0.006796
+ estimated_mw: 0.003344
+ idle_transitions_mws: 0.001484
+ thread_name: "pool-1-thread-1"
+ process_name: "system_server"
+ thread_id: 2655
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.005691
+ estimated_mw: 0.002801
+ idle_transitions_mws: 0.001832
+ thread_name: "pool-1-thread-1"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 3625
+ process_id: 3505
+ }
+ task_info {
+ estimated_mws: 0.005476
+ estimated_mw: 0.002695
+ thread_name: "binder:237_2"
+ process_name: "/system/bin/vold"
+ thread_id: 237
+ process_id: 237
+ }
+ task_info {
+ estimated_mws: 0.004537
+ estimated_mw: 0.002233
+ thread_name: "shell svc 11472"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 11473
+ process_id: 5154
+ }
+ task_info {
+ estimated_mws: 0.003924
+ estimated_mw: 0.001931
+ thread_name: "ksoftirqd/1"
+ process_name: "ksoftirqd/1"
+ thread_id: 29
+ process_id: 29
+ }
+ task_info {
+ estimated_mws: 0.002908
+ estimated_mw: 0.001431
+ thread_name: "BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 5230
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.002492
+ estimated_mw: 0.001226
+ idle_transitions_mws: 0.028896
+ thread_name: "kworker/3:2"
+ process_name: "kworker/3:2"
+ thread_id: 9832
+ process_id: 9832
+ }
+ task_info {
+ estimated_mws: 0.002333
+ estimated_mw: 0.001148
+ idle_transitions_mws: 0.000780
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3577
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.002293
+ estimated_mw: 0.001128
+ idle_transitions_mws: 0.000781
+ thread_name: "InputReader"
+ process_name: "system_server"
+ thread_id: 2560
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.002261
+ estimated_mw: 0.001113
+ thread_name: "DefaultDispatch"
+ process_name: "com.google.android.wearable.media.sessions"
+ thread_id: 3618
+ process_id: 3553
+ }
+ task_info {
+ estimated_mws: 0.002226
+ estimated_mw: 0.001095
+ thread_name: "migration/3"
+ process_name: "migration/3"
+ thread_id: 44
+ process_id: 44
+ }
+ task_info {
+ estimated_mws: 0.002100
+ estimated_mw: 0.001034
+ idle_transitions_mws: 0.000657
+ thread_name: "InputDispatcher"
+ process_name: "system_server"
+ thread_id: 2559
+ process_id: 1629
+ }
+ task_info {
+ estimated_mws: 0.001973
+ estimated_mw: 0.000971
+ idle_transitions_mws: 0.000764
+ thread_name: "kworker/1:0"
+ process_name: "kworker/1:0"
+ thread_id: 10984
+ process_id: 10984
+ }
+ task_info {
+ estimated_mws: 0.001966
+ estimated_mw: 0.000967
+ thread_name: "irq/193-wdog-ba"
+ process_name: "irq/193-wdog-ba"
+ thread_id: 344
+ process_id: 344
+ }
+ task_info {
+ estimated_mws: 0.001867
+ estimated_mw: 0.000919
+ thread_name: "DefaultDispatch"
+ process_name: "com.google.android.wearable.media.sessions"
+ thread_id: 3615
+ process_id: 3553
+ }
+ task_info {
+ estimated_mws: 0.001867
+ estimated_mw: 0.000919
+ idle_transitions_mws: 0.000705
+ thread_name: "irq/25-mmc0"
+ process_name: "irq/25-mmc0"
+ thread_id: 115
+ process_id: 115
+ }
+ task_info {
+ estimated_mws: 0.001728
+ estimated_mw: 0.000851
+ thread_name: "iou-wrk-214"
+ process_name: "/system/bin/lmkd"
+ thread_id: 11440
+ process_id: 214
+ }
+ task_info {
+ estimated_mws: 0.001600
+ estimated_mw: 0.000787
+ idle_transitions_mws: 0.001911
+ thread_name: "DefaultDispatch"
+ process_name: "com.google.android.wearable.media.sessions"
+ thread_id: 3616
+ process_id: 3553
+ }
+ task_info {
+ estimated_mws: 0.001393
+ estimated_mw: 0.000686
+ thread_name: "kworker/u8:1"
+ process_name: "kworker/u8:1"
+ thread_id: 11185
+ process_id: 11185
+ }
+ task_info {
+ estimated_mws: 0.001373
+ estimated_mw: 0.000676
+ idle_transitions_mws: 0.007386
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3576
+ process_id: 2710
+ }
+ task_info {
+ estimated_mws: 0.000811
+ estimated_mw: 0.000399
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3622
+ process_id: 2710
+ }
}
}
diff --git a/test/trace_processor/diff_tests/metrics/android/wattson_trace_threads.out b/test/trace_processor/diff_tests/metrics/android/wattson_trace_threads.out
index 735e78b..bcc7daf 100644
--- a/test/trace_processor/diff_tests/metrics/android/wattson_trace_threads.out
+++ b/test/trace_processor/diff_tests/metrics/android/wattson_trace_threads.out
@@ -1,4047 +1,4050 @@
wattson_trace_threads {
- metric_version: 3
+ metric_version: 4
power_model_version: 1
- task_info {
- estimated_mws: 34.416729
- estimated_mw: 3.979098
- thread_name: "swapper"
- thread_id: 0
- process_id: 0
- }
- task_info {
- estimated_mws: 19.853703
- estimated_mw: 2.295390
- idle_transitions_mws: 0.220895
- thread_name: "RenderThread"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1986
- process_id: 1926
- }
- task_info {
- estimated_mws: 17.530441
- estimated_mw: 2.026786
- idle_transitions_mws: 0.028812
- thread_name: "Jit thread pool"
- process_name: "system_server"
- thread_id: 1344
- process_id: 1302
- }
- task_info {
- estimated_mws: 16.980274
- estimated_mw: 1.963178
- idle_transitions_mws: 0.387957
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 755
- process_id: 755
- }
- task_info {
- estimated_mws: 14.908094
- estimated_mw: 1.723603
- idle_transitions_mws: 0.455047
- thread_name: ".wearable.sysui"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1926
- process_id: 1926
- }
- task_info {
- estimated_mws: 13.373355
- estimated_mw: 1.546164
- idle_transitions_mws: 0.011711
- thread_name: "binder:685_3"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
- thread_id: 804
- process_id: 685
- }
- task_info {
- estimated_mws: 6.747261
- estimated_mw: 0.780086
- idle_transitions_mws: 0.021185
- thread_name: "binder:1302_7"
- process_name: "system_server"
- thread_id: 1671
- process_id: 1302
- }
- task_info {
- estimated_mws: 6.504173
- estimated_mw: 0.751981
- idle_transitions_mws: 0.055166
- thread_name: "binder:1302_A"
- process_name: "system_server"
- thread_id: 2015
- process_id: 1302
- }
- task_info {
- estimated_mws: 4.858775
- estimated_mw: 0.561748
- idle_transitions_mws: 0.082958
- thread_name: "android.anim"
- process_name: "system_server"
- thread_id: 1419
- process_id: 1302
- }
- task_info {
- estimated_mws: 4.769800
- estimated_mw: 0.551462
- idle_transitions_mws: 0.094492
- thread_name: "RenderEngine"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 788
- process_id: 755
- }
- task_info {
- estimated_mws: 4.672233
- estimated_mw: 0.540181
- idle_transitions_mws: 0.012303
- thread_name: "kswapd0"
- process_name: "kswapd0"
- thread_id: 63
- process_id: 63
- }
- task_info {
- estimated_mws: 4.314495
- estimated_mw: 0.498821
- thread_name: "lowpool[2]"
- process_name: "com.google.android.gms"
- thread_id: 3525
- process_id: 2856
- }
- task_info {
- estimated_mws: 4.117818
- estimated_mw: 0.476083
- thread_name: "logd.writer"
- process_name: "/system/bin/logd"
- thread_id: 221
- process_id: 211
- }
- task_info {
- estimated_mws: 4.108276
- estimated_mw: 0.474979
- idle_transitions_mws: 0.001470
- thread_name: "binder:1302_17"
- process_name: "system_server"
- thread_id: 5202
- process_id: 1302
- }
- task_info {
- estimated_mws: 3.723955
- estimated_mw: 0.430546
- idle_transitions_mws: 0.046603
- thread_name: "binder:1302_6"
- process_name: "system_server"
- thread_id: 1662
- process_id: 1302
- }
- task_info {
- estimated_mws: 3.666289
- estimated_mw: 0.423879
- idle_transitions_mws: 0.147155
- thread_name: "e.watchface.rwf"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 1999
- process_id: 1999
- }
- task_info {
- estimated_mws: 3.524869
- estimated_mw: 0.407529
- idle_transitions_mws: 0.003585
- thread_name: "killall"
- process_name: "/system/bin/sh"
- thread_id: 5620
- process_id: 5620
- }
- task_info {
- estimated_mws: 3.495762
- estimated_mw: 0.404163
- idle_transitions_mws: 0.012035
- thread_name: "CachedAppOptimi"
- process_name: "system_server"
- thread_id: 1773
- process_id: 1302
- }
- task_info {
- estimated_mws: 3.459922
- estimated_mw: 0.400020
- idle_transitions_mws: 0.022780
- thread_name: "logcat"
- process_name: "logcat"
- thread_id: 1230
- process_id: 1230
- }
- task_info {
- estimated_mws: 3.429554
- estimated_mw: 0.396509
- idle_transitions_mws: 0.020454
- thread_name: "system_server"
- process_name: "system_server"
- thread_id: 1302
- process_id: 1302
- }
- task_info {
- estimated_mws: 3.300661
- estimated_mw: 0.381607
- idle_transitions_mws: 1.010862
- thread_name: "crtc_commit:80"
- process_name: "crtc_commit:80"
- thread_id: 244
- process_id: 244
- }
- task_info {
- estimated_mws: 3.194881
- estimated_mw: 0.369377
- idle_transitions_mws: 0.163208
- thread_name: "InputDispatcher"
- process_name: "system_server"
- thread_id: 1783
- process_id: 1302
- }
- task_info {
- estimated_mws: 3.011913
- estimated_mw: 0.348223
- idle_transitions_mws: 0.261953
- thread_name: "binder:755_1"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 782
- process_id: 755
- }
- task_info {
- estimated_mws: 3.006022
- estimated_mw: 0.347542
- idle_transitions_mws: 0.064213
- thread_name: "android.display"
- process_name: "system_server"
- thread_id: 1418
- process_id: 1302
- }
- task_info {
- estimated_mws: 2.856301
- estimated_mw: 0.330232
- idle_transitions_mws: 0.000982
- thread_name: "binder:524_2"
- process_name: "/vendor/bin/mcu_mgmtd"
- thread_id: 524
- process_id: 524
- }
- task_info {
- estimated_mws: 2.712443
- estimated_mw: 0.313600
- idle_transitions_mws: 0.314323
- thread_name: "traced_probes"
- process_name: "/system/bin/traced_probes"
- thread_id: 904
- process_id: 904
- }
- task_info {
- estimated_mws: 2.553161
- estimated_mw: 0.295184
- idle_transitions_mws: 0.751582
- thread_name: "kworker/u8:0"
- process_name: "kworker/u8:0"
- thread_id: 8
- process_id: 8
- }
- task_info {
- estimated_mws: 2.487099
- estimated_mw: 0.287547
- idle_transitions_mws: 0.509790
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 883
- process_id: 755
- }
- task_info {
- estimated_mws: 2.386123
- estimated_mw: 0.275872
- idle_transitions_mws: 0.002251
- thread_name: "binder:1302_15"
- process_name: "system_server"
- thread_id: 3754
- process_id: 1302
- }
- task_info {
- estimated_mws: 2.258779
- estimated_mw: 0.261149
- idle_transitions_mws: 0.219690
- thread_name: "logd.reader.per"
- process_name: "/system/bin/logd"
- thread_id: 1274
- process_id: 211
- }
- task_info {
- estimated_mws: 2.171289
- estimated_mw: 0.251034
- idle_transitions_mws: 0.014154
- thread_name: "RenderThread"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 2301
- process_id: 1999
- }
- task_info {
- estimated_mws: 2.143151
- estimated_mw: 0.247781
- idle_transitions_mws: 0.052887
- thread_name: "InputReader"
- process_name: "system_server"
- thread_id: 1784
- process_id: 1302
- }
- task_info {
- estimated_mws: 2.091430
- estimated_mw: 0.241801
- idle_transitions_mws: 2.681956
- thread_name: "rcu_preempt"
- process_name: "rcu_preempt"
- thread_id: 14
- process_id: 14
- }
- task_info {
- estimated_mws: 2.048920
- estimated_mw: 0.236886
- idle_transitions_mws: 0.122795
- thread_name: "binder:1926_4"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2262
- process_id: 1926
- }
- task_info {
- estimated_mws: 1.914560
- estimated_mw: 0.221352
- idle_transitions_mws: 0.033359
- thread_name: "arable.systemui"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2171
- process_id: 2171
- }
- task_info {
- estimated_mws: 1.854433
- estimated_mw: 0.214401
- idle_transitions_mws: 0.046845
- thread_name: "android.ui"
- process_name: "system_server"
- thread_id: 1416
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.777087
- estimated_mw: 0.205458
- idle_transitions_mws: 0.428240
- thread_name: "kworker/u8:4"
- process_name: "kworker/u8:4"
- thread_id: 431
- process_id: 431
- }
- task_info {
- estimated_mws: 1.773777
- estimated_mw: 0.205076
- idle_transitions_mws: 0.584214
- thread_name: "TimerDispatch"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 865
- process_id: 755
- }
- task_info {
- estimated_mws: 1.760400
- estimated_mw: 0.203529
- idle_transitions_mws: 0.078772
- thread_name: "ActivityManager"
- process_name: "system_server"
- thread_id: 1431
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.733169
- estimated_mw: 0.200381
- idle_transitions_mws: 0.039163
- thread_name: "PowerManagerSer"
- process_name: "system_server"
- thread_id: 1506
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.639501
- estimated_mw: 0.189551
- thread_name: "WifiHandlerThre"
- process_name: "system_server"
- thread_id: 1818
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.631037
- estimated_mw: 0.188573
- idle_transitions_mws: 0.170842
- thread_name: "binder:755_5"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 1987
- process_id: 755
- }
- task_info {
- estimated_mws: 1.605931
- estimated_mw: 0.185670
- idle_transitions_mws: 0.400486
- thread_name: "kgsl_dispatcher"
- process_name: "kgsl_dispatcher"
- thread_id: 111
- process_id: 111
- }
- task_info {
- estimated_mws: 1.564964
- estimated_mw: 0.180934
- idle_transitions_mws: 0.000901
- thread_name: "binder:1302_8"
- process_name: "system_server"
- thread_id: 1679
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.476619
- estimated_mw: 0.170720
- idle_transitions_mws: 0.013637
- thread_name: "lowpool[5]"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3489
- process_id: 1949
- }
- task_info {
- estimated_mws: 1.470155
- estimated_mw: 0.169972
- thread_name: "-Executor] idle"
- process_name: "com.google.android.gms"
- thread_id: 5591
- process_id: 2856
- }
- task_info {
- estimated_mws: 1.469958
- estimated_mw: 0.169950
- idle_transitions_mws: 0.002534
- thread_name: "binder:1302_B"
- process_name: "system_server"
- thread_id: 2033
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.390635
- estimated_mw: 0.160779
- idle_transitions_mws: 0.083044
- thread_name: "binder:755_4"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 1125
- process_id: 755
- }
- task_info {
- estimated_mws: 1.327049
- estimated_mw: 0.153427
- idle_transitions_mws: 0.029246
- thread_name: "batterystats-ha"
- process_name: "system_server"
- thread_id: 1484
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.312721
- estimated_mw: 0.151771
- thread_name: "statsd.writer"
- process_name: "/apex/com.android.os.statsd/bin/statsd"
- thread_id: 980
- process_id: 545
- }
- task_info {
- estimated_mws: 1.252738
- estimated_mw: 0.144836
- idle_transitions_mws: 0.705073
- thread_name: "kworker/u8:2"
- process_name: "kworker/u8:2"
- thread_id: 62
- process_id: 62
- }
- task_info {
- estimated_mws: 1.251944
- estimated_mw: 0.144744
- idle_transitions_mws: 0.206733
- thread_name: "app"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 867
- process_id: 755
- }
- task_info {
- estimated_mws: 1.233674
- estimated_mw: 0.142632
- idle_transitions_mws: 0.066972
- thread_name: "system_server"
- process_name: "system_server"
- thread_id: 1343
- process_id: 1302
- }
- task_info {
- estimated_mws: 1.228813
- estimated_mw: 0.142070
- idle_transitions_mws: 0.785543
- thread_name: "irq/33-4520300."
- process_name: "irq/33-4520300.qcom,bwmon-ddr"
- thread_id: 95
- process_id: 95
- }
- task_info {
- estimated_mws: 1.068197
- estimated_mw: 0.123500
- idle_transitions_mws: 0.111670
- thread_name: "logd.klogd"
- process_name: "/system/bin/logd"
- thread_id: 234
- process_id: 211
- }
- task_info {
- estimated_mws: 1.007070
- estimated_mw: 0.116433
- idle_transitions_mws: 0.029416
- thread_name: "android.fg"
- process_name: "system_server"
- thread_id: 1415
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.969980
- estimated_mw: 0.112144
- idle_transitions_mws: 0.455999
- thread_name: "rcuog/0"
- process_name: "rcuog/0"
- thread_id: 15
- process_id: 15
- }
- task_info {
- estimated_mws: 0.952077
- estimated_mw: 0.110075
- idle_transitions_mws: 0.102993
- thread_name: "binder:1926_3"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1940
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.946746
- estimated_mw: 0.109458
- idle_transitions_mws: 0.007196
- thread_name: "gle.android.gms"
- process_name: "com.google.android.gms"
- thread_id: 2856
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.930774
- estimated_mw: 0.107612
- idle_transitions_mws: 0.738786
- thread_name: "crtc_event:80"
- process_name: "crtc_event:80"
- thread_id: 245
- process_id: 245
- }
- task_info {
- estimated_mws: 0.907425
- estimated_mw: 0.104912
- idle_transitions_mws: 0.009862
- thread_name: "binder:755_3"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 1124
- process_id: 755
- }
- task_info {
- estimated_mws: 0.897620
- estimated_mw: 0.103779
- idle_transitions_mws: 0.007379
- thread_name: "init"
- process_name: "/system/bin/init"
- thread_id: 143
- process_id: 1
- }
- task_info {
- estimated_mws: 0.880853
- estimated_mw: 0.101840
- idle_transitions_mws: 0.013499
- thread_name: "wmshell.main"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2260
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.870598
- estimated_mw: 0.100654
- idle_transitions_mws: 0.005937
- thread_name: "Primes-1"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1944
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.847641
- estimated_mw: 0.098000
- idle_transitions_mws: 0.013271
- thread_name: "init"
- process_name: "/system/bin/init"
- thread_id: 1
- process_id: 1
- }
- task_info {
- estimated_mws: 0.846054
- estimated_mw: 0.097817
- thread_name: "binder:1302_D"
- process_name: "system_server"
- thread_id: 2043
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.844958
- estimated_mw: 0.097690
- idle_transitions_mws: 0.202229
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 786
- process_id: 755
- }
- task_info {
- estimated_mws: 0.833920
- estimated_mw: 0.096414
- idle_transitions_mws: 0.342121
- thread_name: "kworker/u8:5"
- process_name: "kworker/u8:5"
- thread_id: 5304
- process_id: 5304
- }
- task_info {
- estimated_mws: 0.780835
- estimated_mw: 0.090276
- idle_transitions_mws: 0.182034
- thread_name: "kworker/2:4"
- process_name: "kworker/2:4"
- thread_id: 4995
- process_id: 4995
- }
- task_info {
- estimated_mws: 0.747755
- estimated_mw: 0.086452
- idle_transitions_mws: 0.004701
- thread_name: "binder:2171_4"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2374
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.746488
- estimated_mw: 0.086305
- idle_transitions_mws: 0.043573
- thread_name: "binder:1999_5"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 3678
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.744159
- estimated_mw: 0.086036
- idle_transitions_mws: 0.120816
- thread_name: "servicemanager"
- process_name: "/system/bin/servicemanager"
- thread_id: 213
- process_id: 213
- }
- task_info {
- estimated_mws: 0.717520
- estimated_mw: 0.082956
- idle_transitions_mws: 0.004484
- thread_name: "wmshell.anim"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2269
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.699681
- estimated_mw: 0.080894
- thread_name: "GoogleApiHandle"
- process_name: "com.google.android.gms"
- thread_id: 3208
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.675997
- estimated_mw: 0.078156
- idle_transitions_mws: 0.003826
- thread_name: "binder:1302_4"
- process_name: "system_server"
- thread_id: 1592
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.647999
- estimated_mw: 0.074919
- thread_name: "batterystats-wo"
- process_name: "system_server"
- thread_id: 1487
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.640576
- estimated_mw: 0.074060
- idle_transitions_mws: 0.005443
- thread_name: ".gms.persistent"
- process_name: "com.google.android.gms.persistent"
- thread_id: 1949
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.631830
- estimated_mw: 0.073049
- idle_transitions_mws: 0.013282
- thread_name: "binder:1926_6"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 5211
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.627672
- estimated_mw: 0.072568
- idle_transitions_mws: 0.000795
- thread_name: "DisplayOffloadB"
- process_name: "system_server"
- thread_id: 1512
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.627487
- estimated_mw: 0.072547
- thread_name: "binder:682_2"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
- thread_id: 682
- process_id: 682
- }
- task_info {
- estimated_mws: 0.624294
- estimated_mw: 0.072178
- idle_transitions_mws: 0.431093
- thread_name: "rcuog/2"
- process_name: "rcuog/2"
- thread_id: 37
- process_id: 37
- }
- task_info {
- estimated_mws: 0.623909
- estimated_mw: 0.072133
- idle_transitions_mws: 0.274820
- thread_name: "kworker/0:6"
- process_name: "kworker/0:6"
- thread_id: 586
- process_id: 586
- }
- task_info {
- estimated_mws: 0.597177
- estimated_mw: 0.069043
- thread_name: "diag-router"
- process_name: "/vendor/bin/diag-router"
- thread_id: 634
- process_id: 634
- }
- task_info {
- estimated_mws: 0.582498
- estimated_mw: 0.067346
- thread_name: "HeapTaskDaemon"
- process_name: "com.google.android.gms"
- thread_id: 2882
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.579675
- estimated_mw: 0.067019
- idle_transitions_mws: 0.102756
- thread_name: "FileWatcherThre"
- process_name: "/vendor/bin/hw/android.hardware.thermal-service.pixel"
- thread_id: 1411
- process_id: 1404
- }
- task_info {
- estimated_mws: 0.568415
- estimated_mw: 0.065717
- idle_transitions_mws: 0.004498
- thread_name: "TaskSnapshotPer"
- process_name: "system_server"
- thread_id: 1913
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.565566
- estimated_mw: 0.065388
- idle_transitions_mws: 0.016823
- thread_name: "lmkd"
- process_name: "/system/bin/lmkd"
- thread_id: 212
- process_id: 212
- }
- task_info {
- estimated_mws: 0.554734
- estimated_mw: 0.064136
- idle_transitions_mws: 0.001437
- thread_name: "binder:1949_8"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3269
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.517529
- estimated_mw: 0.059834
- idle_transitions_mws: 0.084239
- thread_name: "appSf"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 868
- process_id: 755
- }
- task_info {
- estimated_mws: 0.514221
- estimated_mw: 0.059452
- idle_transitions_mws: 0.542479
- thread_name: "kworker/1:1"
- process_name: "kworker/1:1"
- thread_id: 47
- process_id: 47
- }
- task_info {
- estimated_mws: 0.507581
- estimated_mw: 0.058684
- idle_transitions_mws: 0.039847
- thread_name: "android.hardwar"
- process_name: "/vendor/bin/hw/android.hardware.usb-service.qti"
- thread_id: 1861
- process_id: 665
- }
- task_info {
- estimated_mws: 0.504068
- estimated_mw: 0.058278
- idle_transitions_mws: 0.058645
- thread_name: "Primes-Jank"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2389
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.493578
- estimated_mw: 0.057065
- idle_transitions_mws: 0.006902
- thread_name: "binder:2171_3"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2235
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.490345
- estimated_mw: 0.056691
- idle_transitions_mws: 0.005860
- thread_name: "traced"
- process_name: "/system/bin/traced"
- thread_id: 905
- process_id: 905
- }
- task_info {
- estimated_mws: 0.468415
- estimated_mw: 0.054156
- thread_name: "eduling.default"
- process_name: "system_server"
- thread_id: 1761
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.462913
- estimated_mw: 0.053520
- idle_transitions_mws: 0.021445
- thread_name: "binder:545_2"
- process_name: "/apex/com.android.os.statsd/bin/statsd"
- thread_id: 553
- process_id: 545
- }
- task_info {
- estimated_mws: 0.462537
- estimated_mw: 0.053476
- thread_name: "User"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2234
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.454063
- estimated_mw: 0.052497
- idle_transitions_mws: 0.032182
- thread_name: "putmethod.latin"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 4997
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.450612
- estimated_mw: 0.052098
- thread_name: "ueventd"
- process_name: "/system/bin/ueventd"
- thread_id: 145
- process_id: 145
- }
- task_info {
- estimated_mws: 0.448044
- estimated_mw: 0.051801
- thread_name: "wpa_supplicant"
- process_name: "/vendor/bin/hw/wpa_supplicant"
- thread_id: 5214
- process_id: 5214
- }
- task_info {
- estimated_mws: 0.431304
- estimated_mw: 0.049865
- idle_transitions_mws: 0.223655
- thread_name: "rcuop/0"
- process_name: "rcuop/0"
- thread_id: 16
- process_id: 16
- }
- task_info {
- estimated_mws: 0.416635
- estimated_mw: 0.048169
- idle_transitions_mws: 0.003314
- thread_name: "Jit thread pool"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1933
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.404592
- estimated_mw: 0.046777
- thread_name: "pixelstats-vend"
- process_name: "/vendor/bin/pixelstats-vendor"
- thread_id: 267
- process_id: 255
- }
- task_info {
- estimated_mws: 0.396838
- estimated_mw: 0.045880
- idle_transitions_mws: 0.096821
- thread_name: "irq/236-NVT-ts"
- process_name: "irq/236-NVT-ts"
- thread_id: 505
- process_id: 505
- }
- task_info {
- estimated_mws: 0.393249
- estimated_mw: 0.045466
- idle_transitions_mws: 0.004124
- thread_name: "nanohub"
- process_name: "nanohub"
- thread_id: 297
- process_id: 297
- }
- task_info {
- estimated_mws: 0.376686
- estimated_mw: 0.043551
- idle_transitions_mws: 0.007074
- thread_name: "android.bg"
- process_name: "system_server"
- thread_id: 1430
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.375869
- estimated_mw: 0.043456
- idle_transitions_mws: 0.002342
- thread_name: "chre"
- process_name: "/vendor/bin/chre"
- thread_id: 1041
- process_id: 1041
- }
- task_info {
- estimated_mws: 0.373520
- estimated_mw: 0.043185
- idle_transitions_mws: 0.003289
- thread_name: "lowpool[1]"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2279
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.366001
- estimated_mw: 0.042315
- idle_transitions_mws: 0.018638
- thread_name: "TracingMuxer"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 783
- process_id: 755
- }
- task_info {
- estimated_mws: 0.359494
- estimated_mw: 0.041563
- idle_transitions_mws: 0.136611
- thread_name: "kgsl-events"
- process_name: "kgsl-events"
- thread_id: 109
- process_id: 109
- }
- task_info {
- estimated_mws: 0.359130
- estimated_mw: 0.041521
- thread_name: "IpClient.wlan0"
- process_name: "com.android.networkstack.process"
- thread_id: 5216
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.346517
- estimated_mw: 0.040063
- idle_transitions_mws: 0.001877
- thread_name: "binder:257_5"
- process_name: "/system/bin/hw/android.system.suspend-service"
- thread_id: 1491
- process_id: 257
- }
- task_info {
- estimated_mws: 0.341200
- estimated_mw: 0.039448
- idle_transitions_mws: 0.001228
- thread_name: "binder:1901_3"
- process_name: "/vendor/bin/hw/android.hardware.wifi-service-lazy"
- thread_id: 1905
- process_id: 1901
- }
- task_info {
- estimated_mws: 0.335534
- estimated_mw: 0.038793
- thread_name: "binder:740_1"
- process_name: "/system/bin/audioserver"
- thread_id: 821
- process_id: 740
- }
- task_info {
- estimated_mws: 0.331405
- estimated_mw: 0.038316
- idle_transitions_mws: 0.001044
- thread_name: "BG"
- process_name: "com.google.wear.services"
- thread_id: 2023
- process_id: 1948
- }
- task_info {
- estimated_mws: 0.326344
- estimated_mw: 0.037730
- idle_transitions_mws: 0.045342
- thread_name: "kworker/0:5H"
- process_name: "kworker/0:5H"
- thread_id: 1337
- process_id: 1337
- }
- task_info {
- estimated_mws: 0.322384
- estimated_mw: 0.037273
- idle_transitions_mws: 0.002751
- thread_name: "binder:755_2"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 784
- process_id: 755
- }
- task_info {
- estimated_mws: 0.319511
- estimated_mw: 0.036940
- thread_name: "audioserver"
- process_name: "/system/bin/audioserver"
- thread_id: 740
- process_id: 740
- }
- task_info {
- estimated_mws: 0.310996
- estimated_mw: 0.035956
- idle_transitions_mws: 0.007033
- thread_name: "binder:1949_2"
- process_name: "com.google.android.gms.persistent"
- thread_id: 1978
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.302115
- estimated_mw: 0.034929
- idle_transitions_mws: 0.000843
- thread_name: "-Executor] idle"
- process_name: "com.google.android.gms.persistent"
- thread_id: 5602
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.301578
- estimated_mw: 0.034867
- thread_name: "pool-11-thread-"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3329
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.299169
- estimated_mw: 0.034589
- thread_name: "android.io"
- process_name: "system_server"
- thread_id: 1417
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.296825
- estimated_mw: 0.034318
- thread_name: "binder:1901_3"
- process_name: "/vendor/bin/hw/android.hardware.wifi-service-lazy"
- thread_id: 5205
- process_id: 1901
- }
- task_info {
- estimated_mws: 0.294242
- estimated_mw: 0.034019
- idle_transitions_mws: 0.142357
- thread_name: "rcuop/1"
- process_name: "rcuop/1"
- thread_id: 30
- process_id: 30
- }
- task_info {
- estimated_mws: 0.286642
- estimated_mw: 0.033140
- thread_name: "binder:1948_6"
- process_name: "com.google.wear.services"
- thread_id: 5315
- process_id: 1948
- }
- task_info {
- estimated_mws: 0.285983
- estimated_mw: 0.033064
- thread_name: "AssistantHandle"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4081
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.283378
- estimated_mw: 0.032763
- idle_transitions_mws: 0.023300
- thread_name: "binder:1999_1"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 2016
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.279959
- estimated_mw: 0.032367
- idle_transitions_mws: 0.001981
- thread_name: "binder:2182_7"
- process_name: "com.android.phone"
- thread_id: 2694
- process_id: 2182
- }
- task_info {
- estimated_mws: 0.279816
- estimated_mw: 0.032351
- idle_transitions_mws: 0.055476
- thread_name: "kworker/3:2H"
- process_name: "kworker/3:2H"
- thread_id: 226
- process_id: 226
- }
- task_info {
- estimated_mws: 0.277230
- estimated_mw: 0.032052
- idle_transitions_mws: 0.002913
- thread_name: "BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 3005
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.274735
- estimated_mw: 0.031764
- thread_name: "lowpool[3]"
- process_name: "com.google.android.gms"
- thread_id: 3527
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.267749
- estimated_mw: 0.030956
- idle_transitions_mws: 0.038805
- thread_name: "hvdcp_opti"
- process_name: "/vendor/bin/hvdcp_opti"
- thread_id: 1276
- process_id: 1270
- }
- task_info {
- estimated_mws: 0.262081
- estimated_mw: 0.030301
- idle_transitions_mws: 0.190243
- thread_name: "binder:1926_3"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2022
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.259248
- estimated_mw: 0.029973
- idle_transitions_mws: 0.172554
- thread_name: "kworker/3:5"
- process_name: "kworker/3:5"
- thread_id: 104
- process_id: 104
- }
- task_info {
- estimated_mws: 0.256714
- estimated_mw: 0.029680
- idle_transitions_mws: 0.002836
- thread_name: "binder:257_2"
- process_name: "/system/bin/hw/android.system.suspend-service"
- thread_id: 264
- process_id: 257
- }
- task_info {
- estimated_mws: 0.247037
- estimated_mw: 0.028561
- idle_transitions_mws: 0.088455
- thread_name: "SDM_EventThread"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
- thread_id: 727
- process_id: 685
- }
- task_info {
- estimated_mws: 0.244112
- estimated_mw: 0.028223
- thread_name: "POSIX timer 2"
- process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
- thread_id: 1600
- process_id: 664
- }
- task_info {
- estimated_mws: 0.242754
- estimated_mw: 0.028066
- idle_transitions_mws: 0.005424
- thread_name: "binder:2856_4"
- process_name: "com.google.android.gms"
- thread_id: 3679
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.241348
- estimated_mw: 0.027904
- thread_name: "pool-2-thread-1"
- process_name: "com.android.networkstack.process"
- thread_id: 2416
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.231145
- estimated_mw: 0.026724
- idle_transitions_mws: 0.081316
- thread_name: "rcuop/3"
- process_name: "rcuop/3"
- thread_id: 45
- process_id: 45
- }
- task_info {
- estimated_mws: 0.230341
- estimated_mw: 0.026631
- idle_transitions_mws: 0.003605
- thread_name: "f2fs_ckpt-254:4"
- process_name: "f2fs_ckpt-254:43"
- thread_id: 347
- process_id: 347
- }
- task_info {
- estimated_mws: 0.229722
- estimated_mw: 0.026559
- thread_name: "OomAdjuster"
- process_name: "system_server"
- thread_id: 1482
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.226417
- estimated_mw: 0.026177
- idle_transitions_mws: 0.001358
- thread_name: "binder:740_6"
- process_name: "/system/bin/audioserver"
- thread_id: 2639
- process_id: 740
- }
- task_info {
- estimated_mws: 0.226007
- estimated_mw: 0.026130
- idle_transitions_mws: 0.054115
- thread_name: "rcuop/2"
- process_name: "rcuop/2"
- thread_id: 38
- process_id: 38
- }
- task_info {
- estimated_mws: 0.225133
- estimated_mw: 0.026029
- idle_transitions_mws: 0.065811
- thread_name: "kworker/0:7"
- process_name: "kworker/0:7"
- thread_id: 598
- process_id: 598
- }
- task_info {
- estimated_mws: 0.212788
- estimated_mw: 0.024602
- idle_transitions_mws: 0.006439
- thread_name: "queued-work-loo"
- process_name: "system_server"
- thread_id: 1886
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.202562
- estimated_mw: 0.023419
- thread_name: "pool-13-thread-"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3327
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.201687
- estimated_mw: 0.023318
- idle_transitions_mws: 0.001195
- thread_name: "WearSdkThread"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2207
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.201119
- estimated_mw: 0.023252
- thread_name: "qrtr_ns"
- process_name: "qrtr_ns"
- thread_id: 88
- process_id: 88
- }
- task_info {
- estimated_mws: 0.200639
- estimated_mw: 0.023197
- thread_name: "binder:740_7"
- process_name: "/system/bin/audioserver"
- thread_id: 5206
- process_id: 740
- }
- task_info {
- estimated_mws: 0.196587
- estimated_mw: 0.022729
- idle_transitions_mws: 0.002857
- thread_name: "binder:4997_4"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5122
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.194754
- estimated_mw: 0.022517
- thread_name: "m.android.phone"
- process_name: "com.android.phone"
- thread_id: 2182
- process_id: 2182
- }
- task_info {
- estimated_mws: 0.192336
- estimated_mw: 0.022237
- idle_transitions_mws: 0.006386
- thread_name: "HwcAsyncWorker"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 835
- process_id: 755
- }
- task_info {
- estimated_mws: 0.190522
- estimated_mw: 0.022027
- thread_name: "binder:636_2"
- process_name: "/vendor/bin/hw/android.hardware.audio.service"
- thread_id: 636
- process_id: 636
- }
- task_info {
- estimated_mws: 0.188908
- estimated_mw: 0.021841
- thread_name: "SettingsProvide"
- process_name: "system_server"
- thread_id: 1771
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.181172
- estimated_mw: 0.020946
- thread_name: "binder:1302_2"
- process_name: "system_server"
- thread_id: 1350
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.177579
- estimated_mw: 0.020531
- idle_transitions_mws: 0.111479
- thread_name: "RegSampIdle"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 872
- process_id: 755
- }
- task_info {
- estimated_mws: 0.168738
- estimated_mw: 0.019509
- thread_name: "ice] processing"
- process_name: "com.google.android.gms"
- thread_id: 3238
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.162808
- estimated_mw: 0.018823
- thread_name: "binder:682_3"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
- thread_id: 2308
- process_id: 682
- }
- task_info {
- estimated_mws: 0.158857
- estimated_mw: 0.018366
- thread_name: "binder:678_3"
- process_name: "/apex/com.google.wearable.wac.whshal/bin/hw/vendor.google.wearable.wac.whshal@2.0-service"
- thread_id: 1884
- process_id: 678
- }
- task_info {
- estimated_mws: 0.158692
- estimated_mw: 0.018347
- idle_transitions_mws: 0.016263
- thread_name: "binder:650_4"
- process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
- thread_id: 5498
- process_id: 650
- }
- task_info {
- estimated_mws: 0.157956
- estimated_mw: 0.018262
- idle_transitions_mws: 0.002306
- thread_name: "vndservicemanag"
- process_name: "/vendor/bin/vndservicemanager"
- thread_id: 215
- process_id: 215
- }
- task_info {
- estimated_mws: 0.153600
- estimated_mw: 0.017759
- thread_name: "GoogleLocationS"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3355
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.153038
- estimated_mw: 0.017694
- thread_name: "TransportThread"
- process_name: "/vendor/bin/chre"
- thread_id: 1078
- process_id: 1041
- }
- task_info {
- estimated_mws: 0.152058
- estimated_mw: 0.017580
- idle_transitions_mws: 0.068137
- thread_name: "kworker/2:1H"
- process_name: "kworker/2:1H"
- thread_id: 123
- process_id: 123
- }
- task_info {
- estimated_mws: 0.148559
- estimated_mw: 0.017176
- idle_transitions_mws: 0.002115
- thread_name: "BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2120
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.148352
- estimated_mw: 0.017152
- thread_name: "vendor.google.w"
- process_name: "/apex/com.google.wearable.wac.whshal/bin/hw/vendor.google.wearable.wac.whshal@2.0-service"
- thread_id: 1881
- process_id: 678
- }
- task_info {
- estimated_mws: 0.140864
- estimated_mw: 0.016286
- thread_name: "NetworkStats"
- process_name: "system_server"
- thread_id: 1814
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.138579
- estimated_mw: 0.016022
- idle_transitions_mws: 0.021968
- thread_name: "binder:969_2"
- process_name: "/system/vendor/bin/cnd"
- thread_id: 1011
- process_id: 969
- }
- task_info {
- estimated_mws: 0.134607
- estimated_mw: 0.015563
- idle_transitions_mws: 0.010586
- thread_name: "dmabuf-deferred"
- process_name: "dmabuf-deferred-free-worker"
- thread_id: 69
- process_id: 69
- }
- task_info {
- estimated_mws: 0.129449
- estimated_mw: 0.014966
- thread_name: "highpool[5]"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3354
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.126973
- estimated_mw: 0.014680
- thread_name: "ice] processing"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2363
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.126830
- estimated_mw: 0.014663
- thread_name: "ediator.Toggler"
- process_name: "system_server"
- thread_id: 1910
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.125742
- estimated_mw: 0.014538
- idle_transitions_mws: 0.064827
- thread_name: "surfaceflinger"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 875
- process_id: 755
- }
- task_info {
- estimated_mws: 0.123833
- estimated_mw: 0.014317
- thread_name: "wificond"
- process_name: "/system/bin/wificond"
- thread_id: 964
- process_id: 964
- }
- task_info {
- estimated_mws: 0.123248
- estimated_mw: 0.014249
- thread_name: "MobileDataStats"
- process_name: "system_server"
- thread_id: 1912
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.119885
- estimated_mw: 0.013860
- thread_name: "GlobalScheduler"
- process_name: "com.google.android.gms"
- thread_id: 3156
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.119479
- estimated_mw: 0.013814
- idle_transitions_mws: 0.000930
- thread_name: "RenderThread"
- thread_id: 5599
- }
- task_info {
- estimated_mws: 0.119243
- estimated_mw: 0.013786
- idle_transitions_mws: 0.008398
- thread_name: "TouchTimer"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 866
- process_id: 755
- }
- task_info {
- estimated_mws: 0.114249
- estimated_mw: 0.013209
- thread_name: "binder:4038_1"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4050
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.112705
- estimated_mw: 0.013030
- idle_transitions_mws: 0.011129
- thread_name: "displayoffload@"
- process_name: "/vendor/bin/hw/vendor.google_clockwork.displayoffload@2.0-service.1p"
- thread_id: 937
- process_id: 937
- }
- task_info {
- estimated_mws: 0.111279
- estimated_mw: 0.012866
- idle_transitions_mws: 0.003661
- thread_name: "adbd"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5544
- process_id: 5544
- }
- task_info {
- estimated_mws: 0.108114
- estimated_mw: 0.012500
- thread_name: "kworker/1:2H"
- process_name: "kworker/1:2H"
- thread_id: 300
- process_id: 300
- }
- task_info {
- estimated_mws: 0.107442
- estimated_mw: 0.012422
- idle_transitions_mws: 0.003376
- thread_name: "RenderThread"
- thread_id: 5584
- }
- task_info {
- estimated_mws: 0.105863
- estimated_mw: 0.012239
- idle_transitions_mws: 0.006458
- thread_name: "Primes-2"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1946
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.104306
- estimated_mw: 0.012059
- thread_name: "iptables-restor"
- process_name: "/system/bin/iptables-restore"
- thread_id: 558
- process_id: 558
- }
- task_info {
- estimated_mws: 0.104093
- estimated_mw: 0.012035
- thread_name: "RenderThread"
- process_name: "system_server"
- thread_id: 5223
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.102912
- estimated_mw: 0.011898
- idle_transitions_mws: 0.006649
- thread_name: "irq/168-nanohub"
- process_name: "irq/168-nanohub-irq1"
- thread_id: 296
- process_id: 296
- }
- task_info {
- estimated_mws: 0.102167
- estimated_mw: 0.011812
- idle_transitions_mws: 0.003890
- thread_name: "RenderThread"
- thread_id: 5604
- }
- task_info {
- estimated_mws: 0.101945
- estimated_mw: 0.011786
- thread_name: "ksoftirqd/2"
- process_name: "ksoftirqd/2"
- thread_id: 34
- process_id: 34
- }
- task_info {
- estimated_mws: 0.101282
- estimated_mw: 0.011710
- thread_name: "PhotonicModulat"
- process_name: "system_server"
- thread_id: 1899
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.100285
- estimated_mw: 0.011595
- thread_name: "ip6tables-resto"
- process_name: "/system/bin/ip6tables-restore"
- thread_id: 559
- process_id: 559
- }
- task_info {
- estimated_mws: 0.099432
- estimated_mw: 0.011496
- thread_name: "init"
- process_name: "/system/bin/init"
- thread_id: 144
- process_id: 144
- }
- task_info {
- estimated_mws: 0.096314
- estimated_mw: 0.011135
- thread_name: "FrameworkReceiv"
- process_name: ".qtidataservices"
- thread_id: 2793
- process_id: 2118
- }
- task_info {
- estimated_mws: 0.095442
- estimated_mw: 0.011035
- thread_name: "Jit thread pool"
- process_name: "com.google.android.gms.persistent"
- thread_id: 1969
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.094448
- estimated_mw: 0.010920
- thread_name: "pool-14-thread-"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3314
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.093937
- estimated_mw: 0.010861
- thread_name: "binder:1926_2"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1939
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.093621
- estimated_mw: 0.010824
- thread_name: "ChreMsgHandler"
- process_name: "/vendor/bin/chre"
- thread_id: 1080
- process_id: 1041
- }
- task_info {
- estimated_mws: 0.091738
- estimated_mw: 0.010606
- idle_transitions_mws: 0.001106
- thread_name: "DispatcherModul"
- process_name: "/vendor/bin/hw/qcrilNrd"
- thread_id: 1673
- process_id: 1062
- }
- task_info {
- estimated_mws: 0.091698
- estimated_mw: 0.010602
- idle_transitions_mws: 0.047196
- thread_name: "irq/234-pixart_"
- process_name: "irq/234-pixart_pat9126_irq"
- thread_id: 500
- process_id: 500
- }
- task_info {
- estimated_mws: 0.090883
- estimated_mw: 0.010507
- thread_name: "scheduler_threa"
- process_name: "scheduler_thread"
- thread_id: 5198
- process_id: 5198
- }
- task_info {
- estimated_mws: 0.089001
- estimated_mw: 0.010290
- thread_name: "binder:2085_4"
- process_name: "com.google.android.bluetooth"
- thread_id: 2713
- process_id: 2085
- }
- task_info {
- estimated_mws: 0.086934
- estimated_mw: 0.010051
- idle_transitions_mws: 0.001387
- thread_name: "binder:3028_5"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 5434
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.085941
- estimated_mw: 0.009936
- thread_name: "binder:2670_6"
- process_name: "com.android.nfc"
- thread_id: 3159
- process_id: 2670
- }
- task_info {
- estimated_mws: 0.083859
- estimated_mw: 0.009695
- idle_transitions_mws: 0.289424
- thread_name: "psimon"
- process_name: "psimon"
- thread_id: 1480
- process_id: 1480
- }
- task_info {
- estimated_mws: 0.083773
- estimated_mw: 0.009685
- thread_name: "binder:233_2"
- process_name: "/system/bin/vold"
- thread_id: 252
- process_id: 233
- }
- task_info {
- estimated_mws: 0.080959
- estimated_mw: 0.009360
- thread_name: "binder:2049_2"
- process_name: "com.android.networkstack.process"
- thread_id: 2068
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.080298
- estimated_mw: 0.009284
- thread_name: "netd"
- process_name: "/system/bin/netd"
- thread_id: 568
- process_id: 546
- }
- task_info {
- estimated_mws: 0.080265
- estimated_mw: 0.009280
- thread_name: "UEventObserver"
- process_name: "system_server"
- thread_id: 1857
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.079337
- estimated_mw: 0.009173
- thread_name: "RenderThread"
- thread_id: 5619
- }
- task_info {
- estimated_mws: 0.078696
- estimated_mw: 0.009098
- thread_name: "pool-8-thread-1"
- process_name: "com.google.android.gms"
- thread_id: 3102
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.077362
- estimated_mw: 0.008944
- idle_transitions_mws: 0.019002
- thread_name: "mcu_mgmtd"
- process_name: "/vendor/bin/mcu_mgmtd"
- thread_id: 594
- process_id: 524
- }
- task_info {
- estimated_mws: 0.074501
- estimated_mw: 0.008613
- thread_name: "spi0"
- process_name: "spi0"
- thread_id: 295
- process_id: 295
- }
- task_info {
- estimated_mws: 0.073914
- estimated_mw: 0.008546
- thread_name: "com.android.nfc"
- process_name: "com.android.nfc"
- thread_id: 2670
- process_id: 2670
- }
- task_info {
- estimated_mws: 0.072671
- estimated_mw: 0.008402
- idle_transitions_mws: 0.063131
- thread_name: "rcu_exp_gp_kthr"
- process_name: "rcu_exp_gp_kthread_worker"
- thread_id: 19
- process_id: 19
- }
- task_info {
- estimated_mws: 0.070587
- estimated_mw: 0.008161
- idle_transitions_mws: 0.005993
- thread_name: "adbd"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5546
- process_id: 5544
- }
- task_info {
- estimated_mws: 0.070105
- estimated_mw: 0.008105
- thread_name: "servicemanager"
- thread_id: 5598
- }
- task_info {
- estimated_mws: 0.069214
- estimated_mw: 0.008002
- idle_transitions_mws: 0.001447
- thread_name: "android.imms"
- process_name: "system_server"
- thread_id: 1791
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.068600
- estimated_mw: 0.007931
- thread_name: "lowpool[2]"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2321
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.068467
- estimated_mw: 0.007916
- thread_name: "FileObserver"
- process_name: "com.google.android.gms"
- thread_id: 3035
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.068316
- estimated_mw: 0.007898
- thread_name: "binder:546_3"
- process_name: "/system/bin/netd"
- thread_id: 546
- process_id: 546
- }
- task_info {
- estimated_mws: 0.066410
- estimated_mw: 0.007678
- thread_name: "pool-51-thread-"
- process_name: "com.google.android.gms"
- thread_id: 4215
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.064761
- estimated_mw: 0.007487
- thread_name: "AudioService"
- process_name: "system_server"
- thread_id: 1844
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.064339
- estimated_mw: 0.007439
- idle_transitions_mws: 0.003289
- thread_name: "adbd"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5545
- process_id: 5544
- }
- task_info {
- estimated_mws: 0.063188
- estimated_mw: 0.007305
- thread_name: "droid.bluetooth"
- process_name: "com.google.android.bluetooth"
- thread_id: 2085
- process_id: 2085
- }
- task_info {
- estimated_mws: 0.061069
- estimated_mw: 0.007061
- thread_name: "msm_irqbalance"
- process_name: "/vendor/bin/msm_irqbalance"
- thread_id: 2466
- process_id: 2466
- }
- task_info {
- estimated_mws: 0.060920
- estimated_mw: 0.007043
- idle_transitions_mws: 0.003380
- thread_name: "BackgroundInsta"
- process_name: "system_server"
- thread_id: 1875
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.059901
- estimated_mw: 0.006925
- idle_transitions_mws: 0.008904
- thread_name: "ConnectivitySer"
- process_name: "system_server"
- thread_id: 1827
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.059295
- estimated_mw: 0.006855
- idle_transitions_mws: 0.018518
- thread_name: "pool-1-thread-1"
- process_name: "system_server"
- thread_id: 1873
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.059196
- estimated_mw: 0.006844
- thread_name: "binder:3028_1"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3049
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.058807
- estimated_mw: 0.006799
- idle_transitions_mws: 0.001108
- thread_name: "roid.apps.scone"
- process_name: "com.google.android.apps.scone"
- thread_id: 5245
- process_id: 5245
- }
- task_info {
- estimated_mws: 0.058053
- estimated_mw: 0.006712
- thread_name: "UsbFfs-worker"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5560
- process_id: 5544
- }
- task_info {
- estimated_mws: 0.057400
- estimated_mw: 0.006636
- idle_transitions_mws: 0.034371
- thread_name: "android.hardwar"
- process_name: "/vendor/bin/hw/android.hardware.health-service.eos"
- thread_id: 1271
- process_id: 1271
- }
- task_info {
- estimated_mws: 0.056947
- estimated_mw: 0.006584
- idle_transitions_mws: 0.002094
- thread_name: "bgres-controlle"
- process_name: "system_server"
- thread_id: 1495
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.056879
- estimated_mw: 0.006576
- thread_name: "netd"
- process_name: "/system/bin/netd"
- thread_id: 569
- process_id: 546
- }
- task_info {
- estimated_mws: 0.055642
- estimated_mw: 0.006433
- thread_name: "system_server"
- thread_id: 5590
- }
- task_info {
- estimated_mws: 0.054764
- estimated_mw: 0.006332
- thread_name: "binder:2049_4"
- process_name: "com.android.networkstack.process"
- thread_id: 2083
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.053127
- estimated_mw: 0.006142
- thread_name: "binder:1949_4"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2302
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.052984
- estimated_mw: 0.006126
- idle_transitions_mws: 0.001354
- thread_name: "oid.grilservice"
- process_name: "com.google.android.grilservice"
- thread_id: 2129
- process_id: 2129
- }
- task_info {
- estimated_mws: 0.052980
- estimated_mw: 0.006125
- thread_name: "binder:2856_9"
- process_name: "com.google.android.gms"
- thread_id: 5585
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.052715
- estimated_mw: 0.006095
- thread_name: "StateService"
- process_name: "com.google.android.apps.scone"
- thread_id: 5269
- process_id: 5245
- }
- task_info {
- estimated_mws: 0.052232
- estimated_mw: 0.006039
- thread_name: "vndservicemanag"
- thread_id: 5597
- }
- task_info {
- estimated_mws: 0.052027
- estimated_mw: 0.006015
- thread_name: "binder:1949_9"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3359
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.051717
- estimated_mw: 0.005979
- idle_transitions_mws: 0.019339
- thread_name: "Ipc-5004:1"
- process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
- thread_id: 5483
- process_id: 650
- }
- task_info {
- estimated_mws: 0.049546
- estimated_mw: 0.005728
- thread_name: "PackageManager"
- process_name: "system_server"
- thread_id: 1530
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.048227
- estimated_mw: 0.005576
- thread_name: "android.hardwar"
- process_name: "/vendor/bin/hw/android.hardware.power-service"
- thread_id: 660
- process_id: 660
- }
- task_info {
- estimated_mws: 0.047454
- estimated_mw: 0.005486
- thread_name: "BluetoothScanMa"
- process_name: "com.google.android.bluetooth"
- thread_id: 2609
- process_id: 2085
- }
- task_info {
- estimated_mws: 0.046472
- estimated_mw: 0.005373
- thread_name: "subsystem_ramdu"
- process_name: "/system/vendor/bin/subsystem_ramdump"
- thread_id: 816
- process_id: 799
- }
- task_info {
- estimated_mws: 0.046295
- estimated_mw: 0.005352
- thread_name: "Ipc-5004:2"
- process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
- thread_id: 5484
- process_id: 650
- }
- task_info {
- estimated_mws: 0.045805
- estimated_mw: 0.005296
- thread_name: "binder:975_2"
- process_name: "/vendor/bin/imsdaemon"
- thread_id: 1047
- process_id: 975
- }
- task_info {
- estimated_mws: 0.045282
- estimated_mw: 0.005235
- idle_transitions_mws: 0.000972
- thread_name: ".healthservices"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3028
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.045087
- estimated_mw: 0.005213
- idle_transitions_mws: 0.001817
- thread_name: "queued-work-loo"
- process_name: "com.google.android.gms"
- thread_id: 3236
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.043921
- estimated_mw: 0.005078
- thread_name: "powerstateservi"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.powerstateservice@1.0-service"
- thread_id: 276
- process_id: 269
- }
- task_info {
- estimated_mws: 0.043653
- estimated_mw: 0.005047
- idle_transitions_mws: 0.045128
- thread_name: "wlan_logging_th"
- process_name: "wlan_logging_thread"
- thread_id: 368
- process_id: 368
- }
- task_info {
- estimated_mws: 0.043574
- estimated_mw: 0.005038
- thread_name: "FlpThread"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3279
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.043436
- estimated_mw: 0.005022
- thread_name: "Light-P0-2"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5071
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.042985
- estimated_mw: 0.004970
- idle_transitions_mws: 0.004250
- thread_name: "binder:5245_4"
- process_name: "com.google.android.apps.scone"
- thread_id: 5270
- process_id: 5245
- }
- task_info {
- estimated_mws: 0.042946
- estimated_mw: 0.004965
- thread_name: "HWC_UeventThrea"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
- thread_id: 717
- process_id: 685
- }
- task_info {
- estimated_mws: 0.042762
- estimated_mw: 0.004944
- thread_name: "pool-12-thread-"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2371
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.042752
- estimated_mw: 0.004943
- thread_name: "android.hardwar"
- process_name: "/vendor/bin/hw/android.hardware.contexthub-service.wac"
- thread_id: 668
- process_id: 644
- }
- task_info {
- estimated_mws: 0.042468
- estimated_mw: 0.004910
- thread_name: "binder:1948_3"
- process_name: "com.google.wear.services"
- thread_id: 1976
- process_id: 1948
- }
- task_info {
- estimated_mws: 0.042220
- estimated_mw: 0.004881
- thread_name: "netlink socket"
- process_name: "/system/vendor/bin/ipacm"
- thread_id: 538
- process_id: 523
- }
- task_info {
- estimated_mws: 0.040507
- estimated_mw: 0.004683
- idle_transitions_mws: 0.020051
- thread_name: "RegionSampling"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 871
- process_id: 755
- }
- task_info {
- estimated_mws: 0.040385
- estimated_mw: 0.004669
- thread_name: "TransportThread"
- process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
- thread_id: 794
- process_id: 664
- }
- task_info {
- estimated_mws: 0.040349
- estimated_mw: 0.004665
- thread_name: "binder:2856_1"
- process_name: "com.google.android.gms"
- thread_id: 2898
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.040180
- estimated_mw: 0.004645
- thread_name: "servicemanager"
- thread_id: 5595
- }
- task_info {
- estimated_mws: 0.039525
- estimated_mw: 0.004570
- thread_name: "pd-mapper"
- process_name: "/vendor/bin/pd-mapper"
- thread_id: 752
- process_id: 725
- }
- task_info {
- estimated_mws: 0.039196
- estimated_mw: 0.004532
- thread_name: "vndservicemanag"
- thread_id: 5618
- }
- task_info {
- estimated_mws: 0.039101
- estimated_mw: 0.004521
- idle_transitions_mws: 0.001158
- thread_name: "vndservicemanag"
- thread_id: 5605
- }
- task_info {
- estimated_mws: 0.038960
- estimated_mw: 0.004504
- thread_name: "WifiScanningSer"
- process_name: "system_server"
- thread_id: 1823
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.038580
- estimated_mw: 0.004460
- thread_name: "cnss-daemon"
- process_name: "/system/vendor/bin/cnss-daemon"
- thread_id: 5204
- process_id: 1009
- }
- task_info {
- estimated_mws: 0.038053
- estimated_mw: 0.004399
- idle_transitions_mws: 0.002046
- thread_name: "shell svc 5620"
- process_name: "/apex/com.android.adbd/bin/adbd"
- thread_id: 5622
- process_id: 5544
- }
- task_info {
- estimated_mws: 0.037116
- estimated_mw: 0.004291
- thread_name: "rmt_storage"
- process_name: "/vendor/bin/rmt_storage"
- thread_id: 758
- process_id: 758
- }
- task_info {
- estimated_mws: 0.036357
- estimated_mw: 0.004203
- idle_transitions_mws: 0.164265
- thread_name: "halt_drain_rqs"
- process_name: "halt_drain_rqs"
- thread_id: 105
- process_id: 105
- }
- task_info {
- estimated_mws: 0.035907
- estimated_mw: 0.004151
- thread_name: "BG Thread #2"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4106
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.035876
- estimated_mw: 0.004148
- idle_transitions_mws: 0.007692
- thread_name: "-Executor] idle"
- process_name: "com.google.android.gms"
- thread_id: 5592
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.035444
- estimated_mw: 0.004098
- thread_name: "tftp_server"
- process_name: "/vendor/bin/tftp_server"
- thread_id: 759
- process_id: 759
- }
- task_info {
- estimated_mws: 0.035386
- estimated_mw: 0.004091
- thread_name: "FinalizerWatchd"
- process_name: "com.google.android.gms"
- thread_id: 2887
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.035171
- estimated_mw: 0.004066
- idle_transitions_mws: 0.012569
- thread_name: "servicemanager"
- thread_id: 5606
- }
- task_info {
- estimated_mws: 0.035157
- estimated_mw: 0.004065
- thread_name: "Blocking Thread"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 5587
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.035034
- estimated_mw: 0.004050
- thread_name: "vndservicemanag"
- thread_id: 5611
- }
- task_info {
- estimated_mws: 0.034307
- estimated_mw: 0.003966
- thread_name: "vndservicemanag"
- thread_id: 5593
- }
- task_info {
- estimated_mws: 0.034030
- estimated_mw: 0.003934
- thread_name: "servicemanager"
- thread_id: 5621
- }
- task_info {
- estimated_mws: 0.032631
- estimated_mw: 0.003773
- thread_name: "binder:685_3"
- thread_id: 5586
- }
- task_info {
- estimated_mws: 0.031847
- estimated_mw: 0.003682
- idle_transitions_mws: 0.000891
- thread_name: "radioext@1.0-se"
- process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
- thread_id: 676
- process_id: 676
- }
- task_info {
- estimated_mws: 0.031818
- estimated_mw: 0.003679
- idle_transitions_mws: 0.001158
- thread_name: "BgBroadcastRegi"
- process_name: "com.google.wear.services"
- thread_id: 2017
- process_id: 1948
- }
- task_info {
- estimated_mws: 0.031204
- estimated_mw: 0.003608
- idle_transitions_mws: 0.002208
- thread_name: "DefaultExecutor"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 5600
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.030138
- estimated_mw: 0.003484
- thread_name: "Light-P0-1"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5064
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.029778
- estimated_mw: 0.003443
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1368
- process_id: 978
- }
- task_info {
- estimated_mws: 0.029627
- estimated_mw: 0.003425
- thread_name: "atchdog.monitor"
- process_name: "system_server"
- thread_id: 1414
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.029590
- estimated_mw: 0.003421
- idle_transitions_mws: 0.012793
- thread_name: "UsfHalWorker"
- process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
- thread_id: 792
- process_id: 664
- }
- task_info {
- estimated_mws: 0.028316
- estimated_mw: 0.003274
- idle_transitions_mws: 0.003170
- thread_name: "binder:1999_5"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 4985
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.028097
- estimated_mw: 0.003248
- thread_name: "SatelliteContro"
- process_name: "com.android.phone"
- thread_id: 2382
- process_id: 2182
- }
- task_info {
- estimated_mws: 0.027888
- estimated_mw: 0.003224
- idle_transitions_mws: 0.009315
- thread_name: "irq/199-dwc3"
- process_name: "irq/199-dwc3"
- thread_id: 5559
- process_id: 5559
- }
- task_info {
- estimated_mws: 0.027797
- estimated_mw: 0.003214
- thread_name: "pm-service"
- process_name: "/vendor/bin/pm-service"
- thread_id: 745
- process_id: 730
- }
- task_info {
- estimated_mws: 0.027301
- estimated_mw: 0.003156
- idle_transitions_mws: 0.001040
- thread_name: "-Executor] idle"
- process_name: "com.google.android.gms.persistent"
- thread_id: 5603
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.027287
- estimated_mw: 0.003155
- idle_transitions_mws: 0.002419
- thread_name: "perfetto"
- process_name: "perfetto"
- thread_id: 5581
- process_id: 5581
- }
- task_info {
- estimated_mws: 0.026942
- estimated_mw: 0.003115
- thread_name: "ExeSeq-P10-1"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5074
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.026776
- estimated_mw: 0.003096
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1364
- process_id: 978
- }
- task_info {
- estimated_mws: 0.026328
- estimated_mw: 0.003044
- thread_name: "HwBinder:2129_1"
- process_name: "com.google.android.grilservice"
- thread_id: 3649
- process_id: 2129
- }
- task_info {
- estimated_mws: 0.026013
- estimated_mw: 0.003008
- thread_name: "DefaultExecutor"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 5588
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.025701
- estimated_mw: 0.002971
- thread_name: "rkstack.process"
- process_name: "com.android.networkstack.process"
- thread_id: 2049
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.024843
- estimated_mw: 0.002872
- idle_transitions_mws: 0.001271
- thread_name: "hwuiTask1"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1997
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.024811
- estimated_mw: 0.002868
- idle_transitions_mws: 0.000632
- thread_name: "pool-1-thread-1"
- process_name: "com.google.android.apps.scone"
- thread_id: 5271
- process_id: 5245
- }
- task_info {
- estimated_mws: 0.024633
- estimated_mw: 0.002848
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1360
- process_id: 978
- }
- task_info {
- estimated_mws: 0.023564
- estimated_mw: 0.002724
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1366
- process_id: 978
- }
- task_info {
- estimated_mws: 0.023405
- estimated_mw: 0.002706
- thread_name: "cnss-daemon"
- process_name: "/system/vendor/bin/cnss-daemon"
- thread_id: 5613
- process_id: 1009
- }
- task_info {
- estimated_mws: 0.023393
- estimated_mw: 0.002705
- thread_name: "servicemanager"
- thread_id: 5608
- }
- task_info {
- estimated_mws: 0.022813
- estimated_mw: 0.002638
- thread_name: "android.hardwar"
- process_name: "/vendor/bin/hw/android.hardware.contexthub-service.wac"
- thread_id: 644
- process_id: 644
- }
- task_info {
- estimated_mws: 0.022774
- estimated_mw: 0.002633
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1362
- process_id: 978
- }
- task_info {
- estimated_mws: 0.022714
- estimated_mw: 0.002626
- thread_name: "binder:685_3"
- thread_id: 5594
- }
- task_info {
- estimated_mws: 0.022642
- estimated_mw: 0.002618
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1358
- process_id: 978
- }
- task_info {
- estimated_mws: 0.022617
- estimated_mw: 0.002615
- thread_name: "vndservicemanag"
- thread_id: 5607
- }
- task_info {
- estimated_mws: 0.021814
- estimated_mw: 0.002522
- idle_transitions_mws: 0.002423
- thread_name: "it.FitbitMobile"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5377
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.021307
- estimated_mw: 0.002463
- thread_name: "SysUiBg"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2294
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.020941
- estimated_mw: 0.002421
- thread_name: "-Executor] idle"
- process_name: "com.google.android.gms.persistent"
- thread_id: 5623
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.020393
- estimated_mw: 0.002358
- thread_name: "vndservicemanag"
- thread_id: 5582
- }
- task_info {
- estimated_mws: 0.019946
- estimated_mw: 0.002306
- idle_transitions_mws: 0.009476
- thread_name: "qcom,system-poo"
- process_name: "qcom,system-pool-refill-thread"
- thread_id: 81
- process_id: 81
- }
- task_info {
- estimated_mws: 0.019901
- estimated_mw: 0.002301
- idle_transitions_mws: 0.000916
- thread_name: "binder:2129_9"
- process_name: "com.google.android.grilservice"
- thread_id: 5203
- process_id: 2129
- }
- task_info {
- estimated_mws: 0.019723
- estimated_mw: 0.002280
- thread_name: "servicemanager"
- thread_id: 5610
- }
- task_info {
- estimated_mws: 0.019537
- estimated_mw: 0.002259
- thread_name: "cnss-daemon"
- process_name: "/system/vendor/bin/cnss-daemon"
- thread_id: 1009
- process_id: 1009
- }
- task_info {
- estimated_mws: 0.019390
- estimated_mw: 0.002242
- thread_name: "pool-9-thread-1"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 2073
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.019219
- estimated_mw: 0.002222
- thread_name: "binder:1302_3"
- process_name: "system_server"
- thread_id: 1433
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.019059
- estimated_mw: 0.002204
- thread_name: "mcu_mgmtd"
- process_name: "/vendor/bin/mcu_mgmtd"
- thread_id: 587
- process_id: 524
- }
- task_info {
- estimated_mws: 0.018619
- estimated_mw: 0.002153
- thread_name: "WCMTelemetryLog"
- process_name: "system_server"
- thread_id: 1906
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.018508
- estimated_mw: 0.002140
- thread_name: "droid.tethering"
- process_name: "com.android.networkstack.process"
- thread_id: 2158
- process_id: 2049
- }
- task_info {
- estimated_mws: 0.018055
- estimated_mw: 0.002087
- thread_name: "DefaultWallpape"
- process_name: "com.google.android.wearable.watchface.rwf"
- thread_id: 2082
- process_id: 1999
- }
- task_info {
- estimated_mws: 0.017725
- estimated_mw: 0.002049
- idle_transitions_mws: 0.001379
- thread_name: "HeapTaskDaemon"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5386
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.017125
- estimated_mw: 0.001980
- idle_transitions_mws: 0.001201
- thread_name: "WearConnectionT"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2172
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.016887
- estimated_mw: 0.001952
- idle_transitions_mws: 0.001112
- thread_name: "wear-services-w"
- process_name: "com.google.wear.services"
- thread_id: 2029
- process_id: 1948
- }
- task_info {
- estimated_mws: 0.016252
- estimated_mw: 0.001879
- thread_name: "BG Thread #1"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4080
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.016215
- estimated_mw: 0.001875
- idle_transitions_mws: 0.001249
- thread_name: "GlobalScheduler"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2276
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.016141
- estimated_mw: 0.001866
- thread_name: "pool-31-thread-"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 5617
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.016069
- estimated_mw: 0.001858
- idle_transitions_mws: 0.005041
- thread_name: "servicemanager"
- thread_id: 5583
- }
- task_info {
- estimated_mws: 0.015376
- estimated_mw: 0.001778
- idle_transitions_mws: 0.007814
- thread_name: "RenderEngine"
- process_name: "/system/bin/surfaceflinger"
- thread_id: 5601
- process_id: 755
- }
- task_info {
- estimated_mws: 0.015374
- estimated_mw: 0.001777
- thread_name: "pool-11-thread-"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 5596
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.015097
- estimated_mw: 0.001745
- idle_transitions_mws: 0.000833
- thread_name: "dsi_err_workq"
- process_name: "dsi_err_workq"
- thread_id: 5589
- process_id: 5589
- }
- task_info {
- estimated_mws: 0.015062
- estimated_mw: 0.001741
- thread_name: "InteractionJank"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2300
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.015034
- estimated_mw: 0.001738
- thread_name: "vndservicemanag"
- thread_id: 5609
- }
- task_info {
- estimated_mws: 0.014592
- estimated_mw: 0.001687
- idle_transitions_mws: 0.000734
- thread_name: "hwuiTask0"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1996
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.014520
- estimated_mw: 0.001679
- thread_name: "tworkPolicy.uid"
- process_name: "system_server"
- thread_id: 1817
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.014278
- estimated_mw: 0.001651
- thread_name: "highpool[10]"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3417
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.014123
- estimated_mw: 0.001633
- idle_transitions_mws: 0.010036
- thread_name: "LowMemThread"
- process_name: "system_server"
- thread_id: 1481
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.014026
- estimated_mw: 0.001622
- thread_name: "pixelstats-vend"
- process_name: "/vendor/bin/pixelstats-vendor"
- thread_id: 266
- process_id: 255
- }
- task_info {
- estimated_mws: 0.013579
- estimated_mw: 0.001570
- idle_transitions_mws: 0.001160
- thread_name: "kworker/u9:0"
- process_name: "kworker/u9:0"
- thread_id: 64
- process_id: 64
- }
- task_info {
- estimated_mws: 0.013322
- estimated_mw: 0.001540
- idle_transitions_mws: 0.011775
- thread_name: "migration/1"
- process_name: "migration/1"
- thread_id: 25
- process_id: 25
- }
- task_info {
- estimated_mws: 0.013321
- estimated_mw: 0.001540
- thread_name: "GlobalDispatchi"
- process_name: "com.google.android.gms.persistent"
- thread_id: 2290
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.013127
- estimated_mw: 0.001518
- thread_name: "main"
- process_name: "/vendor/bin/hw/qcrilNrd"
- thread_id: 1568
- process_id: 1062
- }
- task_info {
- estimated_mws: 0.012863
- estimated_mw: 0.001487
- thread_name: "servicemanager"
- thread_id: 5612
- }
- task_info {
- estimated_mws: 0.012835
- estimated_mw: 0.001484
- thread_name: "qtidataservices"
- process_name: ".qtidataservices"
- thread_id: 2846
- process_id: 2118
- }
- task_info {
- estimated_mws: 0.012734
- estimated_mw: 0.001472
- thread_name: "shortcut"
- process_name: "system_server"
- thread_id: 1874
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.012433
- estimated_mw: 0.001437
- idle_transitions_mws: 0.001905
- thread_name: "irq/25-mmc0"
- process_name: "irq/25-mmc0"
- thread_id: 120
- process_id: 120
- }
- task_info {
- estimated_mws: 0.012244
- estimated_mw: 0.001416
- thread_name: "GlobalDispatchi"
- process_name: "com.google.android.gms"
- thread_id: 3155
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.012130
- estimated_mw: 0.001402
- thread_name: "ConnectivityThr"
- process_name: "com.google.android.gms"
- thread_id: 4172
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.011932
- estimated_mw: 0.001379
- thread_name: "RenderThread"
- thread_id: 5616
- }
- task_info {
- estimated_mws: 0.011783
- estimated_mw: 0.001362
- thread_name: "AGMIPC@1.0-serv"
- process_name: "/vendor/bin/hw/vendor.qti.hardware.AGMIPC@1.0-service"
- thread_id: 1454
- process_id: 1446
- }
- task_info {
- estimated_mws: 0.011706
- estimated_mw: 0.001353
- thread_name: "binder:2171_2"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2209
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.011675
- estimated_mw: 0.001350
- thread_name: "radioext@1.0-se"
- process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
- thread_id: 714
- process_id: 676
- }
- task_info {
- estimated_mws: 0.011615
- estimated_mw: 0.001343
- thread_name: "servicemanager"
- thread_id: 5615
- }
- task_info {
- estimated_mws: 0.011467
- estimated_mw: 0.001326
- thread_name: "LocApiMsgTask"
- process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
- thread_id: 694
- process_id: 650
- }
- task_info {
- estimated_mws: 0.011318
- estimated_mw: 0.001309
- thread_name: "LocApiMsgTask"
- process_name: "xtra-daemon"
- thread_id: 1090
- process_id: 1031
- }
- task_info {
- estimated_mws: 0.011273
- estimated_mw: 0.001303
- thread_name: "vndservicemanag"
- thread_id: 5614
- }
- task_info {
- estimated_mws: 0.011024
- estimated_mw: 0.001275
- idle_transitions_mws: 0.004820
- thread_name: "TimerThread"
- process_name: "/system/bin/audioserver"
- thread_id: 1486
- process_id: 740
- }
- task_info {
- estimated_mws: 0.010869
- estimated_mw: 0.001257
- idle_transitions_mws: 0.001558
- thread_name: "irq/26-4744000."
- process_name: "irq/26-4744000.sdhci"
- thread_id: 117
- process_id: 117
- }
- task_info {
- estimated_mws: 0.010764
- estimated_mw: 0.001245
- idle_transitions_mws: 0.003066
- thread_name: "SurfaceSyncGrou"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1994
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.010731
- estimated_mw: 0.001241
- idle_transitions_mws: 0.009927
- thread_name: "migration/3"
- process_name: "migration/3"
- thread_id: 40
- process_id: 40
- }
- task_info {
- estimated_mws: 0.010156
- estimated_mw: 0.001174
- thread_name: "id.wearable.app"
- process_name: "com.google.android.wearable.app"
- thread_id: 3857
- process_id: 3857
- }
- task_info {
- estimated_mws: 0.009691
- estimated_mw: 0.001120
- thread_name: "ksoftirqd/0"
- process_name: "ksoftirqd/0"
- thread_id: 13
- process_id: 13
- }
- task_info {
- estimated_mws: 0.009668
- estimated_mw: 0.001118
- thread_name: "cnss-daemon"
- process_name: "/system/vendor/bin/cnss-daemon"
- thread_id: 1052
- process_id: 1009
- }
- task_info {
- estimated_mws: 0.009639
- estimated_mw: 0.001114
- idle_transitions_mws: 0.002373
- thread_name: "kworker/u9:2"
- process_name: "kworker/u9:2"
- thread_id: 338
- process_id: 338
- }
- task_info {
- estimated_mws: 0.009404
- estimated_mw: 0.001087
- thread_name: "binder:2856_3"
- process_name: "com.google.android.gms"
- thread_id: 3157
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.009364
- estimated_mw: 0.001083
- thread_name: "binder:969_3"
- process_name: "/system/vendor/bin/cnd"
- thread_id: 1013
- process_id: 969
- }
- task_info {
- estimated_mws: 0.008837
- estimated_mw: 0.001022
- thread_name: "binder:2118_2"
- process_name: ".qtidataservices"
- thread_id: 2142
- process_id: 2118
- }
- task_info {
- estimated_mws: 0.008775
- estimated_mw: 0.001015
- thread_name: "thermal-engine-"
- process_name: "/vendor/bin/thermal-engine-v2"
- thread_id: 2520
- process_id: 2493
- }
- task_info {
- estimated_mws: 0.008724
- estimated_mw: 0.001009
- thread_name: "binder:2856_7"
- process_name: "com.google.android.gms"
- thread_id: 4825
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.008275
- estimated_mw: 0.000957
- thread_name: "binder:2856_6"
- process_name: "com.google.android.gms"
- thread_id: 4824
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.008136
- estimated_mw: 0.000941
- thread_name: "binder:3857_3"
- process_name: "com.google.android.wearable.app"
- thread_id: 3872
- process_id: 3857
- }
- task_info {
- estimated_mws: 0.007913
- estimated_mw: 0.000915
- thread_name: "binder:975_3"
- process_name: "/vendor/bin/imsdaemon"
- thread_id: 1630
- process_id: 975
- }
- task_info {
- estimated_mws: 0.007892
- estimated_mw: 0.000912
- thread_name: "time_daemon"
- process_name: "/vendor/bin/time_daemon"
- thread_id: 525
- process_id: 522
- }
- task_info {
- estimated_mws: 0.007750
- estimated_mw: 0.000896
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1044
- process_id: 978
- }
- task_info {
- estimated_mws: 0.007591
- estimated_mw: 0.000878
- thread_name: "binder:2856_5"
- process_name: "com.google.android.gms"
- thread_id: 3681
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.007392
- estimated_mw: 0.000855
- thread_name: "binder:2182_1"
- process_name: "com.android.phone"
- thread_id: 2231
- process_id: 2182
- }
- task_info {
- estimated_mws: 0.007384
- estimated_mw: 0.000854
- thread_name: "binder:2171_5"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2638
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.007245
- estimated_mw: 0.000838
- idle_transitions_mws: 0.000922
- thread_name: "qrtr_rx"
- process_name: "qrtr_rx"
- thread_id: 1556
- process_id: 1556
- }
- task_info {
- estimated_mws: 0.007086
- estimated_mw: 0.000819
- thread_name: "radioext@1.0-se"
- process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
- thread_id: 1605
- process_id: 676
- }
- task_info {
- estimated_mws: 0.006850
- estimated_mw: 0.000792
- idle_transitions_mws: 0.005140
- thread_name: "hwservicemanage"
- process_name: "/system/system_ext/bin/hwservicemanager"
- thread_id: 214
- process_id: 214
- }
- task_info {
- estimated_mws: 0.006731
- estimated_mw: 0.000778
- thread_name: "rcub/0"
- process_name: "rcub/0"
- thread_id: 17
- process_id: 17
- }
- task_info {
- estimated_mws: 0.006663
- estimated_mw: 0.000770
- thread_name: "binder:2856_8"
- process_name: "com.google.android.gms"
- thread_id: 4826
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.006650
- estimated_mw: 0.000769
- thread_name: "kthreadd"
- process_name: "kthreadd"
- thread_id: 2
- process_id: 2
- }
- task_info {
- estimated_mws: 0.006574
- estimated_mw: 0.000760
- thread_name: "binder:233_2"
- process_name: "/system/bin/vold"
- thread_id: 233
- process_id: 233
- }
- task_info {
- estimated_mws: 0.006115
- estimated_mw: 0.000707
- thread_name: "cds_ol_rx_threa"
- process_name: "cds_ol_rx_thread"
- thread_id: 5199
- process_id: 5199
- }
- task_info {
- estimated_mws: 0.005829
- estimated_mw: 0.000674
- thread_name: "NsdService"
- process_name: "system_server"
- thread_id: 1831
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.005734
- estimated_mw: 0.000663
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1367
- process_id: 978
- }
- task_info {
- estimated_mws: 0.005699
- estimated_mw: 0.000659
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2895
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.005530
- estimated_mw: 0.000639
- thread_name: "binder:2856_2"
- process_name: "com.google.android.gms"
- thread_id: 2903
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.005484
- estimated_mw: 0.000634
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1361
- process_id: 978
- }
- task_info {
- estimated_mws: 0.005366
- estimated_mw: 0.000620
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1357
- process_id: 978
- }
- task_info {
- estimated_mws: 0.005364
- estimated_mw: 0.000620
- thread_name: "FileObserver"
- process_name: "system_server"
- thread_id: 1498
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.005278
- estimated_mw: 0.000610
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1359
- process_id: 978
- }
- task_info {
- estimated_mws: 0.005261
- estimated_mw: 0.000608
- thread_name: "Lite Thread #0"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4109
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.005011
- estimated_mw: 0.000579
- thread_name: "kworker/3:1H"
- process_name: "kworker/3:1H"
- thread_id: 122
- process_id: 122
- }
- task_info {
- estimated_mws: 0.004996
- estimated_mw: 0.000578
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1365
- process_id: 978
- }
- task_info {
- estimated_mws: 0.004986
- estimated_mw: 0.000576
- idle_transitions_mws: 0.031459
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2890
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.004877
- estimated_mw: 0.000564
- thread_name: "perfetto_hprof_"
- process_name: "com.google.android.gms"
- thread_id: 2880
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.004761
- estimated_mw: 0.000550
- thread_name: "backup-0"
- process_name: "system_server"
- thread_id: 2660
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.004757
- estimated_mw: 0.000550
- thread_name: "tts-player-0"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4209
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.004444
- estimated_mw: 0.000514
- thread_name: "Signal Catcher"
- process_name: "com.google.android.gms"
- thread_id: 2878
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.004331
- estimated_mw: 0.000501
- thread_name: "binder:978_2"
- process_name: "/system/vendor/bin/nicmd"
- thread_id: 1363
- process_id: 978
- }
- task_info {
- estimated_mws: 0.004292
- estimated_mw: 0.000496
- idle_transitions_mws: 0.005500
- thread_name: "f2fs_discard-25"
- process_name: "f2fs_discard-254:43"
- thread_id: 349
- process_id: 349
- }
- task_info {
- estimated_mws: 0.004283
- estimated_mw: 0.000495
- idle_transitions_mws: 0.009490
- thread_name: "irq/24-glink-na"
- process_name: "irq/24-glink-native-rpm-glink"
- thread_id: 86
- process_id: 86
- }
- task_info {
- estimated_mws: 0.004252
- estimated_mw: 0.000492
- idle_transitions_mws: 0.001945
- thread_name: "pool-4-thread-1"
- process_name: "system_server"
- thread_id: 1774
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.004010
- estimated_mw: 0.000464
- thread_name: "PasspointProvis"
- process_name: "system_server"
- thread_id: 1821
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.003934
- estimated_mw: 0.000455
- idle_transitions_mws: 0.001222
- thread_name: "binder:5377_5"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5573
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.003880
- estimated_mw: 0.000449
- thread_name: "BG Thread #3"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4107
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.003872
- estimated_mw: 0.000448
- thread_name: "TransportThread"
- process_name: "/vendor/bin/mcu_mgmtd"
- thread_id: 3540
- process_id: 524
- }
- task_info {
- estimated_mws: 0.003835
- estimated_mw: 0.000443
- thread_name: "kworker/1:1H"
- process_name: "kworker/1:1H"
- thread_id: 127
- process_id: 127
- }
- task_info {
- estimated_mws: 0.003754
- estimated_mw: 0.000434
- thread_name: "watchdog"
- process_name: "system_server"
- thread_id: 1421
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.003628
- estimated_mw: 0.000419
- thread_name: "PackageInstalle"
- process_name: "system_server"
- thread_id: 1744
- process_id: 1302
- }
- task_info {
- estimated_mws: 0.003469
- estimated_mw: 0.000401
- thread_name: "Lite Thread #1"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4118
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.003393
- estimated_mw: 0.000392
- idle_transitions_mws: 0.009600
- thread_name: "FinalizerWatchd"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5389
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.003365
- estimated_mw: 0.000389
- thread_name: "DFacilitator-1"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5128
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.003243
- estimated_mw: 0.000375
- thread_name: "pool-8-thread-1"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 3308
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.002873
- estimated_mw: 0.000332
- thread_name: "lowpool[1]"
- process_name: "com.google.android.gms"
- thread_id: 3503
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002842
- estimated_mw: 0.000329
- thread_name: "ReferenceQueueD"
- process_name: "com.google.android.gms"
- thread_id: 2883
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002778
- estimated_mw: 0.000321
- thread_name: "ipacm-diag"
- process_name: "/system/vendor/bin/ipacm-diag"
- thread_id: 976
- process_id: 976
- }
- task_info {
- estimated_mws: 0.002739
- estimated_mw: 0.000317
- idle_transitions_mws: 0.005358
- thread_name: "migration/2"
- process_name: "migration/2"
- thread_id: 32
- process_id: 32
- }
- task_info {
- estimated_mws: 0.002654
- estimated_mw: 0.000307
- thread_name: "qrtr_rx"
- process_name: "qrtr_rx"
- thread_id: 564
- process_id: 564
- }
- task_info {
- estimated_mws: 0.002601
- estimated_mw: 0.000301
- thread_name: "card0-crtc0"
- process_name: "card0-crtc0"
- thread_id: 247
- process_id: 247
- }
- task_info {
- estimated_mws: 0.002574
- estimated_mw: 0.000298
- thread_name: "pool-7-thread-3"
- process_name: "com.google.android.wearable.healthservices"
- thread_id: 5435
- process_id: 3028
- }
- task_info {
- estimated_mws: 0.002458
- estimated_mw: 0.000284
- thread_name: "RenderThread"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2319
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.002443
- estimated_mw: 0.000282
- thread_name: "highpool[0]"
- process_name: "com.google.android.gms"
- thread_id: 3154
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002437
- estimated_mw: 0.000282
- thread_name: "lowpool[0]"
- process_name: "com.google.android.gms"
- thread_id: 3478
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002241
- estimated_mw: 0.000259
- thread_name: "queued-work-loo"
- process_name: "com.google.android.gms.persistent"
- thread_id: 3533
- process_id: 1949
- }
- task_info {
- estimated_mws: 0.002222
- estimated_mw: 0.000257
- thread_name: "arch_disk_io_2"
- process_name: "com.google.android.gms"
- thread_id: 4174
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002160
- estimated_mw: 0.000250
- thread_name: "binder:523_2"
- process_name: "/system/vendor/bin/ipacm"
- thread_id: 537
- process_id: 523
- }
- task_info {
- estimated_mws: 0.002129
- estimated_mw: 0.000246
- thread_name: "Jit thread pool"
- process_name: "com.google.android.gms"
- thread_id: 2881
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002082
- estimated_mw: 0.000241
- thread_name: "pool-48-thread-"
- process_name: "com.google.android.gms"
- thread_id: 4110
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.002071
- estimated_mw: 0.000239
- thread_name: "RenderThread"
- process_name: "com.google.android.inputmethod.latin"
- thread_id: 5120
- process_id: 4997
- }
- task_info {
- estimated_mws: 0.002020
- estimated_mw: 0.000233
- thread_name: "arch_disk_io_0"
- process_name: "com.google.android.gms"
- thread_id: 4031
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.001985
- estimated_mw: 0.000229
- thread_name: "arch_disk_io_1"
- process_name: "com.google.android.gms"
- thread_id: 4034
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.001856
- estimated_mw: 0.000215
- thread_name: "BG Thread #0"
- process_name: "com.google.android.wearable.assistant"
- thread_id: 4061
- process_id: 4038
- }
- task_info {
- estimated_mws: 0.001782
- estimated_mw: 0.000206
- thread_name: "binder:1926_1"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 1938
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.001776
- estimated_mw: 0.000205
- thread_name: "highpool[3]"
- process_name: "com.google.android.gms"
- thread_id: 3470
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.001776
- estimated_mw: 0.000205
- idle_transitions_mws: 0.005770
- thread_name: "migration/0"
- process_name: "migration/0"
- thread_id: 21
- process_id: 21
- }
- task_info {
- estimated_mws: 0.001772
- estimated_mw: 0.000205
- thread_name: "AsyncTask #2"
- process_name: "com.google.android.gms"
- thread_id: 4164
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.001720
- estimated_mw: 0.000199
- thread_name: "arch_disk_io_3"
- process_name: "com.google.android.gms"
- thread_id: 4175
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.001562
- estimated_mw: 0.000181
- idle_transitions_mws: 0.001329
- thread_name: "POSIX timer 0"
- process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
- thread_id: 850
- process_id: 664
- }
- task_info {
- estimated_mws: 0.001520
- estimated_mw: 0.000176
- thread_name: "ksoftirqd/3"
- process_name: "ksoftirqd/3"
- thread_id: 42
- process_id: 42
- }
- task_info {
- estimated_mws: 0.001401
- estimated_mw: 0.000162
- thread_name: "Primes-1"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5394
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001320
- estimated_mw: 0.000153
- thread_name: "binder:5377_3"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5392
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001316
- estimated_mw: 0.000152
- idle_transitions_mws: 0.008908
- thread_name: "msm-watchdog"
- process_name: "msm-watchdog"
- thread_id: 76
- process_id: 76
- }
- task_info {
- estimated_mws: 0.001222
- estimated_mw: 0.000141
- thread_name: "Lite Thread #1"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5421
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001220
- estimated_mw: 0.000141
- thread_name: "Signal Catcher"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5382
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001179
- estimated_mw: 0.000136
- thread_name: "GoogleApiHandle"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5398
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001127
- estimated_mw: 0.000130
- thread_name: "binder:2171_6"
- process_name: "com.google.android.apps.wearable.systemui"
- thread_id: 2678
- process_id: 2171
- }
- task_info {
- estimated_mws: 0.001103
- estimated_mw: 0.000128
- thread_name: "Blocking Thread"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5574
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.001055
- estimated_mw: 0.000122
- thread_name: "WM.task-3"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5430
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000990
- estimated_mw: 0.000114
- thread_name: "highpool[1]"
- process_name: "com.google.android.gms"
- thread_id: 3373
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.000984
- estimated_mw: 0.000114
- thread_name: "Primes-nativecr"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5397
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000961
- estimated_mw: 0.000111
- thread_name: "binder:740_4"
- process_name: "/system/bin/audioserver"
- thread_id: 2183
- process_id: 740
- }
- task_info {
- estimated_mws: 0.000954
- estimated_mw: 0.000110
- thread_name: "Lite Thread #0"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5404
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000927
- estimated_mw: 0.000107
- thread_name: "BG Thread #0"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5395
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000926
- estimated_mw: 0.000107
- thread_name: "FinalizerDaemon"
- process_name: "com.google.android.gms"
- thread_id: 2885
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.000887
- estimated_mw: 0.000103
- thread_name: "BG Thread #1"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5396
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000865
- estimated_mw: 0.000100
- thread_name: "Jit thread pool"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5385
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000858
- estimated_mw: 0.000099
- thread_name: "highpool[2]"
- process_name: "com.google.android.gms"
- thread_id: 3375
- process_id: 2856
- }
- task_info {
- estimated_mws: 0.000855
- estimated_mw: 0.000099
- thread_name: "ConnectivityThr"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5423
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000828
- estimated_mw: 0.000096
- thread_name: "Profile Saver"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5393
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000808
- estimated_mw: 0.000093
- idle_transitions_mws: 0.000732
- thread_name: "ReferenceQueueD"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5387
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000803
- estimated_mw: 0.000093
- idle_transitions_mws: 0.000679
- thread_name: "binder:5377_4"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5433
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000783
- estimated_mw: 0.000091
- thread_name: "ksoftirqd/1"
- process_name: "ksoftirqd/1"
- thread_id: 27
- process_id: 27
- }
- task_info {
- estimated_mws: 0.000782
- estimated_mw: 0.000090
- thread_name: "HsConnectionMan"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5422
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000769
- estimated_mw: 0.000089
- thread_name: "ADB-JDWP Connec"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5384
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000742
- estimated_mw: 0.000086
- thread_name: "Scheduler Threa"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5428
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000733
- estimated_mw: 0.000085
- thread_name: "WM.task-2"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5429
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000730
- estimated_mw: 0.000084
- thread_name: "Scheduled BG"
- process_name: "com.google.android.wearable.sysui"
- thread_id: 2896
- process_id: 1926
- }
- task_info {
- estimated_mws: 0.000727
- estimated_mw: 0.000084
- thread_name: "DefaultDispatch"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5431
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000726
- estimated_mw: 0.000084
- thread_name: "BG Thread #3"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5400
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000724
- estimated_mw: 0.000084
- thread_name: "WM.task-1"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5427
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000718
- estimated_mw: 0.000083
- thread_name: "binder:5377_1"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5390
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000689
- estimated_mw: 0.000080
- thread_name: "DefaultDispatch"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5432
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000669
- estimated_mw: 0.000077
- thread_name: "BG Thread #2"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5399
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000630
- estimated_mw: 0.000073
- idle_transitions_mws: 0.008723
- thread_name: "binder:5377_2"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5391
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000583
- estimated_mw: 0.000067
- thread_name: "perfetto_hprof_"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5383
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000507
- estimated_mw: 0.000059
- thread_name: "Primes-2"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5444
- process_id: 5377
- }
- task_info {
- estimated_mws: 0.000403
- estimated_mw: 0.000047
- thread_name: "FinalizerDaemon"
- process_name: "com.fitbit.FitbitMobile"
- thread_id: 5388
- process_id: 5377
+ period_info {
+ period_id: 1
+ task_info {
+ estimated_mws: 34.416729
+ estimated_mw: 3.979098
+ thread_name: "swapper"
+ thread_id: 0
+ process_id: 0
+ }
+ task_info {
+ estimated_mws: 19.853703
+ estimated_mw: 2.295390
+ idle_transitions_mws: 0.220895
+ thread_name: "RenderThread"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1986
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 17.530441
+ estimated_mw: 2.026786
+ idle_transitions_mws: 0.028812
+ thread_name: "Jit thread pool"
+ process_name: "system_server"
+ thread_id: 1344
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 16.980274
+ estimated_mw: 1.963178
+ idle_transitions_mws: 0.387957
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 755
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 14.908094
+ estimated_mw: 1.723603
+ idle_transitions_mws: 0.455047
+ thread_name: ".wearable.sysui"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1926
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 13.373355
+ estimated_mw: 1.546164
+ idle_transitions_mws: 0.011711
+ thread_name: "binder:685_3"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
+ thread_id: 804
+ process_id: 685
+ }
+ task_info {
+ estimated_mws: 6.747261
+ estimated_mw: 0.780086
+ idle_transitions_mws: 0.021185
+ thread_name: "binder:1302_7"
+ process_name: "system_server"
+ thread_id: 1671
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 6.504173
+ estimated_mw: 0.751981
+ idle_transitions_mws: 0.055166
+ thread_name: "binder:1302_A"
+ process_name: "system_server"
+ thread_id: 2015
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 4.858775
+ estimated_mw: 0.561748
+ idle_transitions_mws: 0.082958
+ thread_name: "android.anim"
+ process_name: "system_server"
+ thread_id: 1419
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 4.769800
+ estimated_mw: 0.551462
+ idle_transitions_mws: 0.094492
+ thread_name: "RenderEngine"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 788
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 4.672233
+ estimated_mw: 0.540181
+ idle_transitions_mws: 0.012303
+ thread_name: "kswapd0"
+ process_name: "kswapd0"
+ thread_id: 63
+ process_id: 63
+ }
+ task_info {
+ estimated_mws: 4.314495
+ estimated_mw: 0.498821
+ thread_name: "lowpool[2]"
+ process_name: "com.google.android.gms"
+ thread_id: 3525
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 4.117818
+ estimated_mw: 0.476083
+ thread_name: "logd.writer"
+ process_name: "/system/bin/logd"
+ thread_id: 221
+ process_id: 211
+ }
+ task_info {
+ estimated_mws: 4.108276
+ estimated_mw: 0.474979
+ idle_transitions_mws: 0.001470
+ thread_name: "binder:1302_17"
+ process_name: "system_server"
+ thread_id: 5202
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 3.723955
+ estimated_mw: 0.430546
+ idle_transitions_mws: 0.046603
+ thread_name: "binder:1302_6"
+ process_name: "system_server"
+ thread_id: 1662
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 3.666289
+ estimated_mw: 0.423879
+ idle_transitions_mws: 0.147155
+ thread_name: "e.watchface.rwf"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 1999
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 3.524869
+ estimated_mw: 0.407529
+ idle_transitions_mws: 0.003585
+ thread_name: "killall"
+ process_name: "/system/bin/sh"
+ thread_id: 5620
+ process_id: 5620
+ }
+ task_info {
+ estimated_mws: 3.495762
+ estimated_mw: 0.404163
+ idle_transitions_mws: 0.012035
+ thread_name: "CachedAppOptimi"
+ process_name: "system_server"
+ thread_id: 1773
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 3.459922
+ estimated_mw: 0.400020
+ idle_transitions_mws: 0.022780
+ thread_name: "logcat"
+ process_name: "logcat"
+ thread_id: 1230
+ process_id: 1230
+ }
+ task_info {
+ estimated_mws: 3.429554
+ estimated_mw: 0.396509
+ idle_transitions_mws: 0.020454
+ thread_name: "system_server"
+ process_name: "system_server"
+ thread_id: 1302
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 3.300661
+ estimated_mw: 0.381607
+ idle_transitions_mws: 1.010862
+ thread_name: "crtc_commit:80"
+ process_name: "crtc_commit:80"
+ thread_id: 244
+ process_id: 244
+ }
+ task_info {
+ estimated_mws: 3.194881
+ estimated_mw: 0.369377
+ idle_transitions_mws: 0.163208
+ thread_name: "InputDispatcher"
+ process_name: "system_server"
+ thread_id: 1783
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 3.011913
+ estimated_mw: 0.348223
+ idle_transitions_mws: 0.261953
+ thread_name: "binder:755_1"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 782
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 3.006022
+ estimated_mw: 0.347542
+ idle_transitions_mws: 0.064213
+ thread_name: "android.display"
+ process_name: "system_server"
+ thread_id: 1418
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 2.856301
+ estimated_mw: 0.330232
+ idle_transitions_mws: 0.000982
+ thread_name: "binder:524_2"
+ process_name: "/vendor/bin/mcu_mgmtd"
+ thread_id: 524
+ process_id: 524
+ }
+ task_info {
+ estimated_mws: 2.712443
+ estimated_mw: 0.313600
+ idle_transitions_mws: 0.314323
+ thread_name: "traced_probes"
+ process_name: "/system/bin/traced_probes"
+ thread_id: 904
+ process_id: 904
+ }
+ task_info {
+ estimated_mws: 2.553161
+ estimated_mw: 0.295184
+ idle_transitions_mws: 0.751582
+ thread_name: "kworker/u8:0"
+ process_name: "kworker/u8:0"
+ thread_id: 8
+ process_id: 8
+ }
+ task_info {
+ estimated_mws: 2.487099
+ estimated_mw: 0.287547
+ idle_transitions_mws: 0.509790
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 883
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 2.386123
+ estimated_mw: 0.275872
+ idle_transitions_mws: 0.002251
+ thread_name: "binder:1302_15"
+ process_name: "system_server"
+ thread_id: 3754
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 2.258779
+ estimated_mw: 0.261149
+ idle_transitions_mws: 0.219690
+ thread_name: "logd.reader.per"
+ process_name: "/system/bin/logd"
+ thread_id: 1274
+ process_id: 211
+ }
+ task_info {
+ estimated_mws: 2.171289
+ estimated_mw: 0.251034
+ idle_transitions_mws: 0.014154
+ thread_name: "RenderThread"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 2301
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 2.143151
+ estimated_mw: 0.247781
+ idle_transitions_mws: 0.052887
+ thread_name: "InputReader"
+ process_name: "system_server"
+ thread_id: 1784
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 2.091430
+ estimated_mw: 0.241801
+ idle_transitions_mws: 2.681956
+ thread_name: "rcu_preempt"
+ process_name: "rcu_preempt"
+ thread_id: 14
+ process_id: 14
+ }
+ task_info {
+ estimated_mws: 2.048920
+ estimated_mw: 0.236886
+ idle_transitions_mws: 0.122795
+ thread_name: "binder:1926_4"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2262
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 1.914560
+ estimated_mw: 0.221352
+ idle_transitions_mws: 0.033359
+ thread_name: "arable.systemui"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2171
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 1.854433
+ estimated_mw: 0.214401
+ idle_transitions_mws: 0.046845
+ thread_name: "android.ui"
+ process_name: "system_server"
+ thread_id: 1416
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.777087
+ estimated_mw: 0.205458
+ idle_transitions_mws: 0.428240
+ thread_name: "kworker/u8:4"
+ process_name: "kworker/u8:4"
+ thread_id: 431
+ process_id: 431
+ }
+ task_info {
+ estimated_mws: 1.773777
+ estimated_mw: 0.205076
+ idle_transitions_mws: 0.584214
+ thread_name: "TimerDispatch"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 865
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 1.760400
+ estimated_mw: 0.203529
+ idle_transitions_mws: 0.078772
+ thread_name: "ActivityManager"
+ process_name: "system_server"
+ thread_id: 1431
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.733169
+ estimated_mw: 0.200381
+ idle_transitions_mws: 0.039163
+ thread_name: "PowerManagerSer"
+ process_name: "system_server"
+ thread_id: 1506
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.639501
+ estimated_mw: 0.189551
+ thread_name: "WifiHandlerThre"
+ process_name: "system_server"
+ thread_id: 1818
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.631037
+ estimated_mw: 0.188573
+ idle_transitions_mws: 0.170842
+ thread_name: "binder:755_5"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 1987
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 1.605931
+ estimated_mw: 0.185670
+ idle_transitions_mws: 0.400486
+ thread_name: "kgsl_dispatcher"
+ process_name: "kgsl_dispatcher"
+ thread_id: 111
+ process_id: 111
+ }
+ task_info {
+ estimated_mws: 1.564964
+ estimated_mw: 0.180934
+ idle_transitions_mws: 0.000901
+ thread_name: "binder:1302_8"
+ process_name: "system_server"
+ thread_id: 1679
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.476619
+ estimated_mw: 0.170720
+ idle_transitions_mws: 0.013637
+ thread_name: "lowpool[5]"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3489
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 1.470155
+ estimated_mw: 0.169972
+ thread_name: "-Executor] idle"
+ process_name: "com.google.android.gms"
+ thread_id: 5591
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 1.469958
+ estimated_mw: 0.169950
+ idle_transitions_mws: 0.002534
+ thread_name: "binder:1302_B"
+ process_name: "system_server"
+ thread_id: 2033
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.390635
+ estimated_mw: 0.160779
+ idle_transitions_mws: 0.083044
+ thread_name: "binder:755_4"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 1125
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 1.327049
+ estimated_mw: 0.153427
+ idle_transitions_mws: 0.029246
+ thread_name: "batterystats-ha"
+ process_name: "system_server"
+ thread_id: 1484
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.312721
+ estimated_mw: 0.151771
+ thread_name: "statsd.writer"
+ process_name: "/apex/com.android.os.statsd/bin/statsd"
+ thread_id: 980
+ process_id: 545
+ }
+ task_info {
+ estimated_mws: 1.252738
+ estimated_mw: 0.144836
+ idle_transitions_mws: 0.705073
+ thread_name: "kworker/u8:2"
+ process_name: "kworker/u8:2"
+ thread_id: 62
+ process_id: 62
+ }
+ task_info {
+ estimated_mws: 1.251944
+ estimated_mw: 0.144744
+ idle_transitions_mws: 0.206733
+ thread_name: "app"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 867
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 1.233674
+ estimated_mw: 0.142632
+ idle_transitions_mws: 0.066972
+ thread_name: "system_server"
+ process_name: "system_server"
+ thread_id: 1343
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 1.228813
+ estimated_mw: 0.142070
+ idle_transitions_mws: 0.785543
+ thread_name: "irq/33-4520300."
+ process_name: "irq/33-4520300.qcom,bwmon-ddr"
+ thread_id: 95
+ process_id: 95
+ }
+ task_info {
+ estimated_mws: 1.068197
+ estimated_mw: 0.123500
+ idle_transitions_mws: 0.111670
+ thread_name: "logd.klogd"
+ process_name: "/system/bin/logd"
+ thread_id: 234
+ process_id: 211
+ }
+ task_info {
+ estimated_mws: 1.007070
+ estimated_mw: 0.116433
+ idle_transitions_mws: 0.029416
+ thread_name: "android.fg"
+ process_name: "system_server"
+ thread_id: 1415
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.969980
+ estimated_mw: 0.112144
+ idle_transitions_mws: 0.455999
+ thread_name: "rcuog/0"
+ process_name: "rcuog/0"
+ thread_id: 15
+ process_id: 15
+ }
+ task_info {
+ estimated_mws: 0.952077
+ estimated_mw: 0.110075
+ idle_transitions_mws: 0.102993
+ thread_name: "binder:1926_3"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1940
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.946746
+ estimated_mw: 0.109458
+ idle_transitions_mws: 0.007196
+ thread_name: "gle.android.gms"
+ process_name: "com.google.android.gms"
+ thread_id: 2856
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.930774
+ estimated_mw: 0.107612
+ idle_transitions_mws: 0.738786
+ thread_name: "crtc_event:80"
+ process_name: "crtc_event:80"
+ thread_id: 245
+ process_id: 245
+ }
+ task_info {
+ estimated_mws: 0.907425
+ estimated_mw: 0.104912
+ idle_transitions_mws: 0.009862
+ thread_name: "binder:755_3"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 1124
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.897620
+ estimated_mw: 0.103779
+ idle_transitions_mws: 0.007379
+ thread_name: "init"
+ process_name: "/system/bin/init"
+ thread_id: 143
+ process_id: 1
+ }
+ task_info {
+ estimated_mws: 0.880853
+ estimated_mw: 0.101840
+ idle_transitions_mws: 0.013499
+ thread_name: "wmshell.main"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2260
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.870598
+ estimated_mw: 0.100654
+ idle_transitions_mws: 0.005937
+ thread_name: "Primes-1"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1944
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.847641
+ estimated_mw: 0.098000
+ idle_transitions_mws: 0.013271
+ thread_name: "init"
+ process_name: "/system/bin/init"
+ thread_id: 1
+ process_id: 1
+ }
+ task_info {
+ estimated_mws: 0.846054
+ estimated_mw: 0.097817
+ thread_name: "binder:1302_D"
+ process_name: "system_server"
+ thread_id: 2043
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.844958
+ estimated_mw: 0.097690
+ idle_transitions_mws: 0.202229
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 786
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.833920
+ estimated_mw: 0.096414
+ idle_transitions_mws: 0.342121
+ thread_name: "kworker/u8:5"
+ process_name: "kworker/u8:5"
+ thread_id: 5304
+ process_id: 5304
+ }
+ task_info {
+ estimated_mws: 0.780835
+ estimated_mw: 0.090276
+ idle_transitions_mws: 0.182034
+ thread_name: "kworker/2:4"
+ process_name: "kworker/2:4"
+ thread_id: 4995
+ process_id: 4995
+ }
+ task_info {
+ estimated_mws: 0.747755
+ estimated_mw: 0.086452
+ idle_transitions_mws: 0.004701
+ thread_name: "binder:2171_4"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2374
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.746488
+ estimated_mw: 0.086305
+ idle_transitions_mws: 0.043573
+ thread_name: "binder:1999_5"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 3678
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.744159
+ estimated_mw: 0.086036
+ idle_transitions_mws: 0.120816
+ thread_name: "servicemanager"
+ process_name: "/system/bin/servicemanager"
+ thread_id: 213
+ process_id: 213
+ }
+ task_info {
+ estimated_mws: 0.717520
+ estimated_mw: 0.082956
+ idle_transitions_mws: 0.004484
+ thread_name: "wmshell.anim"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2269
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.699681
+ estimated_mw: 0.080894
+ thread_name: "GoogleApiHandle"
+ process_name: "com.google.android.gms"
+ thread_id: 3208
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.675997
+ estimated_mw: 0.078156
+ idle_transitions_mws: 0.003826
+ thread_name: "binder:1302_4"
+ process_name: "system_server"
+ thread_id: 1592
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.647999
+ estimated_mw: 0.074919
+ thread_name: "batterystats-wo"
+ process_name: "system_server"
+ thread_id: 1487
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.640576
+ estimated_mw: 0.074060
+ idle_transitions_mws: 0.005443
+ thread_name: ".gms.persistent"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 1949
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.631830
+ estimated_mw: 0.073049
+ idle_transitions_mws: 0.013282
+ thread_name: "binder:1926_6"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 5211
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.627672
+ estimated_mw: 0.072568
+ idle_transitions_mws: 0.000795
+ thread_name: "DisplayOffloadB"
+ process_name: "system_server"
+ thread_id: 1512
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.627487
+ estimated_mw: 0.072547
+ thread_name: "binder:682_2"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
+ thread_id: 682
+ process_id: 682
+ }
+ task_info {
+ estimated_mws: 0.624294
+ estimated_mw: 0.072178
+ idle_transitions_mws: 0.431093
+ thread_name: "rcuog/2"
+ process_name: "rcuog/2"
+ thread_id: 37
+ process_id: 37
+ }
+ task_info {
+ estimated_mws: 0.623909
+ estimated_mw: 0.072133
+ idle_transitions_mws: 0.274820
+ thread_name: "kworker/0:6"
+ process_name: "kworker/0:6"
+ thread_id: 586
+ process_id: 586
+ }
+ task_info {
+ estimated_mws: 0.597177
+ estimated_mw: 0.069043
+ thread_name: "diag-router"
+ process_name: "/vendor/bin/diag-router"
+ thread_id: 634
+ process_id: 634
+ }
+ task_info {
+ estimated_mws: 0.582498
+ estimated_mw: 0.067346
+ thread_name: "HeapTaskDaemon"
+ process_name: "com.google.android.gms"
+ thread_id: 2882
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.579675
+ estimated_mw: 0.067019
+ idle_transitions_mws: 0.102756
+ thread_name: "FileWatcherThre"
+ process_name: "/vendor/bin/hw/android.hardware.thermal-service.pixel"
+ thread_id: 1411
+ process_id: 1404
+ }
+ task_info {
+ estimated_mws: 0.568415
+ estimated_mw: 0.065717
+ idle_transitions_mws: 0.004498
+ thread_name: "TaskSnapshotPer"
+ process_name: "system_server"
+ thread_id: 1913
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.565566
+ estimated_mw: 0.065388
+ idle_transitions_mws: 0.016823
+ thread_name: "lmkd"
+ process_name: "/system/bin/lmkd"
+ thread_id: 212
+ process_id: 212
+ }
+ task_info {
+ estimated_mws: 0.554734
+ estimated_mw: 0.064136
+ idle_transitions_mws: 0.001437
+ thread_name: "binder:1949_8"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3269
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.517529
+ estimated_mw: 0.059834
+ idle_transitions_mws: 0.084239
+ thread_name: "appSf"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 868
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.514221
+ estimated_mw: 0.059452
+ idle_transitions_mws: 0.542479
+ thread_name: "kworker/1:1"
+ process_name: "kworker/1:1"
+ thread_id: 47
+ process_id: 47
+ }
+ task_info {
+ estimated_mws: 0.507581
+ estimated_mw: 0.058684
+ idle_transitions_mws: 0.039847
+ thread_name: "android.hardwar"
+ process_name: "/vendor/bin/hw/android.hardware.usb-service.qti"
+ thread_id: 1861
+ process_id: 665
+ }
+ task_info {
+ estimated_mws: 0.504068
+ estimated_mw: 0.058278
+ idle_transitions_mws: 0.058645
+ thread_name: "Primes-Jank"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2389
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.493578
+ estimated_mw: 0.057065
+ idle_transitions_mws: 0.006902
+ thread_name: "binder:2171_3"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2235
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.490345
+ estimated_mw: 0.056691
+ idle_transitions_mws: 0.005860
+ thread_name: "traced"
+ process_name: "/system/bin/traced"
+ thread_id: 905
+ process_id: 905
+ }
+ task_info {
+ estimated_mws: 0.468415
+ estimated_mw: 0.054156
+ thread_name: "eduling.default"
+ process_name: "system_server"
+ thread_id: 1761
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.462913
+ estimated_mw: 0.053520
+ idle_transitions_mws: 0.021445
+ thread_name: "binder:545_2"
+ process_name: "/apex/com.android.os.statsd/bin/statsd"
+ thread_id: 553
+ process_id: 545
+ }
+ task_info {
+ estimated_mws: 0.462537
+ estimated_mw: 0.053476
+ thread_name: "User"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2234
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.454063
+ estimated_mw: 0.052497
+ idle_transitions_mws: 0.032182
+ thread_name: "putmethod.latin"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 4997
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.450612
+ estimated_mw: 0.052098
+ thread_name: "ueventd"
+ process_name: "/system/bin/ueventd"
+ thread_id: 145
+ process_id: 145
+ }
+ task_info {
+ estimated_mws: 0.448044
+ estimated_mw: 0.051801
+ thread_name: "wpa_supplicant"
+ process_name: "/vendor/bin/hw/wpa_supplicant"
+ thread_id: 5214
+ process_id: 5214
+ }
+ task_info {
+ estimated_mws: 0.431304
+ estimated_mw: 0.049865
+ idle_transitions_mws: 0.223655
+ thread_name: "rcuop/0"
+ process_name: "rcuop/0"
+ thread_id: 16
+ process_id: 16
+ }
+ task_info {
+ estimated_mws: 0.416635
+ estimated_mw: 0.048169
+ idle_transitions_mws: 0.003314
+ thread_name: "Jit thread pool"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1933
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.404592
+ estimated_mw: 0.046777
+ thread_name: "pixelstats-vend"
+ process_name: "/vendor/bin/pixelstats-vendor"
+ thread_id: 267
+ process_id: 255
+ }
+ task_info {
+ estimated_mws: 0.396838
+ estimated_mw: 0.045880
+ idle_transitions_mws: 0.096821
+ thread_name: "irq/236-NVT-ts"
+ process_name: "irq/236-NVT-ts"
+ thread_id: 505
+ process_id: 505
+ }
+ task_info {
+ estimated_mws: 0.393249
+ estimated_mw: 0.045466
+ idle_transitions_mws: 0.004124
+ thread_name: "nanohub"
+ process_name: "nanohub"
+ thread_id: 297
+ process_id: 297
+ }
+ task_info {
+ estimated_mws: 0.376686
+ estimated_mw: 0.043551
+ idle_transitions_mws: 0.007074
+ thread_name: "android.bg"
+ process_name: "system_server"
+ thread_id: 1430
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.375869
+ estimated_mw: 0.043456
+ idle_transitions_mws: 0.002342
+ thread_name: "chre"
+ process_name: "/vendor/bin/chre"
+ thread_id: 1041
+ process_id: 1041
+ }
+ task_info {
+ estimated_mws: 0.373520
+ estimated_mw: 0.043185
+ idle_transitions_mws: 0.003289
+ thread_name: "lowpool[1]"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2279
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.366001
+ estimated_mw: 0.042315
+ idle_transitions_mws: 0.018638
+ thread_name: "TracingMuxer"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 783
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.359494
+ estimated_mw: 0.041563
+ idle_transitions_mws: 0.136611
+ thread_name: "kgsl-events"
+ process_name: "kgsl-events"
+ thread_id: 109
+ process_id: 109
+ }
+ task_info {
+ estimated_mws: 0.359130
+ estimated_mw: 0.041521
+ thread_name: "IpClient.wlan0"
+ process_name: "com.android.networkstack.process"
+ thread_id: 5216
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.346517
+ estimated_mw: 0.040063
+ idle_transitions_mws: 0.001877
+ thread_name: "binder:257_5"
+ process_name: "/system/bin/hw/android.system.suspend-service"
+ thread_id: 1491
+ process_id: 257
+ }
+ task_info {
+ estimated_mws: 0.341200
+ estimated_mw: 0.039448
+ idle_transitions_mws: 0.001228
+ thread_name: "binder:1901_3"
+ process_name: "/vendor/bin/hw/android.hardware.wifi-service-lazy"
+ thread_id: 1905
+ process_id: 1901
+ }
+ task_info {
+ estimated_mws: 0.335534
+ estimated_mw: 0.038793
+ thread_name: "binder:740_1"
+ process_name: "/system/bin/audioserver"
+ thread_id: 821
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.331405
+ estimated_mw: 0.038316
+ idle_transitions_mws: 0.001044
+ thread_name: "BG"
+ process_name: "com.google.wear.services"
+ thread_id: 2023
+ process_id: 1948
+ }
+ task_info {
+ estimated_mws: 0.326344
+ estimated_mw: 0.037730
+ idle_transitions_mws: 0.045342
+ thread_name: "kworker/0:5H"
+ process_name: "kworker/0:5H"
+ thread_id: 1337
+ process_id: 1337
+ }
+ task_info {
+ estimated_mws: 0.322384
+ estimated_mw: 0.037273
+ idle_transitions_mws: 0.002751
+ thread_name: "binder:755_2"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 784
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.319511
+ estimated_mw: 0.036940
+ thread_name: "audioserver"
+ process_name: "/system/bin/audioserver"
+ thread_id: 740
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.310996
+ estimated_mw: 0.035956
+ idle_transitions_mws: 0.007033
+ thread_name: "binder:1949_2"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 1978
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.302115
+ estimated_mw: 0.034929
+ idle_transitions_mws: 0.000843
+ thread_name: "-Executor] idle"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 5602
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.301578
+ estimated_mw: 0.034867
+ thread_name: "pool-11-thread-"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3329
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.299169
+ estimated_mw: 0.034589
+ thread_name: "android.io"
+ process_name: "system_server"
+ thread_id: 1417
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.296825
+ estimated_mw: 0.034318
+ thread_name: "binder:1901_3"
+ process_name: "/vendor/bin/hw/android.hardware.wifi-service-lazy"
+ thread_id: 5205
+ process_id: 1901
+ }
+ task_info {
+ estimated_mws: 0.294242
+ estimated_mw: 0.034019
+ idle_transitions_mws: 0.142357
+ thread_name: "rcuop/1"
+ process_name: "rcuop/1"
+ thread_id: 30
+ process_id: 30
+ }
+ task_info {
+ estimated_mws: 0.286642
+ estimated_mw: 0.033140
+ thread_name: "binder:1948_6"
+ process_name: "com.google.wear.services"
+ thread_id: 5315
+ process_id: 1948
+ }
+ task_info {
+ estimated_mws: 0.285983
+ estimated_mw: 0.033064
+ thread_name: "AssistantHandle"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4081
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.283378
+ estimated_mw: 0.032763
+ idle_transitions_mws: 0.023300
+ thread_name: "binder:1999_1"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 2016
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.279959
+ estimated_mw: 0.032367
+ idle_transitions_mws: 0.001981
+ thread_name: "binder:2182_7"
+ process_name: "com.android.phone"
+ thread_id: 2694
+ process_id: 2182
+ }
+ task_info {
+ estimated_mws: 0.279816
+ estimated_mw: 0.032351
+ idle_transitions_mws: 0.055476
+ thread_name: "kworker/3:2H"
+ process_name: "kworker/3:2H"
+ thread_id: 226
+ process_id: 226
+ }
+ task_info {
+ estimated_mws: 0.277230
+ estimated_mw: 0.032052
+ idle_transitions_mws: 0.002913
+ thread_name: "BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 3005
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.274735
+ estimated_mw: 0.031764
+ thread_name: "lowpool[3]"
+ process_name: "com.google.android.gms"
+ thread_id: 3527
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.267749
+ estimated_mw: 0.030956
+ idle_transitions_mws: 0.038805
+ thread_name: "hvdcp_opti"
+ process_name: "/vendor/bin/hvdcp_opti"
+ thread_id: 1276
+ process_id: 1270
+ }
+ task_info {
+ estimated_mws: 0.262081
+ estimated_mw: 0.030301
+ idle_transitions_mws: 0.190243
+ thread_name: "binder:1926_3"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2022
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.259248
+ estimated_mw: 0.029973
+ idle_transitions_mws: 0.172554
+ thread_name: "kworker/3:5"
+ process_name: "kworker/3:5"
+ thread_id: 104
+ process_id: 104
+ }
+ task_info {
+ estimated_mws: 0.256714
+ estimated_mw: 0.029680
+ idle_transitions_mws: 0.002836
+ thread_name: "binder:257_2"
+ process_name: "/system/bin/hw/android.system.suspend-service"
+ thread_id: 264
+ process_id: 257
+ }
+ task_info {
+ estimated_mws: 0.247037
+ estimated_mw: 0.028561
+ idle_transitions_mws: 0.088455
+ thread_name: "SDM_EventThread"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
+ thread_id: 727
+ process_id: 685
+ }
+ task_info {
+ estimated_mws: 0.244112
+ estimated_mw: 0.028223
+ thread_name: "POSIX timer 2"
+ process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
+ thread_id: 1600
+ process_id: 664
+ }
+ task_info {
+ estimated_mws: 0.242754
+ estimated_mw: 0.028066
+ idle_transitions_mws: 0.005424
+ thread_name: "binder:2856_4"
+ process_name: "com.google.android.gms"
+ thread_id: 3679
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.241348
+ estimated_mw: 0.027904
+ thread_name: "pool-2-thread-1"
+ process_name: "com.android.networkstack.process"
+ thread_id: 2416
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.231145
+ estimated_mw: 0.026724
+ idle_transitions_mws: 0.081316
+ thread_name: "rcuop/3"
+ process_name: "rcuop/3"
+ thread_id: 45
+ process_id: 45
+ }
+ task_info {
+ estimated_mws: 0.230341
+ estimated_mw: 0.026631
+ idle_transitions_mws: 0.003605
+ thread_name: "f2fs_ckpt-254:4"
+ process_name: "f2fs_ckpt-254:43"
+ thread_id: 347
+ process_id: 347
+ }
+ task_info {
+ estimated_mws: 0.229722
+ estimated_mw: 0.026559
+ thread_name: "OomAdjuster"
+ process_name: "system_server"
+ thread_id: 1482
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.226417
+ estimated_mw: 0.026177
+ idle_transitions_mws: 0.001358
+ thread_name: "binder:740_6"
+ process_name: "/system/bin/audioserver"
+ thread_id: 2639
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.226007
+ estimated_mw: 0.026130
+ idle_transitions_mws: 0.054115
+ thread_name: "rcuop/2"
+ process_name: "rcuop/2"
+ thread_id: 38
+ process_id: 38
+ }
+ task_info {
+ estimated_mws: 0.225133
+ estimated_mw: 0.026029
+ idle_transitions_mws: 0.065811
+ thread_name: "kworker/0:7"
+ process_name: "kworker/0:7"
+ thread_id: 598
+ process_id: 598
+ }
+ task_info {
+ estimated_mws: 0.212788
+ estimated_mw: 0.024602
+ idle_transitions_mws: 0.006439
+ thread_name: "queued-work-loo"
+ process_name: "system_server"
+ thread_id: 1886
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.202562
+ estimated_mw: 0.023419
+ thread_name: "pool-13-thread-"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3327
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.201687
+ estimated_mw: 0.023318
+ idle_transitions_mws: 0.001195
+ thread_name: "WearSdkThread"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2207
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.201119
+ estimated_mw: 0.023252
+ thread_name: "qrtr_ns"
+ process_name: "qrtr_ns"
+ thread_id: 88
+ process_id: 88
+ }
+ task_info {
+ estimated_mws: 0.200639
+ estimated_mw: 0.023197
+ thread_name: "binder:740_7"
+ process_name: "/system/bin/audioserver"
+ thread_id: 5206
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.196587
+ estimated_mw: 0.022729
+ idle_transitions_mws: 0.002857
+ thread_name: "binder:4997_4"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5122
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.194754
+ estimated_mw: 0.022517
+ thread_name: "m.android.phone"
+ process_name: "com.android.phone"
+ thread_id: 2182
+ process_id: 2182
+ }
+ task_info {
+ estimated_mws: 0.192336
+ estimated_mw: 0.022237
+ idle_transitions_mws: 0.006386
+ thread_name: "HwcAsyncWorker"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 835
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.190522
+ estimated_mw: 0.022027
+ thread_name: "binder:636_2"
+ process_name: "/vendor/bin/hw/android.hardware.audio.service"
+ thread_id: 636
+ process_id: 636
+ }
+ task_info {
+ estimated_mws: 0.188908
+ estimated_mw: 0.021841
+ thread_name: "SettingsProvide"
+ process_name: "system_server"
+ thread_id: 1771
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.181172
+ estimated_mw: 0.020946
+ thread_name: "binder:1302_2"
+ process_name: "system_server"
+ thread_id: 1350
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.177579
+ estimated_mw: 0.020531
+ idle_transitions_mws: 0.111479
+ thread_name: "RegSampIdle"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 872
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.168738
+ estimated_mw: 0.019509
+ thread_name: "ice] processing"
+ process_name: "com.google.android.gms"
+ thread_id: 3238
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.162808
+ estimated_mw: 0.018823
+ thread_name: "binder:682_3"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.allocator-service"
+ thread_id: 2308
+ process_id: 682
+ }
+ task_info {
+ estimated_mws: 0.158857
+ estimated_mw: 0.018366
+ thread_name: "binder:678_3"
+ process_name: "/apex/com.google.wearable.wac.whshal/bin/hw/vendor.google.wearable.wac.whshal@2.0-service"
+ thread_id: 1884
+ process_id: 678
+ }
+ task_info {
+ estimated_mws: 0.158692
+ estimated_mw: 0.018347
+ idle_transitions_mws: 0.016263
+ thread_name: "binder:650_4"
+ process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
+ thread_id: 5498
+ process_id: 650
+ }
+ task_info {
+ estimated_mws: 0.157956
+ estimated_mw: 0.018262
+ idle_transitions_mws: 0.002306
+ thread_name: "vndservicemanag"
+ process_name: "/vendor/bin/vndservicemanager"
+ thread_id: 215
+ process_id: 215
+ }
+ task_info {
+ estimated_mws: 0.153600
+ estimated_mw: 0.017759
+ thread_name: "GoogleLocationS"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3355
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.153038
+ estimated_mw: 0.017694
+ thread_name: "TransportThread"
+ process_name: "/vendor/bin/chre"
+ thread_id: 1078
+ process_id: 1041
+ }
+ task_info {
+ estimated_mws: 0.152058
+ estimated_mw: 0.017580
+ idle_transitions_mws: 0.068137
+ thread_name: "kworker/2:1H"
+ process_name: "kworker/2:1H"
+ thread_id: 123
+ process_id: 123
+ }
+ task_info {
+ estimated_mws: 0.148559
+ estimated_mw: 0.017176
+ idle_transitions_mws: 0.002115
+ thread_name: "BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2120
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.148352
+ estimated_mw: 0.017152
+ thread_name: "vendor.google.w"
+ process_name: "/apex/com.google.wearable.wac.whshal/bin/hw/vendor.google.wearable.wac.whshal@2.0-service"
+ thread_id: 1881
+ process_id: 678
+ }
+ task_info {
+ estimated_mws: 0.140864
+ estimated_mw: 0.016286
+ thread_name: "NetworkStats"
+ process_name: "system_server"
+ thread_id: 1814
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.138579
+ estimated_mw: 0.016022
+ idle_transitions_mws: 0.021968
+ thread_name: "binder:969_2"
+ process_name: "/system/vendor/bin/cnd"
+ thread_id: 1011
+ process_id: 969
+ }
+ task_info {
+ estimated_mws: 0.134607
+ estimated_mw: 0.015563
+ idle_transitions_mws: 0.010586
+ thread_name: "dmabuf-deferred"
+ process_name: "dmabuf-deferred-free-worker"
+ thread_id: 69
+ process_id: 69
+ }
+ task_info {
+ estimated_mws: 0.129449
+ estimated_mw: 0.014966
+ thread_name: "highpool[5]"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3354
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.126973
+ estimated_mw: 0.014680
+ thread_name: "ice] processing"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2363
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.126830
+ estimated_mw: 0.014663
+ thread_name: "ediator.Toggler"
+ process_name: "system_server"
+ thread_id: 1910
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.125742
+ estimated_mw: 0.014538
+ idle_transitions_mws: 0.064827
+ thread_name: "surfaceflinger"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 875
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.123833
+ estimated_mw: 0.014317
+ thread_name: "wificond"
+ process_name: "/system/bin/wificond"
+ thread_id: 964
+ process_id: 964
+ }
+ task_info {
+ estimated_mws: 0.123248
+ estimated_mw: 0.014249
+ thread_name: "MobileDataStats"
+ process_name: "system_server"
+ thread_id: 1912
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.119885
+ estimated_mw: 0.013860
+ thread_name: "GlobalScheduler"
+ process_name: "com.google.android.gms"
+ thread_id: 3156
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.119479
+ estimated_mw: 0.013814
+ idle_transitions_mws: 0.000930
+ thread_name: "RenderThread"
+ thread_id: 5599
+ }
+ task_info {
+ estimated_mws: 0.119243
+ estimated_mw: 0.013786
+ idle_transitions_mws: 0.008398
+ thread_name: "TouchTimer"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 866
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.114249
+ estimated_mw: 0.013209
+ thread_name: "binder:4038_1"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4050
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.112705
+ estimated_mw: 0.013030
+ idle_transitions_mws: 0.011129
+ thread_name: "displayoffload@"
+ process_name: "/vendor/bin/hw/vendor.google_clockwork.displayoffload@2.0-service.1p"
+ thread_id: 937
+ process_id: 937
+ }
+ task_info {
+ estimated_mws: 0.111279
+ estimated_mw: 0.012866
+ idle_transitions_mws: 0.003661
+ thread_name: "adbd"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5544
+ process_id: 5544
+ }
+ task_info {
+ estimated_mws: 0.108114
+ estimated_mw: 0.012500
+ thread_name: "kworker/1:2H"
+ process_name: "kworker/1:2H"
+ thread_id: 300
+ process_id: 300
+ }
+ task_info {
+ estimated_mws: 0.107442
+ estimated_mw: 0.012422
+ idle_transitions_mws: 0.003376
+ thread_name: "RenderThread"
+ thread_id: 5584
+ }
+ task_info {
+ estimated_mws: 0.105863
+ estimated_mw: 0.012239
+ idle_transitions_mws: 0.006458
+ thread_name: "Primes-2"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1946
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.104306
+ estimated_mw: 0.012059
+ thread_name: "iptables-restor"
+ process_name: "/system/bin/iptables-restore"
+ thread_id: 558
+ process_id: 558
+ }
+ task_info {
+ estimated_mws: 0.104093
+ estimated_mw: 0.012035
+ thread_name: "RenderThread"
+ process_name: "system_server"
+ thread_id: 5223
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.102912
+ estimated_mw: 0.011898
+ idle_transitions_mws: 0.006649
+ thread_name: "irq/168-nanohub"
+ process_name: "irq/168-nanohub-irq1"
+ thread_id: 296
+ process_id: 296
+ }
+ task_info {
+ estimated_mws: 0.102167
+ estimated_mw: 0.011812
+ idle_transitions_mws: 0.003890
+ thread_name: "RenderThread"
+ thread_id: 5604
+ }
+ task_info {
+ estimated_mws: 0.101945
+ estimated_mw: 0.011786
+ thread_name: "ksoftirqd/2"
+ process_name: "ksoftirqd/2"
+ thread_id: 34
+ process_id: 34
+ }
+ task_info {
+ estimated_mws: 0.101282
+ estimated_mw: 0.011710
+ thread_name: "PhotonicModulat"
+ process_name: "system_server"
+ thread_id: 1899
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.100285
+ estimated_mw: 0.011595
+ thread_name: "ip6tables-resto"
+ process_name: "/system/bin/ip6tables-restore"
+ thread_id: 559
+ process_id: 559
+ }
+ task_info {
+ estimated_mws: 0.099432
+ estimated_mw: 0.011496
+ thread_name: "init"
+ process_name: "/system/bin/init"
+ thread_id: 144
+ process_id: 144
+ }
+ task_info {
+ estimated_mws: 0.096314
+ estimated_mw: 0.011135
+ thread_name: "FrameworkReceiv"
+ process_name: ".qtidataservices"
+ thread_id: 2793
+ process_id: 2118
+ }
+ task_info {
+ estimated_mws: 0.095442
+ estimated_mw: 0.011035
+ thread_name: "Jit thread pool"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 1969
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.094448
+ estimated_mw: 0.010920
+ thread_name: "pool-14-thread-"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3314
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.093937
+ estimated_mw: 0.010861
+ thread_name: "binder:1926_2"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1939
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.093621
+ estimated_mw: 0.010824
+ thread_name: "ChreMsgHandler"
+ process_name: "/vendor/bin/chre"
+ thread_id: 1080
+ process_id: 1041
+ }
+ task_info {
+ estimated_mws: 0.091738
+ estimated_mw: 0.010606
+ idle_transitions_mws: 0.001106
+ thread_name: "DispatcherModul"
+ process_name: "/vendor/bin/hw/qcrilNrd"
+ thread_id: 1673
+ process_id: 1062
+ }
+ task_info {
+ estimated_mws: 0.091698
+ estimated_mw: 0.010602
+ idle_transitions_mws: 0.047196
+ thread_name: "irq/234-pixart_"
+ process_name: "irq/234-pixart_pat9126_irq"
+ thread_id: 500
+ process_id: 500
+ }
+ task_info {
+ estimated_mws: 0.090883
+ estimated_mw: 0.010507
+ thread_name: "scheduler_threa"
+ process_name: "scheduler_thread"
+ thread_id: 5198
+ process_id: 5198
+ }
+ task_info {
+ estimated_mws: 0.089001
+ estimated_mw: 0.010290
+ thread_name: "binder:2085_4"
+ process_name: "com.google.android.bluetooth"
+ thread_id: 2713
+ process_id: 2085
+ }
+ task_info {
+ estimated_mws: 0.086934
+ estimated_mw: 0.010051
+ idle_transitions_mws: 0.001387
+ thread_name: "binder:3028_5"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 5434
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.085941
+ estimated_mw: 0.009936
+ thread_name: "binder:2670_6"
+ process_name: "com.android.nfc"
+ thread_id: 3159
+ process_id: 2670
+ }
+ task_info {
+ estimated_mws: 0.083859
+ estimated_mw: 0.009695
+ idle_transitions_mws: 0.289424
+ thread_name: "psimon"
+ process_name: "psimon"
+ thread_id: 1480
+ process_id: 1480
+ }
+ task_info {
+ estimated_mws: 0.083773
+ estimated_mw: 0.009685
+ thread_name: "binder:233_2"
+ process_name: "/system/bin/vold"
+ thread_id: 252
+ process_id: 233
+ }
+ task_info {
+ estimated_mws: 0.080959
+ estimated_mw: 0.009360
+ thread_name: "binder:2049_2"
+ process_name: "com.android.networkstack.process"
+ thread_id: 2068
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.080298
+ estimated_mw: 0.009284
+ thread_name: "netd"
+ process_name: "/system/bin/netd"
+ thread_id: 568
+ process_id: 546
+ }
+ task_info {
+ estimated_mws: 0.080265
+ estimated_mw: 0.009280
+ thread_name: "UEventObserver"
+ process_name: "system_server"
+ thread_id: 1857
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.079337
+ estimated_mw: 0.009173
+ thread_name: "RenderThread"
+ thread_id: 5619
+ }
+ task_info {
+ estimated_mws: 0.078696
+ estimated_mw: 0.009098
+ thread_name: "pool-8-thread-1"
+ process_name: "com.google.android.gms"
+ thread_id: 3102
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.077362
+ estimated_mw: 0.008944
+ idle_transitions_mws: 0.019002
+ thread_name: "mcu_mgmtd"
+ process_name: "/vendor/bin/mcu_mgmtd"
+ thread_id: 594
+ process_id: 524
+ }
+ task_info {
+ estimated_mws: 0.074501
+ estimated_mw: 0.008613
+ thread_name: "spi0"
+ process_name: "spi0"
+ thread_id: 295
+ process_id: 295
+ }
+ task_info {
+ estimated_mws: 0.073914
+ estimated_mw: 0.008546
+ thread_name: "com.android.nfc"
+ process_name: "com.android.nfc"
+ thread_id: 2670
+ process_id: 2670
+ }
+ task_info {
+ estimated_mws: 0.072671
+ estimated_mw: 0.008402
+ idle_transitions_mws: 0.063131
+ thread_name: "rcu_exp_gp_kthr"
+ process_name: "rcu_exp_gp_kthread_worker"
+ thread_id: 19
+ process_id: 19
+ }
+ task_info {
+ estimated_mws: 0.070587
+ estimated_mw: 0.008161
+ idle_transitions_mws: 0.005993
+ thread_name: "adbd"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5546
+ process_id: 5544
+ }
+ task_info {
+ estimated_mws: 0.070105
+ estimated_mw: 0.008105
+ thread_name: "servicemanager"
+ thread_id: 5598
+ }
+ task_info {
+ estimated_mws: 0.069214
+ estimated_mw: 0.008002
+ idle_transitions_mws: 0.001447
+ thread_name: "android.imms"
+ process_name: "system_server"
+ thread_id: 1791
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.068600
+ estimated_mw: 0.007931
+ thread_name: "lowpool[2]"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2321
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.068467
+ estimated_mw: 0.007916
+ thread_name: "FileObserver"
+ process_name: "com.google.android.gms"
+ thread_id: 3035
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.068316
+ estimated_mw: 0.007898
+ thread_name: "binder:546_3"
+ process_name: "/system/bin/netd"
+ thread_id: 546
+ process_id: 546
+ }
+ task_info {
+ estimated_mws: 0.066410
+ estimated_mw: 0.007678
+ thread_name: "pool-51-thread-"
+ process_name: "com.google.android.gms"
+ thread_id: 4215
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.064761
+ estimated_mw: 0.007487
+ thread_name: "AudioService"
+ process_name: "system_server"
+ thread_id: 1844
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.064339
+ estimated_mw: 0.007439
+ idle_transitions_mws: 0.003289
+ thread_name: "adbd"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5545
+ process_id: 5544
+ }
+ task_info {
+ estimated_mws: 0.063188
+ estimated_mw: 0.007305
+ thread_name: "droid.bluetooth"
+ process_name: "com.google.android.bluetooth"
+ thread_id: 2085
+ process_id: 2085
+ }
+ task_info {
+ estimated_mws: 0.061069
+ estimated_mw: 0.007061
+ thread_name: "msm_irqbalance"
+ process_name: "/vendor/bin/msm_irqbalance"
+ thread_id: 2466
+ process_id: 2466
+ }
+ task_info {
+ estimated_mws: 0.060920
+ estimated_mw: 0.007043
+ idle_transitions_mws: 0.003380
+ thread_name: "BackgroundInsta"
+ process_name: "system_server"
+ thread_id: 1875
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.059901
+ estimated_mw: 0.006925
+ idle_transitions_mws: 0.008904
+ thread_name: "ConnectivitySer"
+ process_name: "system_server"
+ thread_id: 1827
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.059295
+ estimated_mw: 0.006855
+ idle_transitions_mws: 0.018518
+ thread_name: "pool-1-thread-1"
+ process_name: "system_server"
+ thread_id: 1873
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.059196
+ estimated_mw: 0.006844
+ thread_name: "binder:3028_1"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3049
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.058807
+ estimated_mw: 0.006799
+ idle_transitions_mws: 0.001108
+ thread_name: "roid.apps.scone"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 5245
+ process_id: 5245
+ }
+ task_info {
+ estimated_mws: 0.058053
+ estimated_mw: 0.006712
+ thread_name: "UsbFfs-worker"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5560
+ process_id: 5544
+ }
+ task_info {
+ estimated_mws: 0.057400
+ estimated_mw: 0.006636
+ idle_transitions_mws: 0.034371
+ thread_name: "android.hardwar"
+ process_name: "/vendor/bin/hw/android.hardware.health-service.eos"
+ thread_id: 1271
+ process_id: 1271
+ }
+ task_info {
+ estimated_mws: 0.056947
+ estimated_mw: 0.006584
+ idle_transitions_mws: 0.002094
+ thread_name: "bgres-controlle"
+ process_name: "system_server"
+ thread_id: 1495
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.056879
+ estimated_mw: 0.006576
+ thread_name: "netd"
+ process_name: "/system/bin/netd"
+ thread_id: 569
+ process_id: 546
+ }
+ task_info {
+ estimated_mws: 0.055642
+ estimated_mw: 0.006433
+ thread_name: "system_server"
+ thread_id: 5590
+ }
+ task_info {
+ estimated_mws: 0.054764
+ estimated_mw: 0.006332
+ thread_name: "binder:2049_4"
+ process_name: "com.android.networkstack.process"
+ thread_id: 2083
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.053127
+ estimated_mw: 0.006142
+ thread_name: "binder:1949_4"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2302
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.052984
+ estimated_mw: 0.006126
+ idle_transitions_mws: 0.001354
+ thread_name: "oid.grilservice"
+ process_name: "com.google.android.grilservice"
+ thread_id: 2129
+ process_id: 2129
+ }
+ task_info {
+ estimated_mws: 0.052980
+ estimated_mw: 0.006125
+ thread_name: "binder:2856_9"
+ process_name: "com.google.android.gms"
+ thread_id: 5585
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.052715
+ estimated_mw: 0.006095
+ thread_name: "StateService"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 5269
+ process_id: 5245
+ }
+ task_info {
+ estimated_mws: 0.052232
+ estimated_mw: 0.006039
+ thread_name: "vndservicemanag"
+ thread_id: 5597
+ }
+ task_info {
+ estimated_mws: 0.052027
+ estimated_mw: 0.006015
+ thread_name: "binder:1949_9"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3359
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.051717
+ estimated_mw: 0.005979
+ idle_transitions_mws: 0.019339
+ thread_name: "Ipc-5004:1"
+ process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
+ thread_id: 5483
+ process_id: 650
+ }
+ task_info {
+ estimated_mws: 0.049546
+ estimated_mw: 0.005728
+ thread_name: "PackageManager"
+ process_name: "system_server"
+ thread_id: 1530
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.048227
+ estimated_mw: 0.005576
+ thread_name: "android.hardwar"
+ process_name: "/vendor/bin/hw/android.hardware.power-service"
+ thread_id: 660
+ process_id: 660
+ }
+ task_info {
+ estimated_mws: 0.047454
+ estimated_mw: 0.005486
+ thread_name: "BluetoothScanMa"
+ process_name: "com.google.android.bluetooth"
+ thread_id: 2609
+ process_id: 2085
+ }
+ task_info {
+ estimated_mws: 0.046472
+ estimated_mw: 0.005373
+ thread_name: "subsystem_ramdu"
+ process_name: "/system/vendor/bin/subsystem_ramdump"
+ thread_id: 816
+ process_id: 799
+ }
+ task_info {
+ estimated_mws: 0.046295
+ estimated_mw: 0.005352
+ thread_name: "Ipc-5004:2"
+ process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
+ thread_id: 5484
+ process_id: 650
+ }
+ task_info {
+ estimated_mws: 0.045805
+ estimated_mw: 0.005296
+ thread_name: "binder:975_2"
+ process_name: "/vendor/bin/imsdaemon"
+ thread_id: 1047
+ process_id: 975
+ }
+ task_info {
+ estimated_mws: 0.045282
+ estimated_mw: 0.005235
+ idle_transitions_mws: 0.000972
+ thread_name: ".healthservices"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3028
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.045087
+ estimated_mw: 0.005213
+ idle_transitions_mws: 0.001817
+ thread_name: "queued-work-loo"
+ process_name: "com.google.android.gms"
+ thread_id: 3236
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.043921
+ estimated_mw: 0.005078
+ thread_name: "powerstateservi"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.powerstateservice@1.0-service"
+ thread_id: 276
+ process_id: 269
+ }
+ task_info {
+ estimated_mws: 0.043653
+ estimated_mw: 0.005047
+ idle_transitions_mws: 0.045128
+ thread_name: "wlan_logging_th"
+ process_name: "wlan_logging_thread"
+ thread_id: 368
+ process_id: 368
+ }
+ task_info {
+ estimated_mws: 0.043574
+ estimated_mw: 0.005038
+ thread_name: "FlpThread"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3279
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.043436
+ estimated_mw: 0.005022
+ thread_name: "Light-P0-2"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5071
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.042985
+ estimated_mw: 0.004970
+ idle_transitions_mws: 0.004250
+ thread_name: "binder:5245_4"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 5270
+ process_id: 5245
+ }
+ task_info {
+ estimated_mws: 0.042946
+ estimated_mw: 0.004965
+ thread_name: "HWC_UeventThrea"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.display.composer-service"
+ thread_id: 717
+ process_id: 685
+ }
+ task_info {
+ estimated_mws: 0.042762
+ estimated_mw: 0.004944
+ thread_name: "pool-12-thread-"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2371
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.042752
+ estimated_mw: 0.004943
+ thread_name: "android.hardwar"
+ process_name: "/vendor/bin/hw/android.hardware.contexthub-service.wac"
+ thread_id: 668
+ process_id: 644
+ }
+ task_info {
+ estimated_mws: 0.042468
+ estimated_mw: 0.004910
+ thread_name: "binder:1948_3"
+ process_name: "com.google.wear.services"
+ thread_id: 1976
+ process_id: 1948
+ }
+ task_info {
+ estimated_mws: 0.042220
+ estimated_mw: 0.004881
+ thread_name: "netlink socket"
+ process_name: "/system/vendor/bin/ipacm"
+ thread_id: 538
+ process_id: 523
+ }
+ task_info {
+ estimated_mws: 0.040507
+ estimated_mw: 0.004683
+ idle_transitions_mws: 0.020051
+ thread_name: "RegionSampling"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 871
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.040385
+ estimated_mw: 0.004669
+ thread_name: "TransportThread"
+ process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
+ thread_id: 794
+ process_id: 664
+ }
+ task_info {
+ estimated_mws: 0.040349
+ estimated_mw: 0.004665
+ thread_name: "binder:2856_1"
+ process_name: "com.google.android.gms"
+ thread_id: 2898
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.040180
+ estimated_mw: 0.004645
+ thread_name: "servicemanager"
+ thread_id: 5595
+ }
+ task_info {
+ estimated_mws: 0.039525
+ estimated_mw: 0.004570
+ thread_name: "pd-mapper"
+ process_name: "/vendor/bin/pd-mapper"
+ thread_id: 752
+ process_id: 725
+ }
+ task_info {
+ estimated_mws: 0.039196
+ estimated_mw: 0.004532
+ thread_name: "vndservicemanag"
+ thread_id: 5618
+ }
+ task_info {
+ estimated_mws: 0.039101
+ estimated_mw: 0.004521
+ idle_transitions_mws: 0.001158
+ thread_name: "vndservicemanag"
+ thread_id: 5605
+ }
+ task_info {
+ estimated_mws: 0.038960
+ estimated_mw: 0.004504
+ thread_name: "WifiScanningSer"
+ process_name: "system_server"
+ thread_id: 1823
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.038580
+ estimated_mw: 0.004460
+ thread_name: "cnss-daemon"
+ process_name: "/system/vendor/bin/cnss-daemon"
+ thread_id: 5204
+ process_id: 1009
+ }
+ task_info {
+ estimated_mws: 0.038053
+ estimated_mw: 0.004399
+ idle_transitions_mws: 0.002046
+ thread_name: "shell svc 5620"
+ process_name: "/apex/com.android.adbd/bin/adbd"
+ thread_id: 5622
+ process_id: 5544
+ }
+ task_info {
+ estimated_mws: 0.037116
+ estimated_mw: 0.004291
+ thread_name: "rmt_storage"
+ process_name: "/vendor/bin/rmt_storage"
+ thread_id: 758
+ process_id: 758
+ }
+ task_info {
+ estimated_mws: 0.036357
+ estimated_mw: 0.004203
+ idle_transitions_mws: 0.164265
+ thread_name: "halt_drain_rqs"
+ process_name: "halt_drain_rqs"
+ thread_id: 105
+ process_id: 105
+ }
+ task_info {
+ estimated_mws: 0.035907
+ estimated_mw: 0.004151
+ thread_name: "BG Thread #2"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4106
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.035876
+ estimated_mw: 0.004148
+ idle_transitions_mws: 0.007692
+ thread_name: "-Executor] idle"
+ process_name: "com.google.android.gms"
+ thread_id: 5592
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.035444
+ estimated_mw: 0.004098
+ thread_name: "tftp_server"
+ process_name: "/vendor/bin/tftp_server"
+ thread_id: 759
+ process_id: 759
+ }
+ task_info {
+ estimated_mws: 0.035386
+ estimated_mw: 0.004091
+ thread_name: "FinalizerWatchd"
+ process_name: "com.google.android.gms"
+ thread_id: 2887
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.035171
+ estimated_mw: 0.004066
+ idle_transitions_mws: 0.012569
+ thread_name: "servicemanager"
+ thread_id: 5606
+ }
+ task_info {
+ estimated_mws: 0.035157
+ estimated_mw: 0.004065
+ thread_name: "Blocking Thread"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 5587
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.035034
+ estimated_mw: 0.004050
+ thread_name: "vndservicemanag"
+ thread_id: 5611
+ }
+ task_info {
+ estimated_mws: 0.034307
+ estimated_mw: 0.003966
+ thread_name: "vndservicemanag"
+ thread_id: 5593
+ }
+ task_info {
+ estimated_mws: 0.034030
+ estimated_mw: 0.003934
+ thread_name: "servicemanager"
+ thread_id: 5621
+ }
+ task_info {
+ estimated_mws: 0.032631
+ estimated_mw: 0.003773
+ thread_name: "binder:685_3"
+ thread_id: 5586
+ }
+ task_info {
+ estimated_mws: 0.031847
+ estimated_mw: 0.003682
+ idle_transitions_mws: 0.000891
+ thread_name: "radioext@1.0-se"
+ process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
+ thread_id: 676
+ process_id: 676
+ }
+ task_info {
+ estimated_mws: 0.031818
+ estimated_mw: 0.003679
+ idle_transitions_mws: 0.001158
+ thread_name: "BgBroadcastRegi"
+ process_name: "com.google.wear.services"
+ thread_id: 2017
+ process_id: 1948
+ }
+ task_info {
+ estimated_mws: 0.031204
+ estimated_mw: 0.003608
+ idle_transitions_mws: 0.002208
+ thread_name: "DefaultExecutor"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 5600
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.030138
+ estimated_mw: 0.003484
+ thread_name: "Light-P0-1"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5064
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.029778
+ estimated_mw: 0.003443
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1368
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.029627
+ estimated_mw: 0.003425
+ thread_name: "atchdog.monitor"
+ process_name: "system_server"
+ thread_id: 1414
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.029590
+ estimated_mw: 0.003421
+ idle_transitions_mws: 0.012793
+ thread_name: "UsfHalWorker"
+ process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
+ thread_id: 792
+ process_id: 664
+ }
+ task_info {
+ estimated_mws: 0.028316
+ estimated_mw: 0.003274
+ idle_transitions_mws: 0.003170
+ thread_name: "binder:1999_5"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 4985
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.028097
+ estimated_mw: 0.003248
+ thread_name: "SatelliteContro"
+ process_name: "com.android.phone"
+ thread_id: 2382
+ process_id: 2182
+ }
+ task_info {
+ estimated_mws: 0.027888
+ estimated_mw: 0.003224
+ idle_transitions_mws: 0.009315
+ thread_name: "irq/199-dwc3"
+ process_name: "irq/199-dwc3"
+ thread_id: 5559
+ process_id: 5559
+ }
+ task_info {
+ estimated_mws: 0.027797
+ estimated_mw: 0.003214
+ thread_name: "pm-service"
+ process_name: "/vendor/bin/pm-service"
+ thread_id: 745
+ process_id: 730
+ }
+ task_info {
+ estimated_mws: 0.027301
+ estimated_mw: 0.003156
+ idle_transitions_mws: 0.001040
+ thread_name: "-Executor] idle"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 5603
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.027287
+ estimated_mw: 0.003155
+ idle_transitions_mws: 0.002419
+ thread_name: "perfetto"
+ process_name: "perfetto"
+ thread_id: 5581
+ process_id: 5581
+ }
+ task_info {
+ estimated_mws: 0.026942
+ estimated_mw: 0.003115
+ thread_name: "ExeSeq-P10-1"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5074
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.026776
+ estimated_mw: 0.003096
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1364
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.026328
+ estimated_mw: 0.003044
+ thread_name: "HwBinder:2129_1"
+ process_name: "com.google.android.grilservice"
+ thread_id: 3649
+ process_id: 2129
+ }
+ task_info {
+ estimated_mws: 0.026013
+ estimated_mw: 0.003008
+ thread_name: "DefaultExecutor"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 5588
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.025701
+ estimated_mw: 0.002971
+ thread_name: "rkstack.process"
+ process_name: "com.android.networkstack.process"
+ thread_id: 2049
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.024843
+ estimated_mw: 0.002872
+ idle_transitions_mws: 0.001271
+ thread_name: "hwuiTask1"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1997
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.024811
+ estimated_mw: 0.002868
+ idle_transitions_mws: 0.000632
+ thread_name: "pool-1-thread-1"
+ process_name: "com.google.android.apps.scone"
+ thread_id: 5271
+ process_id: 5245
+ }
+ task_info {
+ estimated_mws: 0.024633
+ estimated_mw: 0.002848
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1360
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.023564
+ estimated_mw: 0.002724
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1366
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.023405
+ estimated_mw: 0.002706
+ thread_name: "cnss-daemon"
+ process_name: "/system/vendor/bin/cnss-daemon"
+ thread_id: 5613
+ process_id: 1009
+ }
+ task_info {
+ estimated_mws: 0.023393
+ estimated_mw: 0.002705
+ thread_name: "servicemanager"
+ thread_id: 5608
+ }
+ task_info {
+ estimated_mws: 0.022813
+ estimated_mw: 0.002638
+ thread_name: "android.hardwar"
+ process_name: "/vendor/bin/hw/android.hardware.contexthub-service.wac"
+ thread_id: 644
+ process_id: 644
+ }
+ task_info {
+ estimated_mws: 0.022774
+ estimated_mw: 0.002633
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1362
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.022714
+ estimated_mw: 0.002626
+ thread_name: "binder:685_3"
+ thread_id: 5594
+ }
+ task_info {
+ estimated_mws: 0.022642
+ estimated_mw: 0.002618
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1358
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.022617
+ estimated_mw: 0.002615
+ thread_name: "vndservicemanag"
+ thread_id: 5607
+ }
+ task_info {
+ estimated_mws: 0.021814
+ estimated_mw: 0.002522
+ idle_transitions_mws: 0.002423
+ thread_name: "it.FitbitMobile"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5377
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.021307
+ estimated_mw: 0.002463
+ thread_name: "SysUiBg"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2294
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.020941
+ estimated_mw: 0.002421
+ thread_name: "-Executor] idle"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 5623
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.020393
+ estimated_mw: 0.002358
+ thread_name: "vndservicemanag"
+ thread_id: 5582
+ }
+ task_info {
+ estimated_mws: 0.019946
+ estimated_mw: 0.002306
+ idle_transitions_mws: 0.009476
+ thread_name: "qcom,system-poo"
+ process_name: "qcom,system-pool-refill-thread"
+ thread_id: 81
+ process_id: 81
+ }
+ task_info {
+ estimated_mws: 0.019901
+ estimated_mw: 0.002301
+ idle_transitions_mws: 0.000916
+ thread_name: "binder:2129_9"
+ process_name: "com.google.android.grilservice"
+ thread_id: 5203
+ process_id: 2129
+ }
+ task_info {
+ estimated_mws: 0.019723
+ estimated_mw: 0.002280
+ thread_name: "servicemanager"
+ thread_id: 5610
+ }
+ task_info {
+ estimated_mws: 0.019537
+ estimated_mw: 0.002259
+ thread_name: "cnss-daemon"
+ process_name: "/system/vendor/bin/cnss-daemon"
+ thread_id: 1009
+ process_id: 1009
+ }
+ task_info {
+ estimated_mws: 0.019390
+ estimated_mw: 0.002242
+ thread_name: "pool-9-thread-1"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 2073
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.019219
+ estimated_mw: 0.002222
+ thread_name: "binder:1302_3"
+ process_name: "system_server"
+ thread_id: 1433
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.019059
+ estimated_mw: 0.002204
+ thread_name: "mcu_mgmtd"
+ process_name: "/vendor/bin/mcu_mgmtd"
+ thread_id: 587
+ process_id: 524
+ }
+ task_info {
+ estimated_mws: 0.018619
+ estimated_mw: 0.002153
+ thread_name: "WCMTelemetryLog"
+ process_name: "system_server"
+ thread_id: 1906
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.018508
+ estimated_mw: 0.002140
+ thread_name: "droid.tethering"
+ process_name: "com.android.networkstack.process"
+ thread_id: 2158
+ process_id: 2049
+ }
+ task_info {
+ estimated_mws: 0.018055
+ estimated_mw: 0.002087
+ thread_name: "DefaultWallpape"
+ process_name: "com.google.android.wearable.watchface.rwf"
+ thread_id: 2082
+ process_id: 1999
+ }
+ task_info {
+ estimated_mws: 0.017725
+ estimated_mw: 0.002049
+ idle_transitions_mws: 0.001379
+ thread_name: "HeapTaskDaemon"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5386
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.017125
+ estimated_mw: 0.001980
+ idle_transitions_mws: 0.001201
+ thread_name: "WearConnectionT"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2172
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.016887
+ estimated_mw: 0.001952
+ idle_transitions_mws: 0.001112
+ thread_name: "wear-services-w"
+ process_name: "com.google.wear.services"
+ thread_id: 2029
+ process_id: 1948
+ }
+ task_info {
+ estimated_mws: 0.016252
+ estimated_mw: 0.001879
+ thread_name: "BG Thread #1"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4080
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.016215
+ estimated_mw: 0.001875
+ idle_transitions_mws: 0.001249
+ thread_name: "GlobalScheduler"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2276
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.016141
+ estimated_mw: 0.001866
+ thread_name: "pool-31-thread-"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 5617
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.016069
+ estimated_mw: 0.001858
+ idle_transitions_mws: 0.005041
+ thread_name: "servicemanager"
+ thread_id: 5583
+ }
+ task_info {
+ estimated_mws: 0.015376
+ estimated_mw: 0.001778
+ idle_transitions_mws: 0.007814
+ thread_name: "RenderEngine"
+ process_name: "/system/bin/surfaceflinger"
+ thread_id: 5601
+ process_id: 755
+ }
+ task_info {
+ estimated_mws: 0.015374
+ estimated_mw: 0.001777
+ thread_name: "pool-11-thread-"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 5596
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.015097
+ estimated_mw: 0.001745
+ idle_transitions_mws: 0.000833
+ thread_name: "dsi_err_workq"
+ process_name: "dsi_err_workq"
+ thread_id: 5589
+ process_id: 5589
+ }
+ task_info {
+ estimated_mws: 0.015062
+ estimated_mw: 0.001741
+ thread_name: "InteractionJank"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2300
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.015034
+ estimated_mw: 0.001738
+ thread_name: "vndservicemanag"
+ thread_id: 5609
+ }
+ task_info {
+ estimated_mws: 0.014592
+ estimated_mw: 0.001687
+ idle_transitions_mws: 0.000734
+ thread_name: "hwuiTask0"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1996
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.014520
+ estimated_mw: 0.001679
+ thread_name: "tworkPolicy.uid"
+ process_name: "system_server"
+ thread_id: 1817
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.014278
+ estimated_mw: 0.001651
+ thread_name: "highpool[10]"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3417
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.014123
+ estimated_mw: 0.001633
+ idle_transitions_mws: 0.010036
+ thread_name: "LowMemThread"
+ process_name: "system_server"
+ thread_id: 1481
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.014026
+ estimated_mw: 0.001622
+ thread_name: "pixelstats-vend"
+ process_name: "/vendor/bin/pixelstats-vendor"
+ thread_id: 266
+ process_id: 255
+ }
+ task_info {
+ estimated_mws: 0.013579
+ estimated_mw: 0.001570
+ idle_transitions_mws: 0.001160
+ thread_name: "kworker/u9:0"
+ process_name: "kworker/u9:0"
+ thread_id: 64
+ process_id: 64
+ }
+ task_info {
+ estimated_mws: 0.013322
+ estimated_mw: 0.001540
+ idle_transitions_mws: 0.011775
+ thread_name: "migration/1"
+ process_name: "migration/1"
+ thread_id: 25
+ process_id: 25
+ }
+ task_info {
+ estimated_mws: 0.013321
+ estimated_mw: 0.001540
+ thread_name: "GlobalDispatchi"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 2290
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.013127
+ estimated_mw: 0.001518
+ thread_name: "main"
+ process_name: "/vendor/bin/hw/qcrilNrd"
+ thread_id: 1568
+ process_id: 1062
+ }
+ task_info {
+ estimated_mws: 0.012863
+ estimated_mw: 0.001487
+ thread_name: "servicemanager"
+ thread_id: 5612
+ }
+ task_info {
+ estimated_mws: 0.012835
+ estimated_mw: 0.001484
+ thread_name: "qtidataservices"
+ process_name: ".qtidataservices"
+ thread_id: 2846
+ process_id: 2118
+ }
+ task_info {
+ estimated_mws: 0.012734
+ estimated_mw: 0.001472
+ thread_name: "shortcut"
+ process_name: "system_server"
+ thread_id: 1874
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.012433
+ estimated_mw: 0.001437
+ idle_transitions_mws: 0.001905
+ thread_name: "irq/25-mmc0"
+ process_name: "irq/25-mmc0"
+ thread_id: 120
+ process_id: 120
+ }
+ task_info {
+ estimated_mws: 0.012244
+ estimated_mw: 0.001416
+ thread_name: "GlobalDispatchi"
+ process_name: "com.google.android.gms"
+ thread_id: 3155
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.012130
+ estimated_mw: 0.001402
+ thread_name: "ConnectivityThr"
+ process_name: "com.google.android.gms"
+ thread_id: 4172
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.011932
+ estimated_mw: 0.001379
+ thread_name: "RenderThread"
+ thread_id: 5616
+ }
+ task_info {
+ estimated_mws: 0.011783
+ estimated_mw: 0.001362
+ thread_name: "AGMIPC@1.0-serv"
+ process_name: "/vendor/bin/hw/vendor.qti.hardware.AGMIPC@1.0-service"
+ thread_id: 1454
+ process_id: 1446
+ }
+ task_info {
+ estimated_mws: 0.011706
+ estimated_mw: 0.001353
+ thread_name: "binder:2171_2"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2209
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.011675
+ estimated_mw: 0.001350
+ thread_name: "radioext@1.0-se"
+ process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
+ thread_id: 714
+ process_id: 676
+ }
+ task_info {
+ estimated_mws: 0.011615
+ estimated_mw: 0.001343
+ thread_name: "servicemanager"
+ thread_id: 5615
+ }
+ task_info {
+ estimated_mws: 0.011467
+ estimated_mw: 0.001326
+ thread_name: "LocApiMsgTask"
+ process_name: "/vendor/bin/hw/android.hardware.gnss-aidl-service-qti"
+ thread_id: 694
+ process_id: 650
+ }
+ task_info {
+ estimated_mws: 0.011318
+ estimated_mw: 0.001309
+ thread_name: "LocApiMsgTask"
+ process_name: "xtra-daemon"
+ thread_id: 1090
+ process_id: 1031
+ }
+ task_info {
+ estimated_mws: 0.011273
+ estimated_mw: 0.001303
+ thread_name: "vndservicemanag"
+ thread_id: 5614
+ }
+ task_info {
+ estimated_mws: 0.011024
+ estimated_mw: 0.001275
+ idle_transitions_mws: 0.004820
+ thread_name: "TimerThread"
+ process_name: "/system/bin/audioserver"
+ thread_id: 1486
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.010869
+ estimated_mw: 0.001257
+ idle_transitions_mws: 0.001558
+ thread_name: "irq/26-4744000."
+ process_name: "irq/26-4744000.sdhci"
+ thread_id: 117
+ process_id: 117
+ }
+ task_info {
+ estimated_mws: 0.010764
+ estimated_mw: 0.001245
+ idle_transitions_mws: 0.003066
+ thread_name: "SurfaceSyncGrou"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1994
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.010731
+ estimated_mw: 0.001241
+ idle_transitions_mws: 0.009927
+ thread_name: "migration/3"
+ process_name: "migration/3"
+ thread_id: 40
+ process_id: 40
+ }
+ task_info {
+ estimated_mws: 0.010156
+ estimated_mw: 0.001174
+ thread_name: "id.wearable.app"
+ process_name: "com.google.android.wearable.app"
+ thread_id: 3857
+ process_id: 3857
+ }
+ task_info {
+ estimated_mws: 0.009691
+ estimated_mw: 0.001120
+ thread_name: "ksoftirqd/0"
+ process_name: "ksoftirqd/0"
+ thread_id: 13
+ process_id: 13
+ }
+ task_info {
+ estimated_mws: 0.009668
+ estimated_mw: 0.001118
+ thread_name: "cnss-daemon"
+ process_name: "/system/vendor/bin/cnss-daemon"
+ thread_id: 1052
+ process_id: 1009
+ }
+ task_info {
+ estimated_mws: 0.009639
+ estimated_mw: 0.001114
+ idle_transitions_mws: 0.002373
+ thread_name: "kworker/u9:2"
+ process_name: "kworker/u9:2"
+ thread_id: 338
+ process_id: 338
+ }
+ task_info {
+ estimated_mws: 0.009404
+ estimated_mw: 0.001087
+ thread_name: "binder:2856_3"
+ process_name: "com.google.android.gms"
+ thread_id: 3157
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.009364
+ estimated_mw: 0.001083
+ thread_name: "binder:969_3"
+ process_name: "/system/vendor/bin/cnd"
+ thread_id: 1013
+ process_id: 969
+ }
+ task_info {
+ estimated_mws: 0.008837
+ estimated_mw: 0.001022
+ thread_name: "binder:2118_2"
+ process_name: ".qtidataservices"
+ thread_id: 2142
+ process_id: 2118
+ }
+ task_info {
+ estimated_mws: 0.008775
+ estimated_mw: 0.001015
+ thread_name: "thermal-engine-"
+ process_name: "/vendor/bin/thermal-engine-v2"
+ thread_id: 2520
+ process_id: 2493
+ }
+ task_info {
+ estimated_mws: 0.008724
+ estimated_mw: 0.001009
+ thread_name: "binder:2856_7"
+ process_name: "com.google.android.gms"
+ thread_id: 4825
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.008275
+ estimated_mw: 0.000957
+ thread_name: "binder:2856_6"
+ process_name: "com.google.android.gms"
+ thread_id: 4824
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.008136
+ estimated_mw: 0.000941
+ thread_name: "binder:3857_3"
+ process_name: "com.google.android.wearable.app"
+ thread_id: 3872
+ process_id: 3857
+ }
+ task_info {
+ estimated_mws: 0.007913
+ estimated_mw: 0.000915
+ thread_name: "binder:975_3"
+ process_name: "/vendor/bin/imsdaemon"
+ thread_id: 1630
+ process_id: 975
+ }
+ task_info {
+ estimated_mws: 0.007892
+ estimated_mw: 0.000912
+ thread_name: "time_daemon"
+ process_name: "/vendor/bin/time_daemon"
+ thread_id: 525
+ process_id: 522
+ }
+ task_info {
+ estimated_mws: 0.007750
+ estimated_mw: 0.000896
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1044
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.007591
+ estimated_mw: 0.000878
+ thread_name: "binder:2856_5"
+ process_name: "com.google.android.gms"
+ thread_id: 3681
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.007392
+ estimated_mw: 0.000855
+ thread_name: "binder:2182_1"
+ process_name: "com.android.phone"
+ thread_id: 2231
+ process_id: 2182
+ }
+ task_info {
+ estimated_mws: 0.007384
+ estimated_mw: 0.000854
+ thread_name: "binder:2171_5"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2638
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.007245
+ estimated_mw: 0.000838
+ idle_transitions_mws: 0.000922
+ thread_name: "qrtr_rx"
+ process_name: "qrtr_rx"
+ thread_id: 1556
+ process_id: 1556
+ }
+ task_info {
+ estimated_mws: 0.007086
+ estimated_mw: 0.000819
+ thread_name: "radioext@1.0-se"
+ process_name: "/vendor/bin/hw/vendor.google.radioext@1.0-service"
+ thread_id: 1605
+ process_id: 676
+ }
+ task_info {
+ estimated_mws: 0.006850
+ estimated_mw: 0.000792
+ idle_transitions_mws: 0.005140
+ thread_name: "hwservicemanage"
+ process_name: "/system/system_ext/bin/hwservicemanager"
+ thread_id: 214
+ process_id: 214
+ }
+ task_info {
+ estimated_mws: 0.006731
+ estimated_mw: 0.000778
+ thread_name: "rcub/0"
+ process_name: "rcub/0"
+ thread_id: 17
+ process_id: 17
+ }
+ task_info {
+ estimated_mws: 0.006663
+ estimated_mw: 0.000770
+ thread_name: "binder:2856_8"
+ process_name: "com.google.android.gms"
+ thread_id: 4826
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.006650
+ estimated_mw: 0.000769
+ thread_name: "kthreadd"
+ process_name: "kthreadd"
+ thread_id: 2
+ process_id: 2
+ }
+ task_info {
+ estimated_mws: 0.006574
+ estimated_mw: 0.000760
+ thread_name: "binder:233_2"
+ process_name: "/system/bin/vold"
+ thread_id: 233
+ process_id: 233
+ }
+ task_info {
+ estimated_mws: 0.006115
+ estimated_mw: 0.000707
+ thread_name: "cds_ol_rx_threa"
+ process_name: "cds_ol_rx_thread"
+ thread_id: 5199
+ process_id: 5199
+ }
+ task_info {
+ estimated_mws: 0.005829
+ estimated_mw: 0.000674
+ thread_name: "NsdService"
+ process_name: "system_server"
+ thread_id: 1831
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.005734
+ estimated_mw: 0.000663
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1367
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.005699
+ estimated_mw: 0.000659
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2895
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.005530
+ estimated_mw: 0.000639
+ thread_name: "binder:2856_2"
+ process_name: "com.google.android.gms"
+ thread_id: 2903
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.005484
+ estimated_mw: 0.000634
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1361
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.005366
+ estimated_mw: 0.000620
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1357
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.005364
+ estimated_mw: 0.000620
+ thread_name: "FileObserver"
+ process_name: "system_server"
+ thread_id: 1498
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.005278
+ estimated_mw: 0.000610
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1359
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.005261
+ estimated_mw: 0.000608
+ thread_name: "Lite Thread #0"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4109
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.005011
+ estimated_mw: 0.000579
+ thread_name: "kworker/3:1H"
+ process_name: "kworker/3:1H"
+ thread_id: 122
+ process_id: 122
+ }
+ task_info {
+ estimated_mws: 0.004996
+ estimated_mw: 0.000578
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1365
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.004986
+ estimated_mw: 0.000576
+ idle_transitions_mws: 0.031459
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2890
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.004877
+ estimated_mw: 0.000564
+ thread_name: "perfetto_hprof_"
+ process_name: "com.google.android.gms"
+ thread_id: 2880
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.004761
+ estimated_mw: 0.000550
+ thread_name: "backup-0"
+ process_name: "system_server"
+ thread_id: 2660
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.004757
+ estimated_mw: 0.000550
+ thread_name: "tts-player-0"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4209
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.004444
+ estimated_mw: 0.000514
+ thread_name: "Signal Catcher"
+ process_name: "com.google.android.gms"
+ thread_id: 2878
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.004331
+ estimated_mw: 0.000501
+ thread_name: "binder:978_2"
+ process_name: "/system/vendor/bin/nicmd"
+ thread_id: 1363
+ process_id: 978
+ }
+ task_info {
+ estimated_mws: 0.004292
+ estimated_mw: 0.000496
+ idle_transitions_mws: 0.005500
+ thread_name: "f2fs_discard-25"
+ process_name: "f2fs_discard-254:43"
+ thread_id: 349
+ process_id: 349
+ }
+ task_info {
+ estimated_mws: 0.004283
+ estimated_mw: 0.000495
+ idle_transitions_mws: 0.009490
+ thread_name: "irq/24-glink-na"
+ process_name: "irq/24-glink-native-rpm-glink"
+ thread_id: 86
+ process_id: 86
+ }
+ task_info {
+ estimated_mws: 0.004252
+ estimated_mw: 0.000492
+ idle_transitions_mws: 0.001945
+ thread_name: "pool-4-thread-1"
+ process_name: "system_server"
+ thread_id: 1774
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.004010
+ estimated_mw: 0.000464
+ thread_name: "PasspointProvis"
+ process_name: "system_server"
+ thread_id: 1821
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.003934
+ estimated_mw: 0.000455
+ idle_transitions_mws: 0.001222
+ thread_name: "binder:5377_5"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5573
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.003880
+ estimated_mw: 0.000449
+ thread_name: "BG Thread #3"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4107
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.003872
+ estimated_mw: 0.000448
+ thread_name: "TransportThread"
+ process_name: "/vendor/bin/mcu_mgmtd"
+ thread_id: 3540
+ process_id: 524
+ }
+ task_info {
+ estimated_mws: 0.003835
+ estimated_mw: 0.000443
+ thread_name: "kworker/1:1H"
+ process_name: "kworker/1:1H"
+ thread_id: 127
+ process_id: 127
+ }
+ task_info {
+ estimated_mws: 0.003754
+ estimated_mw: 0.000434
+ thread_name: "watchdog"
+ process_name: "system_server"
+ thread_id: 1421
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.003628
+ estimated_mw: 0.000419
+ thread_name: "PackageInstalle"
+ process_name: "system_server"
+ thread_id: 1744
+ process_id: 1302
+ }
+ task_info {
+ estimated_mws: 0.003469
+ estimated_mw: 0.000401
+ thread_name: "Lite Thread #1"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4118
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.003393
+ estimated_mw: 0.000392
+ idle_transitions_mws: 0.009600
+ thread_name: "FinalizerWatchd"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5389
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.003365
+ estimated_mw: 0.000389
+ thread_name: "DFacilitator-1"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5128
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.003243
+ estimated_mw: 0.000375
+ thread_name: "pool-8-thread-1"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 3308
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.002873
+ estimated_mw: 0.000332
+ thread_name: "lowpool[1]"
+ process_name: "com.google.android.gms"
+ thread_id: 3503
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002842
+ estimated_mw: 0.000329
+ thread_name: "ReferenceQueueD"
+ process_name: "com.google.android.gms"
+ thread_id: 2883
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002778
+ estimated_mw: 0.000321
+ thread_name: "ipacm-diag"
+ process_name: "/system/vendor/bin/ipacm-diag"
+ thread_id: 976
+ process_id: 976
+ }
+ task_info {
+ estimated_mws: 0.002739
+ estimated_mw: 0.000317
+ idle_transitions_mws: 0.005358
+ thread_name: "migration/2"
+ process_name: "migration/2"
+ thread_id: 32
+ process_id: 32
+ }
+ task_info {
+ estimated_mws: 0.002654
+ estimated_mw: 0.000307
+ thread_name: "qrtr_rx"
+ process_name: "qrtr_rx"
+ thread_id: 564
+ process_id: 564
+ }
+ task_info {
+ estimated_mws: 0.002601
+ estimated_mw: 0.000301
+ thread_name: "card0-crtc0"
+ process_name: "card0-crtc0"
+ thread_id: 247
+ process_id: 247
+ }
+ task_info {
+ estimated_mws: 0.002574
+ estimated_mw: 0.000298
+ thread_name: "pool-7-thread-3"
+ process_name: "com.google.android.wearable.healthservices"
+ thread_id: 5435
+ process_id: 3028
+ }
+ task_info {
+ estimated_mws: 0.002458
+ estimated_mw: 0.000284
+ thread_name: "RenderThread"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2319
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.002443
+ estimated_mw: 0.000282
+ thread_name: "highpool[0]"
+ process_name: "com.google.android.gms"
+ thread_id: 3154
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002437
+ estimated_mw: 0.000282
+ thread_name: "lowpool[0]"
+ process_name: "com.google.android.gms"
+ thread_id: 3478
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002241
+ estimated_mw: 0.000259
+ thread_name: "queued-work-loo"
+ process_name: "com.google.android.gms.persistent"
+ thread_id: 3533
+ process_id: 1949
+ }
+ task_info {
+ estimated_mws: 0.002222
+ estimated_mw: 0.000257
+ thread_name: "arch_disk_io_2"
+ process_name: "com.google.android.gms"
+ thread_id: 4174
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002160
+ estimated_mw: 0.000250
+ thread_name: "binder:523_2"
+ process_name: "/system/vendor/bin/ipacm"
+ thread_id: 537
+ process_id: 523
+ }
+ task_info {
+ estimated_mws: 0.002129
+ estimated_mw: 0.000246
+ thread_name: "Jit thread pool"
+ process_name: "com.google.android.gms"
+ thread_id: 2881
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002082
+ estimated_mw: 0.000241
+ thread_name: "pool-48-thread-"
+ process_name: "com.google.android.gms"
+ thread_id: 4110
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.002071
+ estimated_mw: 0.000239
+ thread_name: "RenderThread"
+ process_name: "com.google.android.inputmethod.latin"
+ thread_id: 5120
+ process_id: 4997
+ }
+ task_info {
+ estimated_mws: 0.002020
+ estimated_mw: 0.000233
+ thread_name: "arch_disk_io_0"
+ process_name: "com.google.android.gms"
+ thread_id: 4031
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.001985
+ estimated_mw: 0.000229
+ thread_name: "arch_disk_io_1"
+ process_name: "com.google.android.gms"
+ thread_id: 4034
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.001856
+ estimated_mw: 0.000215
+ thread_name: "BG Thread #0"
+ process_name: "com.google.android.wearable.assistant"
+ thread_id: 4061
+ process_id: 4038
+ }
+ task_info {
+ estimated_mws: 0.001782
+ estimated_mw: 0.000206
+ thread_name: "binder:1926_1"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 1938
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.001776
+ estimated_mw: 0.000205
+ thread_name: "highpool[3]"
+ process_name: "com.google.android.gms"
+ thread_id: 3470
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.001776
+ estimated_mw: 0.000205
+ idle_transitions_mws: 0.005770
+ thread_name: "migration/0"
+ process_name: "migration/0"
+ thread_id: 21
+ process_id: 21
+ }
+ task_info {
+ estimated_mws: 0.001772
+ estimated_mw: 0.000205
+ thread_name: "AsyncTask #2"
+ process_name: "com.google.android.gms"
+ thread_id: 4164
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.001720
+ estimated_mw: 0.000199
+ thread_name: "arch_disk_io_3"
+ process_name: "com.google.android.gms"
+ thread_id: 4175
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.001562
+ estimated_mw: 0.000181
+ idle_transitions_mws: 0.001329
+ thread_name: "POSIX timer 0"
+ process_name: "/vendor/bin/hw/android.hardware.sensors-service.multihal"
+ thread_id: 850
+ process_id: 664
+ }
+ task_info {
+ estimated_mws: 0.001520
+ estimated_mw: 0.000176
+ thread_name: "ksoftirqd/3"
+ process_name: "ksoftirqd/3"
+ thread_id: 42
+ process_id: 42
+ }
+ task_info {
+ estimated_mws: 0.001401
+ estimated_mw: 0.000162
+ thread_name: "Primes-1"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5394
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001320
+ estimated_mw: 0.000153
+ thread_name: "binder:5377_3"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5392
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001316
+ estimated_mw: 0.000152
+ idle_transitions_mws: 0.008908
+ thread_name: "msm-watchdog"
+ process_name: "msm-watchdog"
+ thread_id: 76
+ process_id: 76
+ }
+ task_info {
+ estimated_mws: 0.001222
+ estimated_mw: 0.000141
+ thread_name: "Lite Thread #1"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5421
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001220
+ estimated_mw: 0.000141
+ thread_name: "Signal Catcher"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5382
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001179
+ estimated_mw: 0.000136
+ thread_name: "GoogleApiHandle"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5398
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001127
+ estimated_mw: 0.000130
+ thread_name: "binder:2171_6"
+ process_name: "com.google.android.apps.wearable.systemui"
+ thread_id: 2678
+ process_id: 2171
+ }
+ task_info {
+ estimated_mws: 0.001103
+ estimated_mw: 0.000128
+ thread_name: "Blocking Thread"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5574
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.001055
+ estimated_mw: 0.000122
+ thread_name: "WM.task-3"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5430
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000990
+ estimated_mw: 0.000114
+ thread_name: "highpool[1]"
+ process_name: "com.google.android.gms"
+ thread_id: 3373
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.000984
+ estimated_mw: 0.000114
+ thread_name: "Primes-nativecr"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5397
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000961
+ estimated_mw: 0.000111
+ thread_name: "binder:740_4"
+ process_name: "/system/bin/audioserver"
+ thread_id: 2183
+ process_id: 740
+ }
+ task_info {
+ estimated_mws: 0.000954
+ estimated_mw: 0.000110
+ thread_name: "Lite Thread #0"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5404
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000927
+ estimated_mw: 0.000107
+ thread_name: "BG Thread #0"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5395
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000926
+ estimated_mw: 0.000107
+ thread_name: "FinalizerDaemon"
+ process_name: "com.google.android.gms"
+ thread_id: 2885
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.000887
+ estimated_mw: 0.000103
+ thread_name: "BG Thread #1"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5396
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000865
+ estimated_mw: 0.000100
+ thread_name: "Jit thread pool"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5385
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000858
+ estimated_mw: 0.000099
+ thread_name: "highpool[2]"
+ process_name: "com.google.android.gms"
+ thread_id: 3375
+ process_id: 2856
+ }
+ task_info {
+ estimated_mws: 0.000855
+ estimated_mw: 0.000099
+ thread_name: "ConnectivityThr"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5423
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000828
+ estimated_mw: 0.000096
+ thread_name: "Profile Saver"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5393
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000808
+ estimated_mw: 0.000093
+ idle_transitions_mws: 0.000732
+ thread_name: "ReferenceQueueD"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5387
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000803
+ estimated_mw: 0.000093
+ idle_transitions_mws: 0.000679
+ thread_name: "binder:5377_4"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5433
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000783
+ estimated_mw: 0.000091
+ thread_name: "ksoftirqd/1"
+ process_name: "ksoftirqd/1"
+ thread_id: 27
+ process_id: 27
+ }
+ task_info {
+ estimated_mws: 0.000782
+ estimated_mw: 0.000090
+ thread_name: "HsConnectionMan"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5422
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000769
+ estimated_mw: 0.000089
+ thread_name: "ADB-JDWP Connec"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5384
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000742
+ estimated_mw: 0.000086
+ thread_name: "Scheduler Threa"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5428
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000733
+ estimated_mw: 0.000085
+ thread_name: "WM.task-2"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5429
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000730
+ estimated_mw: 0.000084
+ thread_name: "Scheduled BG"
+ process_name: "com.google.android.wearable.sysui"
+ thread_id: 2896
+ process_id: 1926
+ }
+ task_info {
+ estimated_mws: 0.000727
+ estimated_mw: 0.000084
+ thread_name: "DefaultDispatch"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5431
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000726
+ estimated_mw: 0.000084
+ thread_name: "BG Thread #3"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5400
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000724
+ estimated_mw: 0.000084
+ thread_name: "WM.task-1"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5427
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000718
+ estimated_mw: 0.000083
+ thread_name: "binder:5377_1"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5390
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000689
+ estimated_mw: 0.000080
+ thread_name: "DefaultDispatch"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5432
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000669
+ estimated_mw: 0.000077
+ thread_name: "BG Thread #2"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5399
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000630
+ estimated_mw: 0.000073
+ idle_transitions_mws: 0.008723
+ thread_name: "binder:5377_2"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5391
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000583
+ estimated_mw: 0.000067
+ thread_name: "perfetto_hprof_"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5383
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000507
+ estimated_mw: 0.000059
+ thread_name: "Primes-2"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5444
+ process_id: 5377
+ }
+ task_info {
+ estimated_mws: 0.000403
+ estimated_mw: 0.000047
+ thread_name: "FinalizerDaemon"
+ process_name: "com.fitbit.FitbitMobile"
+ thread_id: 5388
+ process_id: 5377
+ }
}
}
diff --git a/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
index 597cdcd..1ce67ab 100644
--- a/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
@@ -492,7 +492,7 @@
INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3;
SELECT
- HAS_DESCENDANT_SLICE_WITH_NAME(
+ _HAS_DESCENDANT_SLICE_WITH_NAME(
(SELECT id from slice where dur = 60156000),
'SwapEndToPresentationCompositorFrame') AS has_descendant;
""",
@@ -510,7 +510,7 @@
INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3;
SELECT
- HAS_DESCENDANT_SLICE_WITH_NAME(
+ _HAS_DESCENDANT_SLICE_WITH_NAME(
(SELECT id from slice where dur = 77247000),
'SwapEndToPresentationCompositorFrame') AS has_descendant;
""",
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup.out b/test/trace_processor/diff_tests/metrics/startup/android_startup.out
index 42368b3..ec2b075 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup.out
@@ -78,11 +78,15 @@
}
launch_dur: 108
trace_thread_sections {
+ thread_section {
+ start_timestamp: 130
+ end_timestamp: 210
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 130
end_timestamp: 210
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
startup_type: "warm"
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
index 2d6070c..8b53348 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution.out
@@ -148,12 +148,16 @@
}
launch_dur: 999999900
trace_slice_sections {
+ slice_section {
+ start_timestamp: 340
+ end_timestamp: 390
+ slice_id: 20
+ slice_name: "CollectorTransition mark sweep GC"
+ process_pid: 3
+ thread_tid: 5
+ }
start_timestamp: 340
end_timestamp: 390
- slice_id: 20
- slice_name: "CollectorTransition mark sweep GC"
- process_pid: 3
- thread_tid: 5
}
}
slow_start_reason_with_details {
@@ -171,20 +175,24 @@
}
launch_dur: 999999900
trace_slice_sections {
- start_timestamp: 170
- end_timestamp: 500000000
- slice_id: 9
- slice_name: "OpenDexFilesFromOat(something else)"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
+ slice_section {
+ start_timestamp: 170
+ end_timestamp: 500000000
+ slice_id: 9
+ slice_name: "OpenDexFilesFromOat(something else)"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 150
+ end_timestamp: 165
+ slice_id: 5
+ slice_name: "OpenDexFilesFromOat(something)"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 150
- end_timestamp: 165
- slice_id: 5
- slice_name: "OpenDexFilesFromOat(something)"
- process_pid: 3
- thread_tid: 3
+ end_timestamp: 500000000
}
}
slow_start_reason_with_details {
@@ -200,12 +208,16 @@
}
launch_dur: 999999900
trace_slice_sections {
+ slice_section {
+ start_timestamp: 10000000
+ end_timestamp: 50000000
+ slice_id: 21
+ slice_name: "binder transaction"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 10000000
end_timestamp: 50000000
- slice_id: 21
- slice_name: "binder transaction"
- process_pid: 3
- thread_tid: 3
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
index a98505f..a4e1074 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_attribution_slow.out
@@ -107,12 +107,16 @@
}
launch_dur: 999999900000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 340000000000
+ end_timestamp: 390000000000
+ slice_id: 91
+ slice_name: "CollectorTransition mark sweep GC"
+ process_pid: 3
+ thread_tid: 5
+ }
start_timestamp: 340000000000
end_timestamp: 390000000000
- slice_id: 91
- slice_name: "CollectorTransition mark sweep GC"
- process_pid: 3
- thread_tid: 5
}
}
slow_start_reason_with_details {
@@ -129,25 +133,29 @@
}
launch_dur: 999999900000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 155000000000
+ end_timestamp: 165000000000
+ thread_name: "Jit thread pool"
+ process_pid: 3
+ thread_tid: 4
+ }
+ thread_section {
+ start_timestamp: 170000000000
+ end_timestamp: 175000000000
+ thread_name: "Jit thread pool"
+ process_pid: 3
+ thread_tid: 4
+ }
+ thread_section {
+ start_timestamp: 185000000000
+ end_timestamp: 190000000000
+ thread_name: "Jit thread pool"
+ process_pid: 3
+ thread_tid: 4
+ }
start_timestamp: 155000000000
- end_timestamp: 165000000000
- thread_name: "Jit thread pool"
- thread_tid: 4
- process_pid: 3
- }
- trace_thread_sections {
- start_timestamp: 170000000000
- end_timestamp: 175000000000
- thread_name: "Jit thread pool"
- thread_tid: 4
- process_pid: 3
- }
- trace_thread_sections {
- start_timestamp: 185000000000
end_timestamp: 190000000000
- thread_name: "Jit thread pool"
- thread_tid: 4
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -164,28 +172,32 @@
}
launch_dur: 999999900000000000
trace_slice_sections {
- start_timestamp: 200000000000
- end_timestamp: 210000000000
- slice_id: 84
- slice_name: "JIT compiling nothing"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
+ slice_section {
+ start_timestamp: 200000000000
+ end_timestamp: 210000000000
+ slice_id: 84
+ slice_name: "JIT compiling nothing"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 100000000000
+ end_timestamp: 101000000000
+ slice_id: 9
+ slice_name: "JIT compiling something"
+ process_pid: 3
+ thread_tid: 4
+ }
+ slice_section {
+ start_timestamp: 101000000000
+ end_timestamp: 102000000000
+ slice_id: 10
+ slice_name: "JIT compiling something"
+ process_pid: 3
+ thread_tid: 4
+ }
start_timestamp: 100000000000
- end_timestamp: 101000000000
- slice_id: 9
- slice_name: "JIT compiling something"
- process_pid: 3
- thread_tid: 4
- }
- trace_slice_sections {
- start_timestamp: 101000000000
- end_timestamp: 102000000000
- slice_id: 10
- slice_name: "JIT compiling something"
- process_pid: 3
- thread_tid: 4
+ end_timestamp: 210000000000
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
index 2405d7e..04b8915 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown.out
@@ -128,12 +128,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 204000000000
+ end_timestamp: 205000000000
+ slice_id: 13
+ slice_name: "location=/system/framework/oat/arm/com.google.android.calendar.odex status=up-to-date filter=speed reason=install-dm"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 204000000000
end_timestamp: 205000000000
- slice_id: 13
- slice_name: "location=/system/framework/oat/arm/com.google.android.calendar.odex status=up-to-date filter=speed reason=install-dm"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -149,12 +153,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 200000000000
+ end_timestamp: 202000000000
+ slice_id: 12
+ slice_name: "location=error status=io-error-no-oat filter=run-from-apk reason=unknown"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 200000000000
end_timestamp: 202000000000
- slice_id: 12
- slice_name: "location=error status=io-error-no-oat filter=run-from-apk reason=unknown"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -171,12 +179,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 185000000000
+ end_timestamp: 187000000000
+ slice_id: 4
+ slice_name: "bindApplication"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 185000000000
end_timestamp: 187000000000
- slice_id: 4
- slice_name: "bindApplication"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -193,20 +205,24 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 188000000000
+ end_timestamp: 189000000000
+ slice_id: 6
+ slice_name: "inflate"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 191000000000
+ end_timestamp: 192000000000
+ slice_id: 8
+ slice_name: "inflate"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 188000000000
- end_timestamp: 189000000000
- slice_id: 6
- slice_name: "inflate"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
- start_timestamp: 191000000000
end_timestamp: 192000000000
- slice_id: 8
- slice_name: "inflate"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -223,12 +239,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 188000000000
+ end_timestamp: 189000000000
+ slice_id: 7
+ slice_name: "ResourcesManager#getResources"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 188000000000
end_timestamp: 189000000000
- slice_id: 7
- slice_name: "ResourcesManager#getResources"
- thread_tid: 3
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -245,11 +265,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 205000000000
+ end_timestamp: 210000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 205000000000
end_timestamp: 210000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
startup_type: "cold"
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
index 3dbb2aa..f2c7123 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_breakdown_slow.out
@@ -127,12 +127,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 200000000000
+ end_timestamp: 202000000000
+ slice_id: 12
+ slice_name: "location=error status=io-error-no-oat filter=run-from-apk reason=unknown"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 200000000000
end_timestamp: 202000000000
- slice_id: 12
- slice_name: "location=error status=io-error-no-oat filter=run-from-apk reason=unknown"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -149,12 +153,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 185000000000
+ end_timestamp: 195000000000
+ slice_id: 4
+ slice_name: "bindApplication"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 185000000000
end_timestamp: 195000000000
- slice_id: 4
- slice_name: "bindApplication"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -171,20 +179,24 @@
}
launch_dur: 108000000000
trace_slice_sections {
- start_timestamp: 190000000000
- end_timestamp: 192000000000
- slice_id: 8
- slice_name: "inflate"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
+ slice_section {
+ start_timestamp: 190000000000
+ end_timestamp: 192000000000
+ slice_id: 8
+ slice_name: "inflate"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 188000000000
+ end_timestamp: 189000000000
+ slice_id: 7
+ slice_name: "inflate"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 188000000000
- end_timestamp: 189000000000
- slice_id: 7
- slice_name: "inflate"
- process_pid: 3
- thread_tid: 3
+ end_timestamp: 192000000000
}
}
slow_start_reason_with_details {
@@ -201,12 +213,16 @@
}
launch_dur: 108000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 187000000000
+ end_timestamp: 192000000000
+ slice_id: 5
+ slice_name: "ResourcesManager#getResources"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 187000000000
end_timestamp: 192000000000
- slice_id: 5
- slice_name: "ResourcesManager#getResources"
- thread_tid: 3
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -223,11 +239,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 205000000000
+ end_timestamp: 210000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 205000000000
end_timestamp: 210000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
startup_type: "cold"
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
index 4913852..6cb97e0 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_broadcast_multiple.out
@@ -46,25 +46,29 @@
}
launch_dur: 100
trace_slice_sections {
+ slice_section {
+ start_timestamp: 105
+ end_timestamp: 106
+ slice_id: 6
+ slice_name: "Broadcast dispatched from android (2005:system/1000) x"
+ thread_tid: 1
+ }
+ slice_section {
+ start_timestamp: 106
+ end_timestamp: 107
+ slice_id: 8
+ slice_name: "Broadcast dispatched from android (2005:system/1000) x"
+ thread_tid: 1
+ }
+ slice_section {
+ start_timestamp: 107
+ end_timestamp: 108
+ slice_id: 10
+ slice_name: "Broadcast dispatched from android (2005:system/1000) x"
+ thread_tid: 1
+ }
start_timestamp: 105
- end_timestamp: 106
- slice_id: 6
- slice_name: "Broadcast dispatched from android (2005:system/1000) x"
- thread_tid: 1
- }
- trace_slice_sections {
- start_timestamp: 106
- end_timestamp: 107
- slice_id: 8
- slice_name: "Broadcast dispatched from android (2005:system/1000) x"
- thread_tid: 1
- }
- trace_slice_sections {
- start_timestamp: 107
end_timestamp: 108
- slice_id: 10
- slice_name: "Broadcast dispatched from android (2005:system/1000) x"
- thread_tid: 1
}
}
slow_start_reason_with_details {
@@ -81,25 +85,29 @@
}
launch_dur: 100
trace_slice_sections {
+ slice_section {
+ start_timestamp: 100
+ end_timestamp: 101
+ slice_id: 1
+ slice_name: "broadcastReceiveReg: x"
+ thread_tid: 2
+ }
+ slice_section {
+ start_timestamp: 101
+ end_timestamp: 102
+ slice_id: 2
+ slice_name: "broadcastReceiveReg: x"
+ thread_tid: 2
+ }
+ slice_section {
+ start_timestamp: 102
+ end_timestamp: 103
+ slice_id: 3
+ slice_name: "broadcastReceiveReg: x"
+ thread_tid: 2
+ }
start_timestamp: 100
- end_timestamp: 101
- slice_id: 1
- slice_name: "broadcastReceiveReg: x"
- thread_tid: 2
- }
- trace_slice_sections {
- start_timestamp: 101
- end_timestamp: 102
- slice_id: 2
- slice_name: "broadcastReceiveReg: x"
- thread_tid: 2
- }
- trace_slice_sections {
- start_timestamp: 102
end_timestamp: 103
- slice_id: 3
- slice_name: "broadcastReceiveReg: x"
- thread_tid: 2
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
index dabbb5f..f64dceb 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_lock_contention_slow.out
@@ -82,12 +82,16 @@
}
launch_dur: 100000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 112000000000
+ end_timestamp: 115000000000
+ slice_id: 1
+ slice_name: "bindApplication"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 112000000000
end_timestamp: 115000000000
- slice_id: 1
- slice_name: "bindApplication"
- process_pid: 3
- thread_tid: 3
}
}
slow_start_reason_with_details {
@@ -105,28 +109,32 @@
}
launch_dur: 100000000000
trace_slice_sections {
- start_timestamp: 140000000000
- end_timestamp: 157000000000
- slice_id: 5
- slice_name: "Lock contention on a monitor lock (owner tid: 2)"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
+ slice_section {
+ start_timestamp: 140000000000
+ end_timestamp: 157000000000
+ slice_id: 5
+ slice_name: "Lock contention on a monitor lock (owner tid: 2)"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 120000000000
+ end_timestamp: 130000000000
+ slice_id: 4
+ slice_name: "Lock contention on thread list lock (owner tid: 2)"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 155000000000
+ end_timestamp: 160000000000
+ slice_id: 6
+ slice_name: "Lock contention on a monitor lock (owner tid: 3)"
+ process_pid: 3
+ thread_tid: 4
+ }
start_timestamp: 120000000000
- end_timestamp: 130000000000
- slice_id: 4
- slice_name: "Lock contention on thread list lock (owner tid: 2)"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
- start_timestamp: 155000000000
end_timestamp: 160000000000
- slice_id: 6
- slice_name: "Lock contention on a monitor lock (owner tid: 3)"
- process_pid: 3
- thread_tid: 4
}
}
slow_start_reason_with_details {
@@ -144,20 +152,24 @@
}
launch_dur: 100000000000
trace_slice_sections {
+ slice_section {
+ start_timestamp: 140000000000
+ end_timestamp: 157000000000
+ slice_id: 5
+ slice_name: "Lock contention on a monitor lock (owner tid: 2)"
+ process_pid: 3
+ thread_tid: 3
+ }
+ slice_section {
+ start_timestamp: 155000000000
+ end_timestamp: 160000000000
+ slice_id: 6
+ slice_name: "Lock contention on a monitor lock (owner tid: 3)"
+ process_pid: 3
+ thread_tid: 4
+ }
start_timestamp: 140000000000
- end_timestamp: 157000000000
- slice_id: 5
- slice_name: "Lock contention on a monitor lock (owner tid: 2)"
- process_pid: 3
- thread_tid: 3
- }
- trace_slice_sections {
- start_timestamp: 155000000000
end_timestamp: 160000000000
- slice_id: 6
- slice_name: "Lock contention on a monitor lock (owner tid: 3)"
- process_pid: 3
- thread_tid: 4
}
}
startup_type: "cold"
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
index ded275e..17d426d 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_process_track.out
@@ -77,11 +77,15 @@
}
launch_dur: 7
trace_thread_sections {
+ thread_section {
+ start_timestamp: 103
+ end_timestamp: 107
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 103
end_timestamp: 107
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
}
@@ -164,11 +168,15 @@
}
launch_dur: 7
trace_thread_sections {
+ thread_section {
+ start_timestamp: 203
+ end_timestamp: 207
+ thread_name: "com.google.android.calendar"
+ process_pid: 4
+ thread_tid: 4
+ }
start_timestamp: 203
end_timestamp: 207
- thread_name: "com.google.android.calendar"
- thread_tid: 4
- process_pid: 4
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
index 942037e..0ae5b29 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_slow.out
@@ -81,11 +81,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 130000000000
+ end_timestamp: 210000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 130000000000
end_timestamp: 210000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -102,11 +106,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 120000000000
+ end_timestamp: 125000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 120000000000
end_timestamp: 125000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -123,11 +131,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 125000000000
+ end_timestamp: 130000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 125000000000
end_timestamp: 130000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
slow_start_reason_with_details {
@@ -144,11 +156,15 @@
}
launch_dur: 108000000000
trace_thread_sections {
+ thread_section {
+ start_timestamp: 130000000000
+ end_timestamp: 210000000000
+ thread_name: "com.google.android.calendar"
+ process_pid: 3
+ thread_tid: 3
+ }
start_timestamp: 130000000000
end_timestamp: 210000000000
- thread_name: "com.google.android.calendar"
- thread_tid: 3
- process_pid: 3
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out b/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
index 3f52432..1f2000d 100644
--- a/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
+++ b/test/trace_processor/diff_tests/metrics/startup/android_startup_unlock.out
@@ -44,11 +44,15 @@
}
launch_dur: 100
trace_slice_sections {
+ slice_section {
+ start_timestamp: 130
+ end_timestamp: 133
+ slice_id: 1
+ slice_name: "KeyguardUpdateMonitor#onAuthenticationSucceeded"
+ thread_tid: 2
+ }
start_timestamp: 130
end_timestamp: 133
- slice_id: 1
- slice_name: "KeyguardUpdateMonitor#onAuthenticationSucceeded"
- thread_tid: 2
}
}
}
diff --git a/test/trace_processor/diff_tests/metrics/startup/tests_metrics.py b/test/trace_processor/diff_tests/metrics/startup/tests_metrics.py
index ab1e44a..88fae90 100644
--- a/test/trace_processor/diff_tests/metrics/startup/tests_metrics.py
+++ b/test/trace_processor/diff_tests/metrics/startup/tests_metrics.py
@@ -105,6 +105,7 @@
}
battery_aggregates {
avg_power_mw: 9497.722222222223
+ energy_usage_estimate: 1.3017599999999998
}
}
"""))
diff --git a/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto b/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
index 7afbb6a..c8da434 100644
--- a/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
+++ b/test/trace_processor/diff_tests/parser/android/surfaceflinger_layers.textproto
@@ -48,12 +48,9 @@
children: 44
children: 77
children: 87
- type: "Layer"
layer_stack: 0
z: 0
crop {
- left: 0
- top: 0
right: -1
bottom: -1
}
@@ -393,6 +390,18 @@
}
is_virtual: false
}
+ displays {
+ id: 4619827677550801153
+ name: "Common Panel"
+ size {
+ w: 1080
+ h: 2400
+ }
+ layer_stack_space_rect {
+ right: 1080
+ bottom: 2400
+ }
+ }
vsync_id: 24767
}
trusted_uid: 1000
diff --git a/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py b/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py
index c7c8e96..4f91501 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_android_input_event.py
@@ -282,3 +282,24 @@
27,"vsync_id","0"
27,"window_id","0"
"""))
+
+ def test_tables_have_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('input_event_trace.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.input;
+ SELECT COUNT(*) FROM android_key_events
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ UNION ALL
+ SELECT COUNT(*) FROM android_motion_events
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ UNION ALL
+ SELECT COUNT(*) FROM android_input_event_dispatch
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ 6
+ 30
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py
index 74b2760..282a5b3 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_clients.py
@@ -63,3 +63,16 @@
"client.ime_insets_source_consumer.insets_source_consumer.source_control.leash.hash_code","135479902"
"client.ime_insets_source_consumer.insets_source_consumer.source_control.leash.layerId","105"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('inputmethod_clients.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+ SELECT COUNT(*) FROM android_inputmethod_clients
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py
index 612f5b8..1295cfe 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_manager_service.py
@@ -66,3 +66,16 @@
"input_method_manager_service.system_ready","true"
"where","InputMethodManagerService#startInputOrWindowGainedFocus"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('inputmethod_manager_service.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+ SELECT COUNT(*) FROM android_inputmethod_manager_service
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py
index 4ec909c..164fa8c 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_inputmethod_service.py
@@ -64,3 +64,16 @@
"input_method_service.token","android.os.BinderProxy@50043d1"
"where","InputMethodService#doFinishInput"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('inputmethod_service.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.winscope.inputmethod;
+ SELECT COUNT(*) FROM android_inputmethod_service
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
index a8e0328..b691bfb 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
@@ -74,6 +74,18 @@
"type","1"
"""))
+ def test_shell_transitions_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('shell_transitions.textproto'),
+ query="""
+ SELECT COUNT(*) FROM window_manager_shell_transitions
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 6
+ """))
+
def test_has_shell_handlers(self):
return DiffTestBlueprint(
trace=Path('shell_handlers.textproto'),
@@ -89,3 +101,15 @@
2,"RecentsTransitionHandler"
3,"FreeformTaskTransitionHandler"
"""))
+
+ def test_shell_handlers_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('shell_handlers.textproto'),
+ query="""
+ SELECT COUNT(*) FROM window_manager_shell_transition_handlers
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 3
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_layers.py b/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_layers.py
index ac1fcab..6ea3ff3 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_layers.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_layers.py
@@ -81,3 +81,19 @@
2,1,"surfaceflinger_layer"
3,1,"surfaceflinger_layer"
"""))
+
+ def test_tables_have_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('surfaceflinger_layers.textproto'),
+ query="""
+ SELECT COUNT(*) FROM surfaceflinger_layers_snapshot
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ UNION ALL
+ SELECT COUNT(*) FROM surfaceflinger_layer
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ 4
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_transactions.py b/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_transactions.py
index cf83ce4..ff84af8 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_transactions.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_surfaceflinger_transactions.py
@@ -80,3 +80,15 @@
"transactions[0].vsync_id","24769"
"vsync_id","24776"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('surfaceflinger_transactions.textproto'),
+ query="""
+ SELECT COUNT(*) FROM surfaceflinger_transactions
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_viewcapture.py b/test/trace_processor/diff_tests/parser/android/tests_viewcapture.py
index e089ecb..a99946e 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_viewcapture.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_viewcapture.py
@@ -79,3 +79,16 @@
"key","display_value"
"views[1].class_name","STRING DE-INTERNING ERROR"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('viewcapture.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.winscope.viewcapture;
+ SELECT COUNT(*) FROM android_viewcapture
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/android/tests_windowmanager.py b/test/trace_processor/diff_tests/parser/android/tests_windowmanager.py
index 9349b8d..3946458 100644
--- a/test/trace_processor/diff_tests/parser/android/tests_windowmanager.py
+++ b/test/trace_processor/diff_tests/parser/android/tests_windowmanager.py
@@ -63,3 +63,16 @@
"window_manager_service.policy.keyguard_delegate.screen_state","SCREEN_STATE_ON"
"window_manager_service.policy.keyguard_draw_complete","true"
"""))
+
+ def test_table_has_raw_protos(self):
+ return DiffTestBlueprint(
+ trace=Path('windowmanager.textproto'),
+ query="""
+ INCLUDE PERFETTO MODULE android.winscope.windowmanager;
+ SELECT COUNT(*) FROM android_windowmanager
+ WHERE base64_proto IS NOT NULL AND base64_proto_id IS NOT NULL
+ """,
+ out=Csv("""
+ "COUNT(*)"
+ 2
+ """))
diff --git a/test/trace_processor/diff_tests/parser/json/tests.py b/test/trace_processor/diff_tests/parser/json/tests.py
index f5e1d8b..5b981ac 100644
--- a/test/trace_processor/diff_tests/parser/json/tests.py
+++ b/test/trace_processor/diff_tests/parser/json/tests.py
@@ -135,3 +135,18 @@
1000,10000,"Parent",0
1000,5000,"Child",1
"""))
+
+ def test_json_incomplete(self):
+ return DiffTestBlueprint(
+ trace=Json('''
+ [
+ {"name":"typecheck","ph":"X","ts":4619295550.000,"dur":8000.000,"pid":306339,"tid":3},
+ '''),
+ query='''
+ select ts from slice
+ ''',
+ out=Csv('''
+ "ts"
+ 4619295550000
+ ''')
+ )
diff --git a/test/trace_processor/diff_tests/parser/parsing/tests.py b/test/trace_processor/diff_tests/parser/parsing/tests.py
index 54707c1..8af739d 100644
--- a/test/trace_processor/diff_tests/parser/parsing/tests.py
+++ b/test/trace_processor/diff_tests/parser/parsing/tests.py
@@ -1570,3 +1570,76 @@
5230422153284,0,1306,"[NULL]"
5230425693562,0,10,1
"""))
+
+ # Kernel idle tasks created by /sbin/init should be filtered.
+ def test_task_newtask_swapper_by_init(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ packet {
+ first_packet_on_sequence: true
+ ftrace_events {
+ cpu: 1
+ event {
+ timestamp: 1000000
+ pid: 0
+ task_newtask {
+ pid: 1
+ comm: "swapper/0"
+ clone_flags: 8389376
+ oom_score_adj: 0
+ }
+ }
+ event {
+ timestamp: 1000000
+ pid: 0
+ task_newtask {
+ pid: 2
+ comm: "swapper/0"
+ clone_flags: 8390400
+ oom_score_adj: 0
+ }
+ }
+ event {
+ timestamp: 17000000
+ pid: 1
+ task_newtask {
+ pid: 0
+ comm: "swapper/0"
+ clone_flags: 256
+ oom_score_adj: 0
+ }
+ }
+ event {
+ timestamp: 17000000
+ pid: 1
+ task_newtask {
+ pid: 0
+ comm: "swapper/0"
+ clone_flags: 256
+ oom_score_adj: 0
+ }
+ }
+ event {
+ timestamp: 17000000
+ pid: 1
+ task_newtask {
+ pid: 0
+ comm: "swapper/0"
+ clone_flags: 256
+ oom_score_adj: 0
+ }
+ }
+ }
+ trusted_uid: 9999
+ trusted_packet_sequence_id: 2
+ trusted_pid: 521
+ previous_packet_dropped: true
+ }
+ """),
+ query="""
+ SELECT utid, tid, name from thread where tid = 0
+ """,
+ out=Csv("""
+ "utid","tid","name"
+ 0,0,"swapper"
+ """))
diff --git a/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py b/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
index 046d1c4..ccdae4f 100644
--- a/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
+++ b/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
@@ -147,7 +147,7 @@
packet {
timestamp: 4000000
battery {
- current_ua: 510000
+ current_ua: -510000
voltage_uv: 12000000
}
}
diff --git a/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py b/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py
index 6c16720..0570da9 100644
--- a/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/desktop_mode_tests.py
@@ -108,3 +108,37 @@
8966481546961,"[NULL]",8966481546961,3595287192,1000028,1110329
"""))
+ def test_android_desktop_mode_windows_statsd_events_reset_events(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_desktop_mode/task_update_reset_events.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE android.desktop_mode;
+ SELECT * FROM android_desktop_mode_windows;
+ """,
+ out=Csv("""
+ "raw_add_ts","raw_remove_ts","ts","dur","instance_id","uid"
+ 84164708379314,84188981730278,84164708379314,24273350964,1000054,1010210
+ 84182861720998,84184961957530,84182861720998,2100236532,1000056,1010197
+ 84190656246474,"[NULL]",84190656246474,663853800,1000054,1010210
+ 84190656724094,"[NULL]",84190656724094,663376180,1000058,1010222
+ 84199757126076,84223637163807,84199757126076,23880037731,1000054,1010210
+ 84199757350156,84223637751006,84199757350156,23880400850,1000058,1010222
+ 84204112441752,84208226333681,84204112441752,4113891929,1000062,1010226
+ 84226052369131,84241418229490,84226052369131,15365860359,1000054,1010210
+ 84226054551300,84241419846189,84226054551300,15365294889,1000058,1010222
+ 84248341935751,"[NULL]",84248341935751,407771770,1000054,1010210
+ 84248342227662,"[NULL]",84248342227662,407479859,1000058,1010222
+ 84253290279911,84292320102806,84253290279911,39029822895,1000054,1010210
+ 84253290608646,84292320892072,84253290608646,39030283426,1000058,1010222
+ 84294338295719,84856791435520,84294338295719,562453139801,1000054,1010210
+ 84294339318912,84859506938084,84294339318912,565167619172,1000058,1010222
+ 84304813959094,84858368194309,84304813959094,553554235215,1000069,1010211
+ 84335762434858,84852636055309,84335762434858,516873620451,1000070,1010297
+ 84357340686822,84378879321354,84357340686822,21538634532,1000071,1010225
+ 84361592237320,84363638663184,84361592237320,2046425864,1000072,1010317
+ 84370549790954,84853627568534,84370549790954,483077777580,1000074,1010317
+ 84575862070921,84643498791291,84575862070921,67636720370,1000075,1001000
+ 84619864755554,84638100287708,84619864755554,18235532154,1000076,1010235
+ 84782344478655,84804145589506,84782344478655,21801110851,1000077,1010238
+ """))
+
diff --git a/test/trace_processor/diff_tests/stdlib/android/heap_profile_tests.py b/test/trace_processor/diff_tests/stdlib/android/heap_profile_tests.py
new file mode 100644
index 0000000..bbdd809
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/android/heap_profile_tests.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import DataPath
+from python.generators.diff_tests.testing import Csv
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class HeapProfile(TestSuite):
+
+ def test_heap_profile_summary_tree(self):
+ return DiffTestBlueprint(
+ trace=DataPath('system-server-native-profile'),
+ query="""
+ INCLUDE PERFETTO MODULE android.memory.heap_profile.summary_tree;
+
+ SELECT
+ name,
+ self_size,
+ cumulative_size,
+ self_alloc_size,
+ cumulative_alloc_size
+ FROM android_heap_profile_summary_tree
+ ORDER BY cumulative_size DESC, name
+ LIMIT 10;
+ """,
+ out=Csv("""
+ "name","self_size","cumulative_size","self_alloc_size","cumulative_alloc_size"
+ "__pthread_start(void*)",0,84848,0,1084996
+ "__start_thread",0,84848,0,1084996
+ "art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)",0,57144,0,736946
+ "art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)",0,57144,0,736946
+ "art::Thread::CreateCallback(void*)",0,57144,0,736946
+ "art_quick_invoke_stub",0,57144,0,736946
+ "android.os.HandlerThread.run",0,53048,0,197068
+ "com.android.server.UiThread.run",0,53048,0,197068
+ "android::AndroidRuntime::javaThreadShell(void*)",0,27704,0,348050
+ "(anonymous namespace)::nativeInitSensorEventQueue(_JNIEnv*, _jclass*, long, _jobject*, _jobject*, _jstring*, int)",0,26624,0,26624
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/android/tests.py b/test/trace_processor/diff_tests/stdlib/android/tests.py
index bc8d4ea..0baf4b0 100644
--- a/test/trace_processor/diff_tests/stdlib/android/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/tests.py
@@ -1348,6 +1348,7 @@
dur,
slice_id,
job_name || '_' || job_id AS job_name,
+ uid,
job_id,
package_name,
job_namespace,
@@ -1373,15 +1374,17 @@
deadline_ms,
job_start_latency_ms,
num_uncompleted_work_items,
- proc_state
+ proc_state,
+ internal_stop_reason,
+ public_stop_reason
FROM android_job_scheduler_states;
""",
out=Csv("""
- "id","ts","dur","slice_id","job_name","job_id","package_name","job_namespace","effective_priority","has_battery_not_low_constraint","has_charging_constraint","has_connectivity_constraint","has_content_trigger_constraint","has_deadline_constraint","has_idle_constraint","has_storage_not_low_constraint","has_timing_delay_constraint","is_prefetch","is_requested_expedited_job","is_running_as_expedited_job","num_previous_attempts","requested_priority","standby_bucket","is_periodic","has_flex_constraint","is_requested_as_user_initiated_job","is_running_as_user_initiated_job","deadline_ms","job_start_latency_ms","num_uncompleted_work_items","proc_state"
-1,377089754138,83200835,10,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286783",-2746960329031286783,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT"
-2,385507499374,111746552,17,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286782",-2746960329031286782,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,6,0,"PROCESS_STATE_PERSISTENT"
-3,416753734715,129444346,53,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286781",-2746960329031286781,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,5,0,"PROCESS_STATE_PERSISTENT"
-4,422530232411,86735906,59,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286780",-2746960329031286780,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT"
+"id","ts","dur","slice_id","job_name","uid","job_id","package_name","job_namespace","effective_priority","has_battery_not_low_constraint","has_charging_constraint","has_connectivity_constraint","has_content_trigger_constraint","has_deadline_constraint","has_idle_constraint","has_storage_not_low_constraint","has_timing_delay_constraint","is_prefetch","is_requested_expedited_job","is_running_as_expedited_job","num_previous_attempts","requested_priority","standby_bucket","is_periodic","has_flex_constraint","is_requested_as_user_initiated_job","is_running_as_user_initiated_job","deadline_ms","job_start_latency_ms","num_uncompleted_work_items","proc_state","internal_stop_reason","public_stop_reason"
+1,377089754138,83200835,10,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286783",10090,-2746960329031286783,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_CANCELLED","STOP_REASON_CANCELLED_BY_APP"
+2,385507499374,111746552,17,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286782",10090,-2746960329031286782,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,6,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
+3,416753734715,129444346,53,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286781",10090,-2746960329031286781,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,5,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
+4,422530232411,86735906,59,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286780",10090,-2746960329031286780,"com.android.providers.media.module","androidx.work.systemjobscheduler",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
"""))
def test_android_job_scheduler_with_screen_charging_output(self):
@@ -1394,6 +1397,7 @@
dur,
slice_id,
job_name,
+ uid,
job_id,
job_dur,
package_name,
@@ -1422,13 +1426,15 @@
deadline_ms,
job_start_latency_ms,
num_uncompleted_work_items,
- proc_state
+ proc_state,
+ internal_stop_reason,
+ public_stop_reason
from android_job_scheduler_with_screen_charging_states;
""",
out=Csv("""
- "ts","dur","slice_id","job_name","job_id","job_dur","package_name","job_namespace","charging_state","screen_state","effective_priority","has_battery_not_low_constraint","has_charging_constraint","has_connectivity_constraint","has_content_trigger_constraint","has_deadline_constraint","has_idle_constraint","has_storage_not_low_constraint","has_timing_delay_constraint","is_prefetch","is_requested_expedited_job","is_running_as_expedited_job","num_previous_attempts","requested_priority","standby_bucket","is_periodic","has_flex_constraint","is_requested_as_user_initiated_job","is_running_as_user_initiated_job","deadline_ms","job_start_latency_ms","num_uncompleted_work_items","proc_state"
-377089754138,83200835,10,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286783",-2746960329031286783,83200835,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT"
-385507499374,111746552,17,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286782",-2746960329031286782,111746552,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,6,0,"PROCESS_STATE_PERSISTENT"
-416753734715,129444346,53,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286781",-2746960329031286781,129444346,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,5,0,"PROCESS_STATE_PERSISTENT"
-422530232411,86735906,59,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286780",-2746960329031286780,86735906,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT"
+ "ts","dur","slice_id","job_name","uid","job_id","job_dur","package_name","job_namespace","charging_state","screen_state","effective_priority","has_battery_not_low_constraint","has_charging_constraint","has_connectivity_constraint","has_content_trigger_constraint","has_deadline_constraint","has_idle_constraint","has_storage_not_low_constraint","has_timing_delay_constraint","is_prefetch","is_requested_expedited_job","is_running_as_expedited_job","num_previous_attempts","requested_priority","standby_bucket","is_periodic","has_flex_constraint","is_requested_as_user_initiated_job","is_running_as_user_initiated_job","deadline_ms","job_start_latency_ms","num_uncompleted_work_items","proc_state","internal_stop_reason","public_stop_reason"
+377089754138,83200835,10,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286783",10090,-2746960329031286783,83200835,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_CANCELLED","STOP_REASON_CANCELLED_BY_APP"
+385507499374,111746552,17,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286782",10090,-2746960329031286782,111746552,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,6,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
+416753734715,129444346,53,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286781",10090,-2746960329031286781,129444346,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,5,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
+422530232411,86735906,59,"@androidx.work.systemjobscheduler@com.android.providers.media.module/androidx.work.impl.background.systemjob.SystemJobService_-2746960329031286780",10090,-2746960329031286780,86735906,"com.android.providers.media.module","androidx.work.systemjobscheduler","Charging","Unknown",400,1,0,0,0,0,0,0,0,0,0,0,0,400,"EXEMPTED",0,0,0,0,0,3,0,"PROCESS_STATE_PERSISTENT","INTERNAL_STOP_REASON_SUCCESSFUL_FINISH","STOP_REASON_UNDEFINED"
"""))
diff --git a/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
index bb42920..083354a 100755
--- a/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
@@ -393,6 +393,36 @@
-2143831735395280246,"GESTURE_SCROLL_UPDATE_EVENT","STEP_SEND_INPUT_EVENT_UI,STEP_HANDLE_INPUT_EVENT_IMPL,STEP_DID_HANDLE_INPUT_AND_OVERSCROLL,STEP_GESTURE_EVENT_HANDLED"
"""))
+ def test_task_start_time(self):
+ return DiffTestBlueprint(
+ trace=DataPath('scroll_m131.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.input;
+
+ SELECT
+ latency_id,
+ step,
+ task_start_time_ts
+ FROM chrome_input_pipeline_steps
+ ORDER BY latency_id
+ LIMIT 10;
+ """,
+ # STEP_SEND_INPUT_EVENT_UI does not run in a task,
+ # so its task_start_time_ts will be NULL.
+ out=Csv("""
+ "latency_id","step","task_start_time_ts"
+ -2143831735395280256,"STEP_SEND_INPUT_EVENT_UI","[NULL]"
+ -2143831735395280256,"STEP_HANDLE_INPUT_EVENT_IMPL",1292554143003210
+ -2143831735395280256,"STEP_DID_HANDLE_INPUT_AND_OVERSCROLL",1292554153539210
+ -2143831735395280256,"STEP_GESTURE_EVENT_HANDLED",1292554154651257
+ -2143831735395280254,"STEP_SEND_INPUT_EVENT_UI","[NULL]"
+ -2143831735395280254,"STEP_HANDLE_INPUT_EVENT_IMPL",1292554155188210
+ -2143831735395280254,"STEP_DID_HANDLE_INPUT_AND_OVERSCROLL",1292554164359210
+ -2143831735395280254,"STEP_GESTURE_EVENT_HANDLED",1292554165141257
+ -2143831735395280250,"STEP_SEND_INPUT_EVENT_UI","[NULL]"
+ -2143831735395280250,"STEP_HANDLE_INPUT_EVENT_IMPL",1292554131865210
+ """))
+
def test_chrome_coalesced_inputs(self):
return DiffTestBlueprint(
trace=DataPath('scroll_m131.pftrace'),
diff --git a/test/trace_processor/diff_tests/stdlib/common/tests.py b/test/trace_processor/diff_tests/stdlib/common/tests.py
deleted file mode 100644
index d103320..0000000
--- a/test/trace_processor/diff_tests/stdlib/common/tests.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import TestSuite
-
-
-class StdlibCommon(TestSuite):
-
- def test_spans_overlapping_dur_intersect_edge(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 2, 1, 2) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_edge_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(1, 2, 0, 2) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_all(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 3, 1, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_intersect_all_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(1, 1, 0, 3) AS dur
- """,
- out=Csv("""
- "dur"
- 1
- """))
-
- def test_spans_overlapping_dur_no_intersect(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 1, 2, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_no_intersect_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(2, 1, 0, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_negative_dur(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, -1, 0, 1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
-
- def test_spans_overlapping_dur_negative_dur_reversed(self):
- return DiffTestBlueprint(
- trace=TextProto(r"""
-
- """),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT SPANS_OVERLAPPING_DUR(0, 1, 0, -1) AS dur
- """,
- out=Csv("""
- "dur"
- 0
- """))
diff --git a/test/trace_processor/diff_tests/stdlib/sched/tests.py b/test/trace_processor/diff_tests/stdlib/sched/tests.py
index 1de62b5..a327125 100644
--- a/test/trace_processor/diff_tests/stdlib/sched/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/sched/tests.py
@@ -187,3 +187,33 @@
538177,537492,537492
538175,538174,524613
"""))
+
+ def test_sched_latency(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_boot.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.latency;
+
+ SELECT
+ thread_state_id,
+ sched_id,
+ utid,
+ runnable_latency_id,
+ latency_dur
+ FROM sched_latency_for_running_interval
+ ORDER BY thread_state_id DESC
+ LIMIT 10;
+ """,
+ out=Csv("""
+ "thread_state_id","sched_id","utid","runnable_latency_id","latency_dur"
+ 538199,269427,2,538191,91919
+ 538197,269425,2,538191,91919
+ 538195,269423,2,538191,91919
+ 538190,269422,1330,538136,1437215
+ 538188,269420,2,538088,826823
+ 538184,269419,91,538176,131388
+ 538181,269418,319,538178,4883
+ 538179,269417,1022,524619,469849
+ 538177,269416,319,537492,670736
+ 538175,269415,91,538174,12532
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/slices/tests.py b/test/trace_processor/diff_tests/stdlib/slices/tests.py
index 625659e..88b430f 100644
--- a/test/trace_processor/diff_tests/stdlib/slices/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/slices/tests.py
@@ -177,3 +177,27 @@
8,46926
9,17865
"""))
+
+ def test_thread_slice_time_in_state(self):
+ return DiffTestBlueprint(
+ trace=DataPath('example_android_trace_30s.pb'),
+ query="""
+ INCLUDE PERFETTO MODULE slices.time_in_state;
+
+ SELECT id, name, state, io_wait, blocked_function, dur
+ FROM thread_slice_time_in_state
+ LIMIT 10;
+ """,
+ out=Csv("""
+ "id","name","state","io_wait","blocked_function","dur"
+ 0,"Deoptimization JIT inline cache","Running","[NULL]","[NULL]",178646
+ 1,"Deoptimization JIT inline cache","Running","[NULL]","[NULL]",119740
+ 2,"Lock contention on thread list lock (owner tid: 0)","Running","[NULL]","[NULL]",58073
+ 3,"Lock contention on thread list lock (owner tid: 0)","Running","[NULL]","[NULL]",98698
+ 3,"Lock contention on thread list lock (owner tid: 0)","S","[NULL]","[NULL]",56302
+ 4,"monitor contention with owner InputReader (1421) at void com.android.server.power.PowerManagerService.acquireWakeLockInternal(android.os.IBinder, int, java.lang.String, java.lang.String, android.os.WorkSource, java.lang.String, int, int)(PowerManagerService.java:1018) waiters=0 blocking from void com.android.server.power.PowerManagerService.handleSandman()(PowerManagerService.java:2280)","Running","[NULL]","[NULL]",121979
+ 4,"monitor contention with owner InputReader (1421) at void com.android.server.power.PowerManagerService.acquireWakeLockInternal(android.os.IBinder, int, java.lang.String, java.lang.String, android.os.WorkSource, java.lang.String, int, int)(PowerManagerService.java:1018) waiters=0 blocking from void com.android.server.power.PowerManagerService.handleSandman()(PowerManagerService.java:2280)","S","[NULL]","[NULL]",51198
+ 5,"monitor contention with owner main (1204) at void com.android.server.am.ActivityManagerService.onWakefulnessChanged(int)(ActivityManagerService.java:7244) waiters=0 blocking from void com.android.server.am.ActivityManagerService$3.handleMessage(android.os.Message)(ActivityManagerService.java:1704)","Running","[NULL]","[NULL]",45000
+ 5,"monitor contention with owner main (1204) at void com.android.server.am.ActivityManagerService.onWakefulnessChanged(int)(ActivityManagerService.java:7244) waiters=0 blocking from void com.android.server.am.ActivityManagerService$3.handleMessage(android.os.Message)(ActivityManagerService.java:1704)","S","[NULL]","[NULL]",20164377
+ 6,"monitor contention with owner main (1204) at void com.android.server.am.ActivityManagerService.onWakefulnessChanged(int)(ActivityManagerService.java:7244) waiters=1 blocking from com.android.server.wm.ActivityTaskManagerInternal$SleepToken com.android.server.am.ActivityTaskManagerService.acquireSleepToken(java.lang.String, int)(ActivityTaskManagerService.java:5048)","Running","[NULL]","[NULL]",35104
+ """))
diff --git a/test/trace_processor/diff_tests/stdlib/timestamps/tests.py b/test/trace_processor/diff_tests/stdlib/timestamps/tests.py
index a029ce5..d5cc34b 100644
--- a/test/trace_processor/diff_tests/stdlib/timestamps/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/timestamps/tests.py
@@ -22,86 +22,60 @@
class Timestamps(TestSuite):
- def test_ns(self):
+ def test_to_time(self):
return DiffTestBlueprint(
trace=TextProto(""),
query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT ns(4) as result;
+ INCLUDE PERFETTO MODULE time.conversion;
+
+ WITH data(unit, time) AS (
+ VALUES
+ ('ns', time_to_ns(cast_int!(1e14))),
+ ('us', time_to_us(cast_int!(1e14))),
+ ('ms', time_to_ms(cast_int!(1e14))),
+ ('s', time_to_s(cast_int!(1e14))),
+ ('min', time_to_min(cast_int!(1e14))),
+ ('h', time_to_hours(cast_int!(1e14))),
+ ('days', time_to_days(cast_int!(1e14)))
+ )
+ SELECT * FROM data
""",
out=Csv("""
- "result"
- 4
+ "unit","time"
+ "ns",100000000000000
+ "us",100000000000
+ "ms",100000000
+ "s",100000
+ "min",1666
+ "h",27
+ "days",1
"""))
- def test_us(self):
+ def test_from_time(self):
return DiffTestBlueprint(
trace=TextProto(""),
query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT us(4) as result;
- """,
- out=Csv("""
- "result"
- 4000
- """))
+ INCLUDE PERFETTO MODULE time.conversion;
- def test_ms(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT ms(4) as result;
+ WITH data(unit, time) AS (
+ VALUES
+ ('ns', time_from_ns(1)),
+ ('us', time_from_us(1)),
+ ('ms', time_from_ms(1)),
+ ('s', time_from_s(1)),
+ ('min', time_from_min(1)),
+ ('h', time_from_hours(1)),
+ ('days', time_from_days(1))
+ )
+ SELECT * FROM data
""",
out=Csv("""
- "result"
- 4000000
- """))
-
- def test_seconds(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT seconds(4) as result;
- """,
- out=Csv("""
- "result"
- 4000000000
- """))
-
- def test_minutes(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT minutes(1) as result;
- """,
- out=Csv("""
- "result"
- 60000000000
- """))
-
- def test_hours(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT hours(1) as result;
- """,
- out=Csv("""
- "result"
- 3600000000000
- """))
-
- def test_days(self):
- return DiffTestBlueprint(
- trace=TextProto(""),
- query="""
- INCLUDE PERFETTO MODULE common.timestamps;
- SELECT days(1) as result;
- """,
- out=Csv("""
- "result"
- 86400000000000
- """))
+ "unit","time"
+ "ns",1
+ "us",1000
+ "ms",1000000
+ "s",1000000000
+ "min",60000000000
+ "h",3600000000000
+ "days",86400000000000
+ """))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/syntax/include_tests.py b/test/trace_processor/diff_tests/syntax/include_tests.py
index 6c4d88a..6316ae6 100644
--- a/test/trace_processor/diff_tests/syntax/include_tests.py
+++ b/test/trace_processor/diff_tests/syntax/include_tests.py
@@ -23,121 +23,40 @@
def test_import(self):
return DiffTestBlueprint(
- trace=TextProto(r"""
- packet {
- ftrace_events {
- cpu: 1
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|13\n"
- }
- }
- event {
- timestamp: 4000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|20\n"
- }
- }
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.audio|1\n"
- }
- }
- }
- }
- """),
+ trace=TextProto(''),
query="""
- SELECT IMPORT('common.timestamps');
+ SELECT IMPORT('time.conversion');
- SELECT TRACE_START();
+ SELECT 1 AS x;
""",
out=Csv("""
- "TRACE_START()"
- 1000
+ "x"
+ 1
"""))
def test_include_perfetto_module(self):
return DiffTestBlueprint(
- trace=TextProto(r"""
- packet {
- ftrace_events {
- cpu: 1
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|13\n"
- }
- }
- event {
- timestamp: 4000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|20\n"
- }
- }
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.audio|1\n"
- }
- }
- }
- }
- """),
+ trace=TextProto(''),
query="""
- INCLUDE PERFETTO MODULE common.timestamps;
+ INCLUDE PERFETTO MODULE time.conversion;
- SELECT TRACE_START();
+ SELECT time_to_ns(1) AS x
""",
out=Csv("""
- "TRACE_START()"
- 1000
+ "x"
+ 1
"""))
def test_include_and_import(self):
return DiffTestBlueprint(
- trace=TextProto(r"""
- packet {
- ftrace_events {
- cpu: 1
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|13\n"
- }
- }
- event {
- timestamp: 4000
- pid: 1
- print {
- buf: "C|1000|battery_stats.data_conn|20\n"
- }
- }
- event {
- timestamp: 1000
- pid: 1
- print {
- buf: "C|1000|battery_stats.audio|1\n"
- }
- }
- }
- }
- """),
+ trace=TextProto(''),
query="""
- SELECT IMPORT('common.timestamps');
- INCLUDE PERFETTO MODULE common.timestamps;
+ SELECT IMPORT('time.conversion');
+ INCLUDE PERFETTO MODULE time.conversion;
- SELECT TRACE_START();
+ SELECT 1 AS x
""",
out=Csv("""
- "TRACE_START()"
- 1000
+ "x"
+ 1
"""))
diff --git a/test/trace_processor/diff_tests/syntax/table_tests.py b/test/trace_processor/diff_tests/syntax/table_tests.py
index a90e71c..fb2cbc2 100644
--- a/test/trace_processor/diff_tests/syntax/table_tests.py
+++ b/test/trace_processor/diff_tests/syntax/table_tests.py
@@ -412,3 +412,159 @@
"MAX(id)"
20745
"""))
+
+ def test_winscope_proto_to_args_with_defaults_with_nested_fields(self):
+ return DiffTestBlueprint(
+ trace=Path('../parser/android/surfaceflinger_layers.textproto'),
+ query="""
+ SELECT flat_key, key, int_value, string_value, real_value FROM __intrinsic_winscope_proto_to_args_with_defaults('surfaceflinger_layer') AS sfl
+ ORDER BY sfl.base64_proto_id, key
+ LIMIT 95
+ """,
+ out=Csv("""
+ "flat_key","key","int_value","string_value","real_value"
+ "active_buffer","active_buffer","[NULL]","[NULL]","[NULL]"
+ "app_id","app_id",0,"[NULL]","[NULL]"
+ "background_blur_radius","background_blur_radius",0,"[NULL]","[NULL]"
+ "barrier_layer","barrier_layer","[NULL]","[NULL]","[NULL]"
+ "blur_regions","blur_regions","[NULL]","[NULL]","[NULL]"
+ "bounds.bottom","bounds.bottom","[NULL]","[NULL]",24000.000000
+ "bounds.left","bounds.left","[NULL]","[NULL]",-10800.000000
+ "bounds.right","bounds.right","[NULL]","[NULL]",10800.000000
+ "bounds.top","bounds.top","[NULL]","[NULL]",-24000.000000
+ "buffer_transform","buffer_transform","[NULL]","[NULL]","[NULL]"
+ "children","children[0]",4,"[NULL]","[NULL]"
+ "children","children[1]",35,"[NULL]","[NULL]"
+ "children","children[2]",43,"[NULL]","[NULL]"
+ "children","children[3]",45,"[NULL]","[NULL]"
+ "children","children[4]",44,"[NULL]","[NULL]"
+ "children","children[5]",77,"[NULL]","[NULL]"
+ "children","children[6]",87,"[NULL]","[NULL]"
+ "color.a","color.a","[NULL]","[NULL]",1.000000
+ "color.b","color.b","[NULL]","[NULL]",-1.000000
+ "color.g","color.g","[NULL]","[NULL]",-1.000000
+ "color.r","color.r","[NULL]","[NULL]",-1.000000
+ "color_transform","color_transform","[NULL]","[NULL]","[NULL]"
+ "corner_radius","corner_radius","[NULL]","[NULL]",0.000000
+ "corner_radius_crop","corner_radius_crop","[NULL]","[NULL]","[NULL]"
+ "crop.bottom","crop.bottom",-1,"[NULL]","[NULL]"
+ "crop.left","crop.left",0,"[NULL]","[NULL]"
+ "crop.right","crop.right",-1,"[NULL]","[NULL]"
+ "crop.top","crop.top",0,"[NULL]","[NULL]"
+ "curr_frame","curr_frame",0,"[NULL]","[NULL]"
+ "damage_region","damage_region","[NULL]","[NULL]","[NULL]"
+ "dataspace","dataspace","[NULL]","BT709 sRGB Full range","[NULL]"
+ "destination_frame.bottom","destination_frame.bottom",-1,"[NULL]","[NULL]"
+ "destination_frame.left","destination_frame.left",0,"[NULL]","[NULL]"
+ "destination_frame.right","destination_frame.right",-1,"[NULL]","[NULL]"
+ "destination_frame.top","destination_frame.top",0,"[NULL]","[NULL]"
+ "effective_scaling_mode","effective_scaling_mode",0,"[NULL]","[NULL]"
+ "effective_transform","effective_transform","[NULL]","[NULL]","[NULL]"
+ "final_crop","final_crop","[NULL]","[NULL]","[NULL]"
+ "flags","flags",2,"[NULL]","[NULL]"
+ "hwc_composition_type","hwc_composition_type","[NULL]","HWC_TYPE_UNSPECIFIED","[NULL]"
+ "hwc_crop","hwc_crop","[NULL]","[NULL]","[NULL]"
+ "hwc_frame","hwc_frame","[NULL]","[NULL]","[NULL]"
+ "hwc_transform","hwc_transform",0,"[NULL]","[NULL]"
+ "id","id",3,"[NULL]","[NULL]"
+ "input_window_info","input_window_info","[NULL]","[NULL]","[NULL]"
+ "invalidate","invalidate",1,"[NULL]","[NULL]"
+ "is_opaque","is_opaque",0,"[NULL]","[NULL]"
+ "is_protected","is_protected",0,"[NULL]","[NULL]"
+ "is_relative_of","is_relative_of",0,"[NULL]","[NULL]"
+ "is_trusted_overlay","is_trusted_overlay",0,"[NULL]","[NULL]"
+ "layer_stack","layer_stack",0,"[NULL]","[NULL]"
+ "metadata","metadata","[NULL]","[NULL]","[NULL]"
+ "name","name","[NULL]","Display 0 name=\"Built-in Screen\"#3","[NULL]"
+ "original_id","original_id",0,"[NULL]","[NULL]"
+ "owner_uid","owner_uid",1000,"[NULL]","[NULL]"
+ "parent","parent",0,"[NULL]","[NULL]"
+ "pixel_format","pixel_format","[NULL]","Unknown/None","[NULL]"
+ "position","position","[NULL]","[NULL]","[NULL]"
+ "queued_frames","queued_frames",0,"[NULL]","[NULL]"
+ "refresh_pending","refresh_pending",0,"[NULL]","[NULL]"
+ "relatives","relatives","[NULL]","[NULL]","[NULL]"
+ "requested_color.a","requested_color.a","[NULL]","[NULL]",1.000000
+ "requested_color.b","requested_color.b","[NULL]","[NULL]",-1.000000
+ "requested_color.g","requested_color.g","[NULL]","[NULL]",-1.000000
+ "requested_color.r","requested_color.r","[NULL]","[NULL]",-1.000000
+ "requested_corner_radius","requested_corner_radius","[NULL]","[NULL]",0.000000
+ "requested_position","requested_position","[NULL]","[NULL]","[NULL]"
+ "requested_transform.dsdx","requested_transform.dsdx","[NULL]","[NULL]",0.000000
+ "requested_transform.dsdy","requested_transform.dsdy","[NULL]","[NULL]",0.000000
+ "requested_transform.dtdx","requested_transform.dtdx","[NULL]","[NULL]",0.000000
+ "requested_transform.dtdy","requested_transform.dtdy","[NULL]","[NULL]",0.000000
+ "requested_transform.type","requested_transform.type",0,"[NULL]","[NULL]"
+ "screen_bounds.bottom","screen_bounds.bottom","[NULL]","[NULL]",24000.000000
+ "screen_bounds.left","screen_bounds.left","[NULL]","[NULL]",-10800.000000
+ "screen_bounds.right","screen_bounds.right","[NULL]","[NULL]",10800.000000
+ "screen_bounds.top","screen_bounds.top","[NULL]","[NULL]",-24000.000000
+ "shadow_radius","shadow_radius","[NULL]","[NULL]",0.000000
+ "size","size","[NULL]","[NULL]","[NULL]"
+ "source_bounds.bottom","source_bounds.bottom","[NULL]","[NULL]",24000.000000
+ "source_bounds.left","source_bounds.left","[NULL]","[NULL]",-10800.000000
+ "source_bounds.right","source_bounds.right","[NULL]","[NULL]",10800.000000
+ "source_bounds.top","source_bounds.top","[NULL]","[NULL]",-24000.000000
+ "transform.dsdx","transform.dsdx","[NULL]","[NULL]",0.000000
+ "transform.dsdy","transform.dsdy","[NULL]","[NULL]",0.000000
+ "transform.dtdx","transform.dtdx","[NULL]","[NULL]",0.000000
+ "transform.dtdy","transform.dtdy","[NULL]","[NULL]",0.000000
+ "transform.type","transform.type",0,"[NULL]","[NULL]"
+ "transparent_region","transparent_region","[NULL]","[NULL]","[NULL]"
+ "trusted_overlay","trusted_overlay","[NULL]","UNSET","[NULL]"
+ "type","type","[NULL]","[NULL]","[NULL]"
+ "visible_region","visible_region","[NULL]","[NULL]","[NULL]"
+ "window_type","window_type",0,"[NULL]","[NULL]"
+ "z","z",0,"[NULL]","[NULL]"
+ "z_order_relative_of","z_order_relative_of",0,"[NULL]","[NULL]"
+ "active_buffer","active_buffer","[NULL]","[NULL]","[NULL]"
+ """))
+
+ def test_winscope_proto_to_args_with_defaults_with_repeated_fields(self):
+ return DiffTestBlueprint(
+ trace=Path('../parser/android/surfaceflinger_layers.textproto'),
+ query="""
+ SELECT flat_key, key, int_value, string_value, real_value FROM __intrinsic_winscope_proto_to_args_with_defaults('surfaceflinger_layers_snapshot') AS sfs
+ WHERE key != "hwc_blob"
+ ORDER BY sfs.base64_proto_id DESC, key ASC
+ LIMIT 36
+ """,
+ out=Csv("""
+ "flat_key","key","int_value","string_value","real_value"
+ "displays.dpi_x","displays[0].dpi_x","[NULL]","[NULL]",0.000000
+ "displays.dpi_y","displays[0].dpi_y","[NULL]","[NULL]",0.000000
+ "displays.id","displays[0].id",4619827677550801152,"[NULL]","[NULL]"
+ "displays.is_virtual","displays[0].is_virtual",0,"[NULL]","[NULL]"
+ "displays.layer_stack","displays[0].layer_stack",0,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.bottom","displays[0].layer_stack_space_rect.bottom",2400,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.left","displays[0].layer_stack_space_rect.left",0,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.right","displays[0].layer_stack_space_rect.right",1080,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.top","displays[0].layer_stack_space_rect.top",0,"[NULL]","[NULL]"
+ "displays.name","displays[0].name","[NULL]","Common Panel","[NULL]"
+ "displays.size.h","displays[0].size.h",2400,"[NULL]","[NULL]"
+ "displays.size.w","displays[0].size.w",1080,"[NULL]","[NULL]"
+ "displays.transform.dsdx","displays[0].transform.dsdx","[NULL]","[NULL]",0.000000
+ "displays.transform.dsdy","displays[0].transform.dsdy","[NULL]","[NULL]",0.000000
+ "displays.transform.dtdx","displays[0].transform.dtdx","[NULL]","[NULL]",0.000000
+ "displays.transform.dtdy","displays[0].transform.dtdy","[NULL]","[NULL]",0.000000
+ "displays.transform.type","displays[0].transform.type",0,"[NULL]","[NULL]"
+ "displays.dpi_x","displays[1].dpi_x","[NULL]","[NULL]",0.000000
+ "displays.dpi_y","displays[1].dpi_y","[NULL]","[NULL]",0.000000
+ "displays.id","displays[1].id",4619827677550801153,"[NULL]","[NULL]"
+ "displays.is_virtual","displays[1].is_virtual",0,"[NULL]","[NULL]"
+ "displays.layer_stack","displays[1].layer_stack",0,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.bottom","displays[1].layer_stack_space_rect.bottom",2400,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.left","displays[1].layer_stack_space_rect.left",0,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.right","displays[1].layer_stack_space_rect.right",1080,"[NULL]","[NULL]"
+ "displays.layer_stack_space_rect.top","displays[1].layer_stack_space_rect.top",0,"[NULL]","[NULL]"
+ "displays.name","displays[1].name","[NULL]","Common Panel","[NULL]"
+ "displays.size.h","displays[1].size.h",2400,"[NULL]","[NULL]"
+ "displays.size.w","displays[1].size.w",1080,"[NULL]","[NULL]"
+ "displays.transform","displays[1].transform","[NULL]","[NULL]","[NULL]"
+ "elapsed_realtime_nanos","elapsed_realtime_nanos",2749500341063,"[NULL]","[NULL]"
+ "excludes_composition_state","excludes_composition_state",0,"[NULL]","[NULL]"
+ "missed_entries","missed_entries",0,"[NULL]","[NULL]"
+ "vsync_id","vsync_id",24767,"[NULL]","[NULL]"
+ "where","where","[NULL]","bufferLatched","[NULL]"
+ "displays.dpi_x","displays[0].dpi_x","[NULL]","[NULL]",0.000000
+ """))
diff --git a/tools/check_sql_modules.py b/tools/check_sql_modules.py
index f787143..ed8ddc3 100755
--- a/tools/check_sql_modules.py
+++ b/tools/check_sql_modules.py
@@ -106,7 +106,10 @@
if (include_package == "common"):
errors.append(
- "Common module has been deprecated in the standard library.")
+ "Common module has been deprecated in the standard library. "
+ "Please check `slices.with_context` for a replacement for "
+ "`common.slices` and `time.conversion` for replacement for "
+ "`common.timestamps`")
if (package != "viz" and include_package == "viz"):
errors.append("No modules can depend on 'viz' outside 'viz' package.")
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index a4d3261..181039f 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -60,6 +60,8 @@
action='store_true',
help='Update the expected output file with the actual result')
parser.add_argument(
+ '--quiet', action='store_true', help='Only print if the test failed.')
+ parser.add_argument(
'--no-colors', action='store_true', help='Print without coloring')
parser.add_argument(
'trace_processor', type=str, help='location of trace processor binary')
@@ -80,7 +82,8 @@
test_runner = DiffTestsRunner(args.name_filter, args.trace_processor,
args.trace_descriptor, args.no_colors,
- args.override_sql_module, args.test_dir)
+ args.override_sql_module, args.test_dir,
+ args.quiet)
sys.stderr.write(f"[==========] Running {len(test_runner.tests)} tests.\n")
results = test_runner.run_all_tests(args.metrics_descriptor,
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index f5386dc..71340c9 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -302,6 +302,13 @@
],
'libperfetto': [('export_include_dirs', {'include', buildflags_dir}),],
'perfetto': [('required', {'perfetto_persistent_cfg.pbtxt'}),],
+ 'trace_redactor': [
+ ('min_sdk_version', '35'),
+ ('apex_available', {
+ '//apex_available:platform',
+ 'com.android.profiling'
+ }),
+ ],
}
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 1b619f5..a5776ba 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -78,12 +78,16 @@
public_targets = [
'//:libperfetto_client_experimental',
'//src/perfetto_cmd:perfetto',
- '//src/shared_lib:libperfetto_c',
'//src/traced/probes:traced_probes',
'//src/traced/service:traced',
'//src/trace_processor:trace_processor_shell',
'//src/trace_processor:trace_processor',
'//src/traceconv:traceconv',
+]
+
+# These targets will be exported with visibility only to our allowlist.
+allowlist_public_targets = [
+ '//src/shared_lib:libperfetto_c',
'//src/traceconv:libpprofbuilder',
]
@@ -100,7 +104,7 @@
'//src/tools/proto_merger:proto_merger',
'//src/trace_processor/rpc:trace_processor_rpc',
'//test:client_api_example',
-] + public_targets
+] + public_targets + allowlist_public_targets
# Proto target groups which will be made public.
proto_groups = {
@@ -713,8 +717,14 @@
else:
label.srcs = raw_srcs
- if gn_target.name in public_targets:
+ is_public = gn_target.name in public_targets
+ is_public_for_allowlist = gn_target.name in allowlist_public_targets
+ if is_public and is_public_for_allowlist:
+ raise Error('Target %s in both public_targets and allowlist_public_targets', gn.target.name)
+ elif is_public:
label.visibility = ['//visibility:public']
+ elif is_public_for_allowlist:
+ label.visibility = ALLOWLIST_PUBLIC_VISIBILITY
if win_target:
label.win_srcs = list(set(label.srcs) & {s[2:] for s in win_target.sources | win_target.inputs})
diff --git a/tools/gen_tp_table_docs.py b/tools/gen_tp_table_docs.py
index 04339bf..c69ab58 100755
--- a/tools/gen_tp_table_docs.py
+++ b/tools/gen_tp_table_docs.py
@@ -110,6 +110,13 @@
table_docs = []
for parsed in util.parse_tables_from_modules(modules):
table = parsed.table
+
+ # If there is no non-intrinsic alias for the table, don't
+ # include the table in the docs.
+ name = util.public_sql_name(table)
+ if name.startswith('__intrinsic_') or name.startswith('experimental_'):
+ continue
+
doc = table.tabledoc
assert doc
cols = (
@@ -117,7 +124,7 @@
for c in parsed.columns
if not c.is_ancestor)
table_docs.append({
- 'name': util.public_sql_name(table),
+ 'name': name,
'cppClassName': table.class_name,
'defMacro': table.class_name,
'comment': '\n'.join(l.strip() for l in doc.doc.splitlines()),
diff --git a/ui/release/channels.json b/ui/release/channels.json
index c2c76c2..5f8615b 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -6,7 +6,7 @@
},
{
"name": "canary",
- "rev": "4817ff8af4289f905c36a8a1ba6a583afc569af4"
+ "rev": "2db61efa59d1e2eecb6975854c14b2a122fbfa8a"
},
{
"name": "autopush",
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index d3ae7ef..2cce56f 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -236,21 +236,6 @@
font-family: "Roboto Condensed", sans-serif;
}
-.helpful-hint {
- position: absolute;
- z-index: 10;
- right: 5px;
- top: 5px;
- width: 300px;
- background-color: white;
- font-size: 12px;
- color: #3f4040;
- display: grid;
- border-radius: 5px;
- padding: 8px;
- box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
-}
-
.hint-text {
padding-bottom: 5px;
}
diff --git a/ui/src/assets/viewer_page.scss b/ui/src/assets/viewer_page.scss
index 5ed0432..56a8e5b 100644
--- a/ui/src/assets/viewer_page.scss
+++ b/ui/src/assets/viewer_page.scss
@@ -94,6 +94,21 @@
.time-selection-panel {
height: 10px;
}
+
+ .helpful-hint {
+ position: absolute;
+ z-index: 10;
+ right: 5px;
+ top: 5px;
+ width: 300px;
+ background-color: white;
+ font-size: 12px;
+ color: #3f4040;
+ display: grid;
+ border-radius: 5px;
+ padding: 8px;
+ box-shadow: 1px 3px 15px rgba(23, 32, 44, 0.3);
+ }
}
.pf-track-crash-popup {
diff --git a/ui/src/base/assets.ts b/ui/src/base/assets.ts
new file mode 100644
index 0000000..7eee18e
--- /dev/null
+++ b/ui/src/base/assets.ts
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {getServingRoot} from './http_utils';
+
+let rootUrl = '';
+
+/**
+ * This function must be called once while bootstrapping in a direct script
+ * context (i.e. not a promise or callback). Typically frontend/index.ts.
+ */
+export function initAssets() {
+ rootUrl = getServingRoot();
+}
+
+/**
+ * Returns the absolute url of an asset.
+ * assetSrc('assets/image.jpg') -> '/v123-deadbef/assets/image.png';
+ */
+export function assetSrc(relPath: string) {
+ return rootUrl + relPath;
+}
diff --git a/ui/src/common/gcs_uploader.ts b/ui/src/base/gcs_uploader.ts
similarity index 93%
rename from ui/src/common/gcs_uploader.ts
rename to ui/src/base/gcs_uploader.ts
index d2012ee..b2f2bd5 100644
--- a/ui/src/common/gcs_uploader.ts
+++ b/ui/src/base/gcs_uploader.ts
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer} from '../base/deferred';
-import {Time} from '../base/time';
-import {TraceFileStream} from '../core/trace_stream';
+import {defer} from './deferred';
+import {Time} from './time';
export const BUCKET_NAME = 'perfetto-ui-data';
export const MIME_JSON = 'application/json; charset=utf-8';
@@ -184,13 +183,16 @@
* @returns A hex-encoded string containing the hash of the file.
*/
async function hashFileStreaming(file: Blob): Promise<string> {
- const fileStream = new TraceFileStream(file);
+ const CHUNK_SIZE = 32 * 1024 * 1024; // 32MB
+ const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
let chunkDigests = '';
- for (;;) {
- const chunk = await fileStream.readChunk();
- const digest = await crypto.subtle.digest('SHA-1', chunk.data);
+
+ for (let i = 0; i < totalChunks; i++) {
+ const start = i * CHUNK_SIZE;
+ const end = Math.min(start + CHUNK_SIZE, file.size);
+ const chunk = await file.slice(start, end).arrayBuffer();
+ const digest = await crypto.subtle.digest('SHA-1', chunk);
chunkDigests += digestToHex(digest);
- if (chunk.eof) break;
}
return sha1(chunkDigests);
}
diff --git a/ui/src/base/hotkeys.ts b/ui/src/base/hotkeys.ts
index 17e3a00..85c194f 100644
--- a/ui/src/base/hotkeys.ts
+++ b/ui/src/base/hotkeys.ts
@@ -88,7 +88,9 @@
| 'ArrowLeft'
| 'ArrowRight'
| '['
- | ']';
+ | ']'
+ | ','
+ | '.';
export type Key = Alphabet | Number | Special;
export type Modifier =
| ''
diff --git a/ui/src/base/http_utils.ts b/ui/src/base/http_utils.ts
index b9d0c16..869dfb6 100644
--- a/ui/src/base/http_utils.ts
+++ b/ui/src/base/http_utils.ts
@@ -70,7 +70,7 @@
/**
* NOTE: this function can only be called from synchronous contexts. It will
* fail if called in timer handlers or async continuations (e.g. after an await)
- * Use globals.root which caches it on startup.
+ * Use assetSrc(relPath) which caches it on startup.
* @returns the directory where the app is served from, e.g. 'v46.0-a2082649b'
*/
export function getServingRoot() {
diff --git a/ui/src/base/mithril_utils.ts b/ui/src/base/mithril_utils.ts
index b450f64..9b0615d 100644
--- a/ui/src/base/mithril_utils.ts
+++ b/ui/src/base/mithril_utils.ts
@@ -52,3 +52,36 @@
);
},
};
+
+/**
+ * Utility function to pre-bind some mithril attrs of a component, and leave
+ * the others unbound and passed at run-time.
+ * Example use case: the Page API Passes to the registered page a PageAttrs,
+ * which is {subpage:string}. Imagine you write a MyPage component that takes
+ * some extra input attrs (e.g. the App object) and you want to bind them
+ * onActivate(). The results looks like this:
+ *
+ * interface MyPageAttrs extends PageAttrs { app: App; }
+ *
+ * class MyPage extends m.classComponent<MyPageAttrs> {... view() {...} }
+ *
+ * onActivate(app: App) {
+ * pages.register(... bindMithrilApps(MyPage, {app: app});
+ * }
+ *
+ * The return value of bindMithrilApps is a mithril component that takes in
+ * input only a {subpage: string} and passes down to MyPage the combination
+ * of pre-bound and runtime attrs, that is {subpage, app}.
+ */
+export function bindMithrilAttrs<BaseAttrs, Attrs>(
+ component: m.ComponentTypes<Attrs>,
+ boundArgs: Omit<Attrs, keyof BaseAttrs>,
+): m.Component<BaseAttrs> {
+ return {
+ view(vnode: m.Vnode<BaseAttrs>) {
+ const attrs = {...vnode.attrs, ...boundArgs} as Attrs;
+ const emptyAttrs: m.CommonAttributes<Attrs, {}> = {}; // Keep tsc happy.
+ return m<Attrs, {}>(component, {...attrs, ...emptyAttrs});
+ },
+ };
+}
diff --git a/ui/src/base/utils.ts b/ui/src/base/utils.ts
index 910dae7..a82062c 100644
--- a/ui/src/base/utils.ts
+++ b/ui/src/base/utils.ts
@@ -25,6 +25,17 @@
| {success: true; result: T}
| {success: false; error: E};
+// Type util to make sure that exactly one of the passed keys is defined.
+// Example usage:
+// type FooOrBar = ExactlyOne<{foo: number; bar: number}>;
+// const x : FooOrBar = {foo: 42}; // OK
+// const x : FooOrBar = {bar: 42}; // OK
+// const x : FooOrBar = {}; // Compiler error
+// const x : FooOrBar = {foo:1, bar:2}; // Compiler error
+export type ExactlyOne<T, K extends keyof T = keyof T> = K extends keyof T
+ ? {[P in K]: T[P]} & {[P in Exclude<keyof T, K>]?: undefined}
+ : never;
+
// Escape characters that are not allowed inside a css selector
export function escapeCSSSelector(selector: string): string {
return selector.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1');
@@ -52,3 +63,28 @@
map.set(key, value);
return value;
}
+
+// Allows to take an existing class instance (`target`) and override some of its
+// methods via `overrides`. We use this for cases where we want to expose a
+// "manager" (e.g. TrackManager, SidebarManager) to the plugins, but we want to
+// override few of its methods (e.g. to inject the pluginId in the args).
+export function createProxy<T extends object>(
+ target: T,
+ overrides: Partial<T>,
+): T {
+ return new Proxy(target, {
+ get: (target: T, prop: string | symbol, receiver) => {
+ // If the property is overriden, use that; otherwise, use target
+ const overrideValue = (overrides as {[key: symbol | string]: {}})[prop];
+ if (overrideValue !== undefined) {
+ return typeof overrideValue === 'function'
+ ? overrideValue.bind(overrides)
+ : overrideValue;
+ }
+ const baseValue = Reflect.get(target, prop, receiver);
+ return typeof baseValue === 'function'
+ ? baseValue.bind(target)
+ : baseValue;
+ },
+ }) as T;
+}
diff --git a/ui/src/chrome_extension/chrome_tracing_controller.ts b/ui/src/chrome_extension/chrome_tracing_controller.ts
index 916fca9..de15873 100644
--- a/ui/src/chrome_extension/chrome_tracing_controller.ts
+++ b/ui/src/chrome_extension/chrome_tracing_controller.ts
@@ -21,13 +21,13 @@
ConsumerPortResponse,
GetTraceStatsResponse,
ReadBuffersResponse,
-} from '../controller/consumer_port_types';
-import {RpcConsumerPort} from '../controller/record_controller_interfaces';
+} from '../plugins/dev.perfetto.RecordTrace/consumer_port_types';
+import {RpcConsumerPort} from '../plugins/dev.perfetto.RecordTrace/record_controller_interfaces';
import {
browserSupportsPerfettoConfig,
extractTraceConfig,
hasSystemDataSourceConfig,
-} from '../core/trace_config_utils';
+} from '../plugins/dev.perfetto.RecordTrace/trace_config_utils';
import {ITraceStats, TraceConfig} from '../protos';
import {DevToolsSocket} from './devtools_socket';
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
deleted file mode 100644
index 20d92cc..0000000
--- a/ui/src/common/actions.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Draft} from 'immer';
-import {RecordConfig} from '../controller/record_config_types';
-import {createEmptyState} from './empty_state';
-import {
- AdbRecordingTarget,
- LoadedConfig,
- RecordingTarget,
- State,
-} from './state';
-
-type StateDraft = Draft<State>;
-
-export const StateActions = {
- clearState(state: StateDraft, _args: {}) {
- const recordConfig = state.recordConfig;
- const recordingTarget = state.recordingTarget;
- const fetchChromeCategories = state.fetchChromeCategories;
- const extensionInstalled = state.extensionInstalled;
- const availableAdbDevices = state.availableAdbDevices;
- const chromeCategories = state.chromeCategories;
-
- Object.assign(state, createEmptyState());
- state.recordConfig = recordConfig;
- state.recordingTarget = recordingTarget;
- state.fetchChromeCategories = fetchChromeCategories;
- state.extensionInstalled = extensionInstalled;
- state.availableAdbDevices = availableAdbDevices;
- state.chromeCategories = chromeCategories;
- },
-
- requestTrackReload(state: StateDraft, _: {}) {
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
- if (state.lastTrackReloadRequest) {
- state.lastTrackReloadRequest++;
- } else {
- state.lastTrackReloadRequest = 1;
- }
- },
-
- // TODO(hjd): Remove setState - it causes problems due to reuse of ids.
- setState(state: StateDraft, args: {newState: State}): void {
- for (const key of Object.keys(state)) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- delete (state as any)[key];
- }
- for (const key of Object.keys(args.newState)) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (state as any)[key] = (args.newState as any)[key];
- }
- },
-
- setRecordConfig(
- state: StateDraft,
- args: {config: RecordConfig; configType?: LoadedConfig},
- ): void {
- state.recordConfig = args.config;
- state.lastLoadedConfig = args.configType || {type: 'NONE'};
- },
-
- startRecording(state: StateDraft, _: {}): void {
- state.recordingInProgress = true;
- state.lastRecordingError = undefined;
- state.recordingCancelled = false;
- },
-
- stopRecording(state: StateDraft, _: {}): void {
- state.recordingInProgress = false;
- },
-
- cancelRecording(state: StateDraft, _: {}): void {
- state.recordingInProgress = false;
- state.recordingCancelled = true;
- },
-
- setExtensionAvailable(state: StateDraft, args: {available: boolean}): void {
- state.extensionInstalled = args.available;
- },
-
- setRecordingTarget(state: StateDraft, args: {target: RecordingTarget}): void {
- state.recordingTarget = args.target;
- },
-
- setFetchChromeCategories(state: StateDraft, args: {fetch: boolean}): void {
- state.fetchChromeCategories = args.fetch;
- },
-
- setAvailableAdbDevices(
- state: StateDraft,
- args: {devices: AdbRecordingTarget[]},
- ): void {
- state.availableAdbDevices = args.devices;
- },
-
- setChromeCategories(state: StateDraft, args: {categories: string[]}): void {
- state.chromeCategories = args.categories;
- },
-
- setLastRecordingError(state: StateDraft, args: {error?: string}): void {
- state.lastRecordingError = args.error;
- state.recordingStatus = undefined;
- },
-
- setRecordingStatus(state: StateDraft, args: {status?: string}): void {
- state.recordingStatus = args.status;
- state.lastRecordingError = undefined;
- },
-
- togglePerfDebug(state: StateDraft, _: {}): void {
- state.perfDebug = !state.perfDebug;
- },
-
- setTrackFilterTerm(
- state: StateDraft,
- args: {filterTerm: string | undefined},
- ) {
- state.trackFilterTerm = args.filterTerm;
- },
-
- runControllers(state: StateDraft, _args: {}) {
- state.forceRunControllers++;
- },
-};
-
-// When we are on the frontend side, we don't really want to execute the
-// actions above, we just want to serialize them and marshal their
-// arguments, send them over to the controller side and have them being
-// executed there. The magic below takes care of turning each action into a
-// function that returns the marshaled args.
-
-// A DeferredAction is a bundle of Args and a method name. This is the marshaled
-// version of a StateActions method call.
-export interface DeferredAction<Args = {}> {
- type: string;
- args: Args;
-}
-
-// This type magic creates a type function DeferredActions<T> which takes a type
-// T and 'maps' its attributes. For each attribute on T matching the signature:
-// (state: StateDraft, args: Args) => void
-// DeferredActions<T> has an attribute:
-// (args: Args) => DeferredAction<Args>
-type ActionFunction<Args> = (state: StateDraft, args: Args) => void;
-type DeferredActionFunc<T> =
- T extends ActionFunction<infer Args>
- ? (args: Args) => DeferredAction<Args>
- : never;
-type DeferredActions<C> = {
- [P in keyof C]: DeferredActionFunc<C[P]>;
-};
-
-// Actions is an implementation of DeferredActions<typeof StateActions>.
-// (since StateActions is a variable not a type we have to do
-// 'typeof StateActions' to access the (unnamed) type of StateActions).
-// It's a Proxy such that any attribute access returns a function:
-// (args) => {return {type: ATTRIBUTE_NAME, args};}
-export const Actions =
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- new Proxy<DeferredActions<typeof StateActions>>({} as any, {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- get(_: any, prop: string, _2: any) {
- return (args: {}): DeferredAction<{}> => {
- return {
- type: prop,
- args,
- };
- };
- },
- });
diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts
deleted file mode 100644
index cc10366..0000000
--- a/ui/src/common/constants.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-export const TRACE_SUFFIX = '.perfetto-trace';
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
deleted file mode 100644
index daf2eca..0000000
--- a/ui/src/common/empty_state.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {createEmptyRecordConfig} from '../controller/record_config_types';
-import {featureFlags} from '../core/feature_flags';
-import {
- autosaveConfigStore,
- recordTargetStore,
-} from '../frontend/record_config';
-import {State, STATE_VERSION} from './state';
-
-const AUTOLOAD_STARTED_CONFIG_FLAG = featureFlags.register({
- id: 'autoloadStartedConfig',
- name: 'Auto-load last used recording config',
- description:
- 'Starting a recording automatically saves its configuration. ' +
- 'This flag controls whether this config is automatically loaded.',
- defaultValue: true,
-});
-
-export function keyedMap<T>(
- keyFn: (key: T) => string,
- ...values: T[]
-): Map<string, T> {
- const result = new Map<string, T>();
-
- for (const value of values) {
- result.set(keyFn(value), value);
- }
-
- return result;
-}
-
-export function createEmptyState(): State {
- return {
- version: STATE_VERSION,
-
- recordConfig: AUTOLOAD_STARTED_CONFIG_FLAG.get()
- ? autosaveConfigStore.get()
- : createEmptyRecordConfig(),
- displayConfigAsPbtxt: false,
- lastLoadedConfig: {type: 'NONE'},
-
- perfDebug: false,
-
- recordingInProgress: false,
- recordingCancelled: false,
- extensionInstalled: false,
- recordingTarget: recordTargetStore.getValidTarget(),
- availableAdbDevices: [],
-
- fetchChromeCategories: false,
- chromeCategories: undefined,
-
- trackFilterTerm: undefined,
- forceRunControllers: 0,
- };
-}
diff --git a/ui/src/common/recordingV2/recording_error_handling.ts b/ui/src/common/recordingV2/recording_error_handling.ts
deleted file mode 100644
index ffec467..0000000
--- a/ui/src/common/recordingV2/recording_error_handling.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {getErrorMessage} from '../../base/errors';
-import {
- showAllowUSBDebugging,
- showConnectionLostError,
- showExtensionNotInstalled,
- showFailedToPushBinary,
- showIssueParsingTheTracedResponse,
- showNoDeviceSelected,
- showWebsocketConnectionIssue,
- showWebUSBErrorV2,
-} from '../../frontend/error_dialog';
-import {OnMessageCallback} from './recording_interfaces_v2';
-import {
- ALLOW_USB_DEBUGGING,
- BINARY_PUSH_FAILURE,
- BINARY_PUSH_UNKNOWN_RESPONSE,
- EXTENSION_NOT_INSTALLED,
- NO_DEVICE_SELECTED,
- PARSING_UNABLE_TO_DECODE_METHOD,
- PARSING_UNKNWON_REQUEST_ID,
- PARSING_UNRECOGNIZED_MESSAGE,
- PARSING_UNRECOGNIZED_PORT,
- WEBSOCKET_UNABLE_TO_CONNECT,
-} from './recording_utils';
-
-// The pattern for handling recording error can have the following nesting in
-// case of errors:
-// A. wrapRecordingError -> wraps a promise
-// B. onFailure -> has user defined logic and calls showRecordingModal
-// C. showRecordingModal -> shows UX for a given error; this is not called
-// directly by wrapRecordingError, because we want the caller (such as the
-// UI) to dictate the UX
-
-// This method takes a promise and a callback to be execute in case the promise
-// fails. It then awaits the promise and executes the callback in case of
-// failure. In the recording code it is used to wrap:
-// 1. Acessing the WebUSB API.
-// 2. Methods returning promises which can be rejected. For instance:
-// a) When the user clicks 'Add a new device' but then doesn't select a valid
-// device.
-// b) When the user starts a tracing session, but cancels it before they
-// authorize the session on the device.
-export async function wrapRecordingError<T>(
- promise: Promise<T>,
- onFailure: OnMessageCallback,
-): Promise<T | undefined> {
- try {
- return await promise;
- } catch (e) {
- // Sometimes the message is wrapped in an Error object, sometimes not, so
- // we make sure we transform it into a string.
- const errorMessage = getErrorMessage(e);
- onFailure(errorMessage);
- return undefined;
- }
-}
-
-// Shows a modal for every known type of error which can arise during recording.
-// In this way, errors occuring at different levels of the recording process
-// can be handled in a central location.
-export function showRecordingModal(message: string): void {
- if (
- [
- 'Unable to claim interface.',
- 'The specified endpoint is not part of a claimed and selected ' +
- 'alternate interface.',
- // thrown when calling the 'reset' method on a WebUSB device.
- 'Unable to reset the device.',
- ].some((partOfMessage) => message.includes(partOfMessage))
- ) {
- showWebUSBErrorV2();
- } else if (
- [
- 'A transfer error has occurred.',
- 'The device was disconnected.',
- 'The transfer was cancelled.',
- ].some((partOfMessage) => message.includes(partOfMessage)) ||
- isDeviceDisconnectedError(message)
- ) {
- showConnectionLostError();
- } else if (message === ALLOW_USB_DEBUGGING) {
- showAllowUSBDebugging();
- } else if (
- isMessageComposedOf(message, [
- BINARY_PUSH_FAILURE,
- BINARY_PUSH_UNKNOWN_RESPONSE,
- ])
- ) {
- showFailedToPushBinary(message.substring(message.indexOf(':') + 1));
- } else if (message === NO_DEVICE_SELECTED) {
- showNoDeviceSelected();
- } else if (WEBSOCKET_UNABLE_TO_CONNECT === message) {
- showWebsocketConnectionIssue(message);
- } else if (message === EXTENSION_NOT_INSTALLED) {
- showExtensionNotInstalled();
- } else if (
- isMessageComposedOf(message, [
- PARSING_UNKNWON_REQUEST_ID,
- PARSING_UNABLE_TO_DECODE_METHOD,
- PARSING_UNRECOGNIZED_PORT,
- PARSING_UNRECOGNIZED_MESSAGE,
- ])
- ) {
- showIssueParsingTheTracedResponse(message);
- } else {
- throw new Error(`${message}`);
- }
-}
-
-function isDeviceDisconnectedError(message: string) {
- return (
- message.includes('Device with serial') &&
- message.includes('was disconnected.')
- );
-}
-
-function isMessageComposedOf(message: string, issues: string[]) {
- for (const issue of issues) {
- if (message.includes(issue)) {
- return true;
- }
- }
- return false;
-}
-
-// Exception thrown by the Recording logic.
-export class RecordingError extends Error {}
diff --git a/ui/src/common/schema.ts b/ui/src/common/schema.ts
deleted file mode 100644
index c3f33e4..0000000
--- a/ui/src/common/schema.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Engine} from '../trace_processor/engine';
-import {STR} from '../trace_processor/query_result';
-
-const CACHED_SCHEMAS = new WeakMap<Engine, DatabaseSchema>();
-
-export class SchemaError extends Error {
- constructor(message: string) {
- super(message);
- }
-}
-
-// POJO representing the table structure of trace_processor.
-// Exposed for testing.
-export interface DatabaseInfo {
- tables: TableInfo[];
-}
-
-interface TableInfo {
- name: string;
- parent?: TableInfo;
- columns: ColumnInfo[];
-}
-
-interface ColumnInfo {
- name: string;
-}
-
-async function getColumns(
- engine: Engine,
- table: string,
-): Promise<ColumnInfo[]> {
- const result = await engine.query(`PRAGMA table_info(${table});`);
- const it = result.iter({
- name: STR,
- });
- const columns = [];
- for (; it.valid(); it.next()) {
- columns.push({name: it['name']});
- }
- return columns;
-}
-
-// Opinionated view on the schema of the given trace_processor instance
-// suitable for EventSets to use for query generation.
-export class DatabaseSchema {
- private tableToKeys: Map<string, Set<string>>;
-
- constructor(info: DatabaseInfo) {
- this.tableToKeys = new Map();
- for (const tableInfo of info.tables) {
- const columns = new Set(tableInfo.columns.map((c) => c.name));
- this.tableToKeys.set(tableInfo.name, columns);
- }
- }
-
- // Return all the EventSet keys available for a given table. This
- // includes the direct columns on the table (and all parent tables)
- // as well as all direct and indirect joinable tables where the join
- // is N:1 or 1:1. e.g. for the table thread_slice we also include
- // the columns from thread, process, thread_track etc.
- getKeys(tableName: string): Set<string> {
- const columns = this.tableToKeys.get(tableName);
- if (columns === undefined) {
- throw new SchemaError(`Unknown table '${tableName}'`);
- }
- return columns;
- }
-}
-
-// Deliberately not exported. Users should call getSchema below and
-// participate in cacheing.
-async function createSchema(engine: Engine): Promise<DatabaseSchema> {
- const tables: TableInfo[] = [];
- const result = await engine.query(`SELECT name from perfetto_tables;`);
- const it = result.iter({
- name: STR,
- });
- for (; it.valid(); it.next()) {
- const name = it['name'];
- tables.push({
- name,
- columns: await getColumns(engine, name),
- });
- }
-
- const database: DatabaseInfo = {
- tables,
- };
-
- return new DatabaseSchema(database);
-}
-
-// Get the schema for the given engine (from the cache if possible).
-// The schemas are per-engine (i.e. they can't be statically determined
-// at build time) since we might be in httpd mode and not-running
-// against the version of trace_processor we build with.
-export async function getSchema(engine: Engine): Promise<DatabaseSchema> {
- const schema = CACHED_SCHEMAS.get(engine);
- if (schema === undefined) {
- const newSchema = await createSchema(engine);
- CACHED_SCHEMAS.set(engine, newSchema);
- return newSchema;
- }
- return schema;
-}
diff --git a/ui/src/common/schema_unittest.ts b/ui/src/common/schema_unittest.ts
deleted file mode 100644
index f0a6a58..0000000
--- a/ui/src/common/schema_unittest.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {DatabaseInfo, DatabaseSchema, SchemaError} from './schema';
-
-test('DatabaseSchema > getKeys', () => {
- const info: DatabaseInfo = {
- tables: [
- {
- name: 'slice',
- columns: [{name: 'id'}, {name: 'ts'}, {name: 'dur'}],
- },
- ],
- };
- const schema = new DatabaseSchema(info);
- expect(schema.getKeys('slice')).toEqual(new Set(['id', 'ts', 'dur']));
-});
-
-test('DatabaseSchema > getKeys > Sad path', () => {
- const info: DatabaseInfo = {
- tables: [],
- };
- const schema = new DatabaseSchema(info);
- expect(() => schema.getKeys('foo')).toThrow(SchemaError);
-});
diff --git a/ui/src/common/track_helper.ts b/ui/src/common/track_helper.ts
index 3087228..e9ef6fb 100644
--- a/ui/src/common/track_helper.ts
+++ b/ui/src/common/track_helper.ts
@@ -95,6 +95,6 @@
const {start, end} = this.latestTimespan;
const resolution = this.latestResolution;
this.data_ = await this.doFetch(start, end, resolution);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
}
diff --git a/ui/src/controller/app_controller.ts b/ui/src/controller/app_controller.ts
deleted file mode 100644
index b42dc32..0000000
--- a/ui/src/controller/app_controller.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {RECORDING_V2_FLAG} from '../core/feature_flags';
-import {Child, Controller, ControllerInitializerAny} from './controller';
-import {RecordController} from './record_controller';
-
-// The root controller for the entire app. It handles the lifetime of all
-// the other controllers (e.g., track and query controllers) according to the
-// global state.
-export class AppController extends Controller<'main'> {
- // extensionPort is needed for the RecordController to communicate with the
- // extension through the frontend. This is because the controller is running
- // on a worker, and isn't able to directly send messages to the extension.
- private extensionPort: MessagePort;
-
- constructor(extensionPort: MessagePort) {
- super('main');
- this.extensionPort = extensionPort;
- }
-
- // This is the root method that is called every time the controller tree is
- // re-triggered. This can happen due to:
- // - An action received from the frontend.
- // - An internal promise of a nested controller being resolved and manually
- // re-triggering the controllers.
- run() {
- const childControllers: ControllerInitializerAny[] = [];
- if (!RECORDING_V2_FLAG.get()) {
- childControllers.push(
- Child('record', RecordController, {extensionPort: this.extensionPort}),
- );
- }
- return childControllers;
- }
-}
diff --git a/ui/src/controller/controller.ts b/ui/src/controller/controller.ts
deleted file mode 100644
index b70ed4f..0000000
--- a/ui/src/controller/controller.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-export type ControllerAny = Controller</* StateType=*/ unknown>;
-
-export interface ControllerFactory<ConstructorArgs> {
- new (args: ConstructorArgs): ControllerAny;
-}
-
-interface ControllerInitializer<ConstructorArgs> {
- id: string;
- factory: ControllerFactory<ConstructorArgs>;
- args: ConstructorArgs;
-}
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export type ControllerInitializerAny = ControllerInitializer<any>;
-
-export function Child<ConstructorArgs>(
- id: string,
- factory: ControllerFactory<ConstructorArgs>,
- args: ConstructorArgs,
-): ControllerInitializer<ConstructorArgs> {
- return {id, factory, args};
-}
-
-export type Children = ControllerInitializerAny[];
-
-export abstract class Controller<StateType> {
- // This is about the local FSM state, has nothing to do with the global
- // app state.
- private _stateChanged = false;
- private _inRunner = false;
- private _state: StateType;
- private _children = new Map<string, ControllerAny>();
-
- constructor(initialState: StateType) {
- this._state = initialState;
- }
-
- abstract run(): Children | void;
- onDestroy(): void {}
-
- // Invokes the current controller subtree, recursing into children.
- // While doing so handles lifecycle of child controllers.
- // This method should be called only by the runControllers() method in
- // globals.ts. Exposed publicly for testing.
- invoke(): boolean {
- if (this._inRunner) throw new Error('Reentrancy in Controller');
- this._stateChanged = false;
- this._inRunner = true;
- const resArray = this.run();
- let triggerAnotherRun = this._stateChanged;
- this._stateChanged = false;
-
- const nextChildren = new Map<string, ControllerInitializerAny>();
- if (resArray !== undefined) {
- for (const childConfig of resArray) {
- if (nextChildren.has(childConfig.id)) {
- throw new Error(`Duplicate children controller ${childConfig.id}`);
- }
- nextChildren.set(childConfig.id, childConfig);
- }
- }
- const dtors = new Array<() => void>();
- const runners = new Array<() => boolean>();
- for (const key of this._children.keys()) {
- if (nextChildren.has(key)) continue;
- const instance = this._children.get(key)!;
- this._children.delete(key);
- dtors.push(() => instance.onDestroy());
- }
- for (const nextChild of nextChildren.values()) {
- if (!this._children.has(nextChild.id)) {
- const instance = new nextChild.factory(nextChild.args);
- this._children.set(nextChild.id, instance);
- }
- const instance = this._children.get(nextChild.id)!;
- runners.push(() => instance.invoke());
- }
-
- for (const dtor of dtors) dtor(); // Invoke all onDestroy()s.
-
- // Invoke all runner()s.
- for (const runner of runners) {
- const recursiveRes = runner();
- triggerAnotherRun = triggerAnotherRun || recursiveRes;
- }
-
- this._inRunner = false;
- return triggerAnotherRun;
- }
-
- setState(state: StateType) {
- if (!this._inRunner) {
- throw new Error('Cannot setState() outside of the run() method');
- }
- this._stateChanged = state !== this._state;
- this._state = state;
- }
-
- get state(): StateType {
- return this._state;
- }
-}
diff --git a/ui/src/controller/controller_unittest.ts b/ui/src/controller/controller_unittest.ts
deleted file mode 100644
index ff5a558..0000000
--- a/ui/src/controller/controller_unittest.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Child, Controller} from './controller';
-
-const _onCreate = jest.fn();
-const _onDestroy = jest.fn();
-const _run = jest.fn();
-
-type MockStates = 'idle' | 'state1' | 'state2' | 'state3';
-class MockController extends Controller<MockStates> {
- constructor(public type: string) {
- super('idle');
- _onCreate(this.type);
- }
-
- run() {
- return _run(this.type);
- }
-
- onDestroy() {
- return _onDestroy(this.type);
- }
-}
-
-function runControllerTree(rootController: MockController): void {
- for (let runAgain = true, i = 0; runAgain; i++) {
- if (i >= 100) throw new Error('Controller livelock');
- runAgain = rootController.invoke();
- }
-}
-
-beforeEach(() => {
- _onCreate.mockClear();
- _onCreate.mockReset();
- _onDestroy.mockClear();
- _onDestroy.mockReset();
- _run.mockClear();
- _run.mockReset();
-});
-
-test('singleControllerNoTransition', () => {
- const rootCtl = new MockController('root');
- runControllerTree(rootCtl);
- expect(_run).toHaveBeenCalledTimes(1);
- expect(_run).toHaveBeenCalledWith('root');
-});
-
-test('singleControllerThreeTransitions', () => {
- const rootCtl = new MockController('root');
- _run.mockImplementation(() => {
- if (rootCtl.state === 'idle') {
- rootCtl.setState('state1');
- } else if (rootCtl.state === 'state1') {
- rootCtl.setState('state2');
- }
- });
- runControllerTree(rootCtl);
- expect(_run).toHaveBeenCalledTimes(3);
- expect(_run).toHaveBeenCalledWith('root');
-});
-
-test('nestedControllers', () => {
- const rootCtl = new MockController('root');
- let nextState: MockStates = 'idle';
- _run.mockImplementation((type: string) => {
- if (type !== 'root') return;
- rootCtl.setState(nextState);
- if (rootCtl.state === 'idle') return;
-
- if (rootCtl.state === 'state1') {
- return [Child('child1', MockController, 'child1')];
- }
- if (rootCtl.state === 'state2') {
- return [
- Child('child1', MockController, 'child1'),
- Child('child2', MockController, 'child2'),
- ];
- }
- if (rootCtl.state === 'state3') {
- return [
- Child('child1', MockController, 'child1'),
- Child('child3', MockController, 'child3'),
- ];
- }
- throw new Error('Not reached');
- });
- runControllerTree(rootCtl);
- expect(_run).toHaveBeenCalledWith('root');
- expect(_run).toHaveBeenCalledTimes(1);
-
- // Transition the root controller to state1. This will create the first child
- // and re-run both (because of the idle -> state1 transition).
- _run.mockClear();
- _onCreate.mockClear();
- nextState = 'state1';
- runControllerTree(rootCtl);
- expect(_onCreate).toHaveBeenCalledWith('child1');
- expect(_onCreate).toHaveBeenCalledTimes(1);
- expect(_run).toHaveBeenCalledWith('root');
- expect(_run).toHaveBeenCalledWith('child1');
- expect(_run).toHaveBeenCalledTimes(4);
-
- // Transition the root controller to state2. This will create the 2nd child
- // and run the three of them (root + 2 chilren) two times.
- _run.mockClear();
- _onCreate.mockClear();
- nextState = 'state2';
- runControllerTree(rootCtl);
- expect(_onCreate).toHaveBeenCalledWith('child2');
- expect(_onCreate).toHaveBeenCalledTimes(1);
- expect(_run).toHaveBeenCalledWith('root');
- expect(_run).toHaveBeenCalledWith('child1');
- expect(_run).toHaveBeenCalledWith('child2');
- expect(_run).toHaveBeenCalledTimes(6);
-
- // Transition the root controller to state3. This will create the 3rd child
- // and remove the 2nd one.
- _run.mockClear();
- _onCreate.mockClear();
- nextState = 'state3';
- runControllerTree(rootCtl);
- expect(_onCreate).toHaveBeenCalledWith('child3');
- expect(_onDestroy).toHaveBeenCalledWith('child2');
- expect(_onCreate).toHaveBeenCalledTimes(1);
- expect(_run).toHaveBeenCalledWith('root');
- expect(_run).toHaveBeenCalledWith('child1');
- expect(_run).toHaveBeenCalledWith('child3');
- expect(_run).toHaveBeenCalledTimes(6);
-
- // Finally transition back to the idle state. All children should be removed.
- _run.mockClear();
- _onCreate.mockClear();
- _onDestroy.mockClear();
- nextState = 'idle';
- runControllerTree(rootCtl);
- expect(_onDestroy).toHaveBeenCalledWith('child1');
- expect(_onDestroy).toHaveBeenCalledWith('child3');
- expect(_onCreate).toHaveBeenCalledTimes(0);
- expect(_onDestroy).toHaveBeenCalledTimes(2);
- expect(_run).toHaveBeenCalledWith('root');
- expect(_run).toHaveBeenCalledTimes(2);
-});
diff --git a/ui/src/controller/index.ts b/ui/src/controller/index.ts
deleted file mode 100644
index e451a36..0000000
--- a/ui/src/controller/index.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import '../common/recordingV2/target_factories';
-import {assertExists, assertTrue} from '../base/logging';
-import {AppController} from './app_controller';
-import {ControllerAny} from './controller';
-
-let rootController: ControllerAny;
-let runningControllers = false;
-
-export function initController(extensionPort: MessagePort) {
- assertTrue(rootController === undefined);
- rootController = new AppController(extensionPort);
-}
-
-export function runControllers() {
- if (runningControllers) throw new Error('Re-entrant call detected');
-
- // Run controllers locally until all state machines reach quiescence.
- let runAgain = true;
- for (let iter = 0; runAgain; iter++) {
- if (iter > 100) throw new Error('Controllers are stuck in a livelock');
- runningControllers = true;
- try {
- runAgain = assertExists(rootController).invoke();
- } finally {
- runningControllers = false;
- }
- }
-}
diff --git a/ui/src/core/app_impl.ts b/ui/src/core/app_impl.ts
index 0779457..c1045e2 100644
--- a/ui/src/core/app_impl.ts
+++ b/ui/src/core/app_impl.ts
@@ -14,7 +14,7 @@
import {assertExists, assertTrue} from '../base/logging';
import {App} from '../public/app';
-import {TraceImpl} from './trace_impl';
+import {TraceContext, TraceImpl} from './trace_impl';
import {CommandManagerImpl} from './command_manager';
import {OmniboxManagerImpl} from './omnibox_manager';
import {raf} from './raf_scheduler';
@@ -29,16 +29,18 @@
import {CORE_PLUGIN_ID} from './plugin_manager';
import {Router} from './router';
import {AnalyticsInternal, initAnalytics} from './analytics_impl';
+import {createProxy, getOrCreate} from '../base/utils';
+import {PageManagerImpl} from './page_manager';
+import {PageHandler} from '../public/page';
+import {PerfManager} from './perf_manager';
+import {ServiceWorkerController} from '../frontend/service_worker_controller';
+import {FeatureFlagManager, FlagSettings} from '../public/feature_flag';
+import {featureFlags} from './feature_flags';
// The args that frontend/index.ts passes when calling AppImpl.initialize().
// This is to deal with injections that would otherwise cause circular deps.
export interface AppInitArgs {
- rootUrl: string;
initialRouteArgs: RouteArgs;
-
- // TODO(primiano): remove once State is gone.
- // This maps to globals.dispatch(Actions.clearState({})),
- clearState: () => void;
}
/**
@@ -50,12 +52,20 @@
* and should use AppImpl instead.
*/
export class AppContext {
+ // The per-plugin instances of AppImpl (including the CORE_PLUGIN one).
+ private readonly pluginInstances = new Map<string, AppImpl>();
readonly commandMgr = new CommandManagerImpl();
readonly omniboxMgr = new OmniboxManagerImpl();
+ readonly pageMgr = new PageManagerImpl();
readonly sidebarMgr: SidebarManagerImpl;
readonly pluginMgr: PluginManagerImpl;
+ readonly perfMgr = new PerfManager();
readonly analytics: AnalyticsInternal;
- newEngineMode: NewEngineMode = 'USE_HTTP_RPC_IF_AVAILABLE';
+ readonly serviceWorkerController: ServiceWorkerController;
+ httpRpc = {
+ newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE' as NewEngineMode,
+ httpRpcAvailable: false,
+ };
initialRouteArgs: RouteArgs;
isLoadingTrace = false; // Set when calling openTrace().
readonly initArgs: AppInitArgs;
@@ -66,30 +76,71 @@
// via is_internal_user.js
extraSqlPackages: SqlPackage[] = [];
+ // The currently open trace.
+ currentTrace?: TraceContext;
+
+ private static _instance: AppContext;
+
+ static initialize(initArgs: AppInitArgs): AppContext {
+ assertTrue(AppContext._instance === undefined);
+ return (AppContext._instance = new AppContext(initArgs));
+ }
+
+ static get instance(): AppContext {
+ return assertExists(AppContext._instance);
+ }
+
// This constructor is invoked only once, when frontend/index.ts invokes
// AppMainImpl.initialize().
- constructor(initArgs: AppInitArgs) {
+ private constructor(initArgs: AppInitArgs) {
this.initArgs = initArgs;
this.initialRouteArgs = initArgs.initialRouteArgs;
- this.sidebarMgr = new SidebarManagerImpl(
- this.initialRouteArgs.hideSidebar === true ? 'DISABLED' : 'ENABLED',
- );
+ this.serviceWorkerController = new ServiceWorkerController();
this.embeddedMode = this.initialRouteArgs.mode === 'embedded';
this.testingMode =
self.location !== undefined &&
self.location.search.indexOf('testing=1') >= 0;
+ this.sidebarMgr = new SidebarManagerImpl({
+ disabled: this.embeddedMode,
+ hidden: this.initialRouteArgs.hideSidebar,
+ });
this.analytics = initAnalytics(this.testingMode, this.embeddedMode);
- // The rootUrl should point to 'https://ui.perfetto.dev/v1.2.3/'. It's
- // allowed to be empty only in unittests, because there there is no bundle
- // hence no concrete root.
- assertTrue(this.initArgs.rootUrl !== '' || typeof jest !== 'undefined');
this.pluginMgr = new PluginManagerImpl({
- forkForPlugin: (p) => AppImpl.instance.forkForPlugin(p),
+ forkForPlugin: (pluginId) => this.forPlugin(pluginId),
get trace() {
return AppImpl.instance.trace;
},
});
}
+
+ // Gets or creates an instance of AppImpl backed by the current AppContext
+ // for the given plugin.
+ forPlugin(pluginId: string) {
+ return getOrCreate(this.pluginInstances, pluginId, () => {
+ return new AppImpl(this, pluginId);
+ });
+ }
+
+ closeCurrentTrace() {
+ this.omniboxMgr.reset(/* focus= */ false);
+
+ if (this.currentTrace !== undefined) {
+ // This will trigger the unregistration of trace-scoped commands and
+ // sidebar menuitems (and few similar things).
+ this.currentTrace[Symbol.dispose]();
+ this.currentTrace = undefined;
+ }
+ }
+
+ // Called by trace_loader.ts soon after it has created a new TraceImpl.
+ setActiveTrace(traceCtx: TraceContext) {
+ // In 99% this closeCurrentTrace() call is not needed because the real one
+ // is performed by openTrace() in this file. However in some rare cases we
+ // might end up loading a trace while another one is still loading, and this
+ // covers races in that case.
+ this.closeCurrentTrace();
+ this.currentTrace = traceCtx;
+ }
}
/*
@@ -100,35 +151,39 @@
*/
export class AppImpl implements App {
- private appCtx: AppContext;
readonly pluginId: string;
- private currentTrace?: TraceImpl;
+ private readonly appCtx: AppContext;
+ private readonly pageMgrProxy: PageManagerImpl;
- private constructor(appCtx: AppContext, pluginId: string) {
- this.appCtx = appCtx;
- this.pluginId = pluginId;
+ // Invoked by frontend/index.ts.
+ static initialize(args: AppInitArgs) {
+ AppContext.initialize(args).forPlugin(CORE_PLUGIN_ID);
}
// Gets access to the one instance that the core can use. Note that this is
// NOT the only instance, as other AppImpl instance will be created for each
// plugin.
- private static _instance: AppImpl;
-
- // Invoked by frontend/index.ts.
- static initialize(args: AppInitArgs) {
- assertTrue(AppImpl._instance === undefined);
- AppImpl._instance = new AppImpl(new AppContext(args), CORE_PLUGIN_ID);
- }
-
- // For testing purposes only.
- // TODO(primiano): This is only required because today globals.ts abuses
- // createFakeTraceImpl(). It can be removed once globals goes away.
- static get initialized() {
- return AppImpl._instance !== undefined;
- }
-
static get instance(): AppImpl {
- return assertExists(AppImpl._instance);
+ return AppContext.instance.forPlugin(CORE_PLUGIN_ID);
+ }
+
+ // Only called by AppContext.forPlugin().
+ constructor(appCtx: AppContext, pluginId: string) {
+ this.appCtx = appCtx;
+ this.pluginId = pluginId;
+
+ this.pageMgrProxy = createProxy(this.appCtx.pageMgr, {
+ registerPage(pageHandler: PageHandler): Disposable {
+ return appCtx.pageMgr.registerPage({
+ ...pageHandler,
+ pluginId,
+ });
+ },
+ });
+ }
+
+ forPlugin(pluginId: string): AppImpl {
+ return this.appCtx.forPlugin(pluginId);
}
get commands(): CommandManagerImpl {
@@ -151,31 +206,32 @@
return this.appCtx.analytics;
}
+ get pages(): PageManagerImpl {
+ return this.pageMgrProxy;
+ }
+
get trace(): TraceImpl | undefined {
- return this.currentTrace;
+ return this.appCtx.currentTrace?.forPlugin(this.pluginId);
}
- scheduleFullRedraw(): void {
- raf.scheduleFullRedraw();
+ scheduleFullRedraw(force?: 'force'): void {
+ raf.scheduleFullRedraw(force);
}
- forkForPlugin(pluginId: string): AppImpl {
- assertTrue(pluginId != CORE_PLUGIN_ID);
- return new AppImpl(this.appCtx, pluginId);
- }
-
- get newEngineMode() {
- return this.appCtx.newEngineMode;
- }
-
- set newEngineMode(mode: NewEngineMode) {
- this.appCtx.newEngineMode = mode;
+ get httpRpc() {
+ return this.appCtx.httpRpc;
}
get initialRouteArgs(): RouteArgs {
return this.appCtx.initialRouteArgs;
}
+ get featureFlags(): FeatureFlagManager {
+ return {
+ register: (settings: FlagSettings) => featureFlags.register(settings),
+ };
+ }
+
openTraceFromFile(file: File): void {
this.openTrace({type: 'FILE', file});
}
@@ -193,8 +249,7 @@
}
private async openTrace(src: TraceSource) {
- assertTrue(this.pluginId === CORE_PLUGIN_ID);
- this.closeCurrentTrace();
+ this.appCtx.closeCurrentTrace();
this.appCtx.isLoadingTrace = true;
try {
// loadTrace() in trace_loader.ts will do the following:
@@ -220,6 +275,11 @@
}
}
+ // Called by trace_loader.ts soon after it has created a new TraceImpl.
+ setActiveTrace(traceImpl: TraceImpl) {
+ this.appCtx.setActiveTrace(traceImpl.__traceCtxForApp);
+ }
+
get embeddedMode(): boolean {
return this.appCtx.embeddedMode;
}
@@ -228,47 +288,26 @@
return this.appCtx.testingMode;
}
- closeCurrentTrace() {
- // This method should be called only on the core instance, plugins don't
- // have access to openTrace*() methods.
- assertTrue(this.pluginId === CORE_PLUGIN_ID);
- this.omnibox.reset(/* focus= */ false);
-
- if (this.currentTrace !== undefined) {
- // This will trigger the unregistration of trace-scoped commands and
- // sidebar menuitems (and few similar things).
- this.currentTrace[Symbol.dispose]();
- this.currentTrace = undefined;
- }
- this.appCtx.initArgs.clearState();
- }
-
- // Called by trace_loader.ts soon after it has created a new TraceImpl.
- setActiveTrace(traceImpl: TraceImpl) {
- // In 99% this closeCurrentTrace() call is not needed because the real one
- // is performed by openTrace() in this file. However in some rare cases we
- // might end up loading a trace while another one is still loading, and this
- // covers races in that case.
- this.closeCurrentTrace();
- this.currentTrace = traceImpl;
- }
-
get isLoadingTrace() {
return this.appCtx.isLoadingTrace;
}
- get rootUrl() {
- return this.appCtx.initArgs.rootUrl;
- }
-
get extraSqlPackages(): SqlPackage[] {
return this.appCtx.extraSqlPackages;
}
+ get perfDebugging(): PerfManager {
+ return this.appCtx.perfMgr;
+ }
+
+ get serviceWorkerController(): ServiceWorkerController {
+ return this.appCtx.serviceWorkerController;
+ }
+
// Nothing other than TraceImpl's constructor should ever refer to this.
// This is necessary to avoid circular dependencies between trace_impl.ts
// and app_impl.ts.
- get __appCtxForTraceImplCtor() {
+ get __appCtxForTrace() {
return this.appCtx;
}
diff --git a/ui/src/core/command_manager.ts b/ui/src/core/command_manager.ts
index fdf6ee6..ad0f482 100644
--- a/ui/src/core/command_manager.ts
+++ b/ui/src/core/command_manager.ts
@@ -15,6 +15,7 @@
import {FuzzyFinder, FuzzySegment} from '../base/fuzzy';
import {Registry} from '../base/registry';
import {Command, CommandManager} from '../public/command';
+import {raf} from './raf_scheduler';
export interface CommandWithMatchInfo extends Command {
segments: FuzzySegment[];
@@ -39,10 +40,11 @@
return this.registry.register(cmd);
}
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- runCommand(id: string, ...args: any[]): any {
+ runCommand(id: string, ...args: unknown[]): unknown {
const cmd = this.registry.get(id);
- return cmd.callback(...args);
+ const res = cmd.callback(...args);
+ Promise.resolve(res).finally(() => raf.scheduleFullRedraw('force'));
+ return res;
}
// Returns a list of commands that match the search term, along with a list
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index be39f6b..caba9f0 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -22,6 +22,7 @@
// - Be approved by one of Perfetto UI owners.
export const defaultPlugins = [
'com.android.GpuWorkPeriod',
+ 'com.google.PixelCpmTrace',
'com.google.PixelMemory',
'dev.perfetto.AndroidBinderVizPlugin',
'dev.perfetto.AndroidClientServer',
@@ -42,23 +43,30 @@
'dev.perfetto.CriticalPath',
'dev.perfetto.DebugTracks',
'dev.perfetto.DeeplinkQuerystring',
+ 'dev.perfetto.FlagsPage',
'dev.perfetto.Frames',
'dev.perfetto.Ftrace',
'dev.perfetto.HeapProfile',
'dev.perfetto.LargeScreensPerf',
+ 'dev.perfetto.MetricsPage',
'dev.perfetto.PerfSamplesProfile',
'dev.perfetto.PinAndroidPerfMetrics',
'dev.perfetto.PinSysUITracks',
'dev.perfetto.Process',
'dev.perfetto.ProcessSummary',
'dev.perfetto.ProcessThreadGroups',
+ 'dev.perfetto.QueryPage',
+ 'dev.perfetto.RecordTrace',
'dev.perfetto.RestorePinnedTrack',
'dev.perfetto.Sched',
'dev.perfetto.Screenshots',
+ 'dev.perfetto.SqlModules',
'dev.perfetto.Thread',
'dev.perfetto.ThreadState',
'dev.perfetto.TimelineSync',
+ 'dev.perfetto.TraceInfoPage',
'dev.perfetto.TraceMetadata',
+ 'dev.perfetto.VizPage',
'org.chromium.CriticalUserInteraction',
'org.kernel.LinuxKernelSubsystems',
'org.kernel.SuspendResumeLatency',
diff --git a/ui/src/core/fake_trace_impl.ts b/ui/src/core/fake_trace_impl.ts
index 6b3f763..fc944db 100644
--- a/ui/src/core/fake_trace_impl.ts
+++ b/ui/src/core/fake_trace_impl.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {getServingRoot} from '../base/http_utils';
import {Time} from '../base/time';
import {EngineBase} from '../trace_processor/engine';
import {AppImpl} from './app_impl';
@@ -31,23 +30,14 @@
export function initializeAppImplForTesting(): AppImpl {
if (!appImplInitialized) {
appImplInitialized = true;
- AppImpl.initialize({
- rootUrl: getServingRoot(), // NOTE: will be '' in unittests.
- initialRouteArgs: {},
- clearState: () => {},
- });
+ AppImpl.initialize({initialRouteArgs: {}});
}
return AppImpl.instance;
}
-// This is used:
-// - For testing.
-// - By globals.ts before we have an actual trace loaded, to avoid causing
-// if (!= undefined) checks everywhere.
+// For testing purposes only.
export function createFakeTraceImpl(args: FakeTraceImplArgs = {}) {
- if (!AppImpl.initialized) {
- initializeAppImplForTesting();
- }
+ initializeAppImplForTesting();
const fakeTraceInfo: TraceInfoImpl = {
source: {type: 'URL', url: ''},
traceTitle: '',
diff --git a/ui/src/core/feature_flags.ts b/ui/src/core/feature_flags.ts
index 4e745b9..7fa974d 100644
--- a/ui/src/core/feature_flags.ts
+++ b/ui/src/core/feature_flags.ts
@@ -16,20 +16,8 @@
// ~everywhere and the are "statically" initialized (i.e. files construct Flags
// at import time) if this file starts importing anything we will quickly run
// into issues with initialization order which will be a pain.
-
-interface FlagSettings {
- id: string;
- defaultValue: boolean;
- description: string;
- name?: string;
- devOnly?: boolean;
-}
-
-export enum OverrideState {
- DEFAULT = 'DEFAULT',
- TRUE = 'OVERRIDE_TRUE',
- FALSE = 'OVERRIDE_FALSE',
-}
+import {z} from 'zod';
+import {Flag, FlagSettings, OverrideState} from '../public/feature_flag';
export interface FlagStore {
load(): object;
@@ -41,22 +29,6 @@
[id: string]: OverrideState;
}
-// Check if the given object is a valid FlagOverrides.
-// This is necessary since someone could modify the persisted flags
-// behind our backs.
-function isFlagOverrides(o: object): o is FlagOverrides {
- const states = [
- OverrideState.TRUE.toString(),
- OverrideState.FALSE.toString(),
- ];
- for (const v of Object.values(o)) {
- if (typeof v !== 'string' || !states.includes(v)) {
- return false;
- }
- }
- return true;
-}
-
class Flags {
private store: FlagStore;
private flags: Map<string, FlagImpl>;
@@ -102,8 +74,17 @@
load(): void {
const o = this.store.load();
- if (isFlagOverrides(o)) {
- this.overrides = o;
+
+ // Check if the given object is a valid FlagOverrides.
+ // This is necessary since someone could modify the persisted flags
+ // behind our backs.
+ const flagsSchema = z.record(
+ z.string(),
+ z.union([z.literal(OverrideState.TRUE), z.literal(OverrideState.FALSE)]),
+ );
+ const {success, data} = flagsSchema.safeParse(o);
+ if (success) {
+ this.overrides = data;
}
}
@@ -118,54 +99,6 @@
this.store.save(this.overrides);
}
-
- /**
- * Modify an override at runtime and save it back to local storage.
- *
- * This method operates on the raw JSON in local storage and doesn't require
- * the presence of a flag to work. Thus, it may be called at any point in the
- * lifecycle of the flags object.
- *
- * @param key - The key of the flag to modify.
- * @param state - The desired state of the flag override.
- */
- patchOverride(key: string, state: OverrideState): void {
- this.overrides[key] = state;
- this.save();
- }
-}
-
-export interface Flag {
- // A unique identifier for this flag ("magicSorting")
- readonly id: string;
-
- // The name of the flag the user sees ("New track sorting algorithm")
- readonly name: string;
-
- // A longer description which is displayed to the user.
- // "Sort tracks using an embedded tfLite model based on your expression
- // while waiting for the trace to load."
- readonly description: string;
-
- // Whether the flag defaults to true or false.
- // If !flag.isOverridden() then flag.get() === flag.defaultValue
- readonly defaultValue: boolean;
-
- // Get the current value of the flag.
- get(): boolean;
-
- // Override the flag and persist the new value.
- set(value: boolean): void;
-
- // If the flag has been overridden.
- // Note: A flag can be overridden to its default value.
- isOverridden(): boolean;
-
- // Reset the flag to its default setting.
- reset(): void;
-
- // Get the current state of the flag.
- overriddenState(): OverrideState;
}
class FlagImpl implements Flag {
@@ -248,10 +181,3 @@
export const FlagsForTesting = Flags;
export const featureFlags = new Flags(new LocalStorageStore());
-
-export const RECORDING_V2_FLAG = featureFlags.register({
- id: 'recordingv2',
- name: 'Recording V2',
- description: 'Record using V2 interface',
- defaultValue: false,
-});
diff --git a/ui/src/core/load_trace.ts b/ui/src/core/load_trace.ts
index 3776f95..41196c7 100644
--- a/ui/src/core/load_trace.ts
+++ b/ui/src/core/load_trace.ts
@@ -130,7 +130,7 @@
// Check if there is any instance of the trace_processor_shell running in
// HTTP RPC mode (i.e. trace_processor_shell -D).
let useRpc = false;
- if (app.newEngineMode === 'USE_HTTP_RPC_IF_AVAILABLE') {
+ if (app.httpRpc.newEngineMode === 'USE_HTTP_RPC_IF_AVAILABLE') {
useRpc = (await HttpRpcEngine.checkConnection()).connected;
}
let engine;
@@ -147,7 +147,7 @@
ftraceDropUntilAllCpusValid: FTRACE_DROP_UNTIL_FLAG.get(),
});
}
- engine.onResponseReceived = () => raf.scheduleFullRedraw();
+ engine.onResponseReceived = () => raf.scheduleFullRedraw('force');
if (isMetatracingEnabled()) {
engine.enableMetatrace(assertExists(getEnabledMetatracingCategories()));
diff --git a/ui/src/core/page_manager.ts b/ui/src/core/page_manager.ts
new file mode 100644
index 0000000..18e6858
--- /dev/null
+++ b/ui/src/core/page_manager.ts
@@ -0,0 +1,83 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {assertExists, assertTrue} from '../base/logging';
+import {Registry} from '../base/registry';
+import {PageAttrs, PageHandler, PageWithTraceAttrs} from '../public/page';
+import {Router} from './router';
+import {TraceImpl} from './trace_impl';
+
+export interface PageWithTraceImplAttrs extends PageAttrs {
+ trace: TraceImpl;
+}
+
+// This is to allow internal core classes to get a TraceImpl injected rather
+// than just a Trace.
+type PageHandlerInternal = PageHandler<
+ | m.ComponentTypes<PageWithTraceAttrs>
+ | m.ComponentTypes<PageWithTraceImplAttrs>
+>;
+
+export class PageManagerImpl {
+ private readonly registry = new Registry<PageHandlerInternal>((x) => x.route);
+
+ registerPage(pageHandler: PageHandlerInternal): Disposable {
+ assertTrue(/^\/\w*$/.exec(pageHandler.route) !== null);
+ // The pluginId is injected by the proxy in AppImpl / TraceImpl. If this is
+ // undefined somebody (tests) managed to call this method without proxy.
+ assertExists(pageHandler.pluginId);
+ return this.registry.register(pageHandler);
+ }
+
+ // Called by index.ts upon the main frame redraw callback.
+ renderPageForCurrentRoute(
+ trace: TraceImpl | undefined,
+ ): m.Vnode<PageAttrs> | m.Vnode<PageWithTraceImplAttrs> {
+ const route = Router.parseFragment(location.hash);
+ const res = this.renderPageForRoute(trace, route.page, route.subpage);
+ if (res !== undefined) {
+ return res;
+ }
+ // If either the route doesn't exist or requires a trace but the trace is
+ // not loaded, fall back on the default route /.
+ return assertExists(this.renderPageForRoute(trace, '/', ''));
+ }
+
+ // Will return undefined if either: (1) the route does not exist; (2) the
+ // route exists, it requires a trace, but there is no trace loaded.
+ private renderPageForRoute(
+ coreTrace: TraceImpl | undefined,
+ page: string,
+ subpage: string,
+ ) {
+ const handler = this.registry.tryGet(page);
+ if (handler === undefined) {
+ return undefined;
+ }
+ const pluginId = assertExists(handler?.pluginId);
+ const trace = coreTrace?.forkForPlugin(pluginId);
+ const traceRequired = !handler?.traceless;
+ if (traceRequired && trace === undefined) {
+ return undefined;
+ }
+ if (traceRequired) {
+ return m(handler.page as m.ComponentTypes<PageWithTraceImplAttrs>, {
+ subpage,
+ trace: assertExists(trace),
+ });
+ }
+ return m(handler.page, {subpage, trace});
+ }
+}
diff --git a/ui/src/core/perf.ts b/ui/src/core/perf.ts
deleted file mode 100644
index 6e9afaf..0000000
--- a/ui/src/core/perf.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-
-const hooks = {
- isDebug: () => false,
- toggleDebug: () => {},
-};
-
-export function setPerfHooks(isDebug: () => boolean, toggleDebug: () => void) {
- hooks.isDebug = isDebug;
- hooks.toggleDebug = toggleDebug;
-}
-
-// Shorthand for if globals perf debug mode is on.
-export const perfDebug = () => hooks.isDebug();
-
-// Returns performance.now() if perfDebug is enabled, otherwise 0.
-// This is needed because calling performance.now is generally expensive
-// and should not be done for every frame.
-export const debugNow = () => (perfDebug() ? performance.now() : 0);
-
-// Returns execution time of |fn| if perf debug mode is on. Returns 0 otherwise.
-export function measure(fn: () => void): number {
- const start = debugNow();
- fn();
- return debugNow() - start;
-}
-
-// Stores statistics about samples, and keeps a fixed size buffer of most recent
-// samples.
-export class RunningStatistics {
- private _count = 0;
- private _mean = 0;
- private _lastValue = 0;
- private _ptr = 0;
-
- private buffer: number[] = [];
-
- constructor(private _maxBufferSize = 10) {}
-
- addValue(value: number) {
- this._lastValue = value;
- if (this.buffer.length >= this._maxBufferSize) {
- this.buffer[this._ptr++] = value;
- if (this._ptr >= this.buffer.length) {
- this._ptr -= this.buffer.length;
- }
- } else {
- this.buffer.push(value);
- }
-
- this._mean = (this._mean * this._count + value) / (this._count + 1);
- this._count++;
- }
-
- get mean() {
- return this._mean;
- }
- get count() {
- return this._count;
- }
- get bufferMean() {
- return this.buffer.reduce((sum, v) => sum + v, 0) / this.buffer.length;
- }
- get bufferSize() {
- return this.buffer.length;
- }
- get maxBufferSize() {
- return this._maxBufferSize;
- }
- get last() {
- return this._lastValue;
- }
-}
-
-// Returns a summary string representation of a RunningStatistics object.
-export function runningStatStr(stat: RunningStatistics) {
- return (
- `Last: ${stat.last.toFixed(2)}ms | ` +
- `Avg: ${stat.mean.toFixed(2)}ms | ` +
- `Avg${stat.maxBufferSize}: ${stat.bufferMean.toFixed(2)}ms`
- );
-}
-
-export interface PerfStatsSource {
- renderPerfStats(): m.Children;
-}
-
-// Globals singleton class that renders performance stats for the whole app.
-class PerfDisplay {
- private containers: PerfStatsSource[] = [];
-
- addContainer(container: PerfStatsSource) {
- this.containers.push(container);
- }
-
- removeContainer(container: PerfStatsSource) {
- const i = this.containers.indexOf(container);
- this.containers.splice(i, 1);
- }
-
- renderPerfStats(src: PerfStatsSource) {
- if (!perfDebug()) return;
- const perfDisplayEl = document.querySelector('.perf-stats');
- if (!perfDisplayEl) return;
- m.render(perfDisplayEl, [
- m('section', src.renderPerfStats()),
- m(
- 'button.close-button',
- {
- onclick: hooks.toggleDebug,
- },
- m('i.material-icons', 'close'),
- ),
- this.containers.map((c, i) =>
- m('section', m('div', `Panel Container ${i + 1}`), c.renderPerfStats()),
- ),
- ]);
- }
-}
-
-export const perfDisplay = new PerfDisplay();
diff --git a/ui/src/core/perf_manager.ts b/ui/src/core/perf_manager.ts
new file mode 100644
index 0000000..e63e7e8
--- /dev/null
+++ b/ui/src/core/perf_manager.ts
@@ -0,0 +1,145 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {raf} from './raf_scheduler';
+import {PerfStats, PerfStatsContainer, runningStatStr} from './perf_stats';
+
+export class PerfManager {
+ private _enabled = false;
+ readonly containers: PerfStatsContainer[] = [];
+
+ get enabled(): boolean {
+ return this._enabled;
+ }
+
+ set enabled(enabled: boolean) {
+ this._enabled = enabled;
+ raf.setPerfStatsEnabled(true);
+ this.containers.forEach((c) => c.setPerfStatsEnabled(enabled));
+ }
+
+ addContainer(container: PerfStatsContainer): Disposable {
+ this.containers.push(container);
+ return {
+ [Symbol.dispose]: () => {
+ const i = this.containers.indexOf(container);
+ this.containers.splice(i, 1);
+ },
+ };
+ }
+
+ renderPerfStats(): m.Children {
+ if (!this._enabled) return;
+ // The rendering of the perf stats UI is atypical. The main issue is that we
+ // want to redraw the mithril component even if there is no full DOM redraw
+ // happening (and we don't want to force redraws as a side effect). So we
+ // return here just a container and handle its rendering ourselves.
+ const perfMgr = this;
+ let removed = false;
+ return m('.perf-stats', {
+ oncreate(vnode: m.VnodeDOM) {
+ const animationFrame = (dom: Element) => {
+ if (removed) return;
+ m.render(dom, m(PerfStatsUi, {perfMgr}));
+ requestAnimationFrame(() => animationFrame(dom));
+ };
+ animationFrame(vnode.dom);
+ },
+ onremove() {
+ removed = true;
+ },
+ });
+ }
+}
+
+// The mithril component that draws the contents of the perf stats box.
+
+interface PerfStatsUiAttrs {
+ perfMgr: PerfManager;
+}
+
+class PerfStatsUi implements m.ClassComponent<PerfStatsUiAttrs> {
+ view({attrs}: m.Vnode<PerfStatsUiAttrs>) {
+ return m(
+ '.perf-stats',
+ {},
+ m('section', this.renderRafSchedulerStats()),
+ m(
+ 'button.close-button',
+ {
+ onclick: () => (attrs.perfMgr.enabled = false),
+ },
+ m('i.material-icons', 'close'),
+ ),
+ attrs.perfMgr.containers.map((c, i) =>
+ m('section', m('div', `Panel Container ${i + 1}`), c.renderPerfStats()),
+ ),
+ );
+ }
+
+ renderRafSchedulerStats() {
+ return m(
+ 'div',
+ m('div', [
+ m(
+ 'button',
+ {onclick: () => raf.scheduleCanvasRedraw()},
+ 'Do Canvas Redraw',
+ ),
+ ' | ',
+ m(
+ 'button',
+ {onclick: () => raf.scheduleFullRedraw()},
+ 'Do Full Redraw',
+ ),
+ ]),
+ m('div', 'Raf Timing ' + '(Total may not add up due to imprecision)'),
+ m(
+ 'table',
+ this.statTableHeader(),
+ this.statTableRow('Actions', raf.perfStats.rafActions),
+ this.statTableRow('Dom', raf.perfStats.rafDom),
+ this.statTableRow('Canvas', raf.perfStats.rafCanvas),
+ this.statTableRow('Total', raf.perfStats.rafTotal),
+ ),
+ m(
+ 'div',
+ 'Dom redraw: ' +
+ `Count: ${raf.perfStats.domRedraw.count} | ` +
+ runningStatStr(raf.perfStats.domRedraw),
+ ),
+ );
+ }
+
+ statTableHeader() {
+ return m(
+ 'tr',
+ m('th', ''),
+ m('th', 'Last (ms)'),
+ m('th', 'Avg (ms)'),
+ m('th', 'Avg-10 (ms)'),
+ );
+ }
+
+ statTableRow(title: string, stat: PerfStats) {
+ return m(
+ 'tr',
+ m('td', title),
+ m('td', stat.last.toFixed(2)),
+ m('td', stat.mean.toFixed(2)),
+ m('td', stat.bufferMean.toFixed(2)),
+ );
+ }
+}
diff --git a/ui/src/core/perf_stats.ts b/ui/src/core/perf_stats.ts
new file mode 100644
index 0000000..3f1eda0
--- /dev/null
+++ b/ui/src/core/perf_stats.ts
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+
+// The interface that every container (e.g. Track Panels) that exposes granular
+// per-container masurements implements to be perf-stats-aware.
+export interface PerfStatsContainer {
+ setPerfStatsEnabled(enable: boolean): void;
+ renderPerfStats(): m.Children;
+}
+
+// Stores statistics about samples, and keeps a fixed size buffer of most recent
+// samples.
+export class PerfStats {
+ private _count = 0;
+ private _mean = 0;
+ private _lastValue = 0;
+ private _ptr = 0;
+
+ private buffer: number[] = [];
+
+ constructor(private _maxBufferSize = 10) {}
+
+ addValue(value: number) {
+ this._lastValue = value;
+ if (this.buffer.length >= this._maxBufferSize) {
+ this.buffer[this._ptr++] = value;
+ if (this._ptr >= this.buffer.length) {
+ this._ptr -= this.buffer.length;
+ }
+ } else {
+ this.buffer.push(value);
+ }
+
+ this._mean = (this._mean * this._count + value) / (this._count + 1);
+ this._count++;
+ }
+
+ get mean() {
+ return this._mean;
+ }
+ get count() {
+ return this._count;
+ }
+ get bufferMean() {
+ return this.buffer.reduce((sum, v) => sum + v, 0) / this.buffer.length;
+ }
+ get bufferSize() {
+ return this.buffer.length;
+ }
+ get maxBufferSize() {
+ return this._maxBufferSize;
+ }
+ get last() {
+ return this._lastValue;
+ }
+}
+
+// Returns a summary string representation of a RunningStatistics object.
+export function runningStatStr(stat: PerfStats) {
+ return (
+ `Last: ${stat.last.toFixed(2)}ms | ` +
+ `Avg: ${stat.mean.toFixed(2)}ms | ` +
+ `Avg${stat.maxBufferSize}: ${stat.bufferMean.toFixed(2)}ms`
+ );
+}
diff --git a/ui/src/core/perf_unittest.ts b/ui/src/core/perf_stats_unittest.ts
similarity index 86%
rename from ui/src/core/perf_unittest.ts
rename to ui/src/core/perf_stats_unittest.ts
index 5ba357c..1b24bf5 100644
--- a/ui/src/core/perf_unittest.ts
+++ b/ui/src/core/perf_stats_unittest.ts
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {RunningStatistics} from './perf';
+import {PerfStats} from './perf_stats';
test('buffer size is accurate before reaching max capacity', () => {
- const buf = new RunningStatistics(10);
+ const buf = new PerfStats(10);
for (let i = 0; i < 10; i++) {
buf.addValue(i);
@@ -24,7 +24,7 @@
});
test('buffer size is accurate after reaching max capacity', () => {
- const buf = new RunningStatistics(10);
+ const buf = new PerfStats(10);
for (let i = 0; i < 10; i++) {
buf.addValue(i);
@@ -37,7 +37,7 @@
});
test('buffer mean is accurate before reaching max capacity', () => {
- const buf = new RunningStatistics(10);
+ const buf = new PerfStats(10);
buf.addValue(1);
buf.addValue(2);
@@ -47,7 +47,7 @@
});
test('buffer mean is accurate after reaching max capacity', () => {
- const buf = new RunningStatistics(10);
+ const buf = new PerfStats(10);
for (let i = 0; i < 20; i++) {
buf.addValue(2);
diff --git a/ui/src/core/plugin_manager.ts b/ui/src/core/plugin_manager.ts
index 55b5f4a..f444618 100644
--- a/ui/src/core/plugin_manager.ts
+++ b/ui/src/core/plugin_manager.ts
@@ -22,7 +22,8 @@
} from '../public/plugin';
import {Trace} from '../public/trace';
import {defaultPlugins} from './default_plugins';
-import {featureFlags, Flag} from './feature_flags';
+import {featureFlags} from './feature_flags';
+import {Flag} from '../public/feature_flag';
import {TraceImpl} from './trace_impl';
// The pseudo plugin id used for the core instance of AppImpl.
diff --git a/ui/src/core/raf_scheduler.ts b/ui/src/core/raf_scheduler.ts
index c6ca0fc..2935963 100644
--- a/ui/src/core/raf_scheduler.ts
+++ b/ui/src/core/raf_scheduler.ts
@@ -12,39 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {PerfStats} from './perf_stats';
import m from 'mithril';
-import {
- debugNow,
- measure,
- perfDebug,
- perfDisplay,
- PerfStatsSource,
- RunningStatistics,
- runningStatStr,
-} from './perf';
+import {featureFlags} from './feature_flags';
-function statTableHeader() {
- return m(
- 'tr',
- m('th', ''),
- m('th', 'Last (ms)'),
- m('th', 'Avg (ms)'),
- m('th', 'Avg-10 (ms)'),
- );
-}
+export type AnimationCallback = (lastFrameMs: number) => void;
+export type RedrawCallback = () => void;
-function statTableRow(title: string, stat: RunningStatistics) {
- return m(
- 'tr',
- m('td', title),
- m('td', stat.last.toFixed(2)),
- m('td', stat.mean.toFixed(2)),
- m('td', stat.bufferMean.toFixed(2)),
- );
-}
-
-export type ActionCallback = (nowMs: number) => void;
-export type RedrawCallback = (nowMs: number) => void;
+export const AUTOREDRAW_FLAG = featureFlags.register({
+ id: 'mithrilAutoredraw',
+ name: 'Enable Mithril autoredraw',
+ description: 'Turns calls to schedulefullRedraw() a no-op',
+ defaultValue: false,
+});
// This class orchestrates all RAFs in the UI. It ensures that there is only
// one animation frame handler overall and that callbacks are called in
@@ -54,146 +34,176 @@
// - redraw callbacks that will repaint canvases.
// This class guarantees that, on each frame, redraw callbacks are called after
// all action callbacks.
-export class RafScheduler implements PerfStatsSource {
- private actionCallbacks = new Set<ActionCallback>();
+export class RafScheduler {
+ // These happen at the beginning of any animation frame. Used by Animation.
+ private animationCallbacks = new Set<AnimationCallback>();
+
+ // These happen during any animaton frame, after the (optional) DOM redraw.
private canvasRedrawCallbacks = new Set<RedrawCallback>();
- private _syncDomRedraw: RedrawCallback = (_) => {};
+
+ // These happen at the end of full (DOM) animation frames.
+ private postRedrawCallbacks = new Array<RedrawCallback>();
private hasScheduledNextFrame = false;
private requestedFullRedraw = false;
private isRedrawing = false;
private _shutdown = false;
- private _beforeRedraw: () => void = () => {};
- private _afterRedraw: () => void = () => {};
- private _pendingCallbacks: RedrawCallback[] = [];
+ private recordPerfStats = false;
+ private mounts = new Map<Element, m.ComponentTypes>();
- private perfStats = {
- rafActions: new RunningStatistics(),
- rafCanvas: new RunningStatistics(),
- rafDom: new RunningStatistics(),
- rafTotal: new RunningStatistics(),
- domRedraw: new RunningStatistics(),
+ readonly perfStats = {
+ rafActions: new PerfStats(),
+ rafCanvas: new PerfStats(),
+ rafDom: new PerfStats(),
+ rafTotal: new PerfStats(),
+ domRedraw: new PerfStats(),
};
- start(cb: ActionCallback) {
- this.actionCallbacks.add(cb);
- this.maybeScheduleAnimationFrame();
+ constructor() {
+ // Patch m.redraw() to our RAF full redraw.
+ const origSync = m.redraw.sync;
+ const redrawFn = () => this.scheduleFullRedraw('force');
+ redrawFn.sync = origSync;
+ m.redraw = redrawFn;
+
+ m.mount = this.mount.bind(this);
}
- stop(cb: ActionCallback) {
- this.actionCallbacks.delete(cb);
- }
-
- addRedrawCallback(cb: RedrawCallback) {
- this.canvasRedrawCallbacks.add(cb);
- }
-
- removeRedrawCallback(cb: RedrawCallback) {
- this.canvasRedrawCallbacks.delete(cb);
- }
-
- addPendingCallback(cb: RedrawCallback) {
- this._pendingCallbacks.push(cb);
+ // Schedule re-rendering of virtual DOM and canvas.
+ // If a callback is passed it will be executed after the DOM redraw has
+ // completed.
+ scheduleFullRedraw(force?: 'force', cb?: RedrawCallback) {
+ // If we are using autoredraw mode, make this function a no-op unless
+ // 'force' is passed.
+ if (AUTOREDRAW_FLAG.get() && force !== 'force') return;
+ this.requestedFullRedraw = true;
+ cb && this.postRedrawCallbacks.push(cb);
+ this.maybeScheduleAnimationFrame(true);
}
// Schedule re-rendering of canvas only.
- scheduleRedraw() {
+ scheduleCanvasRedraw() {
this.maybeScheduleAnimationFrame(true);
}
+ startAnimation(cb: AnimationCallback) {
+ this.animationCallbacks.add(cb);
+ this.maybeScheduleAnimationFrame();
+ }
+
+ stopAnimation(cb: AnimationCallback) {
+ this.animationCallbacks.delete(cb);
+ }
+
+ addCanvasRedrawCallback(cb: RedrawCallback): Disposable {
+ this.canvasRedrawCallbacks.add(cb);
+ const canvasRedrawCallbacks = this.canvasRedrawCallbacks;
+ return {
+ [Symbol.dispose]() {
+ canvasRedrawCallbacks.delete(cb);
+ },
+ };
+ }
+
+ mount(element: Element, component: m.ComponentTypes | null): void {
+ const mounts = this.mounts;
+ if (component === null) {
+ mounts.delete(element);
+ } else {
+ mounts.set(element, component);
+ }
+ this.syncDomRedrawMountEntry(element, component);
+ }
+
shutdown() {
this._shutdown = true;
}
- set domRedraw(cb: RedrawCallback) {
- this._syncDomRedraw = cb;
- }
-
- set beforeRedraw(cb: () => void) {
- this._beforeRedraw = cb;
- }
-
- set afterRedraw(cb: () => void) {
- this._afterRedraw = cb;
- }
-
- // Schedule re-rendering of virtual DOM and canvas.
- scheduleFullRedraw() {
- this.requestedFullRedraw = true;
- this.maybeScheduleAnimationFrame(true);
- }
-
- // Schedule a full redraw to happen after a short delay (50 ms).
- // This is done to prevent flickering / visual noise and allow the UI to fetch
- // the initial data from the Trace Processor.
- // There is a chance that someone else schedules a full redraw in the
- // meantime, forcing the flicker, but in practice it works quite well and
- // avoids a lot of complexity for the callers.
- scheduleDelayedFullRedraw() {
- // 50ms is half of the responsiveness threshold (100ms):
- // https://web.dev/rail/#response-process-events-in-under-50ms
- const delayMs = 50;
- setTimeout(() => this.scheduleFullRedraw(), delayMs);
- }
-
- syncDomRedraw(nowMs: number) {
- const redrawStart = debugNow();
- this._syncDomRedraw(nowMs);
- if (perfDebug()) {
- this.perfStats.domRedraw.addValue(debugNow() - redrawStart);
- }
+ setPerfStatsEnabled(enabled: boolean) {
+ this.recordPerfStats = enabled;
+ this.scheduleFullRedraw();
}
get hasPendingRedraws(): boolean {
return this.isRedrawing || this.hasScheduledNextFrame;
}
- private syncCanvasRedraw(nowMs: number) {
- const redrawStart = debugNow();
- if (this.isRedrawing) return;
- this._beforeRedraw();
- this.isRedrawing = true;
- for (const redraw of this.canvasRedrawCallbacks) redraw(nowMs);
- this.isRedrawing = false;
- this._afterRedraw();
- for (const cb of this._pendingCallbacks) {
- cb(nowMs);
+ private syncDomRedraw() {
+ const redrawStart = performance.now();
+
+ for (const [element, component] of this.mounts.entries()) {
+ this.syncDomRedrawMountEntry(element, component);
}
- this._pendingCallbacks.splice(0, this._pendingCallbacks.length);
- if (perfDebug()) {
- this.perfStats.rafCanvas.addValue(debugNow() - redrawStart);
+
+ if (this.recordPerfStats) {
+ this.perfStats.domRedraw.addValue(performance.now() - redrawStart);
+ }
+ }
+
+ private syncDomRedrawMountEntry(
+ element: Element,
+ component: m.ComponentTypes | null,
+ ) {
+ // Mithril's render() function takes a third argument which tells us if a
+ // further redraw is needed (e.g. due to managed event handler). This allows
+ // us to implement auto-redraw. The redraw argument is documented in the
+ // official Mithril docs but is just not part of the @types/mithril package.
+ const mithrilRender = m.render as (
+ el: Element,
+ vnodes: m.Children,
+ redraw?: () => void,
+ ) => void;
+
+ mithrilRender(
+ element,
+ component !== null ? m(component) : null,
+ AUTOREDRAW_FLAG.get() ? () => raf.scheduleFullRedraw('force') : undefined,
+ );
+ }
+
+ private syncCanvasRedraw() {
+ const redrawStart = performance.now();
+ if (this.isRedrawing) return;
+ this.isRedrawing = true;
+ this.canvasRedrawCallbacks.forEach((cb) => cb());
+ this.isRedrawing = false;
+ if (this.recordPerfStats) {
+ this.perfStats.rafCanvas.addValue(performance.now() - redrawStart);
}
}
private maybeScheduleAnimationFrame(force = false) {
if (this.hasScheduledNextFrame) return;
- if (this.actionCallbacks.size !== 0 || force) {
+ if (this.animationCallbacks.size !== 0 || force) {
this.hasScheduledNextFrame = true;
window.requestAnimationFrame(this.onAnimationFrame.bind(this));
}
}
- private onAnimationFrame(nowMs: number) {
+ private onAnimationFrame(lastFrameMs: number) {
if (this._shutdown) return;
- const rafStart = debugNow();
this.hasScheduledNextFrame = false;
-
const doFullRedraw = this.requestedFullRedraw;
this.requestedFullRedraw = false;
- const actionTime = measure(() => {
- for (const action of this.actionCallbacks) action(nowMs);
- });
+ const tStart = performance.now();
+ this.animationCallbacks.forEach((cb) => cb(lastFrameMs));
+ const tAnim = performance.now();
+ doFullRedraw && this.syncDomRedraw();
+ const tDom = performance.now();
+ this.syncCanvasRedraw();
+ const tCanvas = performance.now();
- const domTime = measure(() => {
- if (doFullRedraw) this.syncDomRedraw(nowMs);
- });
- const canvasTime = measure(() => this.syncCanvasRedraw(nowMs));
-
- const totalRafTime = debugNow() - rafStart;
- this.updatePerfStats(actionTime, domTime, canvasTime, totalRafTime);
- perfDisplay.renderPerfStats(this);
-
+ const animTime = tAnim - tStart;
+ const domTime = tDom - tAnim;
+ const canvasTime = tCanvas - tDom;
+ const totalTime = tCanvas - tStart;
+ this.updatePerfStats(animTime, domTime, canvasTime, totalTime);
this.maybeScheduleAnimationFrame();
+
+ if (doFullRedraw && this.postRedrawCallbacks.length > 0) {
+ const pendingCbs = this.postRedrawCallbacks.splice(0); // splice = clear.
+ pendingCbs.forEach((cb) => cb());
+ }
}
private updatePerfStats(
@@ -202,42 +212,12 @@
canvasTime: number,
totalRafTime: number,
) {
- if (!perfDebug()) return;
+ if (!this.recordPerfStats) return;
this.perfStats.rafActions.addValue(actionsTime);
this.perfStats.rafDom.addValue(domTime);
this.perfStats.rafCanvas.addValue(canvasTime);
this.perfStats.rafTotal.addValue(totalRafTime);
}
-
- renderPerfStats() {
- return m(
- 'div',
- m('div', [
- m('button', {onclick: () => this.scheduleRedraw()}, 'Do Canvas Redraw'),
- ' | ',
- m(
- 'button',
- {onclick: () => this.scheduleFullRedraw()},
- 'Do Full Redraw',
- ),
- ]),
- m('div', 'Raf Timing ' + '(Total may not add up due to imprecision)'),
- m(
- 'table',
- statTableHeader(),
- statTableRow('Actions', this.perfStats.rafActions),
- statTableRow('Dom', this.perfStats.rafDom),
- statTableRow('Canvas', this.perfStats.rafCanvas),
- statTableRow('Total', this.perfStats.rafTotal),
- ),
- m(
- 'div',
- 'Dom redraw: ' +
- `Count: ${this.perfStats.domRedraw.count} | ` +
- runningStatStr(this.perfStats.domRedraw),
- ),
- );
- }
}
export const raf = new RafScheduler();
diff --git a/ui/src/core/router.ts b/ui/src/core/router.ts
index 8584428..259ed6d 100644
--- a/ui/src/core/router.ts
+++ b/ui/src/core/router.ts
@@ -13,15 +13,11 @@
// limitations under the License.
import m from 'mithril';
-import {assertExists, assertTrue} from '../base/logging';
+import {assertTrue} from '../base/logging';
import {RouteArgs, ROUTE_SCHEMA} from '../public/route_schema';
+import {PageAttrs} from '../public/page';
export const ROUTE_PREFIX = '#!';
-const DEFAULT_ROUTE = '/';
-
-export interface PageAttrs {
- subpage?: string;
-}
// The set of args that can be set on the route via #!/page?a=1&b2.
// Route args are orthogonal to pages (i.e. should NOT make sense only in a
@@ -80,15 +76,12 @@
// triggered by Router.onRouteChanged().
export class Router {
private readonly recentChanges: number[] = [];
- private routes: RoutesMap;
// frontend/index.ts calls maybeOpenTraceFromRoute() + redraw here.
// This event is decoupled for testing and to avoid circular deps.
onRouteChanged: (route: Route) => void = () => {};
- constructor(routes: RoutesMap) {
- assertExists(routes[DEFAULT_ROUTE]);
- this.routes = routes;
+ constructor() {
window.onhashchange = (e: HashChangeEvent) => this.onHashChange(e);
const route = Router.parseUrl(window.location.href);
this.onRouteChanged(route);
@@ -136,18 +129,6 @@
this.onRouteChanged(newRoute);
}
- // Returns the component for the current route in the URL.
- // If no route matches the URL, returns a component corresponding to
- // |this.defaultRoute|.
- resolve(): m.Vnode<PageAttrs> {
- const route = Router.parseFragment(location.hash);
- let component = this.routes[route.page];
- if (component === undefined) {
- component = assertExists(this.routes[DEFAULT_ROUTE]);
- }
- return m(component, {subpage: route.subpage});
- }
-
static navigate(newHash: string) {
assertTrue(newHash.startsWith(ROUTE_PREFIX));
window.location.hash = newHash;
diff --git a/ui/src/core/router_unittest.ts b/ui/src/core/router_unittest.ts
index ec65425..89887d0 100644
--- a/ui/src/core/router_unittest.ts
+++ b/ui/src/core/router_unittest.ts
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {PageManagerImpl} from './page_manager';
+import {CORE_PLUGIN_ID} from './plugin_manager';
import {Router} from './router';
const mockComponent = {
@@ -23,36 +25,50 @@
window.location.hash = '';
});
- test('Default route must be defined', () => {
- expect(() => new Router({'/a': mockComponent})).toThrow();
- });
+ const pluginId = CORE_PLUGIN_ID;
+ const traceless = true;
test('Resolves empty route to default component', () => {
- const router = new Router({'/': mockComponent});
+ const pages = new PageManagerImpl();
+ pages.registerPage({route: '/', page: mockComponent, traceless, pluginId});
window.location.hash = '';
- expect(router.resolve().tag).toBe(mockComponent);
+ expect(pages.renderPageForCurrentRoute(undefined).tag).toBe(mockComponent);
});
test('Resolves subpage route to component of main page', () => {
const nonDefaultComponent = {view() {}};
- const router = new Router({
- '/': mockComponent,
- '/a': nonDefaultComponent,
+ const pages = new PageManagerImpl();
+ pages.registerPage({route: '/', page: mockComponent, traceless, pluginId});
+ pages.registerPage({
+ route: '/a',
+ page: nonDefaultComponent,
+ traceless,
+ pluginId,
});
window.location.hash = '#!/a/subpage';
- expect(router.resolve().tag).toBe(nonDefaultComponent);
- expect(router.resolve().attrs.subpage).toBe('/subpage');
+ expect(pages.renderPageForCurrentRoute(undefined).tag).toBe(
+ nonDefaultComponent,
+ );
+ expect(pages.renderPageForCurrentRoute(undefined).attrs.subpage).toBe(
+ '/subpage',
+ );
});
test('Pass empty subpage if not found in URL', () => {
const nonDefaultComponent = {view() {}};
- const router = new Router({
- '/': mockComponent,
- '/a': nonDefaultComponent,
+ const pages = new PageManagerImpl();
+ pages.registerPage({route: '/', page: mockComponent, traceless, pluginId});
+ pages.registerPage({
+ route: '/a',
+ page: nonDefaultComponent,
+ traceless,
+ pluginId,
});
window.location.hash = '#!/a';
- expect(router.resolve().tag).toBe(nonDefaultComponent);
- expect(router.resolve().attrs.subpage).toBe('');
+ expect(pages.renderPageForCurrentRoute(undefined).tag).toBe(
+ nonDefaultComponent,
+ );
+ expect(pages.renderPageForCurrentRoute(undefined).attrs.subpage).toBe('');
});
});
diff --git a/ui/src/core/scroll_helper.ts b/ui/src/core/scroll_helper.ts
index 59b7b11..c732b91 100644
--- a/ui/src/core/scroll_helper.ts
+++ b/ui/src/core/scroll_helper.ts
@@ -35,7 +35,7 @@
// See comments in ScrollToArgs for the intended semantics.
scrollTo(args: ScrollToArgs) {
const {time, track} = args;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
if (time !== undefined) {
if (time.end === undefined) {
diff --git a/ui/src/core/sidebar_manager.ts b/ui/src/core/sidebar_manager.ts
index 65e2b89..9de9b90 100644
--- a/ui/src/core/sidebar_manager.ts
+++ b/ui/src/core/sidebar_manager.ts
@@ -13,37 +13,40 @@
// limitations under the License.
import {Registry} from '../base/registry';
-import {
- SidebarEnabled,
- SidebarManager,
- SidebarMenuItem,
- SidebarVisibility,
-} from '../public/sidebar';
+import {SidebarManager, SidebarMenuItem} from '../public/sidebar';
import {raf} from './raf_scheduler';
+export type SidebarMenuItemInternal = SidebarMenuItem & {
+ id: string; // A unique id generated by this class at registration time.
+};
+
export class SidebarManagerImpl implements SidebarManager {
- private _sidebarVisibility: SidebarVisibility;
- readonly menuItems = new Registry<SidebarMenuItem>((m) => m.commandId);
+ readonly enabled: boolean;
+ private _visible: boolean;
+ private lastId = 0;
- constructor(public readonly sidebarEnabled: SidebarEnabled) {
- this._sidebarVisibility =
- sidebarEnabled === 'ENABLED' ? 'VISIBLE' : 'HIDDEN';
+ readonly menuItems = new Registry<SidebarMenuItemInternal>((m) => m.id);
+
+ constructor(args: {disabled?: boolean; hidden?: boolean}) {
+ this.enabled = !args.disabled;
+ this._visible = !args.hidden;
}
- addMenuItem(menuItem: SidebarMenuItem): Disposable {
- return this.menuItems.register(menuItem);
+ addMenuItem(item: SidebarMenuItem): Disposable {
+ // Assign a unique id to every item. This simplifies the job of the mithril
+ // component that renders the sidebar.
+ const id = `sidebar_${++this.lastId}`;
+ const itemInt: SidebarMenuItemInternal = {...item, id};
+ return this.menuItems.register(itemInt);
}
- public get sidebarVisibility() {
- return this._sidebarVisibility;
+ public get visible() {
+ return this._visible;
}
- public toggleSidebarVisbility() {
- if (this._sidebarVisibility === 'HIDDEN') {
- this._sidebarVisibility = 'VISIBLE';
- } else {
- this._sidebarVisibility = 'HIDDEN';
- }
+ public toggleVisibility() {
+ if (!this.enabled) return;
+ this._visible = !this._visible;
raf.scheduleFullRedraw();
}
}
diff --git a/ui/src/core/timeline.ts b/ui/src/core/timeline.ts
index d91503c..bc8a613 100644
--- a/ui/src/core/timeline.ts
+++ b/ui/src/core/timeline.ts
@@ -46,7 +46,7 @@
set highlightedSliceId(x) {
this._highlightedSliceId = x;
- raf.scheduleFullRedraw();
+ raf.scheduleCanvasRedraw();
}
get hoveredNoteTimestamp() {
@@ -55,7 +55,7 @@
set hoveredNoteTimestamp(x) {
this._hoveredNoteTimestamp = x;
- raf.scheduleFullRedraw();
+ raf.scheduleCanvasRedraw();
}
get hoveredUtid() {
@@ -64,7 +64,7 @@
set hoveredUtid(x) {
this._hoveredUtid = x;
- raf.scheduleFullRedraw();
+ raf.scheduleCanvasRedraw();
}
get hoveredPid() {
@@ -73,7 +73,7 @@
set hoveredPid(x) {
this._hoveredPid = x;
- raf.scheduleFullRedraw();
+ raf.scheduleCanvasRedraw();
}
// This is used to calculate the tracks within a Y range for area selection.
@@ -95,7 +95,7 @@
.scale(ratio, centerPoint, MIN_DURATION)
.fitWithin(this.traceInfo.start, this.traceInfo.end);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
panVisibleWindow(delta: number) {
@@ -103,7 +103,7 @@
.translate(delta)
.fitWithin(this.traceInfo.start, this.traceInfo.end);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
// Given a timestamp, if |ts| is not currently in view move the view to
@@ -136,7 +136,7 @@
deselectArea() {
this._selectedArea = undefined;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
get selectedArea(): Area | undefined {
@@ -160,7 +160,7 @@
.clampDuration(MIN_DURATION)
.fitWithin(this.traceInfo.start, this.traceInfo.end);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
// Get the bounds of the visible window as a high-precision time span
@@ -174,7 +174,7 @@
set hoverCursorTimestamp(t: time | undefined) {
this._hoverCursorTimestamp = t;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
// Offset between t=0 and the configured time domain.
diff --git a/ui/src/core/trace_impl.ts b/ui/src/core/trace_impl.ts
index c59a744..abed7f5 100644
--- a/ui/src/core/trace_impl.ts
+++ b/ui/src/core/trace_impl.ts
@@ -13,7 +13,6 @@
// limitations under the License.
import {DisposableStack} from '../base/disposable_stack';
-import {assertTrue} from '../base/logging';
import {createStore, Migrate, Store} from '../base/store';
import {TimelineImpl} from './timeline';
import {Command} from '../public/command';
@@ -44,6 +43,14 @@
import {getOrCreate} from '../base/utils';
import {fetchWithProgress} from '../base/http_utils';
import {TraceInfoImpl} from './trace_info_impl';
+import {PageHandler, PageManager} from '../public/page';
+import {createProxy} from '../base/utils';
+import {PageManagerImpl} from './page_manager';
+import {FeatureFlagManager, FlagSettings} from '../public/feature_flag';
+import {featureFlags} from './feature_flags';
+import {SerializedAppState} from './state_serialization_schema';
+import {PostedTrace} from './trace_source';
+import {PerfManager} from './perf_manager';
/**
* Handles the per-trace state of the UI
@@ -53,7 +60,8 @@
* This is the underlying storage for AppImpl, which instead has one instance
* per trace per plugin.
*/
-class TraceContext implements Disposable {
+export class TraceContext implements Disposable {
+ private readonly pluginInstances = new Map<string, TraceImpl>();
readonly appCtx: AppContext;
readonly engine: EngineBase;
readonly omniboxMgr = new OmniboxManagerImpl();
@@ -149,6 +157,16 @@
this.selectionMgr.selectSearchResult(searchResult);
}
+ // Gets or creates an instance of TraceImpl backed by the current TraceContext
+ // for the given plugin.
+ forPlugin(pluginId: string) {
+ return getOrCreate(this.pluginInstances, pluginId, () => {
+ const appForPlugin = this.appCtx.forPlugin(pluginId);
+ return new TraceImpl(appForPlugin, this);
+ });
+ }
+
+ // Called by AppContext.closeCurrentTrace().
[Symbol.dispose]() {
this.trash.dispose();
}
@@ -162,15 +180,16 @@
* for the core.
*/
export class TraceImpl implements Trace {
- private appImpl: AppImpl;
- private traceCtx: TraceContext;
+ private readonly appImpl: AppImpl;
+ private readonly traceCtx: TraceContext;
// This is not the original Engine base, rather an EngineProxy based on the
// same engineBase.
- private engineProxy: EngineProxy;
- private trackMgrProxy: TrackManagerImpl;
- private commandMgrProxy: CommandManagerImpl;
- private sidebarProxy: SidebarManagerImpl;
+ private readonly engineProxy: EngineProxy;
+ private readonly trackMgrProxy: TrackManagerImpl;
+ private readonly commandMgrProxy: CommandManagerImpl;
+ private readonly sidebarProxy: SidebarManagerImpl;
+ private readonly pageMgrProxy: PageManagerImpl;
// This is called by TraceController when loading a new trace, soon after the
// engine has been set up. It obtains a new TraceImpl for the core. From that
@@ -182,15 +201,15 @@
traceInfo: TraceInfoImpl,
): TraceImpl {
const traceCtx = new TraceContext(
- appImpl.__appCtxForTraceImplCtor,
+ appImpl.__appCtxForTrace,
engine,
traceInfo,
);
- const traceImpl = new TraceImpl(appImpl, traceCtx);
- return traceImpl;
+ return traceCtx.forPlugin(CORE_PLUGIN_ID);
}
- private constructor(appImpl: AppImpl, ctx: TraceContext) {
+ // Only called by TraceContext.forPlugin().
+ constructor(appImpl: AppImpl, ctx: TraceContext) {
const pluginId = appImpl.pluginId;
this.appImpl = appImpl;
this.traceCtx = ctx;
@@ -228,6 +247,17 @@
},
});
+ this.pageMgrProxy = createProxy(ctx.appCtx.pageMgr, {
+ registerPage(pageHandler: PageHandler): Disposable {
+ const disposable = appImpl.pages.registerPage({
+ ...pageHandler,
+ pluginId: appImpl.pluginId,
+ });
+ traceUnloadTrash.use(disposable);
+ return disposable;
+ },
+ });
+
// TODO(primiano): remove this injection once we plumb Trace everywhere.
setScrollToFunction((x: ScrollToArgs) => ctx.scrollHelper.scrollTo(x));
}
@@ -240,8 +270,7 @@
// another plugin. This is effectively a way to "fork" the core instance and
// create the N instances for plugins.
forkForPlugin(pluginId: string) {
- assertTrue(pluginId != CORE_PLUGIN_ID);
- return new TraceImpl(this.appImpl.forkForPlugin(pluginId), this.traceCtx);
+ return this.traceCtx.forPlugin(pluginId);
}
mountStore<T>(migrate: Migrate<T>): Store<T> {
@@ -289,6 +318,10 @@
return (pluginArgs ?? {})[this.pluginId];
}
+ get trace() {
+ return this;
+ }
+
get engine() {
return this.engineProxy;
}
@@ -359,6 +392,10 @@
return this.sidebarProxy;
}
+ get pages(): PageManager {
+ return this.pageMgrProxy;
+ }
+
get omnibox(): OmniboxManagerImpl {
return this.appImpl.omnibox;
}
@@ -375,24 +412,32 @@
return this.appImpl.initialRouteArgs;
}
- get rootUrl(): string {
- return this.appImpl.rootUrl;
+ get featureFlags(): FeatureFlagManager {
+ return {
+ register: (settings: FlagSettings) => featureFlags.register(settings),
+ };
}
scheduleFullRedraw(): void {
this.appImpl.scheduleFullRedraw();
}
- [Symbol.dispose]() {
- if (this.pluginId === CORE_PLUGIN_ID) {
- this.traceCtx[Symbol.dispose]();
- }
- }
-
navigate(newHash: string): void {
this.appImpl.navigate(newHash);
}
+ openTraceFromFile(file: File): void {
+ this.appImpl.openTraceFromFile(file);
+ }
+
+ openTraceFromUrl(url: string, serializedAppState?: SerializedAppState) {
+ this.appImpl.openTraceFromUrl(url, serializedAppState);
+ }
+
+ openTraceFromBuffer(args: PostedTrace): void {
+ this.appImpl.openTraceFromBuffer(args);
+ }
+
addEventListener<T extends keyof EventListeners>(
event: T,
callback: EventListeners[T],
@@ -416,9 +461,18 @@
}
}
+ get perfDebugging(): PerfManager {
+ return this.appImpl.perfDebugging;
+ }
+
get trash(): DisposableStack {
return this.traceCtx.trash;
}
+
+ // Nothing other than AppImpl should ever refer to this, hence the __ name.
+ get __traceCtxForApp() {
+ return this.traceCtx;
+ }
}
// A convenience interface to inject the App in Mithril components.
@@ -429,25 +483,3 @@
export interface OptionalTraceImplAttrs {
trace?: TraceImpl;
}
-
-// Allows to take an existing class instance (`target`) and override some of its
-// methods via `overrides`. We use this for cases where we want to expose a
-// "manager" (e.g. TrackManager, SidebarManager) to the plugins, but we want to
-// override few of its methods (e.g. to inject the pluginId in the args).
-function createProxy<T extends object>(target: T, overrides: Partial<T>): T {
- return new Proxy(target, {
- get: (target: T, prop: string | symbol, receiver) => {
- // If the property is overriden, use that; otherwise, use target
- const overrideValue = (overrides as {[key: symbol | string]: {}})[prop];
- if (overrideValue !== undefined) {
- return typeof overrideValue === 'function'
- ? overrideValue.bind(overrides)
- : overrideValue;
- }
- const baseValue = Reflect.get(target, prop, receiver);
- return typeof baseValue === 'function'
- ? baseValue.bind(target)
- : baseValue;
- },
- }) as T;
-}
diff --git a/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts b/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts
index 718e881..cfcdf87 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/event_latency_details_panel.ts
@@ -19,7 +19,6 @@
import {
getDescendantSliceTree,
getSlice,
- getSliceFromConstraints,
SliceDetails,
SliceTreeNode,
} from '../../trace_processor/sql_utils/slice';
@@ -32,7 +31,7 @@
Table,
TableData,
widgetColumn,
-} from '../../frontend/tables/table';
+} from '../../widgets/table';
import {TreeTable, TreeTableAttrs} from '../../frontend/widgets/treetable';
import {LONG, NUM, STR} from '../../trace_processor/query_result';
import {DetailsShell} from '../../widgets/details_shell';
@@ -250,47 +249,51 @@
this.topEventLatencyId,
);
- // TODO(altimin): this should be based on an stdlib table and consider only
- // EventLatencies within the same scroll.
- // This is a copy of the statement in event_latency_track. It should move to
- // stdlib instead of living in the UI code.
- const whereClause = `
- EXTRACT_ARG(arg_set_id, 'event_latency.event_type') IN (
- 'FIRST_GESTURE_SCROLL_UPDATE',
- 'GESTURE_SCROLL_UPDATE',
- 'INERTIAL_GESTURE_SCROLL_UPDATE')
- AND HAS_DESCENDANT_SLICE_WITH_NAME(
- id,
- 'SubmitCompositorFrameToPresentationCompositorFrame')`;
- const prevEventLatency = await getSliceFromConstraints(this.trace.engine, {
- filters: [
- `name = 'EventLatency'`,
- `id < ${this.topEventLatencyId}`,
- whereClause,
- ],
- orderBy: [{fieldName: 'id', direction: 'DESC'}],
- limit: 1,
- });
- if (prevEventLatency.length > 0) {
+ // TODO(altimin): this should only consider EventLatencies within the same scroll.
+ const prevEventLatency = (
+ await this.trace.engine.query(`
+ INCLUDE PERFETTO MODULE chrome.event_latency;
+ SELECT
+ id
+ FROM chrome_event_latencies
+ WHERE event_type IN (
+ 'FIRST_GESTURE_SCROLL_UPDATE',
+ 'GESTURE_SCROLL_UPDATE',
+ 'INERTIAL_GESTURE_SCROLL_UPDATE')
+ AND is_presented
+ AND id < ${this.topEventLatencyId}
+ ORDER BY id DESC
+ LIMIT 1
+ ;
+ `)
+ ).maybeFirstRow({id: NUM});
+ if (prevEventLatency !== undefined) {
this.prevEventLatencyBreakdown = await getDescendantSliceTree(
this.trace.engine,
- prevEventLatency[0].id,
+ asSliceSqlId(prevEventLatency.id),
);
}
- const nextEventLatency = await getSliceFromConstraints(this.trace.engine, {
- filters: [
- `name = 'EventLatency'`,
- `id > ${this.topEventLatencyId}`,
- whereClause,
- ],
- orderBy: ['id'],
- limit: 1,
- });
- if (nextEventLatency.length > 0) {
+ const nextEventLatency = (
+ await this.trace.engine.query(`
+ INCLUDE PERFETTO MODULE chrome.event_latency;
+ SELECT
+ id
+ FROM chrome_event_latencies
+ WHERE event_type IN (
+ 'FIRST_GESTURE_SCROLL_UPDATE',
+ 'GESTURE_SCROLL_UPDATE',
+ 'INERTIAL_GESTURE_SCROLL_UPDATE')
+ AND is_presented
+ AND id > ${this.topEventLatencyId}
+ ORDER BY id DESC
+ LIMIT 1;
+ `)
+ ).maybeFirstRow({id: NUM});
+ if (nextEventLatency !== undefined) {
this.nextEventLatencyBreakdown = await getDescendantSliceTree(
this.trace.engine,
- nextEventLatency[0].id,
+ asSliceSqlId(nextEventLatency.id),
);
}
}
diff --git a/ui/src/core_plugins/chrome_scroll_jank/event_latency_track.ts b/ui/src/core_plugins/chrome_scroll_jank/event_latency_track.ts
index 33b42b4..03ab372 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/event_latency_track.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/event_latency_track.ts
@@ -13,7 +13,6 @@
// limitations under the License.
import {NamedRow} from '../../frontend/named_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {Slice} from '../../public/track';
import {
CustomSqlTableDefConfig,
@@ -22,15 +21,17 @@
import {JANK_COLOR} from './jank_colors';
import {TrackEventSelection} from '../../public/selection';
import {EventLatencySliceDetailsPanel} from './event_latency_details_panel';
+import {Trace} from '../../public/trace';
export const JANKY_LATENCY_NAME = 'Janky EventLatency';
export class EventLatencyTrack extends CustomSqlTableSliceTrack {
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
private baseTable: string,
) {
- super(args);
+ super(trace, uri);
}
getSqlSource(): string {
diff --git a/ui/src/core_plugins/chrome_scroll_jank/index.ts b/ui/src/core_plugins/chrome_scroll_jank/index.ts
index 17f8141..04feac4 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/index.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/index.ts
@@ -21,46 +21,6 @@
import {TopLevelScrollTrack} from './scroll_track';
import {ScrollJankCauseMap} from './scroll_jank_cause_map';
import {TrackNode} from '../../public/workspace';
-import {featureFlags, OverrideState} from '../../core/feature_flags';
-
-// Before plugins were a thing, this plugin was enabled using a feature flag.
-// However, nowadays, plugins themselves can be selectively enabled and
-// disabled. This function inspects local storage to see whether the old feature
-// flag is enabled, and patches the flags settings to enable the chrome scroll
-// jank plugin, before deleting the old flag. This provides a seamless
-// experience for anyone who currently uses the chrome scroll jank plugin.
-//
-// TODO(stevegolton): Remove this code after 2025-01-01. This should give it
-// enough time on stable for most relevant users to have run it at least once.
-function patchChromeScrollJankFlag() {
- try {
- const flagsKey = 'perfettoFeatureFlags';
- const enableScrollJankPluginV2FlagKey = 'enableScrollJankPluginV2';
- const chromeScrollJankPuginFlagKey = 'plugin_perfetto.ChromeScrollJank';
-
- const flagsRaw = localStorage.getItem(flagsKey);
- if (flagsRaw) {
- const flags = JSON.parse(flagsRaw);
- if (flags[enableScrollJankPluginV2FlagKey] === 'OVERRIDE_TRUE') {
- featureFlags.patchOverride(
- chromeScrollJankPuginFlagKey,
- OverrideState.TRUE,
- );
- console.log(
- `Cleared deprecated 'enableScrollJankPluginV2' flag & enabled 'ChromeScrollJank' plugin.`,
- );
- }
-
- // Just remove the original flag
- delete flags[enableScrollJankPluginV2FlagKey];
- localStorage.setItem(flagsKey, JSON.stringify(flags));
- }
- } catch {
- // Ignore - this was very much best-effort.
- }
-}
-
-patchChromeScrollJankFlag();
export default class implements PerfettoPlugin {
static readonly id = 'perfetto.ChromeScrollJank';
@@ -85,6 +45,7 @@
await ctx.engine.query(`
INCLUDE PERFETTO MODULE chrome.chrome_scrolls;
INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_offsets;
+ INCLUDE PERFETTO MODULE chrome.event_latency;
`);
const uri = 'perfetto.ChromeScrollJank#toplevelScrolls';
@@ -93,10 +54,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new TopLevelScrollTrack({
- trace: ctx,
- uri,
- }),
+ track: new TopLevelScrollTrack(ctx, uri),
});
const track = new TrackNode({uri, title});
@@ -109,19 +67,15 @@
): Promise<void> {
const subTableSql = generateSqlWithInternalLayout({
columns: ['id', 'ts', 'dur', 'track_id', 'name'],
- sourceTable: 'slice',
+ sourceTable: 'chrome_event_latencies',
ts: 'ts',
dur: 'dur',
whereClause: `
- EXTRACT_ARG(arg_set_id, 'event_latency.event_type') IN (
+ event_type IN (
'FIRST_GESTURE_SCROLL_UPDATE',
'GESTURE_SCROLL_UPDATE',
'INERTIAL_GESTURE_SCROLL_UPDATE')
- AND has_descendant_slice_with_name(
- id,
- 'SubmitCompositorFrameToPresentationCompositorFrame')
- AND name = 'EventLatency'
- AND depth = 0`,
+ AND is_presented`,
});
// Table name must be unique - it cannot include '-' characters or begin
@@ -209,7 +163,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new EventLatencyTrack({trace: ctx, uri}, baseTable),
+ track: new EventLatencyTrack(ctx, uri, baseTable),
});
const track = new TrackNode({uri, title});
@@ -230,10 +184,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new ScrollJankV3Track({
- trace: ctx,
- uri,
- }),
+ track: new ScrollJankV3Track(ctx, uri),
});
const track = new TrackNode({uri, title});
diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_details_panel.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_details_panel.ts
index 30ab521..eed111f 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/scroll_details_panel.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_details_panel.ts
@@ -20,7 +20,7 @@
Table,
TableData,
widgetColumn,
-} from '../../frontend/tables/table';
+} from '../../widgets/table';
import {DurationWidget} from '../../frontend/widgets/duration';
import {Timestamp} from '../../frontend/widgets/timestamp';
import {
diff --git a/ui/src/core_plugins/commands/index.ts b/ui/src/core_plugins/commands/index.ts
index 248d749..b937daa 100644
--- a/ui/src/core_plugins/commands/index.ts
+++ b/ui/src/core_plugins/commands/index.ts
@@ -94,12 +94,12 @@
export default class implements PerfettoPlugin {
static readonly id = 'perfetto.CoreCommands';
static onActivate(ctx: App) {
- if (ctx.sidebar.sidebarEnabled) {
+ if (ctx.sidebar.enabled) {
ctx.commands.registerCommand({
id: 'perfetto.CoreCommands#ToggleLeftSidebar',
name: 'Toggle left sidebar',
callback: () => {
- ctx.sidebar.toggleSidebarVisbility();
+ ctx.sidebar.toggleVisibility();
},
defaultHotkey: '!Mod+B',
});
@@ -124,14 +124,13 @@
});
ctx.sidebar.addMenuItem({
commandId: OPEN_TRACE_COMMAND_ID,
- group: 'navigation',
+ section: 'navigation',
icon: 'folder_open',
});
- const OPEN_LEGACY_TRACE_COMMAND_ID =
- 'perfetto.CoreCommands#openTraceInLegacyUi';
+ const OPEN_LEGACY_COMMAND_ID = 'perfetto.CoreCommands#openTraceInLegacyUi';
ctx.commands.registerCommand({
- id: OPEN_LEGACY_TRACE_COMMAND_ID,
+ id: OPEN_LEGACY_COMMAND_ID,
name: 'Open with legacy UI',
callback: () => {
input.dataset['useCatapultLegacyUi'] = '1';
@@ -139,8 +138,8 @@
},
});
ctx.sidebar.addMenuItem({
- commandId: OPEN_LEGACY_TRACE_COMMAND_ID,
- group: 'navigation',
+ commandId: OPEN_LEGACY_COMMAND_ID,
+ section: 'navigation',
icon: 'filter_none',
});
}
diff --git a/ui/src/core_plugins/example_traces/index.ts b/ui/src/core_plugins/example_traces/index.ts
index 25eadbc..4b69ec9 100644
--- a/ui/src/core_plugins/example_traces/index.ts
+++ b/ui/src/core_plugins/example_traces/index.ts
@@ -40,8 +40,8 @@
},
});
ctx.sidebar.addMenuItem({
+ section: 'example_traces',
commandId: OPEN_EXAMPLE_ANDROID_TRACE_COMMAND_ID,
- group: 'example_traces',
icon: 'description',
});
@@ -55,8 +55,8 @@
},
});
ctx.sidebar.addMenuItem({
+ section: 'example_traces',
commandId: OPEN_EXAMPLE_CHROME_TRACE_COMMAND_ID,
- group: 'example_traces',
icon: 'description',
});
}
diff --git a/ui/src/frontend/flags_page.ts b/ui/src/core_plugins/flags_page/flags_page.ts
similarity index 92%
rename from ui/src/frontend/flags_page.ts
rename to ui/src/core_plugins/flags_page/flags_page.ts
index cac670d..d8cfdf6 100644
--- a/ui/src/frontend/flags_page.ts
+++ b/ui/src/core_plugins/flags_page/flags_page.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import m from 'mithril';
-import {channelChanged, getNextChannel, setChannel} from '../core/channels';
-import {featureFlags, Flag, OverrideState} from '../core/feature_flags';
-import {raf} from '../core/raf_scheduler';
-import {PageAttrs} from '../core/router';
-import {Router} from '../core/router';
+import {channelChanged, getNextChannel, setChannel} from '../../core/channels';
+import {featureFlags} from '../../core/feature_flags';
+import {Flag, OverrideState} from '../../public/feature_flag';
+import {raf} from '../../core/raf_scheduler';
+import {PageAttrs} from '../../public/page';
+import {Router} from '../../core/router';
const RELEASE_PROCESS_URL =
'https://perfetto.dev/docs/visualization/perfetto-ui-release-process';
diff --git a/ui/src/core_plugins/flags_page/index.ts b/ui/src/core_plugins/flags_page/index.ts
new file mode 100644
index 0000000..85039c4
--- /dev/null
+++ b/ui/src/core_plugins/flags_page/index.ts
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {featureFlags} from '../../core/feature_flags';
+import {App} from '../../public/app';
+import {PerfettoPlugin} from '../../public/plugin';
+import {FlagsPage} from './flags_page';
+import {PluginsPage} from './plugins_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.FlagsPage';
+
+ static onActivate(app: App) {
+ // Flags page
+ app.pages.registerPage({
+ route: '/flags',
+ page: FlagsPage,
+ traceless: true,
+ });
+ app.sidebar.addMenuItem({
+ section: 'support',
+ sortOrder: 3,
+ text: 'Flags',
+ href: '#!/flags',
+ icon: 'emoji_flags',
+ });
+
+ // Plugins page.
+ app.pages.registerPage({
+ route: '/plugins',
+ page: PluginsPage,
+ traceless: true,
+ });
+
+ const PLUGINS_PAGE_IN_NAV_FLAG = featureFlags.register({
+ id: 'showPluginsPageInNav',
+ name: 'Show plugins page',
+ description: 'Show a link to the plugins page in the side bar.',
+ defaultValue: false,
+ });
+ if (PLUGINS_PAGE_IN_NAV_FLAG.get()) {
+ app.sidebar.addMenuItem({
+ section: 'support',
+ text: 'Plugins',
+ href: '#!/plugins',
+ icon: 'extension',
+ sortOrder: 9,
+ });
+ }
+ }
+}
diff --git a/ui/src/frontend/plugins_page.ts b/ui/src/core_plugins/flags_page/plugins_page.ts
similarity index 90%
rename from ui/src/frontend/plugins_page.ts
rename to ui/src/core_plugins/flags_page/plugins_page.ts
index 3984293..ed5bd0b 100644
--- a/ui/src/frontend/plugins_page.ts
+++ b/ui/src/core_plugins/flags_page/plugins_page.ts
@@ -13,14 +13,14 @@
// limitations under the License.
import m from 'mithril';
-import {Button} from '../widgets/button';
-import {exists} from '../base/utils';
-import {defaultPlugins} from '../core/default_plugins';
-import {Intent} from '../widgets/common';
-import {PageAttrs} from '../core/router';
-import {AppImpl} from '../core/app_impl';
-import {PluginWrapper} from '../core/plugin_manager';
-import {raf} from '../core/raf_scheduler';
+import {Button} from '../../widgets/button';
+import {exists} from '../../base/utils';
+import {defaultPlugins} from '../../core/default_plugins';
+import {Intent} from '../../widgets/common';
+import {PageAttrs} from '../../public/page';
+import {AppImpl} from '../../core/app_impl';
+import {PluginWrapper} from '../../core/plugin_manager';
+import {raf} from '../../core/raf_scheduler';
// This flag indicated whether we need to restart the UI to apply plugin
// changes. It is purposely a global as we want it to outlive the Mithril
diff --git a/ui/src/core_plugins/track_utils/index.ts b/ui/src/core_plugins/track_utils/index.ts
index 369d2fe..e13d479 100644
--- a/ui/src/core_plugins/track_utils/index.ts
+++ b/ui/src/core_plugins/track_utils/index.ts
@@ -18,6 +18,7 @@
import {AppImpl} from '../../core/app_impl';
import {getTimeSpanOfSelectionOrVisibleWindow} from '../../public/utils';
import {exists} from '../../base/utils';
+import {LONG, NUM, NUM_NULL} from '../../trace_processor/query_result';
export default class implements PerfettoPlugin {
static readonly id = 'perfetto.TrackUtils';
@@ -83,5 +84,57 @@
id && ctx.workspace.getTrackById(id)?.pin();
},
});
+
+ ctx.commands.registerCommand({
+ id: 'perfetto.SelectNextTrackEvent',
+ name: 'Select next track event',
+ defaultHotkey: '.',
+ callback: async () => {
+ await selectAdjacentTrackEvent(ctx, 'next');
+ },
+ });
+
+ ctx.commands.registerCommand({
+ id: 'perfetto.SelectPreviousTrackEvent',
+ name: 'Select previous track event',
+ defaultHotkey: ',',
+ callback: async () => {
+ await selectAdjacentTrackEvent(ctx, 'prev');
+ },
+ });
}
}
+
+/**
+ * If a track event is currently selected, select the next or previous event on
+ * that same track chronologically ordered by `ts`.
+ */
+async function selectAdjacentTrackEvent(
+ ctx: Trace,
+ direction: 'next' | 'prev',
+) {
+ const selection = ctx.selection.selection;
+ if (selection.kind !== 'track_event') return;
+
+ const td = ctx.tracks.getTrack(selection.trackUri);
+ const dataset = td?.track.getDataset?.();
+ if (!dataset || !dataset.implements({id: NUM, ts: LONG})) return;
+
+ const windowFunc = direction === 'next' ? 'LEAD' : 'LAG';
+ const result = await ctx.engine.query(`
+ WITH
+ CTE AS (
+ SELECT
+ id,
+ ${windowFunc}(id) OVER (ORDER BY ts) AS resultId
+ FROM (${dataset.query()})
+ )
+ SELECT * FROM CTE WHERE id = ${selection.eventId}
+ `);
+ const resultId = result.maybeFirstRow({resultId: NUM_NULL})?.resultId;
+ if (!exists(resultId)) return;
+
+ ctx.selection.selectTrackEvent(selection.trackUri, resultId, {
+ scrollToSelection: true,
+ });
+}
diff --git a/ui/src/frontend/animation.ts b/ui/src/frontend/animation.ts
index c8428c4..74cf065 100644
--- a/ui/src/frontend/animation.ts
+++ b/ui/src/frontend/animation.ts
@@ -31,12 +31,12 @@
}
this.startMs = nowMs;
this.endMs = nowMs + durationMs;
- raf.start(this.boundOnAnimationFrame);
+ raf.startAnimation(this.boundOnAnimationFrame);
}
stop() {
this.endMs = 0;
- raf.stop(this.boundOnAnimationFrame);
+ raf.stopAnimation(this.boundOnAnimationFrame);
}
get startTimeMs(): number {
@@ -45,7 +45,7 @@
private onAnimationFrame(nowMs: number) {
if (nowMs >= this.endMs) {
- raf.stop(this.boundOnAnimationFrame);
+ raf.stopAnimation(this.boundOnAnimationFrame);
return;
}
this.onAnimationStep(Math.max(Math.round(nowMs - this.startMs), 0));
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index c09ccbc..2bea9d9 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -25,7 +25,6 @@
import {MenuDivider, MenuItem, PopupMenu2} from '../widgets/menu';
import {LONG, NUM} from '../trace_processor/query_result';
import {checkerboardExcept} from './checkerboard';
-import {NewTrackArgs} from './track';
import {AsyncDisposableStack} from '../base/disposable_stack';
import {Trace} from '../public/trace';
@@ -180,13 +179,7 @@
unit?: string;
}
-export type BaseCounterTrackArgs = NewTrackArgs & {
- options?: Partial<CounterOptions>;
-};
-
export abstract class BaseCounterTrack implements Track {
- protected trace: Trace;
- protected uri: string;
protected trackUuid = uuidv4Sql();
// This is the over-skirted cached bounds:
@@ -204,7 +197,6 @@
private mousePos = {x: 0, y: 0};
private hover?: CounterTooltipState;
- private defaultOptions: Partial<CounterOptions>;
private options?: CounterOptions;
private readonly trash: AsyncDisposableStack;
@@ -244,10 +236,11 @@
};
}
- constructor(args: BaseCounterTrackArgs) {
- this.trace = args.trace;
- this.uri = args.uri;
- this.defaultOptions = args.options ?? {};
+ constructor(
+ protected readonly trace: Trace,
+ protected readonly uri: string,
+ protected readonly defaultOptions: Partial<CounterOptions> = {},
+ ) {
this.trash = new AsyncDisposableStack();
}
@@ -867,7 +860,7 @@
this.countersKey = countersKey;
this.counters = data;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
private async createTableAndFetchLimits(
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 7ef1cd4..83c9d73 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -28,13 +28,13 @@
import {LONG, NUM} from '../trace_processor/query_result';
import {checkerboardExcept} from './checkerboard';
import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
-import {NewTrackArgs} from './track';
import {BUCKETS_PER_PIXEL, CacheKey} from '../core/timeline_cache';
import {uuidv4Sql} from '../base/uuid';
import {AsyncDisposableStack} from '../base/disposable_stack';
import {TrackMouseEvent, TrackRenderContext} from '../public/track';
import {Point2D, VerticalBounds} from '../base/geom';
import {Trace} from '../public/trace';
+import {SourceDataset, Dataset} from '../trace_processor/dataset';
// The common class that underpins all tracks drawing slices.
@@ -162,8 +162,6 @@
> implements Track
{
protected sliceLayout: SliceLayout = {...DEFAULT_SLICE_LAYOUT};
- protected trace: Trace;
- protected uri: string;
protected trackUuid = uuidv4Sql();
// This is the over-skirted cached bounds:
@@ -239,9 +237,10 @@
_selectedSlice?: SliceT,
): void {}
- constructor(args: NewTrackArgs) {
- this.trace = args.trace;
- this.uri = args.uri;
+ constructor(
+ protected readonly trace: Trace,
+ protected readonly uri: string,
+ ) {
// Work out the extra columns.
// This is the union of the embedder-defined columns and the base columns
// we know about (ts, dur, ...).
@@ -694,7 +693,7 @@
this.onUpdatedSlices(slices);
this.slices = slices;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
private rowToSliceInternal(row: RowT): CastInternal<SliceT> {
@@ -972,6 +971,17 @@
});
return {ts: Time.fromRaw(row.ts), dur: Duration.fromRaw(row.dur)};
}
+
+ getDataset(): Dataset | undefined {
+ return new SourceDataset({
+ src: this.getSqlSource(),
+ schema: {
+ id: NUM,
+ ts: LONG,
+ dur: LONG,
+ },
+ });
+ }
}
// This is the argument passed to onSliceOver(args).
diff --git a/ui/src/frontend/clipboard.ts b/ui/src/frontend/clipboard.ts
deleted file mode 100644
index 8d0b06c..0000000
--- a/ui/src/frontend/clipboard.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {copyToClipboard} from '../base/clipboard';
-import {AppImpl} from '../core/app_impl';
-
-export function onClickCopy(url: string) {
- return (e: Event) => {
- e.preventDefault();
- copyToClipboard(url);
- AppImpl.instance.omnibox.showStatusMessage(
- 'Link copied into the clipboard',
- );
- };
-}
diff --git a/ui/src/frontend/debug.ts b/ui/src/frontend/debug.ts
index 1735ed7..9899380 100644
--- a/ui/src/frontend/debug.ts
+++ b/ui/src/frontend/debug.ts
@@ -14,8 +14,6 @@
import {produce} from 'immer';
import m from 'mithril';
-import {Actions} from '../common/actions';
-import {getSchema} from '../common/schema';
import {raf} from '../core/raf_scheduler';
import {globals} from './globals';
import {App} from '../public/app';
@@ -25,20 +23,16 @@
interface Window {
m: typeof m;
app: App;
- getSchema: typeof getSchema;
globals: typeof globals;
- Actions: typeof Actions;
produce: typeof produce;
raf: typeof raf;
}
}
export function registerDebugGlobals() {
- window.getSchema = getSchema;
window.m = m;
window.app = AppImpl.instance;
window.globals = globals;
- window.Actions = Actions;
window.produce = produce;
window.raf = raf;
}
diff --git a/ui/src/frontend/error_dialog.ts b/ui/src/frontend/error_dialog.ts
index dbe4a02..3e27b07 100644
--- a/ui/src/frontend/error_dialog.ts
+++ b/ui/src/frontend/error_dialog.ts
@@ -14,9 +14,7 @@
import m from 'mithril';
import {ErrorDetails} from '../base/logging';
-import {EXTENSION_URL} from '../common/recordingV2/recording_utils';
-import {GcsUploader} from '../common/gcs_uploader';
-import {RECORDING_V2_FLAG} from '../core/feature_flags';
+import {GcsUploader} from '../base/gcs_uploader';
import {raf} from '../core/raf_scheduler';
import {VERSION} from '../gen/perfetto_version';
import {getCurrentModalKey, showModal} from '../widgets/modal';
@@ -47,22 +45,20 @@
return;
}
- if (!RECORDING_V2_FLAG.get()) {
- if (err.message.includes('Unable to claim interface')) {
- showWebUSBError();
- timeLastReport = now;
- return;
- }
+ if (err.message.includes('Unable to claim interface')) {
+ showWebUSBError();
+ timeLastReport = now;
+ return;
+ }
- if (
- err.message.includes('A transfer error has occurred') ||
- err.message.includes('The device was disconnected') ||
- err.message.includes('The transfer was cancelled')
- ) {
- showConnectionLostError();
- timeLastReport = now;
- return;
- }
+ if (
+ err.message.includes('A transfer error has occurred') ||
+ err.message.includes('The device was disconnected') ||
+ err.message.includes('The transfer was cancelled')
+ ) {
+ showConnectionLostError();
+ timeLastReport = now;
+ return;
}
if (err.message.includes('(ERR:fmt)')) {
@@ -246,7 +242,7 @@
this.uploadStatus = '';
const uploader = new GcsUploader(this.traceData, {
onProgress: () => {
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
this.uploadStatus = uploader.getEtaString();
if (uploader.state === 'UPLOADED') {
this.traceState = 'UPLOADED';
@@ -356,135 +352,6 @@
});
}
-export function showWebUSBErrorV2() {
- showModal({
- title: 'A WebUSB error occurred',
- content: m(
- 'div',
- m(
- 'span',
- `Is adb already running on the host? Run this command and
- try again.`,
- ),
- m('br'),
- m('.modal-bash', '> adb kill-server'),
- m('br'),
- // The statement below covers the following edge case:
- // 1. 'adb server' is running on the device.
- // 2. The user selects the new Android target, so we try to fetch the
- // OS version and do QSS.
- // 3. The error modal is shown.
- // 4. The user runs 'adb kill-server'.
- // At this point we don't have a trigger to try fetching the OS version
- // + QSS again. Therefore, the user will need to refresh the page.
- m(
- 'span',
- "If after running 'adb kill-server', you don't see " +
- "a 'Start Recording' button on the page and you don't see " +
- "'Allow USB debugging' on the device, " +
- 'you will need to reload this page.',
- ),
- m('br'),
- m('br'),
- m('span', 'For details see '),
- m('a', {href: 'http://b/159048331', target: '_blank'}, 'b/159048331'),
- ),
- });
-}
-
-export function showConnectionLostError(): void {
- showModal({
- title: 'Connection with the ADB device lost',
- content: m(
- 'div',
- m('span', `Please connect the device again to restart the recording.`),
- m('br'),
- ),
- });
-}
-
-export function showAllowUSBDebugging(): void {
- showModal({
- title: 'Could not connect to the device',
- content: m(
- 'div',
- m('span', 'Please allow USB debugging on the device.'),
- m('br'),
- ),
- });
-}
-
-export function showNoDeviceSelected(): void {
- showModal({
- title: 'No device was selected for recording',
- content: m(
- 'div',
- m(
- 'span',
- `If you want to connect to an ADB device,
- please select it from the list.`,
- ),
- m('br'),
- ),
- });
-}
-
-export function showExtensionNotInstalled(): void {
- showModal({
- title: 'Perfetto Chrome extension not installed',
- content: m(
- 'div',
- m(
- '.note',
- `To trace Chrome from the Perfetto UI, you need to install our `,
- m('a', {href: EXTENSION_URL, target: '_blank'}, 'Chrome extension'),
- ' and then reload this page.',
- ),
- m('br'),
- ),
- });
-}
-
-export function showWebsocketConnectionIssue(message: string): void {
- showModal({
- title: 'Unable to connect to the device via websocket',
- content: m(
- 'div',
- m('div', 'trace_processor_shell --httpd is unreachable or crashed.'),
- m('pre', message),
- ),
- });
-}
-
-export function showIssueParsingTheTracedResponse(message: string): void {
- showModal({
- title:
- 'A problem was encountered while connecting to' +
- ' the Perfetto tracing service',
- content: m('div', m('span', message), m('br')),
- });
-}
-
-export function showFailedToPushBinary(message: string): void {
- showModal({
- title: 'Failed to push a binary to the device',
- content: m(
- 'div',
- m(
- 'span',
- 'This can happen if your Android device has an OS version lower ' +
- 'than Q. Perfetto tried to push the latest version of its ' +
- 'embedded binary but failed.',
- ),
- m('br'),
- m('br'),
- m('span', 'Error message:'),
- m('br'),
- m('span', message),
- ),
- });
-}
-
function showRpcSequencingError() {
showModal({
title: 'A TraceProcessor RPC error occurred',
@@ -534,3 +401,25 @@
],
});
}
+
+function showWebsocketConnectionIssue(message: string): void {
+ showModal({
+ title: 'Unable to connect to the device via websocket',
+ content: m(
+ 'div',
+ m('div', 'trace_processor_shell --httpd is unreachable or crashed.'),
+ m('pre', message),
+ ),
+ });
+}
+
+function showConnectionLostError(): void {
+ showModal({
+ title: 'Connection with the ADB device lost',
+ content: m(
+ 'div',
+ m('span', `Please connect the device again to restart the recording.`),
+ m('br'),
+ ),
+ });
+}
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 8a1069e..f5be8b4 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -12,128 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists} from '../base/logging';
-import {createStore, Store} from '../base/store';
-import {Actions, DeferredAction} from '../common/actions';
-import {createEmptyState} from '../common/empty_state';
-import {State} from '../common/state';
-import {setPerfHooks} from '../core/perf';
import {raf} from '../core/raf_scheduler';
-import {ServiceWorkerController} from './service_worker_controller';
-import {HttpRpcState} from '../trace_processor/http_rpc_engine';
-import {getServingRoot} from '../base/http_utils';
import {AppImpl} from '../core/app_impl';
-type DispatchMultiple = (actions: DeferredAction[]) => void;
-type TrackDataStore = Map<string, {}>;
-
/**
* Global accessors for state/dispatch in the frontend.
*/
class Globals {
- private _dispatchMultiple?: DispatchMultiple = undefined;
- private _store = createStore<State>(createEmptyState());
- private _serviceWorkerController?: ServiceWorkerController = undefined;
-
- // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
- private _trackDataStore?: TrackDataStore = undefined;
- private _bufferUsage?: number = undefined;
- private _recordingLog?: string = undefined;
- httpRpcState: HttpRpcState = {connected: false};
- showPanningHint = false;
-
// This is normally undefined is injected in via is_internal_user.js.
// WARNING: do not change/rename/move without considering impact on the
// internal_user script.
private _isInternalUser: boolean | undefined = undefined;
- // TODO(hjd): Remove once we no longer need to update UUID on redraw.
- private _publishRedraw?: () => void = undefined;
-
- initialize(dispatchMultiple: DispatchMultiple) {
- this._dispatchMultiple = dispatchMultiple;
-
- setPerfHooks(
- () => this.state.perfDebug,
- () => this.dispatch(Actions.togglePerfDebug({})),
- );
-
- this._serviceWorkerController = new ServiceWorkerController(
- getServingRoot(),
- );
-
- // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
- // TODO(primiano): for posterity: these assignments below are completely
- // pointless and could be done as member variable initializers, as
- // initialize() is only called ever once. (But then i'm going to kill this
- // entire file soon).
- this._trackDataStore = new Map<string, {}>();
- }
-
- get root() {
- return AppImpl.instance.rootUrl;
- }
-
- get publishRedraw(): () => void {
- return this._publishRedraw || (() => {});
- }
-
- set publishRedraw(f: () => void) {
- this._publishRedraw = f;
- }
-
- get state(): State {
- return this._store.state;
- }
-
- get store(): Store<State> {
- return this._store;
- }
-
// WARNING: do not change/rename/move without considering impact on the
// internal_user script.
get extraSqlPackages() {
return AppImpl.instance.extraSqlPackages;
}
- dispatch(action: DeferredAction) {
- this.dispatchMultiple([action]);
- }
-
- dispatchMultiple(actions: DeferredAction[]) {
- assertExists(this._dispatchMultiple)(actions);
- }
-
- get serviceWorkerController() {
- return assertExists(this._serviceWorkerController);
- }
-
- // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
-
- get trackDataStore(): TrackDataStore {
- return assertExists(this._trackDataStore);
- }
-
- get bufferUsage() {
- return this._bufferUsage;
- }
-
- get recordingLog() {
- return this._recordingLog;
- }
-
- setBufferUsage(bufferUsage: number) {
- this._bufferUsage = bufferUsage;
- }
-
- setTrackData(id: string, data: {}) {
- this.trackDataStore.set(id, data);
- }
-
- setRecordingLog(recordingLog: string) {
- this._recordingLog = recordingLog;
- }
-
// This variable is set by the is_internal_user.js script if the user is a
// googler. This is used to avoid exposing features that are not ready yet
// for public consumption. The gated features themselves are not secret.
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 819f271..322748e 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -54,7 +54,7 @@
nativeKeyboardLayoutMap()
.then((keyMap: KeyboardLayoutMap) => {
this.keyMap = keyMap;
- AppImpl.instance.scheduleFullRedraw();
+ AppImpl.instance.scheduleFullRedraw('force');
})
.catch((e) => {
if (
@@ -69,7 +69,7 @@
// The alternative would be to show key mappings for all keyboard
// layouts which is not feasible.
this.keyMap = new EnglishQwertyKeyboardLayoutMap();
- AppImpl.instance.scheduleFullRedraw();
+ AppImpl.instance.scheduleFullRedraw('force');
} else {
// Something unexpected happened. Either the browser doesn't conform
// to the keyboard API spec, or the keyboard API spec has changed!
diff --git a/ui/src/frontend/home_page.ts b/ui/src/frontend/home_page.ts
index a5f3ac9..e38f74c 100644
--- a/ui/src/frontend/home_page.ts
+++ b/ui/src/frontend/home_page.ts
@@ -16,8 +16,8 @@
import {channelChanged, getNextChannel, setChannel} from '../core/channels';
import {Anchor} from '../widgets/anchor';
import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
-import {globals} from './globals';
-import {PageAttrs} from '../core/router';
+import {PageAttrs} from '../public/page';
+import {assetSrc} from '../base/assets';
export class Hints implements m.ClassComponent {
view() {
@@ -74,7 +74,7 @@
'.home-page-center',
m(
'.home-page-title',
- m(`img.logo[src=${globals.root}assets/logo-3d.png]`),
+ m(`img.logo[src=${assetSrc('assets/logo-3d.png')}]`),
'Perfetto',
),
m(Hints),
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index a5b5b0b..600f08b 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -17,17 +17,10 @@
import '../base/static_initializers';
import NON_CORE_PLUGINS from '../gen/all_plugins';
import CORE_PLUGINS from '../gen/all_core_plugins';
-import {Draft} from 'immer';
import m from 'mithril';
import {defer} from '../base/deferred';
import {addErrorHandler, reportError} from '../base/logging';
-import {Store} from '../base/store';
-import {Actions, DeferredAction, StateActions} from '../common/actions';
-import {traceEvent} from '../core/metatracing';
-import {State} from '../common/state';
-import {initController, runControllers} from '../controller';
-import {isGetCategoriesResponse} from '../controller/chrome_proxy_record_controller';
-import {RECORDING_V2_FLAG, featureFlags} from '../core/feature_flags';
+import {featureFlags} from '../core/feature_flags';
import {initLiveReload} from '../core/live_reload';
import {raf} from '../core/raf_scheduler';
import {initWasm} from '../trace_processor/wasm_engine_proxy';
@@ -36,42 +29,28 @@
import {initCssConstants} from './css_constants';
import {registerDebugGlobals} from './debug';
import {maybeShowErrorDialog} from './error_dialog';
-import {ExplorePage} from './explore_page';
import {installFileDropHandler} from './file_drop_handler';
-import {FlagsPage} from './flags_page';
import {globals} from './globals';
import {HomePage} from './home_page';
-import {InsightsPage} from './insights_page';
-import {MetricsPage} from './metrics_page';
-import {PluginsPage} from './plugins_page';
import {postMessageHandler} from './post_message_handler';
-import {QueryPage} from './query_page';
-import {RecordPage, updateAvailableAdbDevices} from './record_page';
-import {RecordPageV2} from './record_page_v2';
import {Route, Router} from '../core/router';
import {CheckHttpRpcConnection} from './rpc_http_dialog';
-import {TraceInfoPage} from './trace_info_page';
import {maybeOpenTraceFromRoute} from './trace_url_handler';
import {ViewerPage} from './viewer_page';
-import {VizPage} from './viz_page';
-import {WidgetsPage} from './widgets_page';
import {HttpRpcEngine} from '../trace_processor/http_rpc_engine';
import {showModal} from '../widgets/modal';
import {IdleDetector} from './idle_detector';
import {IdleDetectorWindow} from './idle_detector_interface';
-import {pageWithTrace} from './pages';
import {AppImpl} from '../core/app_impl';
import {addSqlTableTab} from './sql_table_tab';
-import {getServingRoot} from '../base/http_utils';
import {configureExtensions} from '../public/lib/extensions';
import {
addDebugCounterTrack,
addDebugSliceTrack,
-} from '../public/lib/debug_tracks/debug_tracks';
+} from '../public/lib/tracks/debug_tracks';
import {addVisualizedArgTracks} from './visualized_args_tracks';
import {addQueryResultsTab} from '../public/lib/query_table/query_result_tab';
-
-const EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
+import {assetSrc, initAssets} from '../base/assets';
const CSP_WS_PERMISSIVE_PORT = featureFlags.register({
id: 'cspAllowAnyWebsocketPort',
@@ -83,28 +62,19 @@
defaultValue: false,
});
-function setExtensionAvailability(available: boolean) {
- globals.dispatch(
- Actions.setExtensionAvailable({
- available,
- }),
- );
-}
-
function routeChange(route: Route) {
- raf.scheduleFullRedraw();
- maybeOpenTraceFromRoute(route);
- if (route.fragment) {
- // This needs to happen after the next redraw call. It's not enough
- // to use setTimeout(..., 0); since that may occur before the
- // redraw scheduled above.
- raf.addPendingCallback(() => {
+ raf.scheduleFullRedraw('force', () => {
+ if (route.fragment) {
+ // This needs to happen after the next redraw call. It's not enough
+ // to use setTimeout(..., 0); since that may occur before the
+ // redraw scheduled above.
const e = document.getElementById(route.fragment);
if (e) {
e.scrollIntoView();
}
- });
- }
+ }
+ });
+ maybeOpenTraceFromRoute(route);
}
function setupContentSecurityPolicy() {
@@ -169,71 +139,28 @@
document.head.appendChild(meta);
}
-function setupExtentionPort(extensionLocalChannel: MessageChannel) {
- // We proxy messages between the extension and the controller because the
- // controller's worker can't access chrome.runtime.
- const extensionPort =
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
- window.chrome && chrome.runtime
- ? chrome.runtime.connect(EXTENSION_ID)
- : undefined;
-
- setExtensionAvailability(extensionPort !== undefined);
-
- if (extensionPort) {
- // Send messages to keep-alive the extension port.
- const interval = setInterval(() => {
- extensionPort.postMessage({
- method: 'ExtensionVersion',
- });
- }, 25000);
- extensionPort.onDisconnect.addListener((_) => {
- setExtensionAvailability(false);
- clearInterval(interval);
- void chrome.runtime.lastError; // Needed to not receive an error log.
- });
- // This forwards the messages from the extension to the controller.
- extensionPort.onMessage.addListener(
- (message: object, _port: chrome.runtime.Port) => {
- if (isGetCategoriesResponse(message)) {
- globals.dispatch(Actions.setChromeCategories(message));
- return;
- }
- extensionLocalChannel.port2.postMessage(message);
- },
- );
- }
-
- // This forwards the messages from the controller to the extension
- extensionLocalChannel.port2.onmessage = ({data}) => {
- if (extensionPort) extensionPort.postMessage(data);
- };
-}
-
function main() {
// Setup content security policy before anything else.
setupContentSecurityPolicy();
-
+ initAssets();
AppImpl.initialize({
- rootUrl: getServingRoot(),
initialRouteArgs: Router.parseUrl(window.location.href).args,
- clearState: () => globals.dispatch(Actions.clearState({})),
});
// Wire up raf for widgets.
- setScheduleFullRedraw(() => raf.scheduleFullRedraw());
+ setScheduleFullRedraw((force?: 'force') => raf.scheduleFullRedraw(force));
// Load the css. The load is asynchronous and the CSS is not ready by the time
// appendChild returns.
const cssLoadPromise = defer<void>();
const css = document.createElement('link');
css.rel = 'stylesheet';
- css.href = globals.root + 'perfetto.css';
+ css.href = assetSrc('perfetto.css');
css.onload = () => cssLoadPromise.resolve();
css.onerror = (err) => cssLoadPromise.reject(err);
const favicon = document.head.querySelector('#favicon');
if (favicon instanceof HTMLLinkElement) {
- favicon.href = globals.root + 'assets/favicon.png';
+ favicon.href = assetSrc('assets/favicon.png');
}
// Load the script to detect if this is a Googler (see comments on globals.ts)
@@ -259,20 +186,8 @@
window.addEventListener('error', (e) => reportError(e));
window.addEventListener('unhandledrejection', (e) => reportError(e));
- const extensionLocalChannel = new MessageChannel();
-
- initWasm(globals.root);
- initController(extensionLocalChannel.port1);
-
- // These need to be set before globals.initialize.
- globals.initialize(stateActionDispatcher);
-
- globals.serviceWorkerController.install();
-
- globals.store.subscribe(scheduleRafAndRunControllersOnStateChange);
- globals.publishRedraw = () => raf.scheduleFullRedraw();
-
- setupExtentionPort(extensionLocalChannel);
+ initWasm();
+ AppImpl.instance.serviceWorkerController.install();
// Put debug variables in the global scope for better debugging.
registerDebugGlobals();
@@ -303,25 +218,15 @@
// And replace it with the root <main> element which will be used by mithril.
document.body.innerHTML = '';
- const router = new Router({
- '/': HomePage,
- '/explore': pageWithTrace(ExplorePage),
- '/flags': FlagsPage,
- '/info': pageWithTrace(TraceInfoPage),
- '/insights': pageWithTrace(InsightsPage),
- '/metrics': pageWithTrace(MetricsPage),
- '/plugins': PluginsPage,
- '/query': pageWithTrace(QueryPage),
- '/record': RECORDING_V2_FLAG.get() ? RecordPageV2 : RecordPage,
- '/viewer': pageWithTrace(ViewerPage),
- '/viz': pageWithTrace(VizPage),
- '/widgets': WidgetsPage,
- });
+ const pages = AppImpl.instance.pages;
+ const traceless = true;
+ pages.registerPage({route: '/', traceless, page: HomePage});
+ pages.registerPage({route: '/viewer', page: ViewerPage});
+ const router = new Router();
router.onRouteChanged = routeChange;
- raf.domRedraw = () => {
- m.render(document.body, m(UiMain, router.resolve()));
- };
+ // Mount the main mithril component. This also forces a sync render pass.
+ raf.mount(document.body, UiMain);
if (
(location.origin.startsWith('http://localhost:') ||
@@ -332,20 +237,6 @@
initLiveReload();
}
- if (!RECORDING_V2_FLAG.get()) {
- updateAvailableAdbDevices();
- try {
- navigator.usb.addEventListener('connect', () =>
- updateAvailableAdbDevices(),
- );
- navigator.usb.addEventListener('disconnect', () =>
- updateAvailableAdbDevices(),
- );
- } catch (e) {
- console.error('WebUSB API not supported');
- }
- }
-
// Will update the chip on the sidebar footer that notifies that the RPC is
// connected. Has no effect on the controller (which will repeat this check
// before creating a new engine).
@@ -374,9 +265,6 @@
routeChange(route);
});
- // Force one initial render to get everything in place
- m.render(document.body, m(UiMain, router.resolve()));
-
// Initialize plugins, now that we are ready to go.
const pluginManager = AppImpl.instance.plugins;
CORE_PLUGINS.forEach((p) => pluginManager.registerPlugin(p));
@@ -418,30 +306,6 @@
}
}
-function stateActionDispatcher(actions: DeferredAction[]) {
- const edits = actions.map((action) => {
- return traceEvent(`action.${action.type}`, () => {
- return (draft: Draft<State>) => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (StateActions as any)[action.type](draft, action.args);
- };
- });
- });
- globals.store.edit(edits);
-}
-
-function scheduleRafAndRunControllersOnStateChange(
- store: Store<State>,
- oldState: State,
-) {
- // Only redraw if something actually changed
- if (oldState !== store.state) {
- raf.scheduleFullRedraw();
- }
- // Run in a separate task to avoid avoid reentry.
- setTimeout(runControllers, 0);
-}
-
// TODO(primiano): this injection is to break a cirular dependency. See
// comment in sql_table_tab_interface.ts. Remove once we add an extension
// point for context menus.
diff --git a/ui/src/frontend/legacy_trace_viewer.ts b/ui/src/frontend/legacy_trace_viewer.ts
index 04668f0..25af38b 100644
--- a/ui/src/frontend/legacy_trace_viewer.ts
+++ b/ui/src/frontend/legacy_trace_viewer.ts
@@ -17,9 +17,9 @@
import {assertTrue} from '../base/logging';
import {isString} from '../base/object_utils';
import {showModal} from '../widgets/modal';
-import {globals} from './globals';
import {utf8Decode} from '../base/string_utils';
import {convertToJson} from './trace_converter';
+import {assetSrc} from '../base/assets';
const CTRACE_HEADER = 'TRACE:\n';
@@ -142,7 +142,7 @@
// The location.pathname mangling is to make this code work also when hosted
// in a non-root sub-directory, for the case of CI artifacts.
- const catapultUrl = globals.root + 'assets/catapult_trace_viewer.html';
+ const catapultUrl = assetSrc('assets/catapult_trace_viewer.html');
const newWin = window.open(catapultUrl);
if (newWin) {
// Popup succeedeed.
diff --git a/ui/src/frontend/named_slice_track.ts b/ui/src/frontend/named_slice_track.ts
index ed9b5f0..07043e9 100644
--- a/ui/src/frontend/named_slice_track.ts
+++ b/ui/src/frontend/named_slice_track.ts
@@ -16,7 +16,7 @@
import {TrackEventDetailsPanel} from '../public/details_panel';
import {TrackEventSelection} from '../public/selection';
import {Slice} from '../public/track';
-import {STR_NULL} from '../trace_processor/query_result';
+import {LONG, NUM, STR, STR_NULL} from '../trace_processor/query_result';
import {
BASE_ROW,
BaseSliceTrack,
@@ -26,10 +26,11 @@
SLICE_FLAGS_INSTANT,
} from './base_slice_track';
import {ThreadSliceDetailsPanel} from './thread_slice_details_tab';
-import {NewTrackArgs} from './track';
import {renderDuration} from './widgets/duration';
import {TraceImpl} from '../core/trace_impl';
import {assertIsInstance} from '../base/logging';
+import {SourceDataset, Dataset} from '../trace_processor/dataset';
+import {Trace} from '../public/trace';
export const NAMED_ROW = {
// Base columns (tsq, ts, dur, id, depth).
@@ -44,8 +45,8 @@
SliceType extends Slice = Slice,
RowType extends NamedRow = NamedRow,
> extends BaseSliceTrack<SliceType, RowType> {
- constructor(args: NewTrackArgs) {
- super(args);
+ constructor(trace: Trace, uri: string) {
+ super(trace, uri);
}
// Converts a SQL result row to an "Impl" Slice.
@@ -80,4 +81,16 @@
// because this class is exposed to plugins (which see only Trace).
return new ThreadSliceDetailsPanel(assertIsInstance(this.trace, TraceImpl));
}
+
+ override getDataset(): Dataset | undefined {
+ return new SourceDataset({
+ src: this.getSqlSource(),
+ schema: {
+ id: NUM,
+ name: STR,
+ ts: LONG,
+ dur: LONG,
+ },
+ });
+ }
}
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 21dc29a..ac5b015 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -89,11 +89,11 @@
onmousemove: (e: MouseEvent) => {
this.mouseDragging = true;
this.hoveredX = currentTargetOffset(e).x - TRACK_SHELL_WIDTH;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
onmouseenter: (e: MouseEvent) => {
this.hoveredX = currentTargetOffset(e).x - TRACK_SHELL_WIDTH;
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
onmouseout: () => {
this.hoveredX = null;
diff --git a/ui/src/frontend/omnibox.ts b/ui/src/frontend/omnibox.ts
index 5f1e29a..c94ee3f 100644
--- a/ui/src/frontend/omnibox.ts
+++ b/ui/src/frontend/omnibox.ts
@@ -328,7 +328,13 @@
document.removeEventListener('mousedown', this.onMouseDown);
}
+ // This is defined as an arrow function to have a single handler that can be
+ // added/remove while keeping `this` bound.
private onMouseDown = (e: Event) => {
+ // We need to schedule a redraw manually as this event handler was added
+ // manually to the DOM and doesn't use Mithril's auto-redraw system.
+ raf.scheduleFullRedraw('force');
+
// Don't close if the click was within ourselves or our popup.
if (e.target instanceof Node) {
if (this.popupElement && this.popupElement.contains(e.target)) {
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index e3798e1..8eb41ec 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -241,7 +241,7 @@
const cb = (vizTime: HighPrecisionTimeSpan) => {
this.trace.timeline.updateVisibleTimeHP(vizTime);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
};
const pixelBounds = this.extractBounds(this.timeScale);
const timeScale = this.timeScale;
@@ -445,6 +445,6 @@
this.overviewData.get(key)!.push(value);
}
}
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
}
diff --git a/ui/src/frontend/pages.ts b/ui/src/frontend/pages.ts
deleted file mode 100644
index 4bb6939..0000000
--- a/ui/src/frontend/pages.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-import {TraceImpl} from '../core/trace_impl';
-import {AppImpl} from '../core/app_impl';
-import {HomePage} from './home_page';
-import {PageAttrs} from '../core/router';
-
-export interface PageWithTraceAttrs extends PageAttrs {
- trace: TraceImpl;
-}
-
-export function pageWithTrace(
- component: m.ComponentTypes<PageWithTraceAttrs>,
-): m.Component<PageAttrs> {
- return {
- view(vnode: m.Vnode<PageAttrs>) {
- const trace = AppImpl.instance.trace;
- if (trace !== undefined) {
- return m(component, {...vnode.attrs, trace});
- }
- // Fallback on homepage if trying to open a page that requires a trace
- // while no trace is loaded.
- return m(HomePage);
- },
- };
-}
diff --git a/ui/src/frontend/pan_and_zoom_handler.ts b/ui/src/frontend/pan_and_zoom_handler.ts
index 4536b9e..0009335 100644
--- a/ui/src/frontend/pan_and_zoom_handler.ts
+++ b/ui/src/frontend/pan_and_zoom_handler.ts
@@ -259,12 +259,12 @@
private onWheel(e: WheelEvent) {
if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
this.onPanned(e.deltaX * HORIZONTAL_WHEEL_PAN_SPEED);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
} else if (e.ctrlKey && this.mousePositionX !== null) {
const sign = e.deltaY < 0 ? -1 : 1;
const deltaY = sign * Math.log2(1 + Math.abs(e.deltaY));
this.onZoomed(this.mousePositionX, deltaY * WHEEL_ZOOM_SPEED);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
}
}
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index ab6de73..760e098 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -16,13 +16,10 @@
import {findRef, toHTMLElement} from '../base/dom_utils';
import {assertExists, assertFalse} from '../base/logging';
import {
- PerfStatsSource,
- RunningStatistics,
- debugNow,
- perfDebug,
- perfDisplay,
+ PerfStats,
+ PerfStatsContainer,
runningStatStr,
-} from '../core/perf';
+} from '../core/perf_stats';
import {raf} from '../core/raf_scheduler';
import {SimpleResizeObserver} from '../base/resize_observer';
import {canvasClip} from '../base/canvas_utils';
@@ -94,7 +91,7 @@
}
export class PanelContainer
- implements m.ClassComponent<PanelContainerAttrs>, PerfStatsSource
+ implements m.ClassComponent<PanelContainerAttrs>, PerfStatsContainer
{
private readonly trace: TraceImpl;
private attrs: PanelContainerAttrs;
@@ -105,11 +102,12 @@
// Updated every render cycle in the oncreate/onupdate hook
private panelInfos: PanelInfo[] = [];
- private panelPerfStats = new WeakMap<Panel, RunningStatistics>();
+ private perfStatsEnabled = false;
+ private panelPerfStats = new WeakMap<Panel, PerfStats>();
private perfStats = {
totalPanels: 0,
panelsOnCanvas: 0,
- renderStats: new RunningStatistics(10),
+ renderStats: new PerfStats(10),
};
private ctx?: CanvasRenderingContext2D;
@@ -122,16 +120,8 @@
constructor({attrs}: m.CVnode<PanelContainerAttrs>) {
this.attrs = attrs;
this.trace = attrs.trace;
- const onRedraw = () => this.renderCanvas();
- raf.addRedrawCallback(onRedraw);
- this.trash.defer(() => {
- raf.removeRedrawCallback(onRedraw);
- });
-
- perfDisplay.addContainer(this);
- this.trash.defer(() => {
- perfDisplay.removeContainer(this);
- });
+ this.trash.use(raf.addCanvasRedrawCallback(() => this.renderCanvas()));
+ this.trash.use(attrs.trace.perfDebugging.addContainer(this));
}
getPanelsInRegion(
@@ -352,7 +342,7 @@
const ctx = this.ctx;
const vc = this.virtualCanvas;
- const redrawStart = debugNow();
+ const redrawStart = performance.now();
ctx.resetTransform();
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
@@ -367,7 +357,7 @@
this.drawTopLayerOnCanvas(ctx, vc);
// Collect performance as the last thing we do.
- const redrawDur = debugNow() - redrawStart;
+ const redrawDur = performance.now() - redrawStart;
this.updatePerfStats(
redrawDur,
this.panelInfos.length,
@@ -407,12 +397,12 @@
ctx.save();
ctx.translate(0, panelTop);
canvasClip(ctx, 0, 0, panelWidth, panelHeight);
- const beforeRender = debugNow();
+ const beforeRender = performance.now();
panel.renderCanvas(ctx, panelSize);
this.updatePanelStats(
i,
panel,
- debugNow() - beforeRender,
+ performance.now() - beforeRender,
ctx,
panelSize,
);
@@ -505,10 +495,10 @@
ctx: CanvasRenderingContext2D,
size: Size2D,
) {
- if (!perfDebug()) return;
+ if (!this.perfStatsEnabled) return;
let renderStats = this.panelPerfStats.get(panel);
if (renderStats === undefined) {
- renderStats = new RunningStatistics();
+ renderStats = new PerfStats();
this.panelPerfStats.set(panel, renderStats);
}
renderStats.addValue(renderTime);
@@ -537,12 +527,16 @@
totalPanels: number,
panelsOnCanvas: number,
) {
- if (!perfDebug()) return;
+ if (!this.perfStatsEnabled) return;
this.perfStats.renderStats.addValue(renderTime);
this.perfStats.totalPanels = totalPanels;
this.perfStats.panelsOnCanvas = panelsOnCanvas;
}
+ setPerfStatsEnabled(enable: boolean): void {
+ this.perfStatsEnabled = enable;
+ }
+
renderPerfStats() {
return [
m(
diff --git a/ui/src/frontend/permalink.ts b/ui/src/frontend/permalink.ts
index 75c7f0b..b69916f 100644
--- a/ui/src/frontend/permalink.ts
+++ b/ui/src/frontend/permalink.ts
@@ -14,7 +14,6 @@
import m from 'mithril';
import {assertExists} from '../base/logging';
-import {Actions} from '../common/actions';
import {
JsonSerialize,
parseAppState,
@@ -25,8 +24,7 @@
MIME_BINARY,
MIME_JSON,
GcsUploader,
-} from '../common/gcs_uploader';
-import {globals} from './globals';
+} from '../base/gcs_uploader';
import {
SERIALIZED_STATE_VERSION,
SerializedAppState,
@@ -34,8 +32,7 @@
import {z} from 'zod';
import {showModal} from '../widgets/modal';
import {AppImpl} from '../core/app_impl';
-import {Router} from '../core/router';
-import {onClickCopy} from './clipboard';
+import {CopyableLink} from '../widgets/copyable_link';
// Permalink serialization has two layers:
// 1. Serialization of the app state (state_serialization.ts):
@@ -58,69 +55,54 @@
// 1. parseAppState() does further semantic checks (e.g. version checking).
// 2. We want to still load the traceUrl even if the app state is invalid.
appState: z.any().optional(),
-
- // This is for the very unusual case of clicking on "Share settings" in the
- // recording page. In this case there is no trace or app state. We just
- // create a permalink with the recording state.
- recordingOpts: z.any().optional(),
});
type PermalinkState = z.infer<typeof PERMALINK_SCHEMA>;
-export interface PermalinkOptions {
- mode: 'APP_STATE' | 'RECORDING_OPTS';
-}
-
-export async function createPermalink(opts: PermalinkOptions): Promise<void> {
- const hash = await createPermalinkInternal(opts);
+export async function createPermalink(): Promise<void> {
+ const hash = await createPermalinkInternal();
showPermalinkDialog(hash);
}
// Returns the file name, not the full url (i.e. the name of the GCS object).
-async function createPermalinkInternal(
- opts: PermalinkOptions,
-): Promise<string> {
+async function createPermalinkInternal(): Promise<string> {
const permalinkData: PermalinkState = {};
- if (opts.mode === 'RECORDING_OPTS') {
- permalinkData.recordingOpts = globals.state.recordConfig;
- } else if (opts.mode === 'APP_STATE') {
- // Check if we need to upload the trace file, before serializing the app
- // state.
- let alreadyUploadedUrl = '';
- const trace = assertExists(AppImpl.instance.trace);
- const traceSource = trace.traceInfo.source;
- let dataToUpload: File | ArrayBuffer | undefined = undefined;
- let traceName = trace.traceInfo.traceTitle || 'trace';
- if (traceSource.type === 'FILE') {
- dataToUpload = traceSource.file;
- traceName = dataToUpload.name;
- } else if (traceSource.type === 'ARRAY_BUFFER') {
- dataToUpload = traceSource.buffer;
- } else if (traceSource.type === 'URL') {
- alreadyUploadedUrl = traceSource.url;
- } else {
- throw new Error(`Cannot share trace ${JSON.stringify(traceSource)}`);
- }
-
- // Upload the trace file, unless it's already uploaded (type == 'URL').
- // Internally TraceGcsUploader will skip the upload if an object with the
- // same hash exists already.
- if (alreadyUploadedUrl) {
- permalinkData.traceUrl = alreadyUploadedUrl;
- } else if (dataToUpload !== undefined) {
- updateStatus(`Uploading ${traceName}`);
- const uploader: GcsUploader = new GcsUploader(dataToUpload, {
- mimeType: MIME_BINARY,
- onProgress: () => reportUpdateProgress(uploader),
- });
- await uploader.waitForCompletion();
- permalinkData.traceUrl = uploader.uploadedUrl;
- }
-
- permalinkData.appState = serializeAppState(trace);
+ // Check if we need to upload the trace file, before serializing the app
+ // state.
+ let alreadyUploadedUrl = '';
+ const trace = assertExists(AppImpl.instance.trace);
+ const traceSource = trace.traceInfo.source;
+ let dataToUpload: File | ArrayBuffer | undefined = undefined;
+ let traceName = trace.traceInfo.traceTitle || 'trace';
+ if (traceSource.type === 'FILE') {
+ dataToUpload = traceSource.file;
+ traceName = dataToUpload.name;
+ } else if (traceSource.type === 'ARRAY_BUFFER') {
+ dataToUpload = traceSource.buffer;
+ } else if (traceSource.type === 'URL') {
+ alreadyUploadedUrl = traceSource.url;
+ } else {
+ throw new Error(`Cannot share trace ${JSON.stringify(traceSource)}`);
}
+ // Upload the trace file, unless it's already uploaded (type == 'URL').
+ // Internally TraceGcsUploader will skip the upload if an object with the
+ // same hash exists already.
+ if (alreadyUploadedUrl) {
+ permalinkData.traceUrl = alreadyUploadedUrl;
+ } else if (dataToUpload !== undefined) {
+ updateStatus(`Uploading ${traceName}`);
+ const uploader: GcsUploader = new GcsUploader(dataToUpload, {
+ mimeType: MIME_BINARY,
+ onProgress: () => reportUpdateProgress(uploader),
+ });
+ await uploader.waitForCompletion();
+ permalinkData.traceUrl = uploader.uploadedUrl;
+ }
+
+ permalinkData.appState = serializeAppState(trace);
+
// Serialize the permalink with the app state (or recording state) and upload.
updateStatus(`Creating permalink...`);
const permalinkJson = JsonSerialize(permalinkData);
@@ -167,15 +149,6 @@
}
}
- if (permalink.recordingOpts !== undefined) {
- // This permalink state only contains a RecordConfig. Show the
- // recording page with the config, but keep other state as-is.
- globals.dispatch(
- Actions.setRecordConfig({config: permalink.recordingOpts}),
- );
- Router.navigate('#!/record');
- return;
- }
let serializedAppState: SerializedAppState | undefined = undefined;
if (permalink.appState !== undefined) {
// This is the most common case where the permalink contains the app state
@@ -269,15 +242,8 @@
}
function showPermalinkDialog(hash: string) {
- const url = `${self.location.origin}/#!/?s=${hash}`;
- const linkProps = {title: 'Click to copy the URL', onclick: onClickCopy(url)};
showModal({
title: 'Permalink',
- content: m(
- 'div',
- m(`a[href=${url}]`, linkProps, url),
- m('br'),
- m('i', 'Click on the URL to copy it into the clipboard'),
- ),
+ content: m(CopyableLink, {url: `${self.location.origin}/#!/?s=${hash}`}),
});
}
diff --git a/ui/src/frontend/pivot_table.ts b/ui/src/frontend/pivot_table.ts
index 4736ebd..925cfed 100644
--- a/ui/src/frontend/pivot_table.ts
+++ b/ui/src/frontend/pivot_table.ts
@@ -34,7 +34,6 @@
sliceAggregationColumns,
tables,
} from '../core/pivot_table_query_generator';
-import {PopupMenuButton, popupMenuIcon, PopupMenuItem} from './popup_menu';
import {ReorderableCell, ReorderableCellGroup} from './reorderable_cells';
import {AttributeModalHolder} from './tables/attribute_modal_holder';
import {DurationWidget} from './widgets/duration';
@@ -45,6 +44,9 @@
import {TraceImpl} from '../core/trace_impl';
import {PivotTableManager} from '../core/pivot_table_manager';
import {extensions} from '../public/lib/extensions';
+import {MenuItem, PopupMenu2} from '../widgets/menu';
+import {Button} from '../widgets/button';
+import {popupMenuIcon} from '../widgets/table';
interface PathItem {
tree: PivotTree;
@@ -289,15 +291,14 @@
return m('tr', overallValuesRow);
}
- sortingItem(aggregationIndex: number, order: SortDirection): PopupMenuItem {
+ sortingItem(aggregationIndex: number, order: SortDirection): m.Child {
const pivotMgr = this.pivotMgr;
- return {
- itemType: 'regular',
- text: order === 'DESC' ? 'Highest first' : 'Lowest first',
- callback() {
+ return m(MenuItem, {
+ label: order === 'DESC' ? 'Highest first' : 'Lowest first',
+ onclick: () => {
pivotMgr.setSortColumn(aggregationIndex, order);
},
- };
+ });
}
readableAggregationName(aggregation: Aggregation) {
@@ -313,20 +314,21 @@
aggregation: Aggregation,
index: number,
nameOverride?: string,
- ): PopupMenuItem {
- return {
- itemType: 'regular',
- text: nameOverride ?? readableColumnName(aggregation.column),
- callback: () => this.pivotMgr.addAggregation(aggregation, index),
- };
+ ): m.Child {
+ return m(MenuItem, {
+ label: nameOverride ?? readableColumnName(aggregation.column),
+ onclick: () => {
+ this.pivotMgr.addAggregation(aggregation, index);
+ },
+ });
}
aggregationPopupTableGroup(
table: string,
columns: string[],
index: number,
- ): PopupMenuItem | undefined {
- const items = [];
+ ): m.Child | undefined {
+ const items: m.Child[] = [];
for (const column of columns) {
const tableColumn: TableColumn = {kind: 'regular', table, column};
items.push(
@@ -341,12 +343,7 @@
return undefined;
}
- return {
- itemType: 'group',
- itemId: `aggregations-${table}`,
- text: `Add ${table} aggregation`,
- children: items,
- };
+ return m(MenuItem, {label: `Add ${table} aggregation`}, items);
}
renderAggregationHeaderCell(
@@ -354,7 +351,7 @@
index: number,
removeItem: boolean,
): ReorderableCell {
- const popupItems: PopupMenuItem[] = [];
+ const popupItems: m.Child[] = [];
if (aggregation.sortDirection === undefined) {
popupItems.push(
this.sortingItem(index, 'DESC'),
@@ -377,22 +374,26 @@
continue;
}
const pivotMgr = this.pivotMgr;
- popupItems.push({
- itemType: 'regular',
- text: otherAgg,
- callback() {
- pivotMgr.setAggregationFunction(index, otherAgg);
- },
- });
+ popupItems.push(
+ m(MenuItem, {
+ label: otherAgg,
+ onclick: () => {
+ pivotMgr.setAggregationFunction(index, otherAgg);
+ },
+ }),
+ );
}
}
if (removeItem) {
- popupItems.push({
- itemType: 'regular',
- text: 'Remove',
- callback: () => this.pivotMgr.removeAggregation(index),
- });
+ popupItems.push(
+ m(MenuItem, {
+ label: 'Remove',
+ onclick: () => {
+ this.pivotMgr.removeAggregation(index);
+ },
+ }),
+ );
}
let hasCount = false;
@@ -425,10 +426,15 @@
extraClass: '.aggregation' + markFirst(index),
content: [
this.readableAggregationName(aggregation),
- m(PopupMenuButton, {
- icon: popupMenuIcon(aggregation.sortDirection),
- items: popupItems,
- }),
+ m(
+ PopupMenu2,
+ {
+ trigger: m(Button, {
+ icon: popupMenuIcon(aggregation.sortDirection),
+ }),
+ },
+ popupItems,
+ ),
],
};
}
@@ -441,27 +447,27 @@
selectedPivots: Set<string>,
): ReorderableCell {
const pivotMgr = this.pivotMgr;
- const items: PopupMenuItem[] = [
- {
- itemType: 'regular',
- text: 'Add argument pivot',
- callback: () => {
+ const items: m.Child[] = [
+ m(MenuItem, {
+ label: 'Add argument pivot',
+ onclick: () => {
this.attributeModalHolder.start();
},
- },
+ }),
];
if (queryResult.metadata.pivotColumns.length > 1) {
- items.push({
- itemType: 'regular',
- text: 'Remove',
- callback() {
- pivotMgr.setPivotSelected({column: pivot, selected: false});
- },
- });
+ items.push(
+ m(MenuItem, {
+ label: 'Remove',
+ onclick: () => {
+ pivotMgr.setPivotSelected({column: pivot, selected: false});
+ },
+ }),
+ );
}
for (const table of tables) {
- const group: PopupMenuItem[] = [];
+ const group: m.Child[] = [];
for (const columnName of table.columns) {
const column: TableColumn = {
kind: 'regular',
@@ -471,26 +477,30 @@
if (selectedPivots.has(columnKey(column))) {
continue;
}
- group.push({
- itemType: 'regular',
- text: columnName,
- callback() {
- pivotMgr.setPivotSelected({column, selected: true});
- },
- });
+ group.push(
+ m(MenuItem, {
+ label: columnName,
+ onclick: () => {
+ pivotMgr.setPivotSelected({column, selected: true});
+ },
+ }),
+ );
}
- items.push({
- itemType: 'group',
- itemId: `pivot-${table.name}`,
- text: `Add ${table.displayName} pivot`,
- children: group,
- });
+ items.push(
+ m(
+ MenuItem,
+ {
+ label: `Add ${table.displayName} pivot`,
+ },
+ group,
+ ),
+ );
}
return {
content: [
readableColumnName(pivot),
- m(PopupMenuButton, {icon: 'more_horiz', items}),
+ m(PopupMenu2, {trigger: m(Button, {icon: 'more_horiz'})}, items),
],
};
}
@@ -547,20 +557,20 @@
}),
m(
'td.menu',
- m(PopupMenuButton, {
- icon: 'menu',
- items: [
- {
- itemType: 'regular',
- text: state.constrainToArea
- ? 'Query data for the whole timeline'
- : 'Constrain to selected area',
- callback: () => {
- this.pivotMgr.setConstrainedToArea(!state.constrainToArea);
- },
+ m(
+ PopupMenu2,
+ {
+ trigger: m(Button, {icon: 'menu'}),
+ },
+ m(MenuItem, {
+ label: state.constrainToArea
+ ? 'Query data for the whole timeline'
+ : 'Constrain to selected area',
+ onclick: () => {
+ this.pivotMgr.setConstrainedToArea(!state.constrainToArea);
},
- ],
- }),
+ }),
+ ),
),
),
),
diff --git a/ui/src/frontend/popup_menu.ts b/ui/src/frontend/popup_menu.ts
deleted file mode 100644
index a59a1b5..0000000
--- a/ui/src/frontend/popup_menu.ts
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-import {SortDirection} from '../base/comparison_utils';
-import {raf} from '../core/raf_scheduler';
-
-export interface RegularPopupMenuItem {
- itemType: 'regular';
- // Display text
- text: string;
- // Action on menu item click
- callback: () => void;
-}
-
-// Helper function for simplifying defining menus.
-export function menuItem(
- text: string,
- action: () => void,
-): RegularPopupMenuItem {
- return {
- itemType: 'regular',
- text,
- callback: action,
- };
-}
-
-export interface GroupPopupMenuItem {
- itemType: 'group';
- text: string;
- itemId: string;
- children: PopupMenuItem[];
-}
-
-export type PopupMenuItem = RegularPopupMenuItem | GroupPopupMenuItem;
-
-export interface PopupMenuButtonAttrs {
- // Icon for button opening a menu
- icon: string;
- // List of popup menu items
- items: PopupMenuItem[];
-}
-
-// To ensure having at most one popup menu on the screen at a time, we need to
-// listen to click events on the whole page and close currently opened popup, if
-// there's any. This class, used as a singleton, does exactly that.
-class PopupHolder {
- // Invariant: global listener should be register if and only if this.popup is
- // not undefined.
- popup: PopupMenuButton | undefined = undefined;
- initialized = false;
- listener: (e: MouseEvent) => void;
-
- constructor() {
- this.listener = (e: MouseEvent) => {
- // Only handle those events that are not part of dropdown menu themselves.
- const hasDropdown =
- e.composedPath().find(PopupHolder.isDropdownElement) !== undefined;
- if (!hasDropdown) {
- this.ensureHidden();
- }
- };
- }
-
- static isDropdownElement(target: EventTarget) {
- if (target instanceof HTMLElement) {
- return target.tagName === 'DIV' && target.classList.contains('dropdown');
- }
- return false;
- }
-
- ensureHidden() {
- if (this.popup !== undefined) {
- this.popup.setVisible(false);
- }
- }
-
- clear() {
- if (this.popup !== undefined) {
- this.popup = undefined;
- window.removeEventListener('click', this.listener);
- }
- }
-
- showPopup(popup: PopupMenuButton) {
- this.ensureHidden();
- this.popup = popup;
- window.addEventListener('click', this.listener);
- }
-}
-
-// Singleton instance of PopupHolder
-const popupHolder = new PopupHolder();
-
-// For a table column that can be sorted; the standard popup icon should
-// reflect the current sorting direction. This function returns an icon
-// corresponding to optional SortDirection according to which the column is
-// sorted. (Optional because column might be unsorted)
-export function popupMenuIcon(sortDirection?: SortDirection) {
- switch (sortDirection) {
- case undefined:
- return 'more_horiz';
- case 'DESC':
- return 'arrow_drop_down';
- case 'ASC':
- return 'arrow_drop_up';
- }
-}
-
-// Component that displays a button that shows a popup menu on click.
-export class PopupMenuButton implements m.ClassComponent<PopupMenuButtonAttrs> {
- popupShown = false;
- expandedGroups: Set<string> = new Set();
-
- setVisible(visible: boolean) {
- this.popupShown = visible;
- if (this.popupShown) {
- popupHolder.showPopup(this);
- } else {
- popupHolder.clear();
- }
- raf.scheduleFullRedraw();
- }
-
- renderItem(item: PopupMenuItem): m.Child {
- switch (item.itemType) {
- case 'regular':
- return m(
- 'button.open-menu',
- {
- onclick: () => {
- item.callback();
- // Hide the menu item after the action has been invoked
- this.setVisible(false);
- },
- },
- item.text,
- );
- case 'group':
- const isExpanded = this.expandedGroups.has(item.itemId);
- return m(
- 'div',
- m(
- 'button.open-menu.disallow-selection',
- {
- onclick: () => {
- if (this.expandedGroups.has(item.itemId)) {
- this.expandedGroups.delete(item.itemId);
- } else {
- this.expandedGroups.add(item.itemId);
- }
- raf.scheduleFullRedraw();
- },
- },
- // Show text with up/down arrow, depending on expanded state.
- item.text + (isExpanded ? ' \u25B2' : ' \u25BC'),
- ),
- isExpanded
- ? m(
- 'div.nested-menu',
- item.children.map((item) => this.renderItem(item)),
- )
- : null,
- );
- }
- }
-
- view(vnode: m.Vnode<PopupMenuButtonAttrs, this>) {
- return m(
- '.dropdown',
- m(
- '.dropdown-button',
- {
- onclick: () => {
- this.setVisible(!this.popupShown);
- },
- },
- vnode.children,
- m('i.material-icons', vnode.attrs.icon),
- ),
- m(
- this.popupShown ? '.popup-menu.opened' : '.popup-menu.closed',
- vnode.attrs.items.map((item) => this.renderItem(item)),
- ),
- );
- }
-}
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
deleted file mode 100644
index 09e3eb1..0000000
--- a/ui/src/frontend/publish.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {raf} from '../core/raf_scheduler';
-import {HttpRpcState} from '../trace_processor/http_rpc_engine';
-import {globals} from './globals';
-
-export function publishTrackData(args: {id: string; data: {}}) {
- globals.setTrackData(args.id, args.data);
- raf.scheduleRedraw();
-}
-
-export function publishHttpRpcState(httpRpcState: HttpRpcState) {
- globals.httpRpcState = httpRpcState;
- raf.scheduleFullRedraw();
-}
-
-export function publishBufferUsage(args: {percentage: number}) {
- globals.setBufferUsage(args.percentage);
- globals.publishRedraw();
-}
-
-export function publishRecordingLog(args: {logs: string}) {
- globals.setRecordingLog(args.logs);
- globals.publishRedraw();
-}
-
-export function publishShowPanningHint() {
- globals.showPanningHint = true;
- globals.publishRedraw();
-}
diff --git a/ui/src/frontend/rpc_http_dialog.ts b/ui/src/frontend/rpc_http_dialog.ts
index e98c43d..e031b09 100644
--- a/ui/src/frontend/rpc_http_dialog.ts
+++ b/ui/src/frontend/rpc_http_dialog.ts
@@ -18,7 +18,6 @@
import {StatusResult, TraceProcessorApiVersion} from '../protos';
import {HttpRpcEngine} from '../trace_processor/http_rpc_engine';
import {showModal} from '../widgets/modal';
-import {publishHttpRpcState} from './publish';
import {AppImpl} from '../core/app_impl';
const CURRENT_API_VERSION =
@@ -151,7 +150,7 @@
// having to open a trace).
export async function CheckHttpRpcConnection(): Promise<void> {
const state = await HttpRpcEngine.checkConnection();
- publishHttpRpcState(state);
+ AppImpl.instance.httpRpc.httpRpcAvailable = state.connected;
if (!state.connected) {
// No RPC = exit immediately to the WASM UI.
return;
@@ -159,7 +158,7 @@
const tpStatus = assertExists(state.status);
function forceWasm() {
- AppImpl.instance.newEngineMode = 'FORCE_BUILTIN_WASM';
+ AppImpl.instance.httpRpc.newEngineMode = 'FORCE_BUILTIN_WASM';
}
// Check short version:
diff --git a/ui/src/frontend/service_worker_controller.ts b/ui/src/frontend/service_worker_controller.ts
index 6bd3b81..a246a61 100644
--- a/ui/src/frontend/service_worker_controller.ts
+++ b/ui/src/frontend/service_worker_controller.ts
@@ -18,6 +18,7 @@
// The actual service worker code is in src/service_worker.
// Design doc: http://go/perfetto-offline.
+import {getServingRoot} from '../base/http_utils';
import {reportError} from '../base/logging';
import {raf} from '../core/raf_scheduler';
@@ -52,11 +53,10 @@
}
export class ServiceWorkerController {
+ private readonly servingRoot = getServingRoot();
private _bypassed = false;
private _installing = false;
- constructor(private servingRoot: string) {}
-
// Caller should reload().
async setBypass(bypass: boolean) {
if (!('serviceWorker' in navigator)) return; // Not supported.
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index b327df3..9c03eb7 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -13,9 +13,8 @@
// limitations under the License.
import m from 'mithril';
-import {isString} from '../base/object_utils';
import {getCurrentChannel} from '../core/channels';
-import {TRACE_SUFFIX} from '../common/constants';
+import {TRACE_SUFFIX} from '../public/trace';
import {
disableMetatracingAndGetTrace,
enableMetatracing,
@@ -30,18 +29,23 @@
import {downloadData, downloadUrl} from './download_utils';
import {globals} from './globals';
import {toggleHelp} from './help_modal';
-import {createTraceLink, shareTrace} from './trace_share_utils';
+import {shareTrace} from './trace_share_utils';
import {
convertTraceToJsonAndDownload,
convertTraceToSystraceAndDownload,
} from './trace_converter';
import {openInOldUIWithSizeCheck} from './legacy_trace_viewer';
-import {formatHotkey} from '../base/hotkeys';
-import {SidebarMenuItem} from '../public/sidebar';
+import {SIDEBAR_SECTIONS, SidebarSections} from '../public/sidebar';
import {AppImpl} from '../core/app_impl';
import {Trace} from '../public/trace';
-import {removeFalsyValues} from '../base/array_utils';
import {OptionalTraceImplAttrs, TraceImpl} from '../core/trace_impl';
+import {Command} from '../public/command';
+import {SidebarMenuItemInternal} from '../core/sidebar_manager';
+import {exists, getOrCreate} from '../base/utils';
+import {copyToClipboard} from '../base/clipboard';
+import {classNames} from '../base/classnames';
+import {formatHotkey} from '../base/hotkeys';
+import {assetSrc} from '../base/assets';
const GITILES_URL =
'https://android.googlesource.com/platform/external/perfetto';
@@ -61,231 +65,10 @@
defaultValue: false,
});
-const WIDGETS_PAGE_IN_NAV_FLAG = featureFlags.register({
- id: 'showWidgetsPageInNav',
- name: 'Show widgets page',
- description: 'Show a link to the widgets page in the side bar.',
- defaultValue: false,
-});
-
-const PLUGINS_PAGE_IN_NAV_FLAG = featureFlags.register({
- id: 'showPluginsPageInNav',
- name: 'Show plugins page',
- description: 'Show a link to the plugins page in the side bar.',
- defaultValue: false,
-});
-
-const INSIGHTS_PAGE_IN_NAV_FLAG = featureFlags.register({
- id: 'showInsightsPageInNav',
- name: 'Show insights page',
- description: 'Show a link to the insights page in the side bar.',
- defaultValue: false,
-});
-
-const VIZ_PAGE_IN_NAV_FLAG = featureFlags.register({
- id: 'showVizPageInNav',
- name: 'Show viz page',
- description: 'Show a link to the viz page in the side bar.',
- defaultValue: true,
-});
-
-const EXPLORE_PAGE_IN_NAV_FLAG = featureFlags.register({
- id: 'showExplorePageInNav',
- name: 'Show explore page',
- description: 'Show a link to the explore page in the side bar.',
- defaultValue: false,
-});
-
function shouldShowHiringBanner(): boolean {
return globals.isInternalUser && HIRING_BANNER_FLAG.get();
}
-interface SectionItem {
- t: string;
- a: string | (() => void | Promise<void>);
- i: string;
- tooltip?: string;
- isVisible?: () => boolean;
- internalUserOnly?: boolean;
- disabled?: string; // If !undefined provides the reason why it's disabled.
-}
-
-interface Section {
- title: string;
- summary: string;
- items: SectionItem[];
- expanded?: boolean;
- appendOpenedTraceTitle?: boolean;
-}
-
-function insertSidebarMenuitems(
- groupSelector: SidebarMenuItem['group'],
-): ReadonlyArray<SectionItem> {
- return AppImpl.instance.sidebar.menuItems
- .valuesAsArray()
- .filter(({group}) => group === groupSelector)
- .sort((a, b) => {
- const prioA = a.priority ?? 0;
- const prioB = b.priority ?? 0;
- return prioA - prioB;
- })
- .map((item) => {
- const cmd = AppImpl.instance.commands.getCommand(item.commandId);
- const title = cmd.defaultHotkey
- ? `${cmd.name} [${formatHotkey(cmd.defaultHotkey)}]`
- : cmd.name;
- return {
- t: cmd.name,
- a: cmd.callback,
- i: item.icon,
- title,
- };
- });
-}
-
-function getSections(trace?: TraceImpl): Section[] {
- const downloadDisabled = trace?.traceInfo.downloadable
- ? undefined
- : 'Cannot download external trace';
- return removeFalsyValues([
- {
- title: 'Navigation',
- summary: 'Open or record a new trace',
- expanded: true,
- items: [
- ...insertSidebarMenuitems('navigation'),
- {
- t: 'Record new trace',
- a: '#!/record',
- i: 'fiber_smart_record',
- },
- {
- t: 'Widgets',
- a: '#!/widgets',
- i: 'widgets',
- isVisible: () => WIDGETS_PAGE_IN_NAV_FLAG.get(),
- },
- {
- t: 'Plugins',
- a: '#!/plugins',
- i: 'extension',
- isVisible: () => PLUGINS_PAGE_IN_NAV_FLAG.get(),
- },
- ],
- },
-
- trace && {
- title: 'Current Trace',
- summary: 'Actions on the current trace',
- expanded: true,
- appendOpenedTraceTitle: true,
- items: [
- {t: 'Show timeline', a: '#!/viewer', i: 'line_style'},
- {
- t: 'Share',
- a: async () => await shareTrace(trace),
- i: 'share',
- internalUserOnly: true,
- },
- {
- t: 'Download',
- a: () => downloadTrace(trace),
- i: 'file_download',
- disabled: downloadDisabled,
- },
- {
- t: 'Query (SQL)',
- a: '#!/query',
- i: 'database',
- },
- {
- t: 'Explore',
- a: '#!/explore',
- i: 'data_exploration',
- isVisible: () => EXPLORE_PAGE_IN_NAV_FLAG.get(),
- },
- {
- t: 'Insights',
- a: '#!/insights',
- i: 'insights',
- isVisible: () => INSIGHTS_PAGE_IN_NAV_FLAG.get(),
- },
- {
- t: 'Viz',
- a: '#!/viz',
- i: 'area_chart',
- isVisible: () => VIZ_PAGE_IN_NAV_FLAG.get(),
- },
- {t: 'Metrics', a: '#!/metrics', i: 'speed'},
- {t: 'Info and stats', a: '#!/info', i: 'info'},
- ],
- },
-
- trace && {
- title: 'Convert trace',
- summary: 'Convert to other formats',
- expanded: true,
- items: [
- {
- t: 'Switch to legacy UI',
- a: async () => await openCurrentTraceWithOldUI(trace),
- i: 'filter_none',
- disabled: downloadDisabled,
- },
- {
- t: 'Convert to .json',
- a: async () => await convertTraceToJson(trace),
- i: 'file_download',
- disabled: downloadDisabled,
- },
-
- {
- t: 'Convert to .systrace',
- a: async () => await convertTraceToSystrace(trace),
- i: 'file_download',
- isVisible: () => Boolean(trace?.traceInfo.hasFtrace),
- disabled: downloadDisabled,
- },
- ],
- },
-
- {
- title: 'Example Traces',
- expanded: true,
- summary: 'Open an example trace',
- items: [...insertSidebarMenuitems('example_traces')],
- },
-
- {
- title: 'Support',
- expanded: true,
- summary: 'Documentation & Bugs',
- items: removeFalsyValues([
- {t: 'Keyboard shortcuts', a: toggleHelp, i: 'help'},
- {t: 'Documentation', a: 'https://perfetto.dev/docs', i: 'find_in_page'},
- {t: 'Flags', a: '#!/flags', i: 'emoji_flags'},
- {
- t: 'Report a bug',
- a: getBugReportUrl(),
- i: 'bug_report',
- },
- trace &&
- (isMetatracingEnabled()
- ? {
- t: 'Finalise metatrace',
- a: () => finaliseMetatrace(trace.engine),
- i: 'file_download',
- }
- : {
- t: 'Record metatrace',
- a: () => recordMetatrace(trace.engine),
- i: 'fiber_smart_record',
- }),
- ]),
- },
- ]);
-}
-
async function openCurrentTraceWithOldUI(trace: Trace): Promise<void> {
AppImpl.instance.analytics.logEvent(
'Trace Actions',
@@ -384,6 +167,10 @@
}
}
+async function toggleMetatrace(e: Engine) {
+ return isMetatracingEnabled() ? finaliseMetatrace(e) : recordMetatrace(e);
+}
+
async function finaliseMetatrace(engine: Engine) {
AppImpl.instance.analytics.logEvent('Trace Actions', 'Finalise metatrace');
@@ -422,8 +209,8 @@
// this will eventually become consistent once the engine is created.
if (mode === undefined) {
if (
- globals.httpRpcState.connected &&
- AppImpl.instance.newEngineMode === 'USE_HTTP_RPC_IF_AVAILABLE'
+ AppImpl.instance.httpRpc.httpRpcAvailable &&
+ AppImpl.instance.httpRpc.newEngineMode === 'USE_HTTP_RPC_IF_AVAILABLE'
) {
mode = 'HTTP_RPC';
} else {
@@ -455,7 +242,7 @@
let cssClass = '';
let title = 'Service Worker: ';
let label = 'N/A';
- const ctl = globals.serviceWorkerController;
+ const ctl = AppImpl.instance.serviceWorkerController;
if (!('serviceWorker' in navigator)) {
label = 'N/A';
title += 'not supported by the browser (requires HTTPS)';
@@ -477,8 +264,8 @@
}
const toggle = async () => {
- if (globals.serviceWorkerController.bypassed) {
- globals.serviceWorkerController.setBypass(false);
+ if (ctl.bypassed) {
+ ctl.setBypass(false);
return;
}
showModal({
@@ -510,11 +297,7 @@
{
text: 'Disable and reload',
primary: true,
- action: () => {
- globals.serviceWorkerController
- .setBypass(true)
- .then(() => location.reload());
- },
+ action: () => ctl.setBypass(true).then(() => location.reload()),
},
{text: 'Cancel'},
],
@@ -569,89 +352,23 @@
}
export class Sidebar implements m.ClassComponent<OptionalTraceImplAttrs> {
- private _redrawWhileAnimating = new Animation(() => raf.scheduleFullRedraw());
+ private _redrawWhileAnimating = new Animation(() =>
+ raf.scheduleFullRedraw('force'),
+ );
private _asyncJobPending = new Set<string>();
+ private _sectionExpanded = new Map<string, boolean>();
+
+ constructor() {
+ registerMenuItems();
+ }
view({attrs}: m.CVnode<OptionalTraceImplAttrs>) {
- if (AppImpl.instance.sidebar.sidebarEnabled === 'DISABLED') {
- return null;
- }
- const vdomSections = [];
- const trace = attrs.trace;
- for (const section of getSections(trace)) {
- const vdomItems = [];
- for (const item of section.items) {
- if (item.isVisible !== undefined && !item.isVisible()) {
- continue;
- }
- let css = '';
- let attrs = {
- onclick: this.wrapClickHandler(item),
- href: isString(item.a) ? item.a : '#',
- target: isString(item.a) && !item.a.startsWith('#') ? '_blank' : null,
- disabled: false,
- id: item.t.toLowerCase().replace(/[^\w]/g, '_'),
- };
-
- if (this._asyncJobPending.has(item.t)) {
- css = '.pending';
- }
- if (item.internalUserOnly && !globals.isInternalUser) {
- continue;
- }
- if (item.disabled !== undefined) {
- attrs = {
- onclick: (e: Event) => {
- e.preventDefault();
- alert(item.disabled);
- },
- href: '#',
- target: null,
- disabled: true,
- id: '',
- };
- }
- vdomItems.push(
- m(
- 'li',
- m(
- `a${css}`,
- {...attrs, title: item.tooltip},
- m('i.material-icons', item.i),
- item.t,
- ),
- ),
- );
- }
- if (section.appendOpenedTraceTitle && attrs.trace?.traceInfo.traceTitle) {
- const {traceTitle, traceUrl} = attrs.trace?.traceInfo;
- vdomItems.unshift(m('li', createTraceLink(traceTitle, traceUrl)));
- }
- vdomSections.push(
- m(
- `section${section.expanded ? '.expanded' : ''}`,
- m(
- '.section-header',
- {
- onclick: () => {
- section.expanded = !section.expanded;
- raf.scheduleFullRedraw();
- },
- },
- m('h1', {title: section.summary}, section.title),
- m('h2', section.summary),
- ),
- m('.section-content', m('ul', vdomItems)),
- ),
- );
- }
+ const sidebar = AppImpl.instance.sidebar;
+ if (!sidebar.enabled) return null;
return m(
'nav.sidebar',
{
- class:
- AppImpl.instance.sidebar.sidebarVisibility === 'VISIBLE'
- ? 'show-sidebar'
- : 'hide-sidebar',
+ class: sidebar.visible ? 'show-sidebar' : 'hide-sidebar',
// 150 here matches --sidebar-timing in the css.
// TODO(hjd): Should link to the CSS variable.
ontransitionstart: (e: TransitionEvent) => {
@@ -666,19 +383,16 @@
shouldShowHiringBanner() ? m(HiringBanner) : null,
m(
`header.${getCurrentChannel()}`,
- m(`img[src=${globals.root}assets/brand.png].brand`),
+ m(`img[src=${assetSrc('assets/brand.png')}].brand`),
m(
'button.sidebar-button',
{
- onclick: () => AppImpl.instance.sidebar.toggleSidebarVisbility(),
+ onclick: () => sidebar.toggleVisibility(),
},
m(
'i.material-icons',
{
- title:
- AppImpl.instance.sidebar.sidebarVisibility === 'VISIBLE'
- ? 'Hide menu'
- : 'Show menu',
+ title: sidebar.visible ? 'Hide menu' : 'Show menu',
},
'menu',
),
@@ -688,32 +402,118 @@
'.sidebar-scroll',
m(
'.sidebar-scroll-container',
- ...vdomSections,
+ ...(Object.keys(SIDEBAR_SECTIONS) as SidebarSections[]).map((s) =>
+ this.renderSection(s),
+ ),
m(SidebarFooter, attrs),
),
),
);
}
- // creates the onClick handlers for the items which provided an
- // (async)function in the `a`. If `a` is a url, instead, just return null.
- // We repeate this in view() passes and not in the constructor because new
- // sidebar items can be added by plugins at any time.
+ private renderSection(sectionId: SidebarSections) {
+ const section = SIDEBAR_SECTIONS[sectionId];
+ const menuItems = AppImpl.instance.sidebar.menuItems
+ .valuesAsArray()
+ .filter((item) => item.section === sectionId)
+ .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
+ .map((item) => this.renderItem(item));
+
+ // Don't render empty sections.
+ if (menuItems.length === 0) return undefined;
+
+ const expanded = getOrCreate(this._sectionExpanded, sectionId, () => true);
+ return m(
+ `section${expanded ? '.expanded' : ''}`,
+ m(
+ '.section-header',
+ {
+ onclick: () => {
+ this._sectionExpanded.set(sectionId, !expanded);
+ raf.scheduleFullRedraw();
+ },
+ },
+ m('h1', {title: section.title}, section.title),
+ m('h2', section.summary),
+ ),
+ m('.section-content', m('ul', menuItems)),
+ );
+ }
+
+ private renderItem(item: SidebarMenuItemInternal): m.Child {
+ let href = '#';
+ let disabled = false;
+ let target = null;
+ let command: Command | undefined = undefined;
+ let tooltip = valueOrCallback(item.tooltip);
+ let onclick: (() => unknown | Promise<unknown>) | undefined = undefined;
+ const commandId = 'commandId' in item ? item.commandId : undefined;
+ const action = 'action' in item ? item.action : undefined;
+ let text = valueOrCallback(item.text);
+ const disabReason: boolean | string | undefined = valueOrCallback(
+ item.disabled,
+ );
+
+ if (disabReason === true || typeof disabReason === 'string') {
+ disabled = true;
+ onclick = () => typeof disabReason === 'string' && alert(disabReason);
+ } else if (action !== undefined) {
+ onclick = action;
+ } else if (commandId !== undefined) {
+ const cmdMgr = AppImpl.instance.commands;
+ command = cmdMgr.hasCommand(commandId ?? '')
+ ? cmdMgr.getCommand(commandId)
+ : undefined;
+ if (command === undefined) {
+ disabled = true;
+ } else {
+ text = text !== undefined ? text : command.name;
+ if (command.defaultHotkey !== undefined) {
+ tooltip =
+ `${tooltip ?? command.name}` +
+ ` [${formatHotkey(command.defaultHotkey)}]`;
+ }
+ onclick = () => cmdMgr.runCommand(commandId);
+ }
+ }
+
+ // This is not an else if because in some rare cases the user might want
+ // to have both an href and onclick, with different behaviors. The only case
+ // today is the trace name / URL, where we want the URL in the href to
+ // support right-click -> copy URL, but the onclick does copyToClipboard().
+ if ('href' in item && item.href !== undefined) {
+ href = item.href;
+ target = href.startsWith('#') ? null : '_blank';
+ }
+ return m(
+ 'li',
+ m(
+ 'a',
+ {
+ className: classNames(
+ valueOrCallback(item.cssClass),
+ this._asyncJobPending.has(item.id) && 'pending',
+ ),
+ onclick: onclick && this.wrapClickHandler(item.id, onclick),
+ href,
+ target,
+ disabled,
+ title: tooltip,
+ },
+ exists(item.icon) && m('i.material-icons', valueOrCallback(item.icon)),
+ text,
+ ),
+ );
+ }
+
+ // Creates the onClick handlers for the items which provided a function in the
+ // `action` member. The function can be either sync or async.
// What we want to achieve here is the following:
- // - We want to allow plugins that contribute to the sidebar to just specify
- // either string URLs or (async) functions as actions for a sidebar menu.
- // - When they specify an async function, we want to render a spinner, next
- // to the menu item, until the promise is resolved.
+ // - If the action is async (returns a Promise), we want to render a spinner,
+ // next to the menu item, until the promise is resolved.
// - [Minor] we want to call e.preventDefault() to override the behaviour of
// the <a href='#'> which gets rendered for accessibility reasons.
- private wrapClickHandler(item: SectionItem) {
- // item.a can be either a function or a URL. In the latter case, we
- // don't need to generate any onclick handler.
- const itemAction = item.a;
- if (typeof itemAction !== 'function') {
- return null;
- }
- const itemId = item.t;
+ private wrapClickHandler(itemId: string, itemAction: Function) {
return (e: Event) => {
e.preventDefault(); // Make the <a href="#"> a no-op.
const res = itemAction();
@@ -725,8 +525,129 @@
raf.scheduleFullRedraw();
res.finally(() => {
this._asyncJobPending.delete(itemId);
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
});
};
}
}
+
+// TODO(primiano): The registrations below should be moved to dedicated
+// plugins (most of this really belongs to core_plugins/commads/index.ts).
+// For now i'm keeping everything here as splitting these require moving some
+// functions like share_trace() out of core, splitting out permalink, etc.
+
+let globalItemsRegistered = false;
+const traceItemsRegistered = new WeakSet<TraceImpl>();
+
+function registerMenuItems() {
+ if (!globalItemsRegistered) {
+ globalItemsRegistered = true;
+ registerGlobalSidebarEntries();
+ }
+ const trace = AppImpl.instance.trace;
+ if (trace !== undefined && !traceItemsRegistered.has(trace)) {
+ traceItemsRegistered.add(trace);
+ registerTraceMenuItems(trace);
+ }
+}
+
+function registerGlobalSidebarEntries() {
+ const app = AppImpl.instance;
+ // TODO(primiano): The Open file / Open with legacy entries are registered by
+ // the 'perfetto.CoreCommands' plugins. Make things consistent.
+ app.sidebar.addMenuItem({
+ section: 'support',
+ text: 'Keyboard shortcuts',
+ action: toggleHelp,
+ icon: 'help',
+ });
+ app.sidebar.addMenuItem({
+ section: 'support',
+ text: 'Documentation',
+ href: 'https://perfetto.dev/docs',
+ icon: 'find_in_page',
+ });
+ app.sidebar.addMenuItem({
+ section: 'support',
+ sortOrder: 4,
+ text: 'Report a bug',
+ href: getBugReportUrl(),
+ icon: 'bug_report',
+ });
+}
+
+function registerTraceMenuItems(trace: TraceImpl) {
+ const downloadDisabled = trace.traceInfo.downloadable
+ ? false
+ : 'Cannot download external trace';
+
+ const traceTitle = trace?.traceInfo.traceTitle;
+ traceTitle &&
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: traceTitle,
+ href: trace.traceInfo.traceUrl,
+ action: () => copyToClipboard(trace.traceInfo.traceUrl),
+ tooltip: 'Click to copy the URL',
+ cssClass: 'trace-file-name',
+ });
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Show timeline',
+ href: '#!/viewer',
+ icon: 'line_style',
+ });
+ globals.isInternalUser &&
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Share',
+ action: async () => await shareTrace(trace),
+ icon: 'share',
+ });
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Download',
+ action: () => downloadTrace(trace),
+ icon: 'file_download',
+ disabled: downloadDisabled,
+ });
+ trace.sidebar.addMenuItem({
+ section: 'convert_trace',
+ text: 'Switch to legacy UI',
+ action: async () => await openCurrentTraceWithOldUI(trace),
+ icon: 'filter_none',
+ disabled: downloadDisabled,
+ });
+ trace.sidebar.addMenuItem({
+ section: 'convert_trace',
+ text: 'Convert to .json',
+ action: async () => await convertTraceToJson(trace),
+ icon: 'file_download',
+ disabled: downloadDisabled,
+ });
+ trace.traceInfo.hasFtrace &&
+ trace.sidebar.addMenuItem({
+ section: 'convert_trace',
+ text: 'Convert to .systrace',
+ action: async () => await convertTraceToSystrace(trace),
+ icon: 'file_download',
+ disabled: downloadDisabled,
+ });
+ trace.sidebar.addMenuItem({
+ section: 'support',
+ sortOrder: 5,
+ text: () =>
+ isMetatracingEnabled() ? 'Finalize metatrace' : 'Record metatrace',
+ action: () => toggleMetatrace(trace.engine),
+ icon: () => (isMetatracingEnabled() ? 'download' : 'fiber_smart_record'),
+ });
+}
+
+// Used to deal with fields like the entry name, which can be either a direct
+// string or a callback that returns the string.
+function valueOrCallback<T>(value: T | (() => T)): T;
+function valueOrCallback<T>(value: T | (() => T) | undefined): T | undefined;
+function valueOrCallback<T>(value: T | (() => T) | undefined): T | undefined {
+ if (value === undefined) return undefined;
+ return value instanceof Function ? value() : value;
+}
diff --git a/ui/src/frontend/simple_counter_track.ts b/ui/src/frontend/simple_counter_track.ts
deleted file mode 100644
index aca8f7e..0000000
--- a/ui/src/frontend/simple_counter_track.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {TrackContext} from '../public/track';
-import {BaseCounterTrack, CounterOptions} from './base_counter_track';
-import {
- CounterColumns,
- SqlDataSource,
-} from '../public/lib/debug_tracks/debug_tracks';
-import {uuidv4Sql} from '../base/uuid';
-import {createPerfettoTable} from '../trace_processor/sql_utils';
-import {Trace} from '../public/trace';
-
-export type SimpleCounterTrackConfig = {
- data: SqlDataSource;
- columns: CounterColumns;
- options?: Partial<CounterOptions>;
-};
-
-export class SimpleCounterTrack extends BaseCounterTrack {
- private config: SimpleCounterTrackConfig;
- private sqlTableName: string;
-
- constructor(
- trace: Trace,
- ctx: TrackContext,
- config: SimpleCounterTrackConfig,
- ) {
- super({
- trace,
- uri: ctx.trackUri,
- options: config.options,
- });
- this.config = config;
- this.sqlTableName = `__simple_counter_${uuidv4Sql()}`;
- }
-
- async onInit() {
- return await createPerfettoTable(
- this.engine,
- this.sqlTableName,
- `
- with data as (
- ${this.config.data.sqlSource}
- )
- select
- ${this.config.columns.ts} as ts,
- ${this.config.columns.value} as value
- from data
- order by ts
- `,
- );
- }
-
- getSqlSource(): string {
- return `select * from ${this.sqlTableName}`;
- }
-}
diff --git a/ui/src/frontend/simple_slice_track.ts b/ui/src/frontend/simple_slice_track.ts
deleted file mode 100644
index b9a08de..0000000
--- a/ui/src/frontend/simple_slice_track.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {TrackContext} from '../public/track';
-import {
- CustomSqlTableDefConfig,
- CustomSqlTableSliceTrack,
-} from './tracks/custom_sql_table_slice_track';
-import {
- SliceColumns,
- SqlDataSource,
-} from '../public/lib/debug_tracks/debug_tracks';
-import {uuidv4Sql} from '../base/uuid';
-import {
- ARG_PREFIX,
- DebugSliceDetailsPanel,
-} from '../public/lib/debug_tracks/details_tab';
-import {createPerfettoTable} from '../trace_processor/sql_utils';
-import {Trace} from '../public/trace';
-import {TrackEventSelection} from '../public/selection';
-
-export interface SimpleSliceTrackConfig {
- data: SqlDataSource;
- columns: SliceColumns;
- argColumns: string[];
-}
-
-export class SimpleSliceTrack extends CustomSqlTableSliceTrack {
- private config: SimpleSliceTrackConfig;
- public readonly sqlTableName: string;
-
- constructor(trace: Trace, ctx: TrackContext, config: SimpleSliceTrackConfig) {
- super({
- trace,
- uri: ctx.trackUri,
- });
-
- this.config = config;
- this.sqlTableName = `__simple_slice_${uuidv4Sql(ctx.trackUri)}`;
- }
-
- async getSqlDataSource(): Promise<CustomSqlTableDefConfig> {
- const table = await createPerfettoTable(
- this.engine,
- this.sqlTableName,
- this.createTableQuery(
- this.config.data,
- this.config.columns,
- this.config.argColumns,
- ),
- );
- return {
- sqlTableName: this.sqlTableName,
- disposable: table,
- };
- }
-
- private createTableQuery(
- data: SqlDataSource,
- sliceColumns: SliceColumns,
- argColumns: string[],
- ): string {
- // If the view has clashing names (e.g. "name" coming from joining two
- // different tables, we will see names like "name_1", "name_2", but they
- // won't be addressable from the SQL. So we explicitly name them through a
- // list of columns passed to CTE.
- const dataColumns =
- data.columns !== undefined ? `(${data.columns.join(', ')})` : '';
-
- // TODO(altimin): Support removing this table when the track is closed.
- const dur = sliceColumns.dur === '0' ? 0 : sliceColumns.dur;
- return `
- with data${dataColumns} as (
- ${data.sqlSource}
- ),
- prepared_data as (
- select
- ${sliceColumns.ts} as ts,
- ifnull(cast(${dur} as int), -1) as dur,
- printf('%s', ${sliceColumns.name}) as name
- ${argColumns.length > 0 ? ',' : ''}
- ${argColumns.map((c) => `${c} as ${ARG_PREFIX}${c}`).join(',\n')}
- from data
- )
- select
- row_number() over (order by ts) as id,
- *
- from prepared_data
- order by ts
- `;
- }
-
- override detailsPanel({eventId}: TrackEventSelection) {
- return new DebugSliceDetailsPanel(this.trace, this.sqlTableName, eventId);
- }
-}
diff --git a/ui/src/frontend/sql_table_tab.ts b/ui/src/frontend/sql_table_tab.ts
index 9ab7f84..b803148 100644
--- a/ui/src/frontend/sql_table_tab.ts
+++ b/ui/src/frontend/sql_table_tab.ts
@@ -19,7 +19,7 @@
import {Button} from '../widgets/button';
import {DetailsShell} from '../widgets/details_shell';
import {Popup, PopupPosition} from '../widgets/popup';
-import {AddDebugTrackMenu} from '../public/lib/debug_tracks/add_debug_track_menu';
+import {AddDebugTrackMenu} from '../public/lib/tracks/add_debug_track_menu';
import {Filter} from './widgets/sql/table/column';
import {SqlTableState} from './widgets/sql/table/state';
import {SqlTable} from './widgets/sql/table/table';
@@ -28,6 +28,12 @@
import {MenuItem, PopupMenu2} from '../widgets/menu';
import {addEphemeralTab} from '../common/add_ephemeral_tab';
import {Tab} from '../public/tab';
+import {addChartTab} from './widgets/charts/chart_tab';
+import {
+ ChartOption,
+ createChartConfigFromSqlTableState,
+} from './widgets/charts/chart';
+import {AddChartMenuItem} from './widgets/charts/add_chart_menu';
export interface AddSqlTableTabParams {
table: SqlTableDescription;
@@ -122,6 +128,16 @@
},
m(SqlTable, {
state: this.state,
+ addColumnMenuItems: (column, columnAlias) =>
+ m(AddChartMenuItem, {
+ chartConfig: createChartConfigFromSqlTableState(
+ column,
+ columnAlias,
+ this.state,
+ ),
+ chartOptions: [ChartOption.HISTOGRAM],
+ addChart: (chart) => addChartTab(chart),
+ }),
}),
);
}
diff --git a/ui/src/frontend/tab_panel.ts b/ui/src/frontend/tab_panel.ts
index 0662ef8..04aee7e 100644
--- a/ui/src/frontend/tab_panel.ts
+++ b/ui/src/frontend/tab_panel.ts
@@ -163,7 +163,7 @@
/* onDrag */ (_x, y) => {
const deltaYSinceDragStart = dragStartY - y;
this.resizableHeight = heightWhenDragStarted + deltaYSinceDragStart;
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
},
/* onDragStarted */ (_x, y) => {
this.resizableHeight = this.height;
diff --git a/ui/src/frontend/thread_slice_details_tab.ts b/ui/src/frontend/thread_slice_details_tab.ts
index 7f1f0a5..b549971 100644
--- a/ui/src/frontend/thread_slice_details_tab.ts
+++ b/ui/src/frontend/thread_slice_details_tab.ts
@@ -141,9 +141,9 @@
INCLUDE PERFETTO MODULE android.monitor_contention;`,
)
.then(() =>
- extensions.addDebugSliceTrack(
+ extensions.addDebugSliceTrack({
trace,
- {
+ data: {
sqlSource: `
WITH merged AS (
SELECT s.ts, s.dur, tx.aidl_name AS name, 0 AS depth
@@ -182,12 +182,10 @@
ORDER BY depth
) SELECT ts, dur, name FROM merged`,
},
- `Binder names (${getProcessNameFromSlice(
+ title: `Binder names (${getProcessNameFromSlice(
slice,
)}:${getThreadNameFromSlice(slice)})`,
- {ts: 'ts', dur: 'dur', name: 'name'},
- [],
- ),
+ }),
);
},
},
diff --git a/ui/src/frontend/thread_slice_track.ts b/ui/src/frontend/thread_slice_track.ts
index 6e47c8a..10a829f 100644
--- a/ui/src/frontend/thread_slice_track.ts
+++ b/ui/src/frontend/thread_slice_track.ts
@@ -16,13 +16,13 @@
import {clamp} from '../base/math_utils';
import {NAMED_ROW, NamedSliceTrack} from './named_slice_track';
import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from './slice_layout';
-import {NewTrackArgs} from './track';
import {LONG_NULL} from '../trace_processor/query_result';
import {Slice} from '../public/track';
import {TrackEventDetails} from '../public/selection';
import {ThreadSliceDetailsPanel} from './thread_slice_details_tab';
import {TraceImpl} from '../core/trace_impl';
import {assertIsInstance} from '../base/logging';
+import {Trace} from '../public/trace';
export const THREAD_SLICE_ROW = {
// Base columns (tsq, ts, dur, id, depth).
@@ -35,12 +35,13 @@
export class ThreadSliceTrack extends NamedSliceTrack<Slice, ThreadSliceRow> {
constructor(
- args: NewTrackArgs,
- private trackId: number,
+ trace: Trace,
+ uri: string,
+ private readonly trackId: number,
maxDepth: number,
- private tableName: string = 'slice',
+ private readonly tableName: string = 'slice',
) {
- super(args);
+ super(trace, uri);
this.sliceLayout = {
...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
depthGuess: maxDepth,
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index c055c01..da440d3 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -14,8 +14,6 @@
import m from 'mithril';
import {classNames} from '../base/classnames';
-import {raf} from '../core/raf_scheduler';
-import {globals} from './globals';
import {taskTracker} from './task_tracker';
import {Popup, PopupPosition} from '../widgets/popup';
import {assertFalse} from '../base/logging';
@@ -23,8 +21,6 @@
import {AppImpl} from '../core/app_impl';
import {TraceImpl, TraceImplAttrs} from '../core/trace_impl';
-export const DISMISSED_PANNING_HINT_KEY = 'dismissedPanningHint';
-
class Progress implements m.ClassComponent<TraceImplAttrs> {
view({attrs}: m.CVnode<TraceImplAttrs>): m.Children {
const engine = attrs.trace.engine;
@@ -37,41 +33,6 @@
}
}
-class HelpPanningNotification implements m.ClassComponent {
- view() {
- const dismissed = localStorage.getItem(DISMISSED_PANNING_HINT_KEY);
- // Do not show the help notification in embedded mode because local storage
- // does not persist for iFrames. The host is responsible for communicating
- // to users that they can press '?' for help.
- if (
- AppImpl.instance.embeddedMode ||
- dismissed === 'true' ||
- !globals.showPanningHint
- ) {
- return;
- }
- return m(
- '.helpful-hint',
- m(
- '.hint-text',
- 'Are you trying to pan? Use the WASD keys or hold shift to click ' +
- "and drag. Press '?' for more help.",
- ),
- m(
- 'button.hint-dismiss-button',
- {
- onclick: () => {
- globals.showPanningHint = false;
- localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
- raf.scheduleFullRedraw();
- },
- },
- 'Dismiss',
- ),
- );
- }
-}
-
class TraceErrorIcon implements m.ClassComponent<TraceImplAttrs> {
private tracePopupErrorDismissed = false;
@@ -128,14 +89,10 @@
return m(
'.topbar',
{
- class:
- AppImpl.instance.sidebar.sidebarVisibility === 'VISIBLE'
- ? ''
- : 'hide-sidebar',
+ class: AppImpl.instance.sidebar.visible ? '' : 'hide-sidebar',
},
omnibox,
attrs.trace && m(Progress, {trace: attrs.trace}),
- m(HelpPanningNotification),
attrs.trace && m(TraceErrorIcon, {trace: attrs.trace}),
);
}
diff --git a/ui/src/frontend/trace_converter.ts b/ui/src/frontend/trace_converter.ts
index f220120..7960cc5 100644
--- a/ui/src/frontend/trace_converter.ts
+++ b/ui/src/frontend/trace_converter.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {assetSrc} from '../base/assets';
import {download} from '../base/clipboard';
import {defer} from '../base/deferred';
import {ErrorDetails} from '../base/logging';
@@ -19,7 +20,6 @@
import {time} from '../base/time';
import {AppImpl} from '../core/app_impl';
import {maybeShowErrorDialog} from './error_dialog';
-import {globals} from './globals';
type Args =
| UpdateStatusArgs
@@ -83,7 +83,7 @@
}
}
- const worker = new Worker(globals.root + 'traceconv_bundle.js');
+ const worker = new Worker(assetSrc('traceconv_bundle.js'));
worker.onmessage = handleOnMessage;
worker.postMessage(msg);
return promise;
diff --git a/ui/src/frontend/trace_share_utils.ts b/ui/src/frontend/trace_share_utils.ts
index 418fe51..cf8f185 100644
--- a/ui/src/frontend/trace_share_utils.ts
+++ b/ui/src/frontend/trace_share_utils.ts
@@ -16,11 +16,11 @@
import {TraceUrlSource} from '../core/trace_source';
import {createPermalink} from './permalink';
import {showModal} from '../widgets/modal';
-import {onClickCopy} from './clipboard';
import {globals} from './globals';
import {AppImpl} from '../core/app_impl';
import {Trace} from '../public/trace';
import {TraceImpl} from '../core/trace_impl';
+import {CopyableLink} from '../widgets/copyable_link';
export function isShareable(trace: Trace) {
return globals.isInternalUser && trace.traceInfo.downloadable;
@@ -43,7 +43,7 @@
if (traceUrl) {
msg.push(m('p', 'By using the URL below you can open this trace again.'));
msg.push(m('p', 'Clicking will copy the URL into the clipboard.'));
- msg.push(createTraceLink(traceUrl, traceUrl));
+ msg.push(m(CopyableLink, {url: traceUrl}));
}
showModal({
@@ -61,19 +61,6 @@
);
if (result) {
AppImpl.instance.analytics.logEvent('Trace Actions', 'Create permalink');
- return await createPermalink({mode: 'APP_STATE'});
+ return await createPermalink();
}
}
-
-export function createTraceLink(title: string, url: string) {
- if (url === '') {
- return m('a.trace-file-name', title);
- }
- const linkProps = {
- href: url,
- title: 'Click to copy the URL',
- target: '_blank',
- onclick: onClickCopy(url),
- };
- return m('a.trace-file-name', linkProps, title);
-}
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
deleted file mode 100644
index f10e21e..0000000
--- a/ui/src/frontend/track.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Trace} from '../public/trace';
-
-export interface NewTrackArgs {
- uri: string;
- trace: Trace;
-}
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 3f8cefc..fcae30c 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -94,6 +94,7 @@
SHOW_TRACK_DETAILS_BUTTON.get() &&
renderTrackDetailsButton(node, trackRenderer?.desc),
trackRenderer?.track.getTrackShellButtons?.(),
+ node.removable && renderCloseButton(node),
// Can't pin groups.. yet!
!node.hasChildren && renderPinButton(node),
this.renderAreaSelectionCheckbox(node),
@@ -132,15 +133,15 @@
...pos,
timescale,
});
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
onTrackContentMouseOut: () => {
trackRenderer?.track.onMouseOut?.();
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
onTrackContentClick: (pos, bounds) => {
const timescale = this.getTimescaleForBounds(bounds);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
return (
trackRenderer?.track.onMouseClick?.({
...pos,
@@ -383,6 +384,18 @@
);
}
+function renderCloseButton(node: TrackNode) {
+ return m(Button, {
+ onclick: (e) => {
+ node.remove();
+ e.stopPropagation();
+ },
+ icon: Icons.Close,
+ title: 'Close track',
+ compact: true,
+ });
+}
+
function renderPinButton(node: TrackNode): m.Children {
const isPinned = node.isPinned;
return m(Button, {
diff --git a/ui/src/frontend/tracks/custom_sql_table_slice_track.ts b/ui/src/frontend/tracks/custom_sql_table_slice_track.ts
index 7d3caa3..c157267 100644
--- a/ui/src/frontend/tracks/custom_sql_table_slice_track.ts
+++ b/ui/src/frontend/tracks/custom_sql_table_slice_track.ts
@@ -14,11 +14,11 @@
import {generateSqlWithInternalLayout} from '../../trace_processor/sql_utils/layout';
import {NAMED_ROW, NamedRow, NamedSliceTrack} from '../named_slice_track';
-import {NewTrackArgs} from '../track';
import {createView} from '../../trace_processor/sql_utils';
import {Slice} from '../../public/track';
import {AsyncDisposableStack} from '../../base/disposable_stack';
import {sqlNameSafe} from '../../base/string_utils';
+import {Trace} from '../../public/trace';
export interface CustomSqlImportConfig {
modules: string[];
@@ -39,9 +39,9 @@
> {
protected readonly tableName;
- constructor(args: NewTrackArgs) {
- super(args);
- this.tableName = `customsqltableslicetrack_${sqlNameSafe(args.uri)}`;
+ constructor(trace: Trace, uri: string) {
+ super(trace, uri);
+ this.tableName = `customsqltableslicetrack_${sqlNameSafe(uri)}`;
}
getRowSpec(): NamedRow {
diff --git a/ui/src/frontend/ui_main.ts b/ui/src/frontend/ui_main.ts
index 7cd3f95..b8d8dda 100644
--- a/ui/src/frontend/ui_main.ts
+++ b/ui/src/frontend/ui_main.ts
@@ -18,7 +18,6 @@
import {FuzzyFinder} from '../base/fuzzy';
import {assertExists, assertUnreachable} from '../base/logging';
import {undoCommonChatAppReplacements} from '../base/string_utils';
-import {Actions} from '../common/actions';
import {
DurationPrecision,
setDurationPrecision,
@@ -31,7 +30,6 @@
import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
import {maybeRenderFullscreenModalDialog, showModal} from '../widgets/modal';
import {CookieConsent} from './cookie_consent';
-import {globals} from './globals';
import {toggleHelp} from './help_modal';
import {Omnibox, OmniboxOption} from './omnibox';
import {addQueryResultsTab} from '../public/lib/query_table/query_result_tab';
@@ -54,9 +52,9 @@
// This wrapper creates a new instance of UiMainPerTrace for each new trace
// loaded (including the case of no trace at the beginning).
export class UiMain implements m.ClassComponent {
- view({children}: m.CVnode) {
+ view() {
const currentTraceId = AppImpl.instance.trace?.engine.engineId ?? '';
- return [m(UiMainPerTrace, {key: currentTraceId}, children)];
+ return [m(UiMainPerTrace, {key: currentTraceId})];
}
}
@@ -173,9 +171,8 @@
{
id: 'perfetto.TogglePerformanceMetrics',
name: 'Toggle performance metrics',
- callback: () => {
- globals.dispatch(Actions.togglePerfDebug({}));
- },
+ callback: () =>
+ (app.perfDebugging.enabled = !app.perfDebugging.enabled),
},
{
id: 'perfetto.ShareTrace',
@@ -632,12 +629,13 @@
this.maybeFocusOmnibar();
}
- view({children}: m.Vnode): m.Children {
+ view(): m.Children {
+ const app = AppImpl.instance;
const hotkeys: HotkeyConfig[] = [];
- for (const {id, defaultHotkey} of AppImpl.instance.commands.commands) {
+ for (const {id, defaultHotkey} of app.commands.commands) {
if (defaultHotkey) {
hotkeys.push({
- callback: () => AppImpl.instance.commands.runCommand(id),
+ callback: () => app.commands.runCommand(id),
hotkey: defaultHotkey,
});
}
@@ -653,10 +651,10 @@
omnibox: this.renderOmnibox(),
trace: this.trace,
}),
- children,
+ app.pages.renderPageForCurrentRoute(app.trace),
m(CookieConsent),
maybeRenderFullscreenModalDialog(),
- globals.state.perfDebug && m('.perf-stats'),
+ app.perfDebugging.renderPerfStats(),
),
);
}
diff --git a/ui/src/frontend/value.ts b/ui/src/frontend/value.ts
index 8be8994..a57f2ea 100644
--- a/ui/src/frontend/value.ts
+++ b/ui/src/frontend/value.ts
@@ -14,7 +14,8 @@
import m from 'mithril';
import {Tree, TreeNode} from '../widgets/tree';
-import {PopupMenuButton, PopupMenuItem} from './popup_menu';
+import {PopupMenu2} from '../widgets/menu';
+import {Button} from '../widgets/button';
// This file implements a component for rendering JSON-like values (with
// customisation options like context menu and action buttons).
@@ -109,7 +110,7 @@
// Customisation parameters which apply to any Value (e.g. context menu).
interface ValueParams {
- contextMenu?: PopupMenuItem[];
+ contextMenu?: m.Child[];
}
// Customisation parameters which apply for a primitive value (e.g. showing
@@ -137,10 +138,15 @@
const left = [
name,
value.contextMenu
- ? m(PopupMenuButton, {
- icon: 'arrow_drop_down',
- items: value.contextMenu,
- })
+ ? m(
+ PopupMenu2,
+ {
+ trigger: m(Button, {
+ icon: 'arrow_drop_down',
+ }),
+ },
+ value.contextMenu,
+ )
: null,
];
if (isArray(value)) {
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 372cbb5..9509c1d 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -36,16 +36,15 @@
PanelOrGroup,
RenderedPanelInfo,
} from './panel_container';
-import {publishShowPanningHint} from './publish';
import {TabPanel} from './tab_panel';
import {TickmarkPanel} from './tickmark_panel';
import {TimeAxisPanel} from './time_axis_panel';
import {TimeSelectionPanel} from './time_selection_panel';
-import {DISMISSED_PANNING_HINT_KEY} from './topbar';
import {TrackPanel} from './track_panel';
import {drawVerticalLineAtTime} from './vertical_line_helper';
-import {PageWithTraceAttrs} from './pages';
import {TraceImpl} from '../core/trace_impl';
+import {PageWithTraceImplAttrs} from '../core/page_manager';
+import {AppImpl} from '../core/app_impl';
const OVERVIEW_PANEL_FLAG = featureFlags.register({
id: 'overviewVisible',
@@ -92,7 +91,7 @@
* Top-most level component for the viewer page. Holds tracks, brush timeline,
* panels, and everything else that's part of the main trace viewer page.
*/
-export class ViewerPage implements m.ClassComponent<PageWithTraceAttrs> {
+export class ViewerPage implements m.ClassComponent<PageWithTraceImplAttrs> {
private zoomContent?: PanAndZoomHandler;
// Used to prevent global deselection if a pan/drag select occurred.
private keepCurrentSelection = false;
@@ -104,10 +103,11 @@
private tickmarkPanel: TickmarkPanel;
private timelineWidthPx?: number;
private selectedContainer?: SelectedContainer;
+ private showPanningHint = false;
private readonly PAN_ZOOM_CONTENT_REF = 'pan-and-zoom-content';
- constructor(vnode: m.CVnode<PageWithTraceAttrs>) {
+ constructor(vnode: m.CVnode<PageWithTraceImplAttrs>) {
this.notesPanel = new NotesPanel(vnode.attrs.trace);
this.timeAxisPanel = new TimeAxisPanel(vnode.attrs.trace);
this.timeSelectionPanel = new TimeSelectionPanel(vnode.attrs.trace);
@@ -117,7 +117,7 @@
this.timeSelectionPanel = new TimeSelectionPanel(vnode.attrs.trace);
}
- oncreate({dom, attrs}: m.CVnodeDOM<PageWithTraceAttrs>) {
+ oncreate({dom, attrs}: m.CVnodeDOM<PageWithTraceImplAttrs>) {
const panZoomElRaw = findRef(dom, this.PAN_ZOOM_CONTENT_REF);
const panZoomEl = toHTMLElement(assertExists(panZoomElRaw));
@@ -136,10 +136,6 @@
});
const tDelta = timescale.pxToDuration(pannedPx);
timeline.panVisibleWindow(tDelta);
-
- // If the user has panned they no longer need the hint.
- localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
- raf.scheduleRedraw();
},
onZoomed: (zoomedPositionPx: number, zoomRatio: number) => {
const timeline = attrs.trace.timeline;
@@ -149,7 +145,7 @@
const rect = dom.getBoundingClientRect();
const centerPoint = zoomPx / (rect.width - TRACK_SHELL_WIDTH);
timeline.zoomVisibleWindow(1 - zoomRatio, centerPoint);
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
editSelection: (currentPx: number) => {
if (this.timelineWidthPx === undefined) return false;
@@ -259,9 +255,9 @@
dragEndAbsY: -stackTop + boundedCurrentY,
};
}
- publishShowPanningHint();
+ this.showPanningHint = true;
}
- raf.scheduleRedraw();
+ raf.scheduleCanvasRedraw();
},
endSelection: (edit: boolean) => {
this.selectedContainer = undefined;
@@ -290,7 +286,7 @@
if (this.zoomContent) this.zoomContent[Symbol.dispose]();
}
- view({attrs}: m.CVnode<PageWithTraceAttrs>) {
+ view({attrs}: m.CVnode<PageWithTraceImplAttrs>) {
const scrollingPanels = renderToplevelPanels(attrs.trace);
const result = m(
@@ -371,6 +367,7 @@
m(TabPanel, {
trace: attrs.trace,
}),
+ this.showPanningHint && m(HelpPanningNotification),
);
attrs.trace.tracks.flushOldTracks();
@@ -610,3 +607,36 @@
}
}
}
+
+class HelpPanningNotification implements m.ClassComponent {
+ private readonly PANNING_HINT_KEY = 'dismissedPanningHint';
+ private dismissed = localStorage.getItem(this.PANNING_HINT_KEY) === 'true';
+
+ view() {
+ // Do not show the help notification in embedded mode because local storage
+ // does not persist for iFrames. The host is responsible for communicating
+ // to users that they can press '?' for help.
+ if (AppImpl.instance.embeddedMode || this.dismissed) {
+ return;
+ }
+ return m(
+ '.helpful-hint',
+ m(
+ '.hint-text',
+ 'Are you trying to pan? Use the WASD keys or hold shift to click ' +
+ "and drag. Press '?' for more help.",
+ ),
+ m(
+ 'button.hint-dismiss-button',
+ {
+ onclick: () => {
+ this.dismissed = true;
+ localStorage.setItem(this.PANNING_HINT_KEY, 'true');
+ raf.scheduleFullRedraw();
+ },
+ },
+ 'Dismiss',
+ ),
+ );
+ }
+}
diff --git a/ui/src/frontend/visualized_args_track.ts b/ui/src/frontend/visualized_args_track.ts
index fdea5b3..4b52b9d 100644
--- a/ui/src/frontend/visualized_args_track.ts
+++ b/ui/src/frontend/visualized_args_track.ts
@@ -46,7 +46,7 @@
const escapedArgName = argName.replace(/[^a-zA-Z]/g, '_');
const viewName = `__arg_visualisation_helper_${escapedArgName}_${uuid}_slice`;
- super({trace, uri}, trackId, maxDepth, viewName);
+ super(trace, uri, trackId, maxDepth, viewName);
this.viewName = viewName;
this.argName = argName;
this.onClose = onClose;
diff --git a/ui/src/frontend/widgets/charts/add_chart_menu.ts b/ui/src/frontend/widgets/charts/add_chart_menu.ts
new file mode 100644
index 0000000..cb3bb17
--- /dev/null
+++ b/ui/src/frontend/widgets/charts/add_chart_menu.ts
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {MenuItem} from '../../../widgets/menu';
+import {Icons} from '../../../base/semantic_icons';
+import {Chart, ChartConfig, ChartOption, toTitleCase} from './chart';
+
+interface AddChartMenuItemAttrs {
+ readonly chartConfig: ChartConfig;
+ readonly chartOptions: Array<ChartOption>;
+ readonly addChart: (chart: Chart) => void;
+}
+
+export class AddChartMenuItem
+ implements m.ClassComponent<AddChartMenuItemAttrs>
+{
+ private renderAddChartOptions(
+ config: ChartConfig,
+ chartOptions: Array<ChartOption>,
+ addChart: (chart: Chart) => void,
+ ): m.Children {
+ return chartOptions.map((option) => {
+ return m(MenuItem, {
+ label: toTitleCase(option),
+ onclick: () => addChart({option, config}),
+ });
+ });
+ }
+
+ view({attrs}: m.Vnode<AddChartMenuItemAttrs>) {
+ return m(
+ MenuItem,
+ {label: 'Add chart', icon: Icons.Chart},
+ this.renderAddChartOptions(
+ attrs.chartConfig,
+ attrs.chartOptions,
+ attrs.addChart,
+ ),
+ );
+ }
+}
diff --git a/ui/src/frontend/widgets/charts/chart.ts b/ui/src/frontend/widgets/charts/chart.ts
index 1a78c41..124a52e 100644
--- a/ui/src/frontend/widgets/charts/chart.ts
+++ b/ui/src/frontend/widgets/charts/chart.ts
@@ -11,9 +11,13 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+import m from 'mithril';
import {Row} from '../../../trace_processor/query_result';
import {Engine} from '../../../trace_processor/engine';
-import {TableColumn, TableColumnSet} from '../sql/table/column';
+import {Filter, TableColumn, TableColumnSet} from '../sql/table/column';
+import {Histogram} from './histogram/histogram';
+import {SqlTableState} from '../sql/table/state';
+import {columnTitle} from '../sql/table/table';
export interface VegaLiteChartSpec {
$schema: string;
@@ -41,6 +45,26 @@
};
}
+// Holds the various chart types and human readable string
+export enum ChartOption {
+ HISTOGRAM = 'histogram',
+}
+
+export interface ChartConfig {
+ readonly engine: Engine;
+ readonly columnTitle: string; // Human readable column name (ex: Duration)
+ readonly sqlColumn: string[]; // SQL column name (ex: dur)
+ readonly filters?: Filter[]; // Filters applied to SQL table
+ readonly tableDisplay?: string; // Human readable table name (ex: slices)
+ readonly query: string; // SQL query for the underlying data
+ readonly aggregationType?: 'nominal' | 'quantitative'; // Aggregation type.
+}
+
+export interface Chart {
+ readonly option: ChartOption;
+ readonly config: ChartConfig;
+}
+
export interface ChartData {
readonly rows: Row[];
readonly error?: string;
@@ -65,3 +89,32 @@
return words.join(' ');
}
+
+// renderChartComponent will take a chart option and config and map
+// to the corresponding chart class component.
+export function renderChartComponent(chart: Chart) {
+ switch (chart.option) {
+ case ChartOption.HISTOGRAM:
+ return m(Histogram, chart.config);
+ default:
+ return;
+ }
+}
+
+export function createChartConfigFromSqlTableState(
+ column: TableColumn,
+ columnAlias: string,
+ sqlTableState: SqlTableState,
+) {
+ return {
+ engine: sqlTableState.trace.engine,
+ columnTitle: columnTitle(column),
+ sqlColumn: [columnAlias],
+ filters: sqlTableState?.getFilters(),
+ tableDisplay: sqlTableState.config.displayName ?? sqlTableState.config.name,
+ query: sqlTableState.getSqlQuery(
+ Object.fromEntries([[columnAlias, column.primaryColumn()]]),
+ ),
+ aggregationType: column.aggregation?.().dataType,
+ };
+}
diff --git a/ui/src/frontend/widgets/charts/chart_tab.ts b/ui/src/frontend/widgets/charts/chart_tab.ts
new file mode 100644
index 0000000..6d802e6
--- /dev/null
+++ b/ui/src/frontend/widgets/charts/chart_tab.ts
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {DetailsShell} from '../../../widgets/details_shell';
+import {filterTitle} from '../sql/table/column';
+import {addEphemeralTab} from '../../../common/add_ephemeral_tab';
+import {Tab} from '../../../public/tab';
+import {Chart, renderChartComponent, toTitleCase} from './chart';
+
+export function addChartTab(chart: Chart): void {
+ addEphemeralTab('histogramTab', new ChartTab(chart));
+}
+
+export class ChartTab implements Tab {
+ constructor(private readonly chart: Chart) {}
+
+ render() {
+ return m(
+ DetailsShell,
+ {
+ title: this.getTitle(),
+ description: this.getDescription(),
+ },
+ renderChartComponent(this.chart),
+ );
+ }
+
+ getTitle(): string {
+ return `${toTitleCase(this.chart.config.columnTitle)} Histogram`;
+ }
+
+ private getDescription(): string {
+ let desc = `Count distribution for ${this.chart.config.tableDisplay ?? ''} table`;
+
+ if (this.chart.config.filters && this.chart.config.filters.length > 0) {
+ desc += ' where ';
+ desc += this.chart.config.filters.map((f) => filterTitle(f)).join(', ');
+ }
+
+ return desc;
+ }
+}
diff --git a/ui/src/frontend/widgets/charts/histogram/histogram.ts b/ui/src/frontend/widgets/charts/histogram/histogram.ts
index e4dd0d0..38f4a65 100644
--- a/ui/src/frontend/widgets/charts/histogram/histogram.ts
+++ b/ui/src/frontend/widgets/charts/histogram/histogram.ts
@@ -15,25 +15,14 @@
import m from 'mithril';
import {stringifyJsonWithBigints} from '../../../../base/json_utils';
import {VegaView} from '../../../../widgets/vega_view';
-import {Filter} from '../../../widgets/sql/table/column';
import {HistogramState} from './state';
import {Spinner} from '../../../../widgets/spinner';
-import {Engine} from '../../../../trace_processor/engine';
+import {ChartConfig} from '../chart';
-export interface HistogramConfig {
- engine: Engine;
- columnTitle: string; // Human readable column name (ex: Duration)
- sqlColumn: string[]; // SQL column name (ex: dur)
- filters?: Filter[]; // Filters applied to SQL table
- tableDisplay?: string; // Human readable table name (ex: slices)
- query: string; // SQL query for the underlying data
- aggregationType?: 'nominal' | 'quantitative'; // Aggregation type.
-}
-
-export class Histogram implements m.ClassComponent<HistogramConfig> {
+export class Histogram implements m.ClassComponent<ChartConfig> {
private readonly state: HistogramState;
- constructor({attrs}: m.Vnode<HistogramConfig>) {
+ constructor({attrs}: m.Vnode<ChartConfig>) {
this.state = new HistogramState(
attrs.engine,
attrs.query,
diff --git a/ui/src/frontend/widgets/charts/histogram/tab.ts b/ui/src/frontend/widgets/charts/histogram/tab.ts
deleted file mode 100644
index 096245e..0000000
--- a/ui/src/frontend/widgets/charts/histogram/tab.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-import {DetailsShell} from '../../../../widgets/details_shell';
-import {filterTitle} from '../../../widgets/sql/table/column';
-import {addEphemeralTab} from '../../../../common/add_ephemeral_tab';
-import {Tab} from '../../../../public/tab';
-import {Histogram, HistogramConfig} from './histogram';
-import {toTitleCase} from '../chart';
-
-export function addHistogramTab(config: HistogramConfig): void {
- addEphemeralTab('histogramTab', new HistogramTab(config));
-}
-
-export class HistogramTab implements Tab {
- constructor(private readonly config: HistogramConfig) {}
-
- render() {
- return m(
- DetailsShell,
- {
- title: this.getTitle(),
- description: this.getDescription(),
- },
- m(Histogram, this.config),
- );
- }
-
- getTitle(): string {
- return `${toTitleCase(this.config.columnTitle)} Histogram`;
- }
-
- private getDescription(): string {
- let desc = `Count distribution for ${this.config.tableDisplay ?? ''} table`;
-
- if (this.config.filters && this.config.filters.length > 0) {
- desc += ' where ';
- desc += this.config.filters.map((f) => filterTitle(f)).join(', ');
- }
-
- return desc;
- }
-}
diff --git a/ui/src/frontend/widgets/sql/table/state.ts b/ui/src/frontend/widgets/sql/table/state.ts
index 1f513b8..4540214 100644
--- a/ui/src/frontend/widgets/sql/table/state.ts
+++ b/ui/src/frontend/widgets/sql/table/state.ts
@@ -331,8 +331,15 @@
this.rowCount = undefined;
}
- // Run a delayed UI update to avoid flickering if the query returns quickly.
- raf.scheduleDelayedFullRedraw();
+ // Schedule a full redraw to happen after a short delay (50 ms).
+ // This is done to prevent flickering / visual noise and allow the UI to fetch
+ // the initial data from the Trace Processor.
+ // There is a chance that someone else schedules a full redraw in the
+ // meantime, forcing the flicker, but in practice it works quite well and
+ // avoids a lot of complexity for the callers.
+ // 50ms is half of the responsiveness threshold (100ms):
+ // https://web.dev/rail/#response-process-events-in-under-50ms
+ setTimeout(() => raf.scheduleFullRedraw(), 50);
if (!filtersMatch) {
this.rowCount = await this.loadRowCount();
diff --git a/ui/src/frontend/widgets/sql/table/table.ts b/ui/src/frontend/widgets/sql/table/table.ts
index acd5ffe..761a32c 100644
--- a/ui/src/frontend/widgets/sql/table/table.ts
+++ b/ui/src/frontend/widgets/sql/table/table.ts
@@ -40,14 +40,20 @@
import {SqlTableState} from './state';
import {SqlTableDescription} from './table_description';
import {Intent} from '../../../../widgets/common';
-import {addHistogramTab} from '../../charts/histogram/tab';
import {Form} from '../../../../widgets/form';
import {TextInput} from '../../../../widgets/text_input';
export interface SqlTableConfig {
readonly state: SqlTableState;
+ // For additional menu items to add to the column header menus
+ readonly addColumnMenuItems?: (
+ column: TableColumn,
+ columnAlias: string,
+ ) => m.Children;
}
+type AdditionalColumnMenuItems = Record<string, m.Children>;
+
function renderCell(
column: TableColumn,
row: Row,
@@ -65,7 +71,7 @@
return column.renderCell(sqlValue, getTableManager(state), additionalValues);
}
-function columnTitle(column: TableColumn): string {
+export function columnTitle(column: TableColumn): string {
if (column.getTitle !== undefined) {
const title = column.getTitle();
if (title !== undefined) return title;
@@ -274,7 +280,11 @@
);
}
- renderColumnHeader(column: TableColumn, index: number) {
+ renderColumnHeader(
+ column: TableColumn,
+ index: number,
+ additionalColumnHeaderMenuItems?: m.Children,
+ ) {
const sorted = this.state.isSortedBy(column);
const icon =
sorted === 'ASC'
@@ -327,27 +337,7 @@
{label: 'Add filter', icon: Icons.Filter},
this.renderColumnFilterOptions(column),
),
- m(MenuItem, {
- label: 'Create histogram',
- icon: Icons.Chart,
- onclick: () => {
- const columnAlias =
- this.state.getCurrentRequest().columns[
- sqlColumnId(column.primaryColumn())
- ];
- addHistogramTab({
- engine: this.state.trace.engine,
- sqlColumn: [columnAlias],
- columnTitle: columnTitle(column),
- filters: this.state.getFilters(),
- tableDisplay: this.table.displayName ?? this.table.name,
- query: this.state.getSqlQuery(
- Object.fromEntries([[columnAlias, column.primaryColumn()]]),
- ),
- aggregationType: column.aggregation?.().dataType,
- });
- },
- }),
+ additionalColumnHeaderMenuItems,
// Menu items before divider apply to selected column
m(MenuDivider),
// Menu items after divider apply to entire table
@@ -355,13 +345,49 @@
);
}
- view() {
+ getAdditionalColumnMenuItems(
+ addColumnMenuItems?: (
+ column: TableColumn,
+ columnAlias: string,
+ ) => m.Children,
+ ) {
+ if (addColumnMenuItems === undefined) return;
+
+ const additionalColumnMenuItems: AdditionalColumnMenuItems = {};
+ this.state.getSelectedColumns().forEach((column) => {
+ const columnAlias =
+ this.state.getCurrentRequest().columns[
+ sqlColumnId(column.primaryColumn())
+ ];
+
+ additionalColumnMenuItems[columnAlias] = addColumnMenuItems(
+ column,
+ columnAlias,
+ );
+ });
+
+ return additionalColumnMenuItems;
+ }
+
+ view({attrs}: m.Vnode<SqlTableConfig>) {
const rows = this.state.getDisplayedRows();
+ const additionalColumnMenuItems = this.getAdditionalColumnMenuItems(
+ attrs.addColumnMenuItems,
+ );
const columns = this.state.getSelectedColumns();
const columnDescriptors = columns.map((column, i) => {
return {
- title: this.renderColumnHeader(column, i),
+ title: this.renderColumnHeader(
+ column,
+ i,
+ additionalColumnMenuItems &&
+ additionalColumnMenuItems[
+ this.state.getCurrentRequest().columns[
+ sqlColumnId(column.primaryColumn())
+ ]
+ ],
+ ),
render: (row: Row) => renderCell(column, row, this.state),
};
});
diff --git a/ui/src/frontend/widgets/thread_state.ts b/ui/src/frontend/widgets/thread_state.ts
index d6c0f7b..b7cfd69 100644
--- a/ui/src/frontend/widgets/thread_state.ts
+++ b/ui/src/frontend/widgets/thread_state.ts
@@ -58,6 +58,6 @@
if (state.thread === undefined) return null;
return m(ThreadStateRef, {
- id: state.threadStateSqlId,
+ id: state.id,
});
}
diff --git a/ui/src/plugins/com.android.GpuWorkPeriod/index.ts b/ui/src/plugins/com.android.GpuWorkPeriod/index.ts
index daa6f39..1bae632 100644
--- a/ui/src/plugins/com.android.GpuWorkPeriod/index.ts
+++ b/ui/src/plugins/com.android.GpuWorkPeriod/index.ts
@@ -17,10 +17,7 @@
import {Trace} from '../../public/trace';
import {TrackNode} from '../../public/workspace';
import {SLICE_TRACK_KIND} from '../../public/track_kinds';
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
export default class implements PerfettoPlugin {
static readonly id = 'com.android.GpuWorkPeriod';
@@ -59,19 +56,17 @@
for (; it.valid(); it.next()) {
const {trackId, gpuId, uid, packageName} = it;
const uri = `/gpu_work_period_${gpuId}_${uid}`;
- const config: SimpleSliceTrackConfig = {
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource: `
select ts, dur, name
from slice
where track_id = ${trackId}
`,
- columns: ['ts', 'dur', 'name'],
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
- };
- const track = new SimpleSliceTrack(ctx, {trackUri: uri}, config);
+ });
ctx.tracks.registerTrack({
uri,
title: packageName,
diff --git a/ui/src/plugins/com.android.InputEvents/index.ts b/ui/src/plugins/com.android.InputEvents/index.ts
index 86a5eac..b8e7184 100644
--- a/ui/src/plugins/com.android.InputEvents/index.ts
+++ b/ui/src/plugins/com.android.InputEvents/index.ts
@@ -15,10 +15,7 @@
import {LONG} from '../../trace_processor/query_result';
import {PerfettoPlugin} from '../../public/plugin';
import {Trace} from '../../public/trace';
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {TrackNode} from '../../public/workspace';
import {getOrCreateUserInteractionGroup} from '../../public/standard_groups';
@@ -45,18 +42,16 @@
WHERE end_to_end_latency_dur IS NOT NULL
`;
- const config: SimpleSliceTrackConfig = {
- data: {
- sqlSource: SQL_SOURCE,
- columns: ['ts', 'dur', 'name'],
- },
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
- };
await ctx.engine.query('INCLUDE PERFETTO MODULE android.input;');
const uri = 'com.android.InputEvents#InputEventsTrack';
const title = 'Input Events';
- const track = new SimpleSliceTrack(ctx, {trackUri: uri}, config);
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
+ data: {
+ sqlSource: SQL_SOURCE,
+ },
+ });
ctx.tracks.registerTrack({
uri,
title: title,
diff --git a/ui/src/plugins/dev.perfetto.ExampleNestedTracks/index.ts b/ui/src/plugins/com.example.ExampleNestedTracks/index.ts
similarity index 85%
rename from ui/src/plugins/dev.perfetto.ExampleNestedTracks/index.ts
rename to ui/src/plugins/com.example.ExampleNestedTracks/index.ts
index 9108d21..be99948 100644
--- a/ui/src/plugins/dev.perfetto.ExampleNestedTracks/index.ts
+++ b/ui/src/plugins/com.example.ExampleNestedTracks/index.ts
@@ -14,14 +14,11 @@
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {TrackNode} from '../../public/workspace';
export default class implements PerfettoPlugin {
- static readonly id = 'dev.perfetto.ExampleNestedTracks';
+ static readonly id = 'com.example.ExampleNestedTracks';
async onTraceLoad(ctx: Trace): Promise<void> {
const traceStartTime = ctx.traceInfo.start;
const traceDur = ctx.traceInfo.end - ctx.traceInfo.start;
@@ -40,17 +37,16 @@
('Bar', ${traceStartTime}, ${traceDur / 2n}, 'bbb'),
('Baz', ${traceStartTime}, ${traceDur / 3n}, 'bbb');
`);
- const config: SimpleSliceTrackConfig = {
+
+ const title = 'Test Track';
+ const uri = `com.example.ExampleNestedTracks#TestTrack`;
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource: 'select * from example_events',
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
- };
-
- const title = 'Test Track';
- const uri = `/test_track`;
- const track = new SimpleSliceTrack(ctx, {trackUri: uri}, config);
+ });
ctx.tracks.registerTrack({
uri,
title,
diff --git a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts b/ui/src/plugins/com.example.ExampleSimpleCommand/index.ts
similarity index 89%
rename from ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts
rename to ui/src/plugins/com.example.ExampleSimpleCommand/index.ts
index 2df958f..cf473b3 100644
--- a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts
+++ b/ui/src/plugins/com.example.ExampleSimpleCommand/index.ts
@@ -17,10 +17,10 @@
// This is just an example plugin, used to prove that the plugin system works.
export default class implements PerfettoPlugin {
- static readonly id = 'dev.perfetto.ExampleSimpleCommand';
+ static readonly id = 'com.example.ExampleSimpleCommand';
static onActivate(ctx: App): void {
ctx.commands.registerCommand({
- id: 'dev.perfetto.ExampleSimpleCommand#LogHelloWorld',
+ id: 'com.example.ExampleSimpleCommand#LogHelloWorld',
name: 'Log "Hello, world!"',
callback: () => console.log('Hello, world!'),
});
diff --git a/ui/src/plugins/dev.perfetto.ExampleState/index.ts b/ui/src/plugins/com.example.ExampleState/index.ts
similarity index 94%
rename from ui/src/plugins/dev.perfetto.ExampleState/index.ts
rename to ui/src/plugins/com.example.ExampleState/index.ts
index f7410d4..9e1c296 100644
--- a/ui/src/plugins/dev.perfetto.ExampleState/index.ts
+++ b/ui/src/plugins/com.example.ExampleState/index.ts
@@ -25,7 +25,7 @@
// This example plugin shows using state that is persisted in the
// permalink.
export default class implements PerfettoPlugin {
- static readonly id = 'dev.perfetto.ExampleState';
+ static readonly id = 'com.example.ExampleState';
private store: Store<State> = createStore({counter: 0});
private migrate(initialState: unknown): State {
@@ -46,7 +46,7 @@
ctx.trash.use(this.store);
ctx.commands.registerCommand({
- id: 'dev.perfetto.ExampleState#ShowCounter',
+ id: 'com.example.ExampleState#ShowCounter',
name: 'Show ExampleState counter',
callback: () => {
const counter = this.store.state.counter;
diff --git a/ui/src/plugins/com.example.Skeleton/index.ts b/ui/src/plugins/com.example.Skeleton/index.ts
index d737940..5746950 100644
--- a/ui/src/plugins/com.example.Skeleton/index.ts
+++ b/ui/src/plugins/com.example.Skeleton/index.ts
@@ -16,19 +16,12 @@
import {App} from '../../public/app';
import {MetricVisualisation} from '../../public/plugin';
import {PerfettoPlugin} from '../../public/plugin';
-import {createStore, Store} from '../../base/store';
-
-interface State {
- foo: string;
-}
// SKELETON: Rename this class to match your plugin.
export default class implements PerfettoPlugin {
// SKELETON: Update pluginId to match the directory of the plugin.
static readonly id = 'com.example.Skeleton';
- private store: Store<State> = createStore({foo: 'foo'});
-
/**
* This hook is called when the plugin is activated manually, or when the UI
* starts up with this plugin enabled. This is typically before a trace has
@@ -38,8 +31,8 @@
* This hook should be used for adding commands that don't depend on the
* trace.
*/
- static onActivate(_: App): void {
- //
+ static onActivate(app: App): void {
+ console.log('SkeletonPlugin::onActivate()', app.pluginId);
}
/**
@@ -50,26 +43,23 @@
* It should not be used for finding tracks from other plugins as there is no
* guarantee those tracks will have been added yet.
*/
- async onTraceLoad(ctx: Trace): Promise<void> {
- this.store = ctx.mountStore((_: unknown): State => {
- return {foo: 'bar'};
- });
-
- this.store.edit((state) => {
- state.foo = 'baz';
- });
+ async onTraceLoad(trace: Trace): Promise<void> {
+ console.log('SkeletonPlugin::onTraceLoad()', trace.traceInfo.traceTitle);
// This is an example of how to access the pluginArgs pushed by the
// postMessage when deep-linking to the UI.
- if (ctx.openerPluginArgs !== undefined) {
- console.log(`Postmessage args for ${ctx.pluginId}`, ctx.openerPluginArgs);
+ if (trace.openerPluginArgs !== undefined) {
+ console.log(
+ `Postmessage args for ${trace.pluginId}`,
+ trace.openerPluginArgs,
+ );
}
/**
- * This hook is called when the trace has finished loading, and all plugins
- * have returned from their onTraceLoad calls. The UI can be considered
- * 'ready' at this point. All tracks and commands should now be available,
- * and the timeline is ready to use.
+ * The 'traceready' event is fired when the trace has finished loading, and
+ * all plugins have returned from their onTraceLoad calls. The UI can be
+ * considered 'ready' at this point. All tracks and commands should now be
+ * available, and the timeline is ready to use.
*
* This is where any automations should be done - things that you would
* usually do manually after the trace has loaded but you'd like to automate
@@ -94,8 +84,8 @@
* TODO(stevegolton): Update this comment if the semantics of track adding
* changes.
*/
- ctx.addEventListener('traceready', async () => {
- console.log('onTraceReady called');
+ trace.addEventListener('traceready', async () => {
+ console.log('SkeletonPlugin::traceready');
});
}
diff --git a/ui/src/plugins/com.google.PixelCpmTrace/OWNERS b/ui/src/plugins/com.google.PixelCpmTrace/OWNERS
new file mode 100644
index 0000000..9833dcb
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelCpmTrace/OWNERS
@@ -0,0 +1,2 @@
+sashwinbalaji@google.com
+spirani@google.com
diff --git a/ui/src/plugins/com.google.PixelCpmTrace/index.ts b/ui/src/plugins/com.google.PixelCpmTrace/index.ts
new file mode 100644
index 0000000..dcb3934
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelCpmTrace/index.ts
@@ -0,0 +1,74 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {createQueryCounterTrack} from '../../public/lib/tracks/query_counter_track';
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {COUNTER_TRACK_KIND} from '../../public/track_kinds';
+import {TrackNode} from '../../public/workspace';
+import {NUM, STR} from '../../trace_processor/query_result';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'com.google.PixelCpmTrace';
+
+ async onTraceLoad(ctx: Trace): Promise<void> {
+ const group = new TrackNode({
+ title: 'Central Power Manager',
+ isSummary: true,
+ });
+
+ const {engine} = ctx;
+ const result = await engine.query(`
+ select
+ id AS trackId,
+ extract_arg(dimension_arg_set_id, 'name') AS trackName
+ FROM track
+ WHERE classification = 'pixel_cpm_trace'
+ ORDER BY trackName
+ `);
+
+ const it = result.iter({trackId: NUM, trackName: STR});
+ for (let group_added = false; it.valid(); it.next()) {
+ const {trackId, trackName} = it;
+ const uri = `/cpm_trace_${trackName}`;
+ const track = await createQueryCounterTrack({
+ trace: ctx,
+ uri,
+ data: {
+ sqlSource: `
+ select ts, value
+ from counter
+ where track_id = ${trackId}
+ `,
+ columns: ['ts', 'value'],
+ },
+ columns: {ts: 'ts', value: 'value'},
+ });
+ ctx.tracks.registerTrack({
+ uri,
+ title: trackName,
+ tags: {
+ kind: COUNTER_TRACK_KIND,
+ trackIds: [trackId],
+ },
+ track,
+ });
+ group.addChildInOrder(new TrackNode({uri, title: trackName}));
+ if (!group_added) {
+ ctx.workspace.addChildInOrder(group);
+ group_added = true;
+ }
+ }
+ }
+}
diff --git a/ui/src/plugins/com.google.PixelMemory/index.ts b/ui/src/plugins/com.google.PixelMemory/index.ts
index 7ebb36d..30d1520 100644
--- a/ui/src/plugins/com.google.PixelMemory/index.ts
+++ b/ui/src/plugins/com.google.PixelMemory/index.ts
@@ -14,7 +14,7 @@
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
-import {addDebugCounterTrack} from '../../public/lib/debug_tracks/debug_tracks';
+import {addDebugCounterTrack} from '../../public/lib/tracks/debug_tracks';
export default class implements PerfettoPlugin {
static readonly id = 'com.google.PixelMemory';
@@ -42,9 +42,9 @@
);
`;
await ctx.engine.query(RSS_ALL);
- await addDebugCounterTrack(
- ctx,
- {
+ await addDebugCounterTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT
ts,
@@ -54,9 +54,8 @@
`,
columns: ['ts', 'value'],
},
- pid + '_rss_anon_file_swap_shmem_gpu',
- {ts: 'ts', value: 'value'},
- );
+ title: pid + '_rss_anon_file_swap_shmem_gpu',
+ });
},
});
}
diff --git a/ui/src/plugins/dev.perfetto.AndroidClientServer/index.ts b/ui/src/plugins/dev.perfetto.AndroidClientServer/index.ts
index 405ad8c..ab095b4 100644
--- a/ui/src/plugins/dev.perfetto.AndroidClientServer/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidClientServer/index.ts
@@ -188,19 +188,17 @@
name: STR,
});
for (; it.valid(); it.next()) {
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT ts, dur, name
FROM __enhanced_binder_for_slice_${sliceId}
WHERE binder_id = ${it.id}
`,
},
- it.name,
- {ts: 'ts', dur: 'dur', name: 'name'},
- [],
- );
+ title: it.name,
+ });
}
},
});
diff --git a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
index 7320bcc..2fef0d4 100644
--- a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
@@ -12,11 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {SimpleSliceTrackConfig} from '../../frontend/simple_slice_track';
import {addDebugSliceTrack} from '../../public/debug_tracks';
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
-import {addAndPinSliceTrack} from './trackUtils';
import {addQueryResultsTab} from '../../public/lib/query_table/query_result_tab';
/**
@@ -31,9 +29,8 @@
trackName: string,
cujNames?: string | string[],
) {
- const jankCujTrackConfig: SimpleSliceTrackConfig =
- generateJankCujTrackConfig(cujNames);
- addAndPinSliceTrack(ctx, jankCujTrackConfig, trackName);
+ const jankCujTrackConfig = generateJankCujTrackConfig(cujNames);
+ addDebugSliceTrack({trace: ctx, title: trackName, ...jankCujTrackConfig});
}
const JANK_CUJ_QUERY_PRECONDITIONS = `
@@ -45,11 +42,9 @@
* Generate the Track config for a multiple Jank CUJ slices
*
* @param {string | string[]} cujNames List of Jank CUJs to pin, default empty
- * @returns {SimpleSliceTrackConfig} Returns the track config for given CUJs
+ * @returns Returns the track config for given CUJs
*/
-function generateJankCujTrackConfig(
- cujNames: string | string[] = [],
-): SimpleSliceTrackConfig {
+function generateJankCujTrackConfig(cujNames: string | string[] = []) {
// This method expects the caller to have run JANK_CUJ_QUERY_PRECONDITIONS
// Not running the precondition query here to save time in case already run
const jankCujQuery = JANK_CUJ_QUERY;
@@ -62,15 +57,13 @@
.join(',')})`
: '';
- const jankCujTrackConfig: SimpleSliceTrackConfig = {
+ return {
data: {
sqlSource: `${jankCujQuery}${filterCuj}`,
columns: jankCujColumns,
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
argColumns: jankCujColumns,
};
- return jankCujTrackConfig;
}
const JANK_CUJ_QUERY = `
@@ -245,16 +238,14 @@
id: 'dev.perfetto.AndroidCujs#PinLatencyCUJs',
name: 'Add track: Android latency CUJs',
callback: () => {
- addDebugSliceTrack(
- ctx,
- {
+ addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: LATENCY_CUJ_QUERY,
columns: LATENCY_COLUMNS,
},
- 'Latency CUJs',
- {ts: 'ts', dur: 'dur', name: 'name'},
- [],
- );
+ title: 'Latency CUJs',
+ });
},
});
@@ -273,16 +264,15 @@
name: 'Add track: Android Blocking calls during CUJs',
callback: () => {
ctx.engine.query(JANK_CUJ_QUERY_PRECONDITIONS).then(() =>
- addDebugSliceTrack(
- ctx,
- {
+ addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: BLOCKING_CALLS_DURING_CUJS_QUERY,
columns: BLOCKING_CALLS_DURING_CUJS_COLUMNS,
},
- 'Blocking calls during CUJs',
- {ts: 'ts', dur: 'dur', name: 'name'},
- BLOCKING_CALLS_DURING_CUJS_COLUMNS,
- ),
+ title: 'Blocking calls during CUJs',
+ argColumns: BLOCKING_CALLS_DURING_CUJS_COLUMNS,
+ }),
);
},
});
diff --git a/ui/src/plugins/dev.perfetto.AndroidCujs/trackUtils.ts b/ui/src/plugins/dev.perfetto.AndroidCujs/trackUtils.ts
index b16ace8..ca01328 100644
--- a/ui/src/plugins/dev.perfetto.AndroidCujs/trackUtils.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidCujs/trackUtils.ts
@@ -12,50 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {SimpleSliceTrackConfig} from '../../frontend/simple_slice_track';
-import {addDebugSliceTrack} from '../../public/debug_tracks';
import {Trace} from '../../public/trace';
/**
- * Adds debug tracks from SimpleSliceTrackConfig
- * Static tracks cannot be added on command
- * TODO: b/349502258 - To be removed later
- *
- * @param {Trace} ctx Context for trace methods and properties
- * @param {SimpleSliceTrackConfig} config Track config to add
- * @param {string} trackName Track name to display
- */
-export function addDebugTrackOnCommand(
- ctx: Trace,
- config: SimpleSliceTrackConfig,
- trackName: string,
-) {
- addDebugSliceTrack(
- ctx,
- config.data,
- trackName,
- config.columns,
- config.argColumns,
- );
-}
-
-/**
- * Registers and pins tracks on traceload or command
- *
- * @param {Trace} ctx Context for trace methods and properties
- * @param {SimpleSliceTrackConfig} config Track config to add
- * @param {string} trackName Track name to display
- * type 'static' expects caller to pass uri string
- */
-export function addAndPinSliceTrack(
- ctx: Trace,
- config: SimpleSliceTrackConfig,
- trackName: string,
-) {
- addDebugTrackOnCommand(ctx, config, trackName);
-}
-
-/**
* Sets focus on a specific slice within the trace data.
*
* Takes and adds desired slice to current selection
diff --git a/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts
index 1cc2a40..59f6bbf 100644
--- a/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidDesktopMode/index.ts
@@ -12,10 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {PerfettoPlugin} from '../../public/plugin';
import {Trace} from '../../public/trace';
import {TrackNode} from '../../public/workspace';
@@ -40,37 +37,34 @@
static readonly id = 'dev.perfetto.AndroidDesktopMode';
async onTraceLoad(ctx: Trace): Promise<void> {
- ctx.addEventListener('traceready', async () => {
- await ctx.engine.query(INCLUDE_DESKTOP_MODULE_QUERY);
- this.registerTrack(ctx, QUERY);
- ctx.commands.registerCommand({
- id: 'dev.perfetto.DesktopMode#AddTrackDesktopWindowss',
- name: 'Add Track: ' + TRACK_NAME,
- callback: () => this.addSimpleTrack(ctx),
- });
+ await ctx.engine.query(INCLUDE_DESKTOP_MODULE_QUERY);
+ await this.registerTrack(ctx, QUERY);
+ ctx.commands.registerCommand({
+ id: 'dev.perfetto.DesktopMode#AddTrackDesktopWindowss',
+ name: 'Add Track: ' + TRACK_NAME,
+ callback: () => this.addSimpleTrack(ctx),
});
}
- private registerTrack(_ctx: Trace, sql: string) {
- const config: SimpleSliceTrackConfig = {
+ private async registerTrack(ctx: Trace, sql: string) {
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri: TRACK_URI,
data: {
sqlSource: sql,
columns: COLUMNS,
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
- };
- const track = new SimpleSliceTrack(_ctx, {trackUri: TRACK_URI}, config);
- _ctx.tracks.registerTrack({
+ });
+ ctx.tracks.registerTrack({
uri: TRACK_URI,
title: TRACK_NAME,
track,
});
}
- private addSimpleTrack(_ctx: Trace) {
+ private addSimpleTrack(ctx: Trace) {
const trackNode = new TrackNode({uri: TRACK_URI, title: TRACK_NAME});
- _ctx.workspace.addChildInOrder(trackNode);
+ ctx.workspace.addChildInOrder(trackNode);
trackNode.pin();
}
}
diff --git a/ui/src/plugins/dev.perfetto.AndroidDmabuf/index.ts b/ui/src/plugins/dev.perfetto.AndroidDmabuf/index.ts
index 2979f83..a946cc2 100644
--- a/ui/src/plugins/dev.perfetto.AndroidDmabuf/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidDmabuf/index.ts
@@ -13,9 +13,9 @@
// limitations under the License.
import {
- SimpleCounterTrack,
- SimpleCounterTrackConfig,
-} from '../../frontend/simple_counter_track';
+ createQueryCounterTrack,
+ SqlDataSource,
+} from '../../public/lib/tracks/query_counter_track';
import {PerfettoPlugin} from '../../public/plugin';
import {
getOrCreateGroupForProcess,
@@ -25,15 +25,20 @@
import {TrackNode} from '../../public/workspace';
import {NUM_NULL} from '../../trace_processor/query_result';
-function registerAllocsTrack(
+async function registerAllocsTrack(
ctx: Trace,
uri: string,
- config: SimpleCounterTrackConfig,
-): void {
+ dataSource: SqlDataSource,
+) {
+ const track = await createQueryCounterTrack({
+ trace: ctx,
+ uri,
+ data: dataSource,
+ });
ctx.tracks.registerTrack({
uri,
title: `dmabuf allocs`,
- track: new SimpleCounterTrack(ctx, {trackUri: uri}, config),
+ track: track,
});
}
@@ -56,29 +61,21 @@
for (; it.valid(); it.next()) {
if (it.upid != null) {
const uri = `/android_process_dmabuf_upid_${it.upid}`;
- const config: SimpleCounterTrackConfig = {
- data: {
- sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
+ const config: SqlDataSource = {
+ sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
WHERE upid = ${it.upid}`,
- columns: ['ts', 'value'],
- },
- columns: {ts: 'ts', value: 'value'},
};
- registerAllocsTrack(ctx, uri, config);
+ await registerAllocsTrack(ctx, uri, config);
getOrCreateGroupForProcess(ctx.workspace, it.upid).addChildInOrder(
new TrackNode({uri, title: 'dmabuf allocs'}),
);
} else if (it.utid != null) {
const uri = `/android_process_dmabuf_utid_${it.utid}`;
- const config: SimpleCounterTrackConfig = {
- data: {
- sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
+ const config: SqlDataSource = {
+ sqlSource: `SELECT ts, value FROM _android_memory_cumulative_dmabuf
WHERE utid = ${it.utid}`,
- columns: ['ts', 'value'],
- },
- columns: {ts: 'ts', value: 'value'},
};
- registerAllocsTrack(ctx, uri, config);
+ await registerAllocsTrack(ctx, uri, config);
getOrCreateGroupForThread(ctx.workspace, it.utid).addChildInOrder(
new TrackNode({uri, title: 'dmabuf allocs'}),
);
diff --git a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
index a69af5b..45c471c 100644
--- a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
@@ -15,15 +15,9 @@
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
import {Engine} from '../../trace_processor/engine';
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {CounterOptions} from '../../frontend/base_counter_track';
-import {
- SimpleCounterTrack,
- SimpleCounterTrackConfig,
-} from '../../frontend/simple_counter_track';
+import {createQueryCounterTrack} from '../../public/lib/tracks/query_counter_track';
import {TrackNode} from '../../public/workspace';
interface ContainedTrace {
@@ -1120,24 +1114,23 @@
}
}
- addSliceTrack(
+ async addSliceTrack(
ctx: Trace,
name: string,
query: string,
groupName?: string,
columns: string[] = [],
- ): void {
- const config: SimpleSliceTrackConfig = {
+ ) {
+ const uri = `/long_battery_tracing_${name}`;
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource: query,
columns: ['ts', 'dur', 'name', ...columns],
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
argColumns: columns,
- };
-
- const uri = `/long_battery_tracing_${name}`;
- const track = new SimpleSliceTrack(ctx, {trackUri: uri}, config);
+ });
ctx.tracks.registerTrack({
uri,
title: name,
@@ -1147,43 +1140,43 @@
this.addTrack(ctx, trackNode, groupName);
}
- addCounterTrack(
+ async addCounterTrack(
ctx: Trace,
name: string,
query: string,
groupName: string,
options?: Partial<CounterOptions>,
- ): void {
- const config: SimpleCounterTrackConfig = {
+ ) {
+ const uri = `/long_battery_tracing_${name}`;
+ const track = await createQueryCounterTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource: query,
columns: ['ts', 'value'],
},
- columns: {ts: 'ts', value: 'value'},
options,
- };
-
- const uri = `/long_battery_tracing_${name}`;
+ });
ctx.tracks.registerTrack({
uri,
title: name,
- track: new SimpleCounterTrack(ctx, {trackUri: uri}, config),
+ track,
});
- const track = new TrackNode({uri, title: name});
- this.addTrack(ctx, track, groupName);
+ const trackNode = new TrackNode({uri, title: name});
+ this.addTrack(ctx, trackNode, groupName);
}
- addBatteryStatsState(
+ async addBatteryStatsState(
ctx: Trace,
name: string,
track: string,
groupName: string,
features: Set<string>,
- ): void {
+ ) {
if (!features.has(`track.${track}`)) {
return;
}
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
name,
`SELECT ts, safe_dur AS dur, value_name AS name
@@ -1193,18 +1186,18 @@
);
}
- addBatteryStatsEvent(
+ async addBatteryStatsEvent(
ctx: Trace,
name: string,
track: string,
groupName: string | undefined,
features: Set<string>,
- ): void {
+ ) {
if (!features.has(`track.${track}`)) {
return;
}
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
name,
`SELECT ts, safe_dur AS dur, str_value AS name
@@ -1227,15 +1220,19 @@
await e.query(`INCLUDE PERFETTO MODULE android.suspend;`);
await e.query(`INCLUDE PERFETTO MODULE counters.intervals;`);
- this.addSliceTrack(ctx, 'Device State: Screen state', SCREEN_STATE);
- this.addSliceTrack(ctx, 'Device State: Charging', CHARGING);
- this.addSliceTrack(ctx, 'Device State: Suspend / resume', SUSPEND_RESUME);
- this.addSliceTrack(ctx, 'Device State: Doze light state', DOZE_LIGHT);
- this.addSliceTrack(ctx, 'Device State: Doze deep state', DOZE_DEEP);
+ await this.addSliceTrack(ctx, 'Device State: Screen state', SCREEN_STATE);
+ await this.addSliceTrack(ctx, 'Device State: Charging', CHARGING);
+ await this.addSliceTrack(
+ ctx,
+ 'Device State: Suspend / resume',
+ SUSPEND_RESUME,
+ );
+ await this.addSliceTrack(ctx, 'Device State: Doze light state', DOZE_LIGHT);
+ await this.addSliceTrack(ctx, 'Device State: Doze deep state', DOZE_DEEP);
query('Device State: Top app', 'battery_stats.top');
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Device State: Long wakelocks',
`SELECT
@@ -1256,7 +1253,7 @@
query('Device State: Jobs', 'battery_stats.job');
if (features.has('atom.thermal_throttling_severity_state_changed')) {
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Device State: Thermal throttling',
THERMAL_THROTTLING,
@@ -1296,7 +1293,7 @@
? {unit}
: undefined;
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
countersIt.ui_name,
`select ts, ${unit === '%' ? 100.0 : 1.0} * counter_value as value
@@ -1367,7 +1364,7 @@
as ${safeArg(arg)}`;
}
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
tracks.get(atom)!.ui_name,
`select ts, dur, slice_name as name, ${args.map((a) => argSql(a)).join(', ')}
@@ -1393,13 +1390,18 @@
await e.query(NETWORK_SUMMARY);
await e.query(RADIO_TRANSPORT_TYPE);
- this.addSliceTrack(ctx, 'Default network', DEFAULT_NETWORK, groupName);
+ await this.addSliceTrack(
+ ctx,
+ 'Default network',
+ DEFAULT_NETWORK,
+ groupName,
+ );
if (features.has('atom.network_tethering_reported')) {
- this.addSliceTrack(ctx, 'Tethering', TETHERING, groupName);
+ await this.addSliceTrack(ctx, 'Tethering', TETHERING, groupName);
}
if (features.has('net.wifi')) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Wifi total bytes',
`select ts, sum(value) as value from network_summary where dev_type = 'wifi' group by 1`,
@@ -1411,7 +1413,7 @@
);
const it = result.iter({pkg: 'str'});
for (; it.valid(); it.next()) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
`Top wifi: ${it.pkg}`,
`select ts, value from network_summary where dev_type = 'wifi' and pkg = '${it.pkg}'`,
@@ -1442,7 +1444,7 @@
features,
);
if (features.has('net.modem')) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Modem total bytes',
`select ts, sum(value) as value from network_summary where dev_type = 'modem' group by 1`,
@@ -1454,7 +1456,7 @@
);
const it = result.iter({pkg: 'str'});
for (; it.valid(); it.next()) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
`Top modem: ${it.pkg}`,
`select ts, value from network_summary where dev_type = 'modem' and pkg = '${it.pkg}'`,
@@ -1470,7 +1472,7 @@
groupName,
features,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Cellular connection',
`select ts, dur, name from radio_transport`,
@@ -1494,8 +1496,8 @@
}
async addModemRil(ctx: Trace, groupName: string): Promise<void> {
- const rilStrength = (band: string, value: string): void =>
- this.addSliceTrack(
+ const rilStrength = async (band: string, value: string) =>
+ await this.addSliceTrack(
ctx,
`Modem signal strength ${band} ${value}`,
`SELECT ts, dur, name FROM RilScreenOn WHERE band_name = '${band}' AND value_name = '${value}'`,
@@ -1507,19 +1509,19 @@
await e.query(MODEM_RIL_STRENGTH);
await e.query(MODEM_RIL_CHANNELS_PREAMBLE);
- rilStrength('LTE', 'rsrp');
- rilStrength('LTE', 'rssi');
- rilStrength('NR', 'rsrp');
- rilStrength('NR', 'rssi');
+ await rilStrength('LTE', 'rsrp');
+ await rilStrength('LTE', 'rssi');
+ await rilStrength('NR', 'rsrp');
+ await rilStrength('NR', 'rssi');
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Modem channel config',
MODEM_RIL_CHANNELS,
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Modem cell reselection',
MODEM_CELL_RESELECTION,
@@ -1545,7 +1547,7 @@
);
const countersIt = counters.iter({name: 'str'});
for (; countersIt.valid(); countersIt.next()) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
countersIt.name,
`select ts, value from pixel_modem_counters where name = '${countersIt.name}'`,
@@ -1557,7 +1559,7 @@
);
const slicesIt = slices.iter({track_name: 'str'});
for (; slicesIt.valid(); slicesIt.next()) {
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
slicesIt.track_name,
`select ts, dur, slice_name as name from pixel_modem_slices
@@ -1579,7 +1581,7 @@
const result = await e.query(KERNEL_WAKELOCKS_SUMMARY);
const it = result.iter({wakelock_name: 'str'});
for (; it.valid(); it.next()) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
it.wakelock_name,
`select ts, dur, value from kernel_wakelocks where wakelock_name = "${it.wakelock_name}"`,
@@ -1627,7 +1629,7 @@
let labelOther = false;
for (; it.valid(); it.next()) {
labelOther = true;
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
`Wakeup ${it.item}`,
`${sqlPrefix} where item="${it.item}"`,
@@ -1636,7 +1638,7 @@
);
items.push(it.item);
}
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
labelOther ? 'Other wakeups' : 'Wakeups',
`${sqlPrefix} where item not in ('${items.join("','")}')`,
@@ -1659,7 +1661,7 @@
);
const it = result.iter({pkg: 'str', cluster: 'str'});
for (; it.valid(); it.next()) {
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
`CPU (${it.cluster}): ${it.pkg}`,
`select ts, value from high_cpu where pkg = "${it.pkg}" and cluster="${it.cluster}"`,
@@ -1678,140 +1680,140 @@
return;
}
const groupName = 'Bluetooth';
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'BLE Scans (opportunistic)',
bleScanQuery('opportunistic'),
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'BLE Scans (filtered)',
bleScanQuery('filtered'),
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'BLE Scans (unfiltered)',
bleScanQuery('not filtered'),
groupName,
);
- this.addSliceTrack(ctx, 'BLE Scan Results', BLE_RESULTS, groupName);
- this.addSliceTrack(ctx, 'Connections (ACL)', BT_CONNS_ACL, groupName);
- this.addSliceTrack(ctx, 'Connections (SCO)', BT_CONNS_SCO, groupName);
- this.addSliceTrack(
+ await this.addSliceTrack(ctx, 'BLE Scan Results', BLE_RESULTS, groupName);
+ await this.addSliceTrack(ctx, 'Connections (ACL)', BT_CONNS_ACL, groupName);
+ await this.addSliceTrack(ctx, 'Connections (SCO)', BT_CONNS_SCO, groupName);
+ await this.addSliceTrack(
ctx,
'Link-level Events',
BT_LINK_LEVEL_EVENTS,
groupName,
BT_LINK_LEVEL_EVENTS_COLUMNS,
);
- this.addSliceTrack(ctx, 'A2DP Audio', BT_A2DP_AUDIO, groupName);
- this.addSliceTrack(
+ await this.addSliceTrack(ctx, 'A2DP Audio', BT_A2DP_AUDIO, groupName);
+ await this.addSliceTrack(
ctx,
'Bytes Transferred (L2CAP/RFCOMM)',
BT_BYTES,
groupName,
);
await ctx.engine.query(BT_ACTIVITY);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'ACL Classic Active Count',
'select ts, dur, acl_active_count as value from bt_activity',
groupName,
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'ACL Classic Sniff Count',
'select ts, dur, acl_sniff_count as value from bt_activity',
groupName,
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'ACL BLE Count',
'select ts, dur, acl_ble_count as value from bt_activity',
groupName,
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Advertising Instance Count',
'select ts, dur, advertising_count as value from bt_activity',
groupName,
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'LE Scan Duty Cycle Maximum',
'select ts, dur, le_scan_duty_cycle as value from bt_activity',
groupName,
{unit: '%'},
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Inquiry Active',
"select ts, dur, 'Active' as name from bt_activity where inquiry_active",
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'SCO Active',
"select ts, dur, 'Active' as name from bt_activity where sco_active",
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'A2DP Active',
"select ts, dur, 'Active' as name from bt_activity where a2dp_active",
groupName,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'LE Audio Active',
"select ts, dur, 'Active' as name from bt_activity where le_audio_active",
groupName,
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Controller Idle Time',
'select ts, dur, controller_idle_pct as value from bt_activity',
groupName,
{yRangeSharingKey: 'bt_controller_time', unit: '%'},
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Controller TX Time',
'select ts, dur, controller_tx_pct as value from bt_activity',
groupName,
{yRangeSharingKey: 'bt_controller_time', unit: '%'},
);
- this.addCounterTrack(
+ await this.addCounterTrack(
ctx,
'Controller RX Time',
'select ts, dur, controller_rx_pct as value from bt_activity',
groupName,
{yRangeSharingKey: 'bt_controller_time', unit: '%'},
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Quality reports',
BT_QUALITY_REPORTS,
groupName,
BT_QUALITY_REPORTS_COLUMNS,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'RSSI Reports',
BT_RSSI_REPORTS,
groupName,
BT_RSSI_REPORTS_COLUMNS,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'HAL Crashes',
BT_HAL_CRASHES,
groupName,
BT_HAL_CRASHES_COLUMNS,
);
- this.addSliceTrack(
+ await this.addSliceTrack(
ctx,
'Code Path Counter',
BT_CODE_PATH_COUNTER,
@@ -1832,8 +1834,8 @@
bySubscription.get(trace.subscription)!.push(trace);
}
- bySubscription.forEach((traces, subscription) =>
- this.addSliceTrack(
+ for (const [subscription, traces] of bySubscription) {
+ await this.addSliceTrack(
ctx,
subscription,
traces
@@ -1848,8 +1850,8 @@
.join(' UNION ALL '),
'Other traces',
['link'],
- ),
- );
+ );
+ }
}
async findFeatures(e: Engine): Promise<Set<string>> {
diff --git a/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts b/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
index 590a9ee..56e57fb 100644
--- a/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
@@ -27,16 +27,16 @@
tableOrQuery: string,
columns: string[],
): Promise<void> {
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `SELECT ${columns.join(',')} FROM ${tableOrQuery}`,
columns: columns,
},
- trackName,
- {ts: columns[0], dur: columns[1], name: columns[2]},
- columns.slice(2),
- );
+ title: trackName,
+ columns: {ts: columns[0], dur: columns[1], name: columns[2]},
+ argColumns: columns.slice(2),
+ });
}
async onTraceLoad(ctx: Trace): Promise<void> {
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
index 697899f..ab56328 100644
--- a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
@@ -34,9 +34,9 @@
'intent',
'table_name',
];
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT
start_id AS id,
@@ -51,10 +51,9 @@
`,
columns: sliceColumns,
},
- 'app_' + sliceName + '_start reason: ' + reason,
- {ts: 'ts', dur: 'dur', name: sliceName},
- sliceColumns,
- );
+ title: 'app_' + sliceName + '_start reason: ' + reason,
+ argColumns: sliceColumns,
+ });
}
async onTraceLoad(ctx: Trace): Promise<void> {
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts
index afd4923..c78abf8 100644
--- a/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts
@@ -85,18 +85,23 @@
)
`;
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource:
sqlPrefix +
`
SELECT * FROM target_thread_ipc_slice WHERE ts IS NOT NULL`,
},
- 'Rutime IPC:' + tid,
- {ts: 'ts', dur: 'dur', name: 'ipc'},
- ['instruction', 'cycle', 'stall_backend_mem', 'l3_cache_miss'],
- );
+ title: 'Rutime IPC:' + tid,
+ columns: {ts: 'ts', dur: 'dur', name: 'ipc'},
+ argColumns: [
+ 'instruction',
+ 'cycle',
+ 'stall_backend_mem',
+ 'l3_cache_miss',
+ ],
+ });
addQueryResultsTab(ctx, {
query:
sqlPrefix +
diff --git a/ui/src/plugins/dev.perfetto.AndroidStartup/index.ts b/ui/src/plugins/dev.perfetto.AndroidStartup/index.ts
index d16b94e..88cf8b6 100644
--- a/ui/src/plugins/dev.perfetto.AndroidStartup/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidStartup/index.ts
@@ -15,10 +15,7 @@
import {LONG} from '../../trace_processor/query_result';
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
-import {
- SimpleSliceTrack,
- SimpleSliceTrackConfig,
-} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {TrackNode} from '../../public/workspace';
export default class implements PerfettoPlugin {
static readonly id = 'dev.perfetto.AndroidStartup';
@@ -46,13 +43,13 @@
FROM android_startup_opinionated_breakdown
`;
- const trackNode = this.loadStartupTrack(
+ const trackNode = await this.loadStartupTrack(
ctx,
trackSource,
`/android_startups`,
'Android App Startups',
);
- const trackBreakdownNode = this.loadStartupTrack(
+ const trackBreakdownNode = await this.loadStartupTrack(
ctx,
trackBreakdownSource,
`/android_startups_breakdown`,
@@ -63,21 +60,20 @@
trackNode.addChildLast(trackBreakdownNode);
}
- private loadStartupTrack(
+ private async loadStartupTrack(
ctx: Trace,
sqlSource: string,
uri: string,
title: string,
- ): TrackNode {
- const config: SimpleSliceTrackConfig = {
+ ): Promise<TrackNode> {
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource,
columns: ['ts', 'dur', 'name'],
},
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
- };
- const track = new SimpleSliceTrack(ctx, {trackUri: uri}, config);
+ });
ctx.tracks.registerTrack({
uri,
title,
diff --git a/ui/src/plugins/dev.perfetto.AsyncSlices/async_slice_track.ts b/ui/src/plugins/dev.perfetto.AsyncSlices/async_slice_track.ts
index 1bb31a5..0d55ff1 100644
--- a/ui/src/plugins/dev.perfetto.AsyncSlices/async_slice_track.ts
+++ b/ui/src/plugins/dev.perfetto.AsyncSlices/async_slice_track.ts
@@ -16,10 +16,17 @@
import {clamp} from '../../base/math_utils';
import {NAMED_ROW, NamedSliceTrack} from '../../frontend/named_slice_track';
import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
-import {NewTrackArgs} from '../../frontend/track';
import {TrackEventDetails} from '../../public/selection';
+import {Trace} from '../../public/trace';
import {Slice} from '../../public/track';
-import {LONG_NULL} from '../../trace_processor/query_result';
+import {SourceDataset, Dataset} from '../../trace_processor/dataset';
+import {
+ LONG,
+ LONG_NULL,
+ NUM,
+ NUM_NULL,
+ STR,
+} from '../../trace_processor/query_result';
export const THREAD_SLICE_ROW = {
// Base columns (tsq, ts, dur, id, depth).
@@ -32,11 +39,12 @@
export class AsyncSliceTrack extends NamedSliceTrack<Slice, ThreadSliceRow> {
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
maxDepth: number,
private readonly trackIds: number[],
) {
- super(args);
+ super(trace, uri);
this.sliceLayout = {
...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
depthGuess: maxDepth,
@@ -104,4 +112,21 @@
tableName: 'slice',
};
}
+
+ override getDataset(): Dataset {
+ return new SourceDataset({
+ src: `slice`,
+ filter: {
+ col: 'track_id',
+ in: this.trackIds,
+ },
+ schema: {
+ id: NUM,
+ name: STR,
+ ts: LONG,
+ dur: LONG,
+ parent_id: NUM_NULL,
+ },
+ });
+ }
}
diff --git a/ui/src/plugins/dev.perfetto.AsyncSlices/index.ts b/ui/src/plugins/dev.perfetto.AsyncSlices/index.ts
index 9b0c6e7..ecd8dab 100644
--- a/ui/src/plugins/dev.perfetto.AsyncSlices/index.ts
+++ b/ui/src/plugins/dev.perfetto.AsyncSlices/index.ts
@@ -197,7 +197,7 @@
kind: SLICE_TRACK_KIND,
scope: 'global',
},
- track: new AsyncSliceTrack({trace: ctx, uri}, maxDepth, trackIds),
+ track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
});
const trackNode = new TrackNode({
uri,
@@ -283,7 +283,7 @@
scope: 'process',
upid,
},
- track: new AsyncSliceTrack({trace: ctx, uri}, maxDepth, trackIds),
+ track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
});
const track = new TrackNode({uri, title, sortOrder: 30});
trackIds.forEach((id) => {
@@ -384,7 +384,7 @@
chips: removeFalsyValues([
isKernelThread === 0 && isMainThread === 1 && 'main thread',
]),
- track: new AsyncSliceTrack({trace: ctx, uri}, maxDepth, trackIds),
+ track: new AsyncSliceTrack(ctx, uri, maxDepth, trackIds),
});
const track = new TrackNode({uri, title, sortOrder: 20});
trackIds.forEach((id) => {
diff --git a/ui/src/plugins/dev.perfetto.AsyncSlices/slice_selection_aggregator.ts b/ui/src/plugins/dev.perfetto.AsyncSlices/slice_selection_aggregator.ts
index 55f7c95..23226bc 100644
--- a/ui/src/plugins/dev.perfetto.AsyncSlices/slice_selection_aggregator.ts
+++ b/ui/src/plugins/dev.perfetto.AsyncSlices/slice_selection_aggregator.ts
@@ -16,16 +16,27 @@
import {AreaSelection} from '../../public/selection';
import {Engine} from '../../trace_processor/engine';
import {AreaSelectionAggregator} from '../../public/selection';
-import {SLICE_TRACK_KIND} from '../../public/track_kinds';
+import {UnionDataset} from '../../trace_processor/dataset';
+import {LONG, NUM, STR} from '../../trace_processor/query_result';
export class SliceSelectionAggregator implements AreaSelectionAggregator {
readonly id = 'slice_aggregation';
async createAggregateView(engine: Engine, area: AreaSelection) {
- const selectedTrackKeys = getSelectedTrackSqlIds(area);
-
- if (selectedTrackKeys.length === 0) return false;
-
+ const desiredSchema = {
+ id: NUM,
+ name: STR,
+ ts: LONG,
+ dur: LONG,
+ };
+ const validDatasets = area.tracks
+ .map((track) => track.track.getDataset?.())
+ .filter((ds) => ds !== undefined)
+ .filter((ds) => ds.implements(desiredSchema));
+ if (validDatasets.length === 0) {
+ return false;
+ }
+ const unionDataset = new UnionDataset(validDatasets);
await engine.query(`
create or replace perfetto table ${this.id} as
select
@@ -33,12 +44,13 @@
sum(dur) AS total_dur,
sum(dur)/count() as avg_dur,
count() as occurrences
- from slices
- where track_id in (${selectedTrackKeys})
- and ts + dur > ${area.start}
+ from (${unionDataset.optimize().query()})
+ where
+ ts + dur > ${area.start}
and ts < ${area.end}
group by name
`);
+
return true;
}
@@ -83,14 +95,3 @@
];
}
}
-
-function getSelectedTrackSqlIds(area: AreaSelection): number[] {
- const selectedTrackKeys: number[] = [];
- for (const trackInfo of area.tracks) {
- if (trackInfo?.tags?.kind === SLICE_TRACK_KIND) {
- trackInfo.tags.trackIds &&
- selectedTrackKeys.push(...trackInfo.tags.trackIds);
- }
- }
- return selectedTrackKeys;
-}
diff --git a/ui/src/plugins/dev.perfetto.Chaos/index.ts b/ui/src/plugins/dev.perfetto.Chaos/index.ts
index fa469be..358ecca 100644
--- a/ui/src/plugins/dev.perfetto.Chaos/index.ts
+++ b/ui/src/plugins/dev.perfetto.Chaos/index.ts
@@ -50,21 +50,19 @@
id: 'dev.perfetto.Chaos#AddCrashingDebugTrack',
name: 'Chaos: add crashing debug track',
callback: () => {
- addDebugSliceTrack(
- ctx,
- {
+ addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
- syntactically
- invalid
- query
- over
- many
- `,
+ syntactically
+ invalid
+ query
+ over
+ many
+ `,
},
- `Chaos track`,
- {ts: 'ts', dur: 'dur', name: 'name'},
- [],
- );
+ title: `Chaos track`,
+ });
},
});
}
diff --git a/ui/src/plugins/dev.perfetto.Counter/index.ts b/ui/src/plugins/dev.perfetto.Counter/index.ts
index 074e2c4..ef063ca 100644
--- a/ui/src/plugins/dev.perfetto.Counter/index.ts
+++ b/ui/src/plugins/dev.perfetto.Counter/index.ts
@@ -85,10 +85,6 @@
// options.yRangeSharingKey = 'mem';
// }
- if (name.startsWith('battery_stats.')) {
- options.yRangeSharingKey = 'battery_stats';
- }
-
// All 'Entity residency: foo bar1234' tracks should share a y-axis
// with 'Entity residency: foo baz5678' etc tracks:
{
@@ -163,16 +159,16 @@
kind: COUNTER_TRACK_KIND,
trackIds: [trackId],
},
- track: new TraceProcessorCounterTrack({
- trace: ctx,
+ track: new TraceProcessorCounterTrack(
+ ctx,
uri,
- trackId,
- trackName: title,
- options: {
+ {
...getDefaultCounterOptions(title),
unit,
},
- }),
+ trackId,
+ title,
+ ),
});
const track = new TrackNode({uri, title});
ctx.workspace.addChildInOrder(track);
@@ -242,13 +238,13 @@
trackIds: [trackId],
scope,
},
- track: new TraceProcessorCounterTrack({
- trace: ctx,
+ track: new TraceProcessorCounterTrack(
+ ctx,
uri,
- trackId: trackId,
- trackName: name,
- options: getDefaultCounterOptions(name),
- }),
+ getDefaultCounterOptions(name),
+ trackId,
+ name,
+ ),
});
const trackNode = new TrackNode({uri, title: name, sortOrder: -20});
ctx.workspace.addChildInOrder(trackNode);
@@ -309,13 +305,13 @@
upid: upid ?? undefined,
scope: 'thread',
},
- track: new TraceProcessorCounterTrack({
- trace: ctx,
+ track: new TraceProcessorCounterTrack(
+ ctx,
uri,
- trackId: trackId,
- trackName: name,
- options: getDefaultCounterOptions(name),
- }),
+ getDefaultCounterOptions(name),
+ trackId,
+ name,
+ ),
});
const group = getOrCreateGroupForThread(ctx.workspace, utid);
const track = new TrackNode({uri, title: name, sortOrder: 30});
@@ -367,13 +363,13 @@
upid,
scope: 'process',
},
- track: new TraceProcessorCounterTrack({
- trace: ctx,
+ track: new TraceProcessorCounterTrack(
+ ctx,
uri,
- trackId: trackId,
- trackName: name,
- options: getDefaultCounterOptions(name),
- }),
+ getDefaultCounterOptions(name),
+ trackId,
+ name,
+ ),
});
const group = getOrCreateGroupForProcess(ctx.workspace, upid);
const track = new TrackNode({uri, title: name, sortOrder: 20});
@@ -402,13 +398,13 @@
trackIds: [it.id],
scope: 'gpuFreq',
},
- track: new TraceProcessorCounterTrack({
- trace: ctx,
+ track: new TraceProcessorCounterTrack(
+ ctx,
uri,
- trackId: it.id,
- trackName: name,
- options: getDefaultCounterOptions(name),
- }),
+ getDefaultCounterOptions(name),
+ it.id,
+ name,
+ ),
});
const track = new TrackNode({uri, title: name, sortOrder: -20});
ctx.workspace.addChildInOrder(track);
diff --git a/ui/src/plugins/dev.perfetto.Counter/trace_processor_counter_track.ts b/ui/src/plugins/dev.perfetto.Counter/trace_processor_counter_track.ts
index ddbdbcb..a76fcef 100644
--- a/ui/src/plugins/dev.perfetto.Counter/trace_processor_counter_track.ts
+++ b/ui/src/plugins/dev.perfetto.Counter/trace_processor_counter_track.ts
@@ -12,33 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {LONG, LONG_NULL, NUM} from '../../trace_processor/query_result';
+import {Time} from '../../base/time';
import {
BaseCounterTrack,
- BaseCounterTrackArgs,
+ CounterOptions,
} from '../../frontend/base_counter_track';
-
-import {TrackMouseEvent} from '../../public/track';
import {TrackEventDetails} from '../../public/selection';
-import {Time} from '../../base/time';
+import {Trace} from '../../public/trace';
+import {TrackMouseEvent} from '../../public/track';
+import {LONG, LONG_NULL, NUM} from '../../trace_processor/query_result';
import {CounterDetailsPanel} from './counter_details_panel';
-interface TraceProcessorCounterTrackArgs extends BaseCounterTrackArgs {
- readonly trackId: number;
- readonly trackName: string;
- readonly rootTable?: string;
-}
-
export class TraceProcessorCounterTrack extends BaseCounterTrack {
- private readonly trackId: number;
- private readonly rootTable: string;
- private readonly trackName: string;
-
- constructor(args: TraceProcessorCounterTrackArgs) {
- super(args);
- this.trackId = args.trackId;
- this.rootTable = args.rootTable ?? 'counter';
- this.trackName = args.trackName;
+ constructor(
+ trace: Trace,
+ uri: string,
+ options: Partial<CounterOptions>,
+ private readonly trackId: number,
+ private readonly trackName: string,
+ private readonly rootTable: string = 'counter',
+ ) {
+ super(trace, uri, options);
}
getSqlSource() {
diff --git a/ui/src/plugins/dev.perfetto.CpuProfile/cpu_profile_track.ts b/ui/src/plugins/dev.perfetto.CpuProfile/cpu_profile_track.ts
index 4baeddf..0bd6fe8 100644
--- a/ui/src/plugins/dev.perfetto.CpuProfile/cpu_profile_track.ts
+++ b/ui/src/plugins/dev.perfetto.CpuProfile/cpu_profile_track.ts
@@ -20,10 +20,10 @@
OnSliceClickArgs,
} from '../../frontend/base_slice_track';
import {NAMED_ROW, NamedRow} from '../../frontend/named_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {NUM} from '../../trace_processor/query_result';
import {Slice} from '../../public/track';
import {CpuProfileSampleFlamegraphDetailsPanel} from './cpu_profile_details_panel';
+import {Trace} from '../../public/trace';
interface CpuProfileRow extends NamedRow {
callsiteId: number;
@@ -31,10 +31,11 @@
export class CpuProfileTrack extends BaseSliceTrack<Slice, CpuProfileRow> {
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
private utid: number,
) {
- super(args);
+ super(trace, uri);
}
protected getRowSpec(): CpuProfileRow {
diff --git a/ui/src/plugins/dev.perfetto.CpuProfile/index.ts b/ui/src/plugins/dev.perfetto.CpuProfile/index.ts
index 31e49fe..e33e341 100644
--- a/ui/src/plugins/dev.perfetto.CpuProfile/index.ts
+++ b/ui/src/plugins/dev.perfetto.CpuProfile/index.ts
@@ -60,13 +60,7 @@
utid,
...(exists(upid) && {upid}),
},
- track: new CpuProfileTrack(
- {
- trace: ctx,
- uri,
- },
- utid,
- ),
+ track: new CpuProfileTrack(ctx, uri, utid),
});
const group = getOrCreateGroupForThread(ctx.workspace, utid);
const track = new TrackNode({uri, title, sortOrder: -40});
diff --git a/ui/src/plugins/dev.perfetto.CpuSlices/cpu_slice_track.ts b/ui/src/plugins/dev.perfetto.CpuSlices/cpu_slice_track.ts
index 2c5b456..95e35b5 100644
--- a/ui/src/plugins/dev.perfetto.CpuSlices/cpu_slice_track.ts
+++ b/ui/src/plugins/dev.perfetto.CpuSlices/cpu_slice_track.ts
@@ -42,6 +42,7 @@
import {Trace} from '../../public/trace';
import {exists} from '../../base/utils';
import {ThreadMap} from '../dev.perfetto.Thread/threads';
+import {Dataset, SourceDataset} from '../../trace_processor/dataset';
export interface Data extends TrackData {
// Slices are stored in a columnar fashion. All fields have the same length.
@@ -96,6 +97,21 @@
this.lastRowId = it.firstRow({lastRowId: NUM}).lastRowId;
}
+ getDataset(): Dataset | undefined {
+ return new SourceDataset({
+ src: 'select id, ts, dur, cpu from sched where utid != 0',
+ schema: {
+ id: NUM,
+ ts: LONG,
+ dur: LONG,
+ },
+ filter: {
+ col: 'cpu',
+ eq: this.cpu,
+ },
+ });
+ }
+
async onUpdate({
visibleWindow,
resolution,
diff --git a/ui/src/plugins/dev.perfetto.CpuSlices/sched_details_tab.ts b/ui/src/plugins/dev.perfetto.CpuSlices/sched_details_tab.ts
index 1f4d36f..f4b3b93 100644
--- a/ui/src/plugins/dev.perfetto.CpuSlices/sched_details_tab.ts
+++ b/ui/src/plugins/dev.perfetto.CpuSlices/sched_details_tab.ts
@@ -34,6 +34,7 @@
import {TrackEventDetailsPanel} from '../../public/details_panel';
import {TrackEventSelection} from '../../public/selection';
import {ThreadDesc, ThreadMap} from '../dev.perfetto.Thread/threads';
+import {assetSrc} from '../../base/assets';
const MIN_NORMAL_SCHED_PRIORITY = 100;
@@ -112,7 +113,7 @@
m(
'.slice-details-latency-panel',
m('img.slice-details-image', {
- src: `${this.trace.rootUrl}assets/scheduling_latency.png`,
+ src: assetSrc('assets/scheduling_latency.png'),
}),
this.renderWakeupText(data),
this.renderDisplayLatencyText(data),
diff --git a/ui/src/plugins/dev.perfetto.CpuidleTimeInState/index.ts b/ui/src/plugins/dev.perfetto.CpuidleTimeInState/index.ts
index 0e08329..3e4aaa1 100644
--- a/ui/src/plugins/dev.perfetto.CpuidleTimeInState/index.ts
+++ b/ui/src/plugins/dev.perfetto.CpuidleTimeInState/index.ts
@@ -16,39 +16,36 @@
import {PerfettoPlugin} from '../../public/plugin';
import {CounterOptions} from '../../frontend/base_counter_track';
import {TrackNode} from '../../public/workspace';
-import {
- SimpleCounterTrack,
- SimpleCounterTrackConfig,
-} from '../../frontend/simple_counter_track';
+import {createQueryCounterTrack} from '../../public/lib/tracks/query_counter_track';
export default class implements PerfettoPlugin {
static readonly id = 'dev.perfetto.CpuidleTimeInState';
- private addCounterTrack(
+ private async addCounterTrack(
ctx: Trace,
name: string,
query: string,
group?: TrackNode,
options?: Partial<CounterOptions>,
- ): void {
- const config: SimpleCounterTrackConfig = {
+ ) {
+ const uri = `/cpuidle_time_in_state_${name}`;
+ const track = await createQueryCounterTrack({
+ trace: ctx,
+ uri,
data: {
sqlSource: query,
columns: ['ts', 'value'],
},
columns: {ts: 'ts', value: 'value'},
options,
- };
-
- const uri = `/cpuidle_time_in_state_${name}`;
+ });
ctx.tracks.registerTrack({
uri,
title: name,
- track: new SimpleCounterTrack(ctx, {trackUri: uri}, config),
+ track,
});
- const track = new TrackNode({uri, title: name});
-
+ const trackNode = new TrackNode({uri, title: name});
if (group) {
- group.addChildInOrder(track);
+ group.addChildInOrder(trackNode);
}
}
diff --git a/ui/src/plugins/dev.perfetto.CriticalPath/index.ts b/ui/src/plugins/dev.perfetto.CriticalPath/index.ts
index b1217ea..94acec3 100644
--- a/ui/src/plugins/dev.perfetto.CriticalPath/index.ts
+++ b/ui/src/plugins/dev.perfetto.CriticalPath/index.ts
@@ -145,9 +145,9 @@
ctx.engine
.query(`INCLUDE PERFETTO MODULE sched.thread_executing_span;`)
.then(() =>
- addDebugSliceTrack(
- ctx,
- {
+ addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT
cr.id,
@@ -168,10 +168,10 @@
`,
columns: sliceLiteColumnNames,
},
- `${thdInfo.name}`,
- sliceLiteColumns,
- sliceLiteColumnNames,
- ),
+ title: `${thdInfo.name}`,
+ columns: sliceLiteColumns,
+ argColumns: sliceLiteColumnNames,
+ }),
);
},
});
@@ -189,9 +189,9 @@
`INCLUDE PERFETTO MODULE sched.thread_executing_span_with_slice;`,
)
.then(() =>
- addDebugSliceTrack(
- ctx,
- {
+ addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT cr.id, cr.utid, cr.ts, cr.dur, cr.name, cr.table_name
FROM
@@ -203,10 +203,10 @@
`,
columns: sliceColumnNames,
},
- `${thdInfo.name}`,
- sliceColumns,
- sliceColumnNames,
- ),
+ title: `${thdInfo.name}`,
+ columns: sliceColumns,
+ argColumns: sliceColumnNames,
+ }),
);
},
});
@@ -223,9 +223,9 @@
await ctx.engine.query(
`INCLUDE PERFETTO MODULE sched.thread_executing_span;`,
);
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT
cr.id,
@@ -245,11 +245,12 @@
`,
columns: criticalPathsliceLiteColumnNames,
},
- (await getThreadInfo(ctx.engine, trackUtid as Utid)).name ??
+ title:
+ (await getThreadInfo(ctx.engine, trackUtid as Utid)).name ??
'<thread name>',
- criticalPathsliceLiteColumns,
- criticalPathsliceLiteColumnNames,
- );
+ columns: criticalPathsliceLiteColumns,
+ argColumns: criticalPathsliceLiteColumnNames,
+ });
},
});
@@ -265,9 +266,9 @@
await ctx.engine.query(
`INCLUDE PERFETTO MODULE sched.thread_executing_span_with_slice;`,
);
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: `
SELECT cr.id, cr.utid, cr.ts, cr.dur, cr.name, cr.table_name
FROM
@@ -279,11 +280,12 @@
`,
columns: criticalPathsliceColumnNames,
},
- (await getThreadInfo(ctx.engine, trackUtid as Utid)).name ??
+ title:
+ (await getThreadInfo(ctx.engine, trackUtid as Utid)).name ??
'<thread name>',
- criticalPathSliceColumns,
- criticalPathsliceColumnNames,
- );
+ columns: criticalPathSliceColumns,
+ argColumns: criticalPathsliceColumnNames,
+ });
},
});
diff --git a/ui/src/plugins/dev.perfetto.Debug/index.ts b/ui/src/plugins/dev.perfetto.Debug/index.ts
index ce558d7..90d9ba2 100644
--- a/ui/src/plugins/dev.perfetto.Debug/index.ts
+++ b/ui/src/plugins/dev.perfetto.Debug/index.ts
@@ -15,7 +15,7 @@
import {
addDebugCounterTrack,
addDebugSliceTrack,
-} from '../../public/lib/debug_tracks/debug_tracks';
+} from '../../public/lib/tracks/debug_tracks';
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
import {exists} from '../../base/utils';
@@ -32,15 +32,13 @@
// or is the wrong type, we prompt the user for it.
const query = await getStringFromArgOrPrompt(ctx, arg);
if (exists(query)) {
- await addDebugSliceTrack(
- ctx,
- {
+ await addDebugSliceTrack({
+ trace: ctx,
+ data: {
sqlSource: query,
},
- 'Debug slice track',
- {ts: 'ts', dur: 'dur', name: 'name'},
- [],
- );
+ title: 'Debug slice track',
+ });
}
},
});
@@ -51,14 +49,13 @@
callback: async (arg: unknown) => {
const query = await getStringFromArgOrPrompt(ctx, arg);
if (exists(query)) {
- await addDebugCounterTrack(
- ctx,
- {
+ await addDebugCounterTrack({
+ trace: ctx,
+ data: {
sqlSource: query,
},
- 'Debug slice track',
- {ts: 'ts', value: 'value'},
- );
+ title: 'Debug slice track',
+ });
}
},
});
diff --git a/ui/src/plugins/dev.perfetto.ExplorePage/OWNERS b/ui/src/plugins/dev.perfetto.ExplorePage/OWNERS
new file mode 100644
index 0000000..99ba254
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.ExplorePage/OWNERS
@@ -0,0 +1 @@
+lydiatse@google.com
diff --git a/ui/src/frontend/explore_page.ts b/ui/src/plugins/dev.perfetto.ExplorePage/explore_page.ts
similarity index 75%
rename from ui/src/frontend/explore_page.ts
rename to ui/src/plugins/dev.perfetto.ExplorePage/explore_page.ts
index c3246a1..1c60d4f 100644
--- a/ui/src/frontend/explore_page.ts
+++ b/ui/src/plugins/dev.perfetto.ExplorePage/explore_page.ts
@@ -13,25 +13,35 @@
// limitations under the License.
import m from 'mithril';
-import {PageWithTraceAttrs} from './pages';
-import {Trace} from '../public/trace';
+import {PageWithTraceAttrs} from '../../public/page';
+import {Trace} from '../../public/trace';
import {
DurationColumn,
ProcessColumnSet,
StandardColumn,
ThreadColumnSet,
TimestampColumn,
-} from './widgets/sql/table/well_known_columns';
-import {SqlTableState} from './widgets/sql/table/state';
-import {SqlTable} from './widgets/sql/table/table';
-import {exists} from '../base/utils';
-import {Menu, MenuItem, MenuItemAttrs} from '../widgets/menu';
-import {TableColumn, TableColumnSet} from './widgets/sql/table/column';
-import {Button} from '../widgets/button';
-import {Icons} from '../base/semantic_icons';
-import {DetailsShell} from '../widgets/details_shell';
+} from '../../frontend/widgets/sql/table/well_known_columns';
+import {SqlTableState} from '../../frontend/widgets/sql/table/state';
+import {SqlTable} from '../../frontend/widgets/sql/table/table';
+import {exists} from '../../base/utils';
+import {Menu, MenuItem, MenuItemAttrs} from '../../widgets/menu';
+import {
+ TableColumn,
+ TableColumnSet,
+} from '../../frontend/widgets/sql/table/column';
+import {Button} from '../../widgets/button';
+import {Icons} from '../../base/semantic_icons';
+import {DetailsShell} from '../../widgets/details_shell';
+import {
+ Chart,
+ ChartOption,
+ createChartConfigFromSqlTableState,
+ renderChartComponent,
+} from '../../frontend/widgets/charts/chart';
+import {AddChartMenuItem} from '../../frontend/widgets/charts/add_chart_menu';
-interface ExplorePageState {
+interface ExploreTableState {
sqlTableState?: SqlTableState;
selectedTable?: ExplorableTable;
}
@@ -43,13 +53,12 @@
}
export class ExplorePage implements m.ClassComponent<PageWithTraceAttrs> {
- private readonly state: ExplorePageState;
+ private readonly state: ExploreTableState;
+ private readonly charts: Chart[];
constructor() {
- this.state = {
- sqlTableState: undefined,
- selectedTable: undefined,
- };
+ this.charts = [];
+ this.state = {};
}
// Show menu with standard library tables
@@ -112,7 +121,7 @@
this.state.selectedTable = table;
- const sqlTableState = new SqlTableState(
+ this.state.sqlTableState = new SqlTableState(
trace,
{
name: table.name,
@@ -120,7 +129,6 @@
},
{imports: [table.module]},
);
- this.state.sqlTableState = sqlTableState;
},
});
});
@@ -159,6 +167,16 @@
},
m(SqlTable, {
state: sqlTableState,
+ addColumnMenuItems: (column, columnAlias) =>
+ m(AddChartMenuItem, {
+ chartConfig: createChartConfigFromSqlTableState(
+ column,
+ columnAlias,
+ sqlTableState,
+ ),
+ chartOptions: [ChartOption.HISTOGRAM],
+ addChart: (chart) => this.charts.push(chart),
+ }),
}),
);
}
@@ -167,6 +185,7 @@
return m(
'.explore-page',
m(Menu, this.renderSelectableTablesMenuItems(attrs.trace)),
+ this.charts.map((chart) => renderChartComponent(chart)),
this.state.selectedTable && this.renderSqlTable(),
);
}
diff --git a/ui/src/plugins/dev.perfetto.ExplorePage/index.ts b/ui/src/plugins/dev.perfetto.ExplorePage/index.ts
new file mode 100644
index 0000000..5c18701
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.ExplorePage/index.ts
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {ExplorePage} from './explore_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.ExplorePage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/explore', page: ExplorePage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Explore',
+ href: '#!/explore',
+ icon: 'data_exploration',
+ });
+ }
+}
diff --git a/ui/src/plugins/dev.perfetto.Frames/actual_frames_track.ts b/ui/src/plugins/dev.perfetto.Frames/actual_frames_track.ts
index 03be4f2..c6ef4b3 100644
--- a/ui/src/plugins/dev.perfetto.Frames/actual_frames_track.ts
+++ b/ui/src/plugins/dev.perfetto.Frames/actual_frames_track.ts
@@ -20,6 +20,7 @@
import {STR_NULL} from '../../trace_processor/query_result';
import {Slice} from '../../public/track';
import {Trace} from '../../public/trace';
+import {TrackEventDetails} from '../../public/selection';
// color named and defined based on Material Design color palettes
// 500 colors indicate a timeline slice is not a partial jank (not a jank or
@@ -54,7 +55,7 @@
uri: string,
private trackIds: number[],
) {
- super({trace, uri});
+ super(trace, uri);
this.sliceLayout = {
...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
depthGuess: maxDepth,
@@ -90,6 +91,26 @@
colorScheme: getColorSchemeForJank(row.jankTag, row.jankSeverityType),
};
}
+
+ override async getSelectionDetails(
+ id: number,
+ ): Promise<TrackEventDetails | undefined> {
+ const baseDetails = await super.getSelectionDetails(id);
+ if (!baseDetails) return undefined;
+ return {
+ ...baseDetails,
+ tableName: 'slice',
+ };
+ }
+
+ // Override dataset from base class NamedSliceTrack as we don't want these
+ // tracks to participate in generic area selection aggregation (frames tracks
+ // have their own dedicated aggregation panel).
+ // TODO(stevegolton): In future CLs this will be handled with aggregation keys
+ // instead, as this track will have to expose a dataset anyway.
+ override getDataset() {
+ return undefined;
+ }
}
function getColorSchemeForJank(
diff --git a/ui/src/plugins/dev.perfetto.Frames/expected_frames_track.ts b/ui/src/plugins/dev.perfetto.Frames/expected_frames_track.ts
index bd41be4..27557fc 100644
--- a/ui/src/plugins/dev.perfetto.Frames/expected_frames_track.ts
+++ b/ui/src/plugins/dev.perfetto.Frames/expected_frames_track.ts
@@ -22,6 +22,7 @@
import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
import {Slice} from '../../public/track';
import {Trace} from '../../public/trace';
+import {TrackEventDetails} from '../../public/selection';
const GREEN = makeColorScheme(new HSLColor('#4CAF50')); // Green 500
@@ -32,7 +33,7 @@
uri: string,
private trackIds: number[],
) {
- super({trace, uri});
+ super(trace, uri);
this.sliceLayout = {
...SLICE_LAYOUT_FIT_CONTENT_DEFAULTS,
depthGuess: maxDepth,
@@ -61,4 +62,15 @@
getRowSpec(): NamedRow {
return NAMED_ROW;
}
+
+ override async getSelectionDetails(
+ id: number,
+ ): Promise<TrackEventDetails | undefined> {
+ const baseDetails = await super.getSelectionDetails(id);
+ if (!baseDetails) return undefined;
+ return {
+ ...baseDetails,
+ tableName: 'slice',
+ };
+ }
}
diff --git a/ui/src/plugins/dev.perfetto.Ftrace/ftrace_track.ts b/ui/src/plugins/dev.perfetto.Ftrace/ftrace_track.ts
index 31ea7de..ea87d0e 100644
--- a/ui/src/plugins/dev.perfetto.Ftrace/ftrace_track.ts
+++ b/ui/src/plugins/dev.perfetto.Ftrace/ftrace_track.ts
@@ -20,18 +20,22 @@
import {TrackData} from '../../common/track_data';
import {Engine} from '../../trace_processor/engine';
import {Track} from '../../public/track';
-import {LONG, STR} from '../../trace_processor/query_result';
+import {LONG, NUM, STR} from '../../trace_processor/query_result';
import {FtraceFilter} from './common';
import {Monitor} from '../../base/monitor';
import {TrackRenderContext} from '../../public/track';
+import {SourceDataset, Dataset} from '../../trace_processor/dataset';
const MARGIN = 2;
const RECT_HEIGHT = 18;
+const RECT_WIDTH = 8;
const TRACK_HEIGHT = RECT_HEIGHT + 2 * MARGIN;
-export interface Data extends TrackData {
- timestamps: BigInt64Array;
- names: string[];
+interface Data extends TrackData {
+ events: Array<{
+ timestamp: time;
+ color: string;
+ }>;
}
export interface Config {
@@ -53,6 +57,25 @@
this.monitor = new Monitor([() => store.state]);
}
+ getDataset(): Dataset {
+ return new SourceDataset({
+ // 'ftrace_event' doesn't have a dur column, but injecting dur=0 (all
+ // ftrace events are effectively 'instant') allows us to participate in
+ // generic slice aggregations
+ src: 'select id, ts, 0 as dur, name, cpu from ftrace_event',
+ schema: {
+ id: NUM,
+ name: STR,
+ ts: LONG,
+ dur: LONG,
+ },
+ filter: {
+ col: 'cpu',
+ eq: this.cpu,
+ },
+ });
+ }
+
async onUpdate({
visibleWindow,
resolution,
@@ -92,21 +115,22 @@
order by tsQuant limit ${LIMIT};`);
const rowCount = queryRes.numRows();
- const result: Data = {
+
+ const it = queryRes.iter({tsQuant: LONG, name: STR});
+ const events = [];
+ for (let row = 0; it.valid(); it.next(), row++) {
+ events.push({
+ timestamp: Time.fromRaw(it.tsQuant),
+ color: colorForFtrace(it.name).base.cssString,
+ });
+ }
+ return {
start,
end,
resolution,
length: rowCount,
- timestamps: new BigInt64Array(rowCount),
- names: [],
+ events,
};
-
- const it = queryRes.iter({tsQuant: LONG, name: STR});
- for (let row = 0; it.valid(); it.next(), row++) {
- result.timestamps[row] = it.tsQuant;
- result.names[row] = it.name;
- }
- return result;
}
render({ctx, size, timescale}: TrackRenderContext): void {
@@ -125,21 +149,10 @@
dataStartPx,
dataEndPx,
);
-
- const diamondSideLen = RECT_HEIGHT / Math.sqrt(2);
-
- for (let i = 0; i < data.timestamps.length; i++) {
- const name = data.names[i];
- ctx.fillStyle = colorForFtrace(name).base.cssString;
- const timestamp = Time.fromRaw(data.timestamps[i]);
- const xPos = Math.floor(timescale.timeToPx(timestamp));
-
- // Draw a diamond over the event
- ctx.save();
- ctx.translate(xPos, MARGIN);
- ctx.rotate(Math.PI / 4);
- ctx.fillRect(0, 0, diamondSideLen, diamondSideLen);
- ctx.restore();
+ for (const e of data.events) {
+ ctx.fillStyle = e.color;
+ const xPos = Math.floor(timescale.timeToPx(e.timestamp));
+ ctx.fillRect(xPos - RECT_WIDTH / 2, MARGIN, RECT_WIDTH, RECT_HEIGHT);
}
}
}
diff --git a/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts b/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts
index 9705156..b34717e 100644
--- a/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts
+++ b/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts
@@ -21,13 +21,14 @@
NamedRow,
NamedSliceTrack,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {TrackNode} from '../../public/workspace';
class GpuPidTrack extends NamedSliceTrack {
- upid: number;
-
- constructor(args: NewTrackArgs, upid: number) {
- super(args);
+ constructor(
+ trace: Trace,
+ uri: string,
+ protected readonly upid: number,
+ ) {
+ super(trace, uri);
this.upid = upid;
}
@@ -84,7 +85,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new GpuPidTrack({trace: ctx, uri}, upid),
+ track: new GpuPidTrack(ctx, uri, upid),
});
const track = new TrackNode({uri, title});
track.uri = uri;
diff --git a/ui/src/plugins/dev.perfetto.HeapProfile/heap_profile_track.ts b/ui/src/plugins/dev.perfetto.HeapProfile/heap_profile_track.ts
index d4effa4..45f1d0e 100644
--- a/ui/src/plugins/dev.perfetto.HeapProfile/heap_profile_track.ts
+++ b/ui/src/plugins/dev.perfetto.HeapProfile/heap_profile_track.ts
@@ -19,13 +19,13 @@
OnSliceClickArgs,
OnSliceOverArgs,
} from '../../frontend/base_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {
ProfileType,
profileType,
TrackEventDetails,
TrackEventSelection,
} from '../../public/selection';
+import {Trace} from '../../public/trace';
import {Slice} from '../../public/track';
import {LONG, STR} from '../../trace_processor/query_result';
import {HeapProfileFlamegraphDetailsPanel} from './heap_profile_details_panel';
@@ -44,12 +44,13 @@
HeapProfileRow
> {
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
private readonly tableName: string,
private readonly upid: number,
private readonly heapProfileIsIncomplete: boolean,
) {
- super(args);
+ super(trace, uri);
}
getSqlSource(): string {
diff --git a/ui/src/plugins/dev.perfetto.HeapProfile/index.ts b/ui/src/plugins/dev.perfetto.HeapProfile/index.ts
index f4f9121..2e0591f 100644
--- a/ui/src/plugins/dev.perfetto.HeapProfile/index.ts
+++ b/ui/src/plugins/dev.perfetto.HeapProfile/index.ts
@@ -92,15 +92,7 @@
kind: HEAP_PROFILE_TRACK_KIND,
upid,
},
- track: new HeapProfileTrack(
- {
- trace: ctx,
- uri,
- },
- tableName,
- upid,
- incomplete,
- ),
+ track: new HeapProfileTrack(ctx, uri, tableName, upid, incomplete),
});
const group = getOrCreateGroupForProcess(ctx.workspace, upid);
const track = new TrackNode({uri, title, sortOrder: -30});
diff --git a/ui/src/plugins/dev.perfetto.InsightsPage/index.ts b/ui/src/plugins/dev.perfetto.InsightsPage/index.ts
new file mode 100644
index 0000000..fdd84a1
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.InsightsPage/index.ts
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {InsightsPage} from './insights_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.InsightsPage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/insights', page: InsightsPage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Insights',
+ href: '#!/insights',
+ icon: 'insights',
+ });
+ }
+}
diff --git a/ui/src/frontend/insights_page.ts b/ui/src/plugins/dev.perfetto.InsightsPage/insights_page.ts
similarity index 93%
rename from ui/src/frontend/insights_page.ts
rename to ui/src/plugins/dev.perfetto.InsightsPage/insights_page.ts
index 77f8496..5b0b742 100644
--- a/ui/src/frontend/insights_page.ts
+++ b/ui/src/plugins/dev.perfetto.InsightsPage/insights_page.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {PageWithTraceAttrs} from './pages';
+import {PageWithTraceAttrs} from '../../public/page';
export class InsightsPage implements m.ClassComponent<PageWithTraceAttrs> {
view() {
diff --git a/ui/src/plugins/dev.perfetto.MetricsPage/index.ts b/ui/src/plugins/dev.perfetto.MetricsPage/index.ts
new file mode 100644
index 0000000..55cda40
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.MetricsPage/index.ts
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {MetricsPage} from './metrics_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.MetricsPage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/metrics', page: MetricsPage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Metrics',
+ href: '#!/metrics',
+ icon: 'speed',
+ sortOrder: 9,
+ });
+ }
+}
diff --git a/ui/src/frontend/metrics_page.ts b/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts
similarity index 87%
rename from ui/src/frontend/metrics_page.ts
rename to ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts
index 6cdb197..8f1f0f3 100644
--- a/ui/src/frontend/metrics_page.ts
+++ b/ui/src/plugins/dev.perfetto.MetricsPage/metrics_page.ts
@@ -20,17 +20,16 @@
pending,
Result,
success,
-} from '../base/result';
-import {raf} from '../core/raf_scheduler';
-import {MetricVisualisation} from '../public/plugin';
-import {Engine} from '../trace_processor/engine';
-import {STR} from '../trace_processor/query_result';
-import {Select} from '../widgets/select';
-import {Spinner} from '../widgets/spinner';
-import {VegaView} from '../widgets/vega_view';
-import {PageWithTraceAttrs} from './pages';
-import {assertExists} from '../base/logging';
-import {AppImpl} from '../core/app_impl';
+} from '../../base/result';
+import {MetricVisualisation} from '../../public/plugin';
+import {Engine} from '../../trace_processor/engine';
+import {STR} from '../../trace_processor/query_result';
+import {Select} from '../../widgets/select';
+import {Spinner} from '../../widgets/spinner';
+import {VegaView} from '../../widgets/vega_view';
+import {PageWithTraceAttrs} from '../../public/page';
+import {assertExists} from '../../base/logging';
+import {Trace} from '../../public/trace';
type Format = 'json' | 'prototext' | 'proto';
const FORMATS: Format[] = ['json', 'prototext', 'proto'];
@@ -58,7 +57,8 @@
}
class MetricsController {
- engine: Engine;
+ private readonly trace: Trace;
+ private readonly engine: Engine;
private _metrics: string[];
private _selected?: string;
private _result: Result<string>;
@@ -66,8 +66,9 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _json: any;
- constructor(engine: Engine) {
- this.engine = engine;
+ constructor(trace: Trace) {
+ this.trace = trace;
+ this.engine = trace.engine.getProxy('MetricsPage');
this._metrics = [];
this._result = success('');
this._json = {};
@@ -82,7 +83,7 @@
}
get visualisations(): MetricVisualisation[] {
- return AppImpl.instance.plugins
+ return this.trace.plugins
.metricVisualisations()
.filter((v) => v.metric === this.selected);
}
@@ -145,10 +146,10 @@
}
})
.finally(() => {
- raf.scheduleFullRedraw();
+ this.trace.scheduleFullRedraw();
});
}
- raf.scheduleFullRedraw();
+ this.trace.scheduleFullRedraw();
}
}
@@ -244,8 +245,7 @@
private controller?: MetricsController;
oninit({attrs}: m.Vnode<PageWithTraceAttrs>) {
- const engine = attrs.trace.engine.getProxy('MetricsPage');
- this.controller = new MetricsController(engine);
+ this.controller = new MetricsController(attrs.trace);
}
view() {
diff --git a/ui/src/plugins/dev.perfetto.PerfSamplesProfile/index.ts b/ui/src/plugins/dev.perfetto.PerfSamplesProfile/index.ts
index 26ac305..04e8b13 100644
--- a/ui/src/plugins/dev.perfetto.PerfSamplesProfile/index.ts
+++ b/ui/src/plugins/dev.perfetto.PerfSamplesProfile/index.ts
@@ -57,13 +57,7 @@
kind: PERF_SAMPLES_PROFILE_TRACK_KIND,
upid,
},
- track: new ProcessPerfSamplesProfileTrack(
- {
- trace: ctx,
- uri,
- },
- upid,
- ),
+ track: new ProcessPerfSamplesProfileTrack(ctx, uri, upid),
});
const group = getOrCreateGroupForProcess(ctx.workspace, upid);
const track = new TrackNode({uri, title, sortOrder: -40});
@@ -103,13 +97,7 @@
utid,
upid: upid ?? undefined,
},
- track: new ThreadPerfSamplesProfileTrack(
- {
- trace: ctx,
- uri,
- },
- utid,
- ),
+ track: new ThreadPerfSamplesProfileTrack(ctx, uri, utid),
});
const group = getOrCreateGroupForThread(ctx.workspace, utid);
const track = new TrackNode({uri, title, sortOrder: -50});
diff --git a/ui/src/plugins/dev.perfetto.PerfSamplesProfile/perf_samples_profile_track.ts b/ui/src/plugins/dev.perfetto.PerfSamplesProfile/perf_samples_profile_track.ts
index 839d0d1..06f3244 100644
--- a/ui/src/plugins/dev.perfetto.PerfSamplesProfile/perf_samples_profile_track.ts
+++ b/ui/src/plugins/dev.perfetto.PerfSamplesProfile/perf_samples_profile_track.ts
@@ -19,7 +19,6 @@
BaseSliceTrack,
OnSliceClickArgs,
} from '../../frontend/base_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {NAMED_ROW, NamedRow} from '../../frontend/named_slice_track';
import {getColorForSample} from '../../public/lib/colorizer';
import {
@@ -37,6 +36,7 @@
import {time} from '../../base/time';
import {TrackEventDetailsPanel} from '../../public/details_panel';
import {Flamegraph, FLAMEGRAPH_STATE_SCHEMA} from '../../widgets/flamegraph';
+import {Trace} from '../../public/trace';
interface PerfSampleRow extends NamedRow {
callsiteId: number;
@@ -46,8 +46,8 @@
Slice,
PerfSampleRow
> {
- constructor(args: NewTrackArgs) {
- super(args);
+ constructor(trace: Trace, uri: string) {
+ super(trace, uri);
}
protected getRowSpec(): PerfSampleRow {
@@ -75,10 +75,11 @@
export class ProcessPerfSamplesProfileTrack extends BasePerfSamplesProfileTrack {
constructor(
- args: NewTrackArgs,
- private upid: number,
+ trace: Trace,
+ uri: string,
+ private readonly upid: number,
) {
- super(args);
+ super(trace, uri);
}
getSqlSource(): string {
@@ -170,10 +171,11 @@
export class ThreadPerfSamplesProfileTrack extends BasePerfSamplesProfileTrack {
constructor(
- args: NewTrackArgs,
- private utid: number,
+ trace: Trace,
+ uri: string,
+ private readonly utid: number,
) {
- super(args);
+ super(trace, uri);
}
getSqlSource(): string {
diff --git a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts
index 55d1821..f3704ac 100644
--- a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts
+++ b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/fullTraceJankMetricHandler.ts
@@ -19,8 +19,7 @@
MetricHandler,
} from './metricUtils';
import {Trace} from '../../../public/trace';
-import {addAndPinSliceTrack} from '../../dev.perfetto.AndroidCujs/trackUtils';
-import {SimpleSliceTrackConfig} from '../../../frontend/simple_slice_track';
+import {addDebugSliceTrack} from '../../../public/debug_tracks';
class FullTraceJankMetricHandler implements MetricHandler {
/**
@@ -55,16 +54,12 @@
INCLUDE PERFETTO MODULE android.frames.jank_type;
INCLUDE PERFETTO MODULE slices.slices;
`;
- const {config: fullTraceJankConfig, trackName: trackName} =
- this.fullTraceJankConfig(metricData);
+ const config = this.fullTraceJankConfig(metricData);
await ctx.engine.query(INCLUDE_PREQUERY);
- addAndPinSliceTrack(ctx, fullTraceJankConfig, trackName);
+ addDebugSliceTrack({trace: ctx, ...config});
}
- private fullTraceJankConfig(metricData: FullTraceMetricData): {
- config: SimpleSliceTrackConfig;
- trackName: string;
- } {
+ private fullTraceJankConfig(metricData: FullTraceMetricData) {
let jankTypeFilter;
let jankTypeDisplayName;
if (metricData.jankType?.includes('app')) {
@@ -115,18 +110,18 @@
'process_name',
'pid',
];
- const fullTraceJankConfig: SimpleSliceTrackConfig = {
+
+ const trackName = jankTypeDisplayName + ' missed frames in ' + processName;
+
+ return {
data: {
sqlSource: fullTraceJankQuery,
columns: fullTraceJankColumns,
},
columns: {ts: 'ts', dur: 'dur', name: 'name'},
argColumns: fullTraceJankColumns,
+ tableName: trackName,
};
-
- const trackName = jankTypeDisplayName + ' missed frames in ' + processName;
-
- return {config: fullTraceJankConfig, trackName: trackName};
}
}
diff --git a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinBlockingCall.ts b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinBlockingCall.ts
index 3ca5f30..1ae9ac6 100644
--- a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinBlockingCall.ts
+++ b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinBlockingCall.ts
@@ -18,9 +18,8 @@
MetricHandler,
} from './metricUtils';
import {Trace} from '../../../public/trace';
-import {SimpleSliceTrackConfig} from '../../../frontend/simple_slice_track';
import {addJankCUJDebugTrack} from '../../dev.perfetto.AndroidCujs';
-import {addAndPinSliceTrack} from '../../dev.perfetto.AndroidCujs/trackUtils';
+import {addDebugSliceTrack} from '../../../public/debug_tracks';
class BlockingCallMetricHandler implements MetricHandler {
/**
@@ -54,9 +53,8 @@
*/
public addMetricTrack(metricData: BlockingCallMetricData, ctx: Trace): void {
this.pinSingleCuj(ctx, metricData);
- const {config: blockingCallMetricConfig, trackName: trackName} =
- this.blockingCallTrackConfig(metricData);
- addAndPinSliceTrack(ctx, blockingCallMetricConfig, trackName);
+ const config = this.blockingCallTrackConfig(metricData);
+ addDebugSliceTrack({trace: ctx, ...config});
}
private pinSingleCuj(ctx: Trace, metricData: BlockingCallMetricData) {
@@ -64,10 +62,7 @@
addJankCUJDebugTrack(ctx, trackName, metricData.cujName);
}
- private blockingCallTrackConfig(metricData: BlockingCallMetricData): {
- config: SimpleSliceTrackConfig;
- trackName: string;
- } {
+ private blockingCallTrackConfig(metricData: BlockingCallMetricData) {
const cuj = metricData.cujName;
const processName = metricData.process;
const blockingCallName = metricData.blockingCallName;
@@ -81,18 +76,16 @@
AND name = "${blockingCallName}"
`;
- const blockingCallMetricConfig: SimpleSliceTrackConfig = {
+ const trackName = 'Blocking calls in ' + processName;
+ return {
data: {
sqlSource: blockingCallDuringCujQuery,
columns: ['name', 'ts', 'dur'],
},
columns: {ts: 'ts', dur: 'dur', name: 'name'},
argColumns: ['name', 'ts', 'dur'],
+ trackName,
};
-
- const trackName = 'Blocking calls in ' + processName;
-
- return {config: blockingCallMetricConfig, trackName: trackName};
}
}
diff --git a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinCujScoped.ts b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinCujScoped.ts
index 4ae752a..5d61843 100644
--- a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinCujScoped.ts
+++ b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/handlers/pinCujScoped.ts
@@ -20,14 +20,11 @@
} from './metricUtils';
import {NUM} from '../../../trace_processor/query_result';
import {Trace} from '../../../public/trace';
-import {SimpleSliceTrackConfig} from '../../../frontend/simple_slice_track';
// TODO(primiano): make deps check stricter, we shouldn't allow plugins to
// depend on each other.
-import {
- addAndPinSliceTrack,
- focusOnSlice,
-} from '../../dev.perfetto.AndroidCujs/trackUtils';
+import {focusOnSlice} from '../../dev.perfetto.AndroidCujs/trackUtils';
+import {addDebugSliceTrack} from '../../../public/debug_tracks';
const ENABLE_FOCUS_ON_FIRST_JANK = true;
@@ -63,12 +60,11 @@
*/
public async addMetricTrack(metricData: CujScopedMetricData, ctx: Trace) {
// TODO: b/349502258 - Refactor to single API
- const {
- config: cujScopedJankSlice,
- trackName: trackName,
- tableName: tableName,
- } = await this.cujScopedTrackConfig(metricData, ctx);
- addAndPinSliceTrack(ctx, cujScopedJankSlice, trackName);
+ const {tableName, ...config} = await this.cujScopedTrackConfig(
+ metricData,
+ ctx,
+ );
+ addDebugSliceTrack({trace: ctx, ...config});
if (ENABLE_FOCUS_ON_FIRST_JANK) {
await this.focusOnFirstJank(ctx, tableName);
}
@@ -77,11 +73,7 @@
private async cujScopedTrackConfig(
metricData: CujScopedMetricData,
ctx: Trace,
- ): Promise<{
- config: SimpleSliceTrackConfig;
- trackName: string;
- tableName: string;
- }> {
+ ) {
let jankTypeFilter;
let jankTypeDisplayName = 'all';
if (metricData.jankType?.includes('app')) {
@@ -114,20 +106,20 @@
FROM ${tableWithJankyFramesName}
`;
- const cujScopedJankSlice: SimpleSliceTrackConfig = {
+ const trackName = jankTypeDisplayName + ' missed frames in ' + processName;
+
+ const cujScopedJankSlice = {
data: {
sqlSource: jankyFramesDuringCujQuery,
columns: ['id', 'ts', 'dur'],
},
columns: {ts: 'ts', dur: 'dur', name: 'id'},
argColumns: ['id', 'ts', 'dur'],
+ trackName,
};
- const trackName = jankTypeDisplayName + ' missed frames in ' + processName;
-
return {
- config: cujScopedJankSlice,
- trackName: trackName,
+ ...cujScopedJankSlice,
tableName: tableWithJankyFramesName,
};
}
diff --git a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/index.ts b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/index.ts
index b7df5a6..430acd8 100644
--- a/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/index.ts
+++ b/ui/src/plugins/dev.perfetto.PinAndroidPerfMetrics/index.ts
@@ -61,21 +61,19 @@
}
async onTraceLoad(ctx: Trace) {
- ctx.addEventListener('traceready', () => {
- ctx.commands.registerCommand({
- id: 'dev.perfetto.PinAndroidPerfMetrics#PinAndroidPerfMetrics',
- name: 'Add and Pin: Jank Metric Slice',
- callback: async (metric) => {
- metric = prompt('Metrics names (separated by comma)', '');
- if (metric === null) return;
- const metricList = metric.split(',');
- this.callHandlers(metricList, ctx);
- },
- });
- if (metrics.length !== 0) {
- this.callHandlers(metrics, ctx);
- }
+ ctx.commands.registerCommand({
+ id: 'dev.perfetto.PinAndroidPerfMetrics#PinAndroidPerfMetrics',
+ name: 'Add and Pin: Jank Metric Slice',
+ callback: async (metric) => {
+ metric = prompt('Metrics names (separated by comma)', '');
+ if (metric === null) return;
+ const metricList = metric.split(',');
+ this.callHandlers(metricList, ctx);
+ },
});
+ if (metrics.length !== 0) {
+ this.callHandlers(metrics, ctx);
+ }
}
private async callHandlers(metricsList: string[], ctx: Trace) {
diff --git a/ui/src/plugins/dev.perfetto.QueryPage/index.ts b/ui/src/plugins/dev.perfetto.QueryPage/index.ts
new file mode 100644
index 0000000..2fd4b77
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.QueryPage/index.ts
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {QueryPage} from './query_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.QueryPage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/query', page: QueryPage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Query (SQL)',
+ href: '#!/query',
+ icon: 'database',
+ sortOrder: 1,
+ });
+ }
+}
diff --git a/ui/src/frontend/query_history.ts b/ui/src/plugins/dev.perfetto.QueryPage/query_history.ts
similarity index 92%
rename from ui/src/frontend/query_history.ts
rename to ui/src/plugins/dev.perfetto.QueryPage/query_history.ts
index 1b2129e..a98c987 100644
--- a/ui/src/frontend/query_history.ts
+++ b/ui/src/plugins/dev.perfetto.QueryPage/query_history.ts
@@ -13,15 +13,16 @@
// limitations under the License.
import m from 'mithril';
-import {Icons} from '../base/semantic_icons';
-import {assertTrue} from '../base/logging';
-import {Icon} from '../widgets/icon';
-import {raf} from '../core/raf_scheduler';
+import {Icons} from '../../base/semantic_icons';
+import {assertTrue} from '../../base/logging';
+import {Icon} from '../../widgets/icon';
import {z} from 'zod';
+import {Trace} from '../../public/trace';
const QUERY_HISTORY_KEY = 'queryHistory';
export interface QueryHistoryComponentAttrs {
+ trace: Trace;
runQuery: (query: string) => void;
setQuery: (query: string) => void;
}
@@ -37,7 +38,7 @@
for (let i = queryHistoryStorage.data.length - 1; i >= 0; i--) {
const entry = queryHistoryStorage.data[i];
const arr = entry.starred ? starred : unstarred;
- arr.push({index: i, entry, runQuery, setQuery});
+ arr.push({trace: attrs.trace, index: i, entry, runQuery, setQuery});
}
return m(
'.query-history',
@@ -52,6 +53,7 @@
}
export interface HistoryItemComponentAttrs {
+ trace: Trace;
index: number;
entry: QueryHistoryEntry;
runQuery: (query: string) => void;
@@ -75,7 +77,7 @@
vnode.attrs.index,
!vnode.attrs.entry.starred,
);
- raf.scheduleFullRedraw();
+ vnode.attrs.trace.scheduleFullRedraw();
},
},
m(Icon, {icon: Icons.Star, filled: vnode.attrs.entry.starred}),
@@ -99,7 +101,7 @@
{
onclick: () => {
queryHistoryStorage.remove(vnode.attrs.index);
- raf.scheduleFullRedraw();
+ vnode.attrs.trace.scheduleFullRedraw();
},
},
m(Icon, {icon: 'delete'}),
diff --git a/ui/src/frontend/query_page.ts b/ui/src/plugins/dev.perfetto.QueryPage/query_page.ts
similarity index 83%
rename from ui/src/frontend/query_page.ts
rename to ui/src/plugins/dev.perfetto.QueryPage/query_page.ts
index 6e2d6fd..9ceaefc 100644
--- a/ui/src/frontend/query_page.ts
+++ b/ui/src/plugins/dev.perfetto.QueryPage/query_page.ts
@@ -13,17 +13,16 @@
// limitations under the License.
import m from 'mithril';
-import {SimpleResizeObserver} from '../base/resize_observer';
-import {undoCommonChatAppReplacements} from '../base/string_utils';
-import {QueryResponse, runQuery} from '../public/lib/query_table/queries';
-import {raf} from '../core/raf_scheduler';
-import {Callout} from '../widgets/callout';
-import {Editor} from '../widgets/editor';
-import {PageWithTraceAttrs} from './pages';
+import {SimpleResizeObserver} from '../../base/resize_observer';
+import {undoCommonChatAppReplacements} from '../../base/string_utils';
+import {QueryResponse, runQuery} from '../../public/lib/query_table/queries';
+import {Callout} from '../../widgets/callout';
+import {Editor} from '../../widgets/editor';
+import {PageWithTraceAttrs} from '../../public/page';
import {QueryHistoryComponent, queryHistoryStorage} from './query_history';
-import {Trace, TraceAttrs} from '../public/trace';
-import {addQueryResultsTab} from '../public/lib/query_table/query_result_tab';
-import {QueryTable} from '../public/lib/query_table/query_table';
+import {Trace, TraceAttrs} from '../../public/trace';
+import {addQueryResultsTab} from '../../public/lib/query_table/query_result_tab';
+import {QueryTable} from '../../public/lib/query_table/query_table';
interface QueryPageState {
enteredText: string;
@@ -59,10 +58,9 @@
return;
}
state.queryResult = resp;
- raf.scheduleFullRedraw();
+ trace.scheduleFullRedraw();
},
);
- raf.scheduleDelayedFullRedraw();
}
export type QueryInputAttrs = TraceAttrs;
@@ -99,7 +97,7 @@
onUpdate: (text: string) => {
state.enteredText = text;
- raf.scheduleFullRedraw();
+ attrs.trace.scheduleFullRedraw();
},
});
}
@@ -128,11 +126,12 @@
fillParent: false,
}),
m(QueryHistoryComponent, {
+ trace: attrs.trace,
runQuery: (q: string) => runManualQuery(attrs.trace, q),
setQuery: (q: string) => {
state.enteredText = q;
state.generation++;
- raf.scheduleFullRedraw();
+ attrs.trace.scheduleFullRedraw();
},
}),
);
diff --git a/ui/src/controller/adb.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb.ts
similarity index 98%
rename from ui/src/controller/adb.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb.ts
index e188ea7..5197d23 100644
--- a/ui/src/controller/adb.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb.ts
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists} from '../base/logging';
-import {isString} from '../base/object_utils';
-import {utf8Decode, utf8Encode} from '../base/string_utils';
+import {assertExists} from '../../base/logging';
+import {isString} from '../../base/object_utils';
+import {utf8Decode, utf8Encode} from '../../base/string_utils';
import {Adb, AdbMsg, AdbStream, CmdType} from './adb_interfaces';
export const VERSION_WITH_CHECKSUM = 0x01000000;
diff --git a/ui/src/controller/adb_base_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_base_controller.ts
similarity index 88%
rename from ui/src/controller/adb_base_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_base_controller.ts
index beab6c9..c447df5 100644
--- a/ui/src/controller/adb_base_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb_base_controller.ts
@@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {exists} from '../base/utils';
-import {isAdbTarget} from '../common/state';
+import {exists} from '../../base/utils';
+import {RecordingState, RecordingTarget, isAdbTarget} from './state';
import {
extractDurationFromTraceConfig,
extractTraceConfig,
-} from '../core/trace_config_utils';
-import {globals} from '../frontend/globals';
+} from './trace_config_utils';
import {Adb} from './adb_interfaces';
import {ReadBuffersResponse} from './consumer_port_types';
import {Consumer, RpcConsumerPort} from './record_controller_interfaces';
@@ -44,10 +43,16 @@
protected adb: Adb;
protected state = AdbConnectionState.READY_TO_CONNECT;
protected device?: USBDevice;
+ protected recState: RecordingState;
- protected constructor(adb: Adb, consumer: Consumer) {
+ protected constructor(
+ adb: Adb,
+ consumer: Consumer,
+ recState: RecordingState,
+ ) {
super(consumer);
this.adb = adb;
+ this.recState = recState;
}
async handleCommand(method: string, params: Uint8Array) {
@@ -74,10 +79,10 @@
this.deviceDisconnected()
) {
this.state = AdbConnectionState.AUTH_IN_PROGRESS;
- this.device = await this.findDevice();
+ this.device = await this.findDevice(this.recState.recordingTarget);
if (!this.device) {
this.state = AdbConnectionState.READY_TO_CONNECT;
- const target = globals.state.recordingTarget;
+ const target = this.recState.recordingTarget;
throw Error(
`Device with serial ${
isAdbTarget(target) ? target.serial : 'n/a'
@@ -91,7 +96,7 @@
await this.adb.connect(this.device);
// During the authentication the device may have been disconnected.
- if (!globals.state.recordingInProgress || this.deviceDisconnected()) {
+ if (!this.recState.recordingInProgress || this.deviceDisconnected()) {
throw Error('Recording not in progress after adb authorization.');
}
@@ -140,9 +145,10 @@
};
}
- async findDevice(): Promise<USBDevice | undefined> {
+ async findDevice(
+ connectedDevice: RecordingTarget,
+ ): Promise<USBDevice | undefined> {
if (!('usb' in navigator)) return undefined;
- const connectedDevice = globals.state.recordingTarget;
if (!isAdbTarget(connectedDevice)) return undefined;
const devices = await navigator.usb.getDevices();
return devices.find((d) => d.serialNumber === connectedDevice.serial);
diff --git a/ui/src/controller/adb_interfaces.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_interfaces.ts
similarity index 100%
rename from ui/src/controller/adb_interfaces.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_interfaces.ts
diff --git a/ui/src/controller/adb_jsdomtest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_jsdomtest.ts
similarity index 97%
rename from ui/src/controller/adb_jsdomtest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_jsdomtest.ts
index 9f51a97..1d228a5 100644
--- a/ui/src/controller/adb_jsdomtest.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb_jsdomtest.ts
@@ -19,7 +19,7 @@
DEFAULT_MAX_PAYLOAD_BYTES,
VERSION_WITH_CHECKSUM,
} from './adb';
-import {utf8Encode} from '../base/string_utils';
+import {utf8Encode} from '../../base/string_utils';
test('startAuthentication', async () => {
const adb = new AdbOverWebUsb();
diff --git a/ui/src/controller/adb_record_controller_jsdomtest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_record_controller_jsdomtest.ts
similarity index 91%
rename from ui/src/controller/adb_record_controller_jsdomtest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_record_controller_jsdomtest.ts
index c25de72..6078a59 100644
--- a/ui/src/controller/adb_record_controller_jsdomtest.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb_record_controller_jsdomtest.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import {dingus} from 'dingusjs';
-import {utf8Encode} from '../base/string_utils';
-import {EnableTracingRequest, TraceConfig} from '../protos';
+import {utf8Encode} from '../../base/string_utils';
+import {EnableTracingRequest, TraceConfig} from '../../protos';
import {AdbStream, MockAdb, MockAdbStream} from './adb_interfaces';
import {AdbConsumerPort} from './adb_shell_controller';
import {Consumer} from './record_controller_interfaces';
+import {createEmptyState} from './empty_state';
function generateMockConsumer(): Consumer {
return {
@@ -28,7 +29,11 @@
}
const mainCallback = generateMockConsumer();
const adbMock = new MockAdb();
-const adbController = new AdbConsumerPort(adbMock, mainCallback);
+const adbController = new AdbConsumerPort(
+ adbMock,
+ mainCallback,
+ createEmptyState(),
+);
const mockIntArray = new Uint8Array();
const enableTracingRequest = new EnableTracingRequest();
@@ -60,7 +65,11 @@
test('enableTracing', async () => {
const mainCallback = generateMockConsumer();
const adbMock = new MockAdb();
- const adbController = new AdbConsumerPort(adbMock, mainCallback);
+ const adbController = new AdbConsumerPort(
+ adbMock,
+ mainCallback,
+ createEmptyState(),
+ );
adbController.sendErrorMessage = jest
.fn()
diff --git a/ui/src/controller/adb_shell_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_shell_controller.ts
similarity index 95%
rename from ui/src/controller/adb_shell_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_shell_controller.ts
index 7f7f359..623dc5d 100644
--- a/ui/src/controller/adb_shell_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb_shell_controller.ts
@@ -12,8 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {base64Encode, utf8Decode} from '../base/string_utils';
-import {extractTraceConfig} from '../core/trace_config_utils';
+import {base64Encode, utf8Decode} from '../../base/string_utils';
+import {RecordingState} from './state';
+import {extractTraceConfig} from './trace_config_utils';
import {AdbBaseConsumerPort, AdbConnectionState} from './adb_base_controller';
import {Adb, AdbStream} from './adb_interfaces';
import {ReadBuffersResponse} from './consumer_port_types';
@@ -31,8 +32,8 @@
shellState: AdbShellState = AdbShellState.READY;
private recordShell?: AdbStream;
- constructor(adb: Adb, consumer: Consumer) {
- super(adb, consumer);
+ constructor(adb: Adb, consumer: Consumer, recState: RecordingState) {
+ super(adb, consumer, recState);
this.adb = adb;
}
diff --git a/ui/src/controller/adb_socket_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/adb_socket_controller.ts
similarity index 97%
rename from ui/src/controller/adb_socket_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/adb_socket_controller.ts
index 4908c27..a676747 100644
--- a/ui/src/controller/adb_socket_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/adb_socket_controller.ts
@@ -20,13 +20,14 @@
GetTraceStatsResponse,
IPCFrame,
ReadBuffersResponse,
-} from '../protos';
+} from '../../protos';
import {AdbBaseConsumerPort, AdbConnectionState} from './adb_base_controller';
import {Adb, AdbStream} from './adb_interfaces';
import {isReadBuffersResponse} from './consumer_port_types';
import {Consumer} from './record_controller_interfaces';
-import {exists} from '../base/utils';
-import {assertTrue} from '../base/logging';
+import {exists} from '../../base/utils';
+import {assertTrue} from '../../base/logging';
+import {RecordingState} from './state';
enum SocketState {
DISCONNECTED,
@@ -82,8 +83,8 @@
private socketCommandQueue: Command[] = [];
- constructor(adb: Adb, consumer: Consumer) {
- super(adb, consumer);
+ constructor(adb: Adb, consumer: Consumer, recState: RecordingState) {
+ super(adb, consumer, recState);
}
async invoke(method: string, params: Uint8Array) {
diff --git a/ui/src/frontend/recording/advanced_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/advanced_settings.ts
similarity index 91%
rename from ui/src/frontend/recording/advanced_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/advanced_settings.ts
index c933093..35e6fe2 100644
--- a/ui/src/frontend/recording/advanced_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/advanced_settings.ts
@@ -13,18 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {
- Dropdown,
- DropdownAttrs,
- Probe,
- ProbeAttrs,
- Slider,
- SliderAttrs,
- Textarea,
- TextareaAttrs,
- Toggle,
- ToggleAttrs,
-} from '../record_widgets';
+import {Dropdown, Probe, Slider, Textarea, Toggle} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
const FTRACE_CATEGORIES = new Map<string, string>();
@@ -52,6 +41,7 @@
implements m.ClassComponent<RecordingSectionAttrs>
{
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
m(
@@ -64,7 +54,8 @@
enabled by other probes.`,
setEnabled: (cfg, val) => (cfg.ftrace = val),
isEnabled: (cfg) => cfg.ftrace,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Toggle, {
title: 'Resolve kernel symbols',
cssClass: '.thin',
@@ -73,7 +64,8 @@
(userdebug/eng builds only).`,
setEnabled: (cfg, val) => (cfg.symbolizeKsyms = val),
isEnabled: (cfg) => cfg.symbolizeKsyms,
- } as ToggleAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Buf size',
cssClass: '.thin',
@@ -82,7 +74,8 @@
zeroIsDefault: true,
set: (cfg, val) => (cfg.ftraceBufferSizeKb = val),
get: (cfg) => cfg.ftraceBufferSizeKb,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Drain rate',
cssClass: '.thin',
@@ -91,14 +84,16 @@
zeroIsDefault: true,
set: (cfg, val) => (cfg.ftraceDrainPeriodMs = val),
get: (cfg) => cfg.ftraceDrainPeriodMs,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Dropdown, {
title: 'Event groups',
cssClass: '.multicolumn.ftrace-events',
options: FTRACE_CATEGORIES,
set: (cfg, val) => (cfg.ftraceEvents = val),
get: (cfg) => cfg.ftraceEvents,
- } as DropdownAttrs),
+ recCfg,
+ }),
m(Textarea, {
placeholder:
'Add extra events, one per line, e.g.:\n' +
@@ -106,7 +101,8 @@
'kmem/*',
set: (cfg, val) => (cfg.ftraceExtraEvents = val),
get: (cfg) => cfg.ftraceExtraEvents,
- } as TextareaAttrs),
+ recCfg,
+ }),
),
);
}
diff --git a/ui/src/frontend/recording/android_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/android_settings.ts
similarity index 87%
rename from ui/src/frontend/recording/android_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/android_settings.ts
index a0930d2..7c0d741 100644
--- a/ui/src/frontend/recording/android_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/android_settings.ts
@@ -14,24 +14,14 @@
import m from 'mithril';
import {AtomId, DataSourceDescriptor} from '../../protos';
-import {globals} from '../globals';
-import {
- Dropdown,
- DropdownAttrs,
- Probe,
- ProbeAttrs,
- Slider,
- SliderAttrs,
- Textarea,
- TextareaAttrs,
- Toggle,
- ToggleAttrs,
-} from '../record_widgets';
+import {Dropdown, Probe, Slider, Textarea, Toggle} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
+import {RecordConfig} from './record_config_types';
const PUSH_ATOM_IDS = new Map<string, string>();
const PULL_ATOM_IDS = new Map<string, string>();
for (const key in AtomId) {
+ if (!Object.hasOwn(AtomId, key)) continue;
const value = Number(AtomId[key]);
if (!isNaN(value)) {
if (value > 2 && value < 9999) {
@@ -90,9 +80,13 @@
return false;
}
-class AtraceAppsList implements m.ClassComponent {
- view() {
- if (globals.state.recordConfig.allAtraceApps) {
+interface AtraceAppsListAttrs {
+ recCfg: RecordConfig;
+}
+
+class AtraceAppsList implements m.ClassComponent<AtraceAppsListAttrs> {
+ view({attrs}: m.CVnode<AtraceAppsListAttrs>) {
+ if (attrs.recCfg.allAtraceApps) {
return m('div');
}
@@ -105,7 +99,8 @@
cssClass: '.record-apps-list',
set: (cfg, val) => (cfg.atraceApps = val),
get: (cfg) => cfg.atraceApps,
- } as TextareaAttrs);
+ recCfg: attrs.recCfg,
+ });
}
}
@@ -113,6 +108,7 @@
implements m.ClassComponent<RecordingSectionAttrs>
{
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
let atraceCategories = DEFAULT_ATRACE_CATEGORIES;
for (const dataSource of attrs.dataSources) {
if (
@@ -145,21 +141,24 @@
os.Trace())`,
setEnabled: (cfg, val) => (cfg.atrace = val),
isEnabled: (cfg) => cfg.atrace,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Dropdown, {
title: 'Categories',
cssClass: '.multicolumn.atrace-categories',
options: atraceCategories,
set: (cfg, val) => (cfg.atraceCats = val),
get: (cfg) => cfg.atraceCats,
- } as DropdownAttrs),
+ recCfg,
+ }),
m(Toggle, {
title: 'Record events from all Android apps and services',
descr: '',
setEnabled: (cfg, val) => (cfg.allAtraceApps = val),
isEnabled: (cfg) => cfg.allAtraceApps,
- } as ToggleAttrs),
- m(AtraceAppsList),
+ recCfg,
+ }),
+ m(AtraceAppsList, {recCfg}),
),
m(
Probe,
@@ -170,14 +169,16 @@
specified, all buffers are selected.`,
setEnabled: (cfg, val) => (cfg.androidLogs = val),
isEnabled: (cfg) => cfg.androidLogs,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Dropdown, {
title: 'Buffers',
cssClass: '.multicolumn',
options: LOG_BUFFERS,
set: (cfg, val) => (cfg.androidLogBuffers = val),
get: (cfg) => cfg.androidLogBuffers,
- } as DropdownAttrs),
+ recCfg,
+ }),
),
m(Probe, {
title: 'Frame timeline',
@@ -186,7 +187,8 @@
Requires Android 12 (S) or above.`,
setEnabled: (cfg, val) => (cfg.androidFrameTimeline = val),
isEnabled: (cfg) => cfg.androidFrameTimeline,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(Probe, {
title: 'Game intervention list',
img: '',
@@ -194,7 +196,8 @@
Requires Android 13 (T) or above.`,
setEnabled: (cfg, val) => (cfg.androidGameInterventionList = val),
isEnabled: (cfg) => cfg.androidGameInterventionList,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(
Probe,
{
@@ -204,7 +207,8 @@
Requires Android 14 (U) or above.`,
setEnabled: (cfg, val) => (cfg.androidNetworkTracing = val),
isEnabled: (cfg) => cfg.androidNetworkTracing,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -212,39 +216,43 @@
unit: 'ms',
set: (cfg, val) => (cfg.androidNetworkTracingPollMs = val),
get: (cfg) => cfg.androidNetworkTracingPollMs,
- } as SliderAttrs),
+ recCfg,
+ }),
),
m(
Probe,
{
title: 'Statsd Atoms',
img: '',
- descr: 'Record instances of statsd atoms to the \'Statsd Atoms\' track.',
- setEnabled: (cfg, val) => cfg.androidStatsd = val,
+ descr:
+ "Record instances of statsd atoms to the 'Statsd Atoms' track.",
+ setEnabled: (cfg, val) => (cfg.androidStatsd = val),
isEnabled: (cfg) => cfg.androidStatsd,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Dropdown, {
title: 'Pushed Atoms',
cssClass: '.singlecolumn',
options: PUSH_ATOM_IDS,
set: (cfg, val) => (cfg.androidStatsdPushedAtoms = val),
get: (cfg) => cfg.androidStatsdPushedAtoms,
- } as DropdownAttrs),
+ recCfg,
+ }),
m(Textarea, {
placeholder:
- 'Add raw pushed atoms IDs, one per line, e.g.:\n' +
- '818\n' +
- '819',
+ 'Add raw pushed atoms IDs, one per line, e.g.:\n' + '818\n' + '819',
set: (cfg, val) => (cfg.androidStatsdRawPushedAtoms = val),
get: (cfg) => cfg.androidStatsdRawPushedAtoms,
- } as TextareaAttrs),
+ recCfg,
+ }),
m(Dropdown, {
title: 'Pulled Atoms',
cssClass: '.singlecolumn',
options: PULL_ATOM_IDS,
set: (cfg, val) => (cfg.androidStatsdPulledAtoms = val),
get: (cfg) => cfg.androidStatsdPulledAtoms,
- } as DropdownAttrs),
+ recCfg,
+ }),
m(Textarea, {
placeholder:
'Add raw pulled atom IDs, one per line, e.g.:\n' +
@@ -252,7 +260,8 @@
'10064\n',
set: (cfg, val) => (cfg.androidStatsdRawPulledAtoms = val),
get: (cfg) => cfg.androidStatsdRawPulledAtoms,
- } as TextareaAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Pulled atom pull frequency (ms)',
cssClass: '.thin',
@@ -260,17 +269,16 @@
unit: 'ms',
set: (cfg, val) => (cfg.androidStatsdPulledAtomPullFrequencyMs = val),
get: (cfg) => cfg.androidStatsdPulledAtomPullFrequencyMs,
- } as SliderAttrs),
- m(Textarea, {
+ recCfg,
+ }),
+ m(Textarea, {
placeholder:
'Add pulled atom packages, one per line, e.g.:\n' +
'com.android.providers.telephony',
set: (cfg, val) => (cfg.androidStatsdPulledAtomPackages = val),
get: (cfg) => cfg.androidStatsdPulledAtomPackages,
- } as TextareaAttrs),
-
-
-
+ recCfg,
+ }),
),
);
}
diff --git a/ui/src/controller/chrome_proxy_record_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/chrome_proxy_record_controller.ts
similarity index 96%
rename from ui/src/controller/chrome_proxy_record_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/chrome_proxy_record_controller.ts
index d1e1b63..ef0b999 100644
--- a/ui/src/controller/chrome_proxy_record_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/chrome_proxy_record_controller.ts
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {binaryDecode, binaryEncode} from '../base/string_utils';
-import {TRACE_SUFFIX} from '../common/constants';
+import {binaryDecode, binaryEncode} from '../../base/string_utils';
+import {TRACE_SUFFIX} from '../../public/trace';
import {
ConsumerPortResponse,
hasProperty,
diff --git a/ui/src/frontend/recording/chrome_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/chrome_settings.ts
similarity index 83%
rename from ui/src/frontend/recording/chrome_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/chrome_settings.ts
index d48bc67..fd09d82 100644
--- a/ui/src/frontend/recording/chrome_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/chrome_settings.ts
@@ -12,24 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {produce} from 'immer';
import m from 'mithril';
-import {Actions} from '../../common/actions';
-import {DataSource} from '../../common/recordingV2/recording_interfaces_v2';
-import {getBuiltinChromeCategoryList, isChromeTarget} from '../../common/state';
+import {DataSource} from './recordingV2/recording_interfaces_v2';
+import {
+ RecordingState,
+ getBuiltinChromeCategoryList,
+ isChromeTarget,
+} from './state';
import {
MultiSelect,
MultiSelectDiff,
Option as MultiSelectOption,
} from '../../widgets/multiselect';
import {Section} from '../../widgets/section';
-import {globals} from '../globals';
-import {
- CategoryGetter,
- CompactProbe,
- Toggle,
- ToggleAttrs,
-} from '../record_widgets';
+import {CategoryGetter, CompactProbe, Toggle} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
function extractChromeCategories(
@@ -46,26 +42,28 @@
class ChromeCategoriesSelection
implements m.ClassComponent<RecordingSectionAttrs>
{
+ private recState: RecordingState;
private defaultCategoryOptions: MultiSelectOption[] | undefined = undefined;
private disabledByDefaultCategoryOptions: MultiSelectOption[] | undefined =
undefined;
- static updateValue(attrs: CategoryGetter, diffs: MultiSelectDiff[]) {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- const values = attrs.get(draft);
- for (const diff of diffs) {
- const value = diff.id;
- const index = values.indexOf(value);
- const enabled = diff.checked;
- if (enabled && index === -1) {
- values.push(value);
- }
- if (!enabled && index !== -1) {
- values.splice(index, 1);
- }
+ constructor({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ this.recState = attrs.recState;
+ }
+
+ private updateValue(attrs: CategoryGetter, diffs: MultiSelectDiff[]) {
+ const values = attrs.get(this.recState.recordConfig);
+ for (const diff of diffs) {
+ const value = diff.id;
+ const index = values.indexOf(value);
+ const enabled = diff.checked;
+ if (enabled && index === -1) {
+ values.push(value);
}
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ if (!enabled && index !== -1) {
+ values.splice(index, 1);
+ }
+ }
}
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
@@ -83,12 +81,12 @@
// back to an integrated list of categories from a recent version of
// Chrome.
const enabled = new Set(
- categoryConfigGetter.get(globals.state.recordConfig),
+ categoryConfigGetter.get(this.recState.recordConfig),
);
let categories =
- globals.state.chromeCategories ||
+ attrs.recState.chromeCategories ||
extractChromeCategories(attrs.dataSources);
- if (!categories || !isChromeTarget(globals.state.recordingTarget)) {
+ if (!categories || !isChromeTarget(attrs.recState.recordingTarget)) {
categories = getBuiltinChromeCategoryList();
}
this.defaultCategoryOptions = [];
@@ -139,7 +137,7 @@
}
}
});
- ChromeCategoriesSelection.updateValue(categoryConfigGetter, diffs);
+ this.updateValue(categoryConfigGetter, diffs);
},
}),
),
@@ -161,7 +159,7 @@
}
}
});
- ChromeCategoriesSelection.updateValue(categoryConfigGetter, diffs);
+ this.updateValue(categoryConfigGetter, diffs);
},
}),
),
@@ -171,57 +169,68 @@
export class ChromeSettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
CompactProbe({
title: 'Task scheduling',
setEnabled: (cfg, val) => (cfg.taskScheduling = val),
isEnabled: (cfg) => cfg.taskScheduling,
+ recCfg,
}),
CompactProbe({
title: 'IPC flows',
setEnabled: (cfg, val) => (cfg.ipcFlows = val),
isEnabled: (cfg) => cfg.ipcFlows,
+ recCfg,
}),
CompactProbe({
title: 'Javascript execution',
setEnabled: (cfg, val) => (cfg.jsExecution = val),
isEnabled: (cfg) => cfg.jsExecution,
+ recCfg,
}),
CompactProbe({
title: 'Web content rendering, layout and compositing',
setEnabled: (cfg, val) => (cfg.webContentRendering = val),
isEnabled: (cfg) => cfg.webContentRendering,
+ recCfg,
}),
CompactProbe({
title: 'UI rendering & surface compositing',
setEnabled: (cfg, val) => (cfg.uiRendering = val),
isEnabled: (cfg) => cfg.uiRendering,
+ recCfg,
}),
CompactProbe({
title: 'Input events',
setEnabled: (cfg, val) => (cfg.inputEvents = val),
isEnabled: (cfg) => cfg.inputEvents,
+ recCfg,
}),
CompactProbe({
title: 'Navigation & Loading',
setEnabled: (cfg, val) => (cfg.navigationAndLoading = val),
isEnabled: (cfg) => cfg.navigationAndLoading,
+ recCfg,
}),
CompactProbe({
title: 'Chrome Logs',
setEnabled: (cfg, val) => (cfg.chromeLogs = val),
isEnabled: (cfg) => cfg.chromeLogs,
+ recCfg,
}),
CompactProbe({
title: 'Audio',
setEnabled: (cfg, val) => (cfg.audio = val),
isEnabled: (cfg) => cfg.audio,
+ recCfg,
}),
CompactProbe({
title: 'Video',
setEnabled: (cfg, val) => (cfg.video = val),
isEnabled: (cfg) => cfg.video,
+ recCfg,
}),
m(Toggle, {
title: 'Remove untyped and sensitive data like URLs from the trace',
@@ -230,7 +239,8 @@
' with third-parties.',
setEnabled: (cfg, val) => (cfg.chromePrivacyFiltering = val),
isEnabled: (cfg) => cfg.chromePrivacyFiltering,
- } as ToggleAttrs),
+ recCfg,
+ }),
m(ChromeCategoriesSelection, attrs),
);
}
diff --git a/ui/src/controller/consumer_port_types.ts b/ui/src/plugins/dev.perfetto.RecordTrace/consumer_port_types.ts
similarity index 98%
rename from ui/src/controller/consumer_port_types.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/consumer_port_types.ts
index 973205f..732e9e8 100644
--- a/ui/src/controller/consumer_port_types.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/consumer_port_types.ts
@@ -18,7 +18,7 @@
IFreeBuffersResponse,
IGetTraceStatsResponse,
IReadBuffersResponse,
-} from '../protos';
+} from '../../protos';
export interface Typed {
type: string;
diff --git a/ui/src/frontend/recording/cpu_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/cpu_settings.ts
similarity index 90%
rename from ui/src/frontend/recording/cpu_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/cpu_settings.ts
index cf20d22..06b2713 100644
--- a/ui/src/frontend/recording/cpu_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/cpu_settings.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import m from 'mithril';
-import {Probe, ProbeAttrs, Slider, SliderAttrs} from '../record_widgets';
+import {Probe, Slider} from './record_widgets';
import {POLL_INTERVAL_MS, RecordingSectionAttrs} from './recording_sections';
export class CpuSettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
m(
@@ -29,7 +30,8 @@
Allows to periodically monitor CPU usage.`,
setEnabled: (cfg, val) => (cfg.cpuCoarse = val),
isEnabled: (cfg) => cfg.cpuCoarse,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -37,7 +39,8 @@
unit: 'ms',
set: (cfg, val) => (cfg.cpuCoarsePollMs = val),
get: (cfg) => cfg.cpuCoarsePollMs,
- } as SliderAttrs),
+ recCfg,
+ }),
),
m(Probe, {
title: 'Scheduling details',
@@ -45,7 +48,8 @@
descr: 'Enables high-detailed tracking of scheduling events',
setEnabled: (cfg, val) => (cfg.cpuSched = val),
isEnabled: (cfg) => cfg.cpuSched,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(
Probe,
{
@@ -55,7 +59,8 @@
'Records cpu frequency and idle state changes via ftrace and sysfs',
setEnabled: (cfg, val) => (cfg.cpuFreq = val),
isEnabled: (cfg) => cfg.cpuFreq,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Sysfs poll interval',
cssClass: '.thin',
@@ -63,7 +68,8 @@
unit: 'ms',
set: (cfg, val) => (cfg.cpuFreqPollMs = val),
get: (cfg) => cfg.cpuFreqPollMs,
- } as SliderAttrs),
+ recCfg,
+ }),
),
m(Probe, {
title: 'Syscalls',
@@ -72,7 +78,8 @@
requires a userdebug or eng build.`,
setEnabled: (cfg, val) => (cfg.cpuSyscall = val),
isEnabled: (cfg) => cfg.cpuSyscall,
- } as ProbeAttrs),
+ recCfg,
+ }),
);
}
}
diff --git a/ui/src/plugins/dev.perfetto.RecordTrace/empty_state.ts b/ui/src/plugins/dev.perfetto.RecordTrace/empty_state.ts
new file mode 100644
index 0000000..bfafe3f
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/empty_state.ts
@@ -0,0 +1,34 @@
+// 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {autosaveConfigStore, recordTargetStore} from './record_config';
+import {RecordingState} from './state';
+
+export function createEmptyState(): RecordingState {
+ return {
+ recordConfig: autosaveConfigStore.get(),
+ lastLoadedConfig: {type: 'NONE'},
+
+ recordingInProgress: false,
+ recordingCancelled: false,
+ extensionInstalled: false,
+ recordingTarget: recordTargetStore.getValidTarget(),
+ availableAdbDevices: [],
+
+ fetchChromeCategories: false,
+ chromeCategories: undefined,
+ bufferUsage: 0,
+ recordingLog: '',
+ };
+}
diff --git a/ui/src/frontend/recording/etw_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/etw_settings.ts
similarity index 90%
rename from ui/src/frontend/recording/etw_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/etw_settings.ts
index 53b913a..eefb8ac 100644
--- a/ui/src/frontend/recording/etw_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/etw_settings.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import m from 'mithril';
-import {Probe, ProbeAttrs} from '../record_widgets';
+import {Probe} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
export class EtwSettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
m(Probe, {
@@ -26,14 +27,16 @@
descr: `Enables to recording of context switches.`,
setEnabled: (cfg, val) => (cfg.etwCSwitch = val),
isEnabled: (cfg) => cfg.etwCSwitch,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(Probe, {
title: 'Dispatcher',
img: null,
descr: 'Enables to get thread state.',
setEnabled: (cfg, val) => (cfg.etwThreadState = val),
isEnabled: (cfg) => cfg.etwThreadState,
- } as ProbeAttrs),
+ recCfg,
+ }),
);
}
}
diff --git a/ui/src/frontend/recording/gpu_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/gpu_settings.ts
similarity index 91%
rename from ui/src/frontend/recording/gpu_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/gpu_settings.ts
index 45d0700..1040f75 100644
--- a/ui/src/frontend/recording/gpu_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/gpu_settings.ts
@@ -13,11 +13,12 @@
// limitations under the License.
import m from 'mithril';
-import {Probe, ProbeAttrs} from '../record_widgets';
+import {Probe} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
export class GpuSettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
m(Probe, {
@@ -26,7 +27,8 @@
descr: 'Records gpu frequency via ftrace',
setEnabled: (cfg, val) => (cfg.gpuFreq = val),
isEnabled: (cfg) => cfg.gpuFreq,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(Probe, {
title: 'GPU memory',
img: 'rec_gpu_mem_total.png',
@@ -34,7 +36,8 @@
(Available on recent Android 12+ kernels)`,
setEnabled: (cfg, val) => (cfg.gpuMemTotal = val),
isEnabled: (cfg) => cfg.gpuMemTotal,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(Probe, {
title: 'GPU work period',
img: 'rec_cpu_voltage.png',
@@ -42,7 +45,8 @@
(Available on recent Android 14+ kernels)`,
setEnabled: (cfg, val) => (cfg.gpuWorkPeriod = val),
isEnabled: (cfg) => cfg.gpuWorkPeriod,
- } as ProbeAttrs),
+ recCfg,
+ }),
);
}
}
diff --git a/ui/src/plugins/dev.perfetto.RecordTrace/index.ts b/ui/src/plugins/dev.perfetto.RecordTrace/index.ts
new file mode 100644
index 0000000..e0c5a1f
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/index.ts
@@ -0,0 +1,56 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {RecordPage} from './record_page';
+import {RecordPageV2} from './record_page_v2';
+import {App} from '../../public/app';
+import {PerfettoPlugin} from '../../public/plugin';
+import {RecordingPageController} from './recordingV2/recording_page_controller';
+import {RecordingManager} from './recording_manager';
+import {PageAttrs} from '../../public/page';
+import {bindMithrilAttrs} from '../../base/mithril_utils';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.RecordTrace';
+
+ static onActivate(app: App) {
+ app.sidebar.addMenuItem({
+ section: 'navigation',
+ text: 'Record new trace',
+ href: '#!/record',
+ icon: 'fiber_smart_record',
+ sortOrder: 2,
+ });
+
+ const RECORDING_V2_FLAG = app.featureFlags.register({
+ id: 'recordingv2',
+ name: 'Recording V2',
+ description: 'Record using V2 interface',
+ defaultValue: false,
+ });
+ const useRecordingV2 = RECORDING_V2_FLAG.get();
+
+ const recMgr = new RecordingManager(app, useRecordingV2);
+ let page: m.ClassComponent<PageAttrs>;
+ if (useRecordingV2) {
+ const recCtl = new RecordingPageController(app, recMgr);
+ recCtl.initFactories();
+ page = bindMithrilAttrs(RecordPageV2, {app, recCtl, recMgr});
+ } else {
+ page = bindMithrilAttrs(RecordPage, {app, recMgr});
+ }
+ app.pages.registerPage({route: '/record', traceless: true, page});
+ }
+}
diff --git a/ui/src/frontend/recording/linux_perf_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/linux_perf_settings.ts
similarity index 90%
rename from ui/src/frontend/recording/linux_perf_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/linux_perf_settings.ts
index 00f1324..a0fcf9f 100644
--- a/ui/src/frontend/recording/linux_perf_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/linux_perf_settings.ts
@@ -13,14 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {
- Probe,
- ProbeAttrs,
- Slider,
- SliderAttrs,
- Textarea,
- TextareaAttrs,
-} from '../record_widgets';
+import {Probe, Slider, Textarea} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
const PLACEHOLDER_TEXT = `Filters for processes to profile, one per line e.g.:
@@ -37,6 +30,7 @@
{
config = {targets: []} as LinuxPerfConfiguration;
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
return m(
`.record-section${attrs.cssClass}`,
m(
@@ -48,7 +42,8 @@
function calls) of processes.`,
setEnabled: (cfg, val) => (cfg.tracePerf = val),
isEnabled: (cfg) => cfg.tracePerf,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Sampling Frequency',
cssClass: '.thin',
@@ -56,7 +51,8 @@
unit: 'hz',
set: (cfg, val) => (cfg.timebaseFrequency = val),
get: (cfg) => cfg.timebaseFrequency,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Textarea, {
placeholder: PLACEHOLDER_TEXT,
cssClass: '.record-apps-list',
@@ -64,7 +60,8 @@
cfg.targetCmdLine = val.split('\n');
},
get: (cfg) => cfg.targetCmdLine.join('\n'),
- } as TextareaAttrs),
+ recCfg,
+ }),
),
);
}
diff --git a/ui/src/frontend/recording/memory_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/memory_settings.ts
similarity index 89%
rename from ui/src/frontend/recording/memory_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/memory_settings.ts
index 857051f..231306f 100644
--- a/ui/src/frontend/recording/memory_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/memory_settings.ts
@@ -14,19 +14,7 @@
import m from 'mithril';
import {MeminfoCounters, VmstatCounters} from '../../protos';
-import {globals} from '../globals';
-import {
- Dropdown,
- DropdownAttrs,
- Probe,
- ProbeAttrs,
- Slider,
- SliderAttrs,
- Textarea,
- TextareaAttrs,
- Toggle,
- ToggleAttrs,
-} from '../record_widgets';
+import {Dropdown, Probe, Slider, Textarea, Toggle} from './record_widgets';
import {POLL_INTERVAL_MS, RecordingSectionAttrs} from './recording_sections';
class HeapSettings implements m.ClassComponent<RecordingSectionAttrs> {
@@ -61,7 +49,7 @@
256 * 1024 * 1024,
512 * 1024 * 1024,
];
-
+ const recCfg = attrs.recState.recordConfig;
return m(
`.${attrs.cssClass}`,
m(Textarea, {
@@ -75,7 +63,8 @@
'1503',
set: (cfg, val) => (cfg.hpProcesses = val),
get: (cfg) => cfg.hpProcesses,
- } as TextareaAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Sampling interval',
cssClass: '.thin',
@@ -87,7 +76,8 @@
min: 0,
set: (cfg, val) => (cfg.hpSamplingIntervalBytes = val),
get: (cfg) => cfg.hpSamplingIntervalBytes,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Continuous dumps interval ',
description: 'Time between following dumps (0 = disabled)',
@@ -99,22 +89,24 @@
cfg.hpContinuousDumpsInterval = val;
},
get: (cfg) => cfg.hpContinuousDumpsInterval,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Continuous dumps phase',
description: 'Time before first dump',
cssClass: `.thin${
- globals.state.recordConfig.hpContinuousDumpsInterval === 0
+ attrs.recState.recordConfig.hpContinuousDumpsInterval === 0
? '.greyed-out'
: ''
}`,
values: valuesForMS,
unit: 'ms',
min: 0,
- disabled: globals.state.recordConfig.hpContinuousDumpsInterval === 0,
+ disabled: attrs.recState.recordConfig.hpContinuousDumpsInterval === 0,
set: (cfg, val) => (cfg.hpContinuousDumpsPhase = val),
get: (cfg) => cfg.hpContinuousDumpsPhase,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: `Shared memory buffer`,
cssClass: '.thin',
@@ -125,14 +117,16 @@
min: 0,
set: (cfg, val) => (cfg.hpSharedMemoryBuffer = val),
get: (cfg) => cfg.hpSharedMemoryBuffer,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Toggle, {
title: 'Block client',
cssClass: '.thin',
descr: `Slow down target application if profiler cannot keep up.`,
setEnabled: (cfg, val) => (cfg.hpBlockClient = val),
isEnabled: (cfg) => cfg.hpBlockClient,
- } as ToggleAttrs),
+ recCfg,
+ }),
m(Toggle, {
title: 'All custom allocators (Q+)',
cssClass: '.thin',
@@ -140,7 +134,8 @@
sample from those.`,
setEnabled: (cfg, val) => (cfg.hpAllHeaps = val),
isEnabled: (cfg) => cfg.hpAllHeaps,
- } as ToggleAttrs),
+ recCfg,
+ }),
// TODO(hjd): Add advanced options.
);
}
@@ -159,7 +154,7 @@
30 * 60 * 1000,
60 * 60 * 1000,
];
-
+ const recCfg = attrs.recState.recordConfig;
return m(
`.${attrs.cssClass}`,
m(Textarea, {
@@ -167,7 +162,8 @@
placeholder: 'One per line, e.g.:\n' + 'com.android.vending\n' + '1503',
set: (cfg, val) => (cfg.jpProcesses = val),
get: (cfg) => cfg.jpProcesses,
- } as TextareaAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Continuous dumps interval ',
description: 'Time between following dumps (0 = disabled)',
@@ -179,28 +175,31 @@
cfg.jpContinuousDumpsInterval = val;
},
get: (cfg) => cfg.jpContinuousDumpsInterval,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Continuous dumps phase',
description: 'Time before first dump',
cssClass: `.thin${
- globals.state.recordConfig.jpContinuousDumpsInterval === 0
+ attrs.recState.recordConfig.jpContinuousDumpsInterval === 0
? '.greyed-out'
: ''
}`,
values: valuesForMS,
unit: 'ms',
min: 0,
- disabled: globals.state.recordConfig.jpContinuousDumpsInterval === 0,
+ disabled: attrs.recState.recordConfig.jpContinuousDumpsInterval === 0,
set: (cfg, val) => (cfg.jpContinuousDumpsPhase = val),
get: (cfg) => cfg.jpContinuousDumpsPhase,
- } as SliderAttrs),
+ recCfg,
+ }),
);
}
}
export class MemorySettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
const meminfoOpts = new Map<string, string>();
for (const x in MeminfoCounters) {
if (
@@ -230,7 +229,8 @@
process. (Available on Android 10+)`,
setEnabled: (cfg, val) => (cfg.heapProfiling = val),
isEnabled: (cfg) => cfg.heapProfiling,
- } as ProbeAttrs,
+ recCfg,
+ },
m(HeapSettings, attrs),
),
m(
@@ -242,7 +242,8 @@
Android app. (Available on Android 11+)`,
setEnabled: (cfg, val) => (cfg.javaHeapDump = val),
isEnabled: (cfg) => cfg.javaHeapDump,
- } as ProbeAttrs,
+ recCfg,
+ },
m(JavaHeapDumpSettings, attrs),
),
m(
@@ -253,7 +254,8 @@
descr: 'Polling of /proc/meminfo',
setEnabled: (cfg, val) => (cfg.meminfo = val),
isEnabled: (cfg) => cfg.meminfo,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -261,14 +263,16 @@
unit: 'ms',
set: (cfg, val) => (cfg.meminfoPeriodMs = val),
get: (cfg) => cfg.meminfoPeriodMs,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Dropdown, {
title: 'Select counters',
cssClass: '.multicolumn',
options: meminfoOpts,
set: (cfg, val) => (cfg.meminfoCounters = val),
get: (cfg) => cfg.meminfoCounters,
- } as DropdownAttrs),
+ recCfg,
+ }),
),
m(Probe, {
title: 'High-frequency memory events',
@@ -278,7 +282,8 @@
on recent Android Q+ kernels`,
setEnabled: (cfg, val) => (cfg.memHiFreq = val),
isEnabled: (cfg) => cfg.memHiFreq,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(Probe, {
title: 'Low memory killer',
img: 'rec_lmk.png',
@@ -287,7 +292,8 @@
adjustments.`,
setEnabled: (cfg, val) => (cfg.memLmk = val),
isEnabled: (cfg) => cfg.memLmk,
- } as ProbeAttrs),
+ recCfg,
+ }),
m(
Probe,
{
@@ -298,7 +304,8 @@
/proc/status counters) and oom_score_adj.`,
setEnabled: (cfg, val) => (cfg.procStats = val),
isEnabled: (cfg) => cfg.procStats,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -306,7 +313,8 @@
unit: 'ms',
set: (cfg, val) => (cfg.procStatsPeriodMs = val),
get: (cfg) => cfg.procStatsPeriodMs,
- } as SliderAttrs),
+ recCfg,
+ }),
),
m(
Probe,
@@ -318,7 +326,8 @@
compression and pagecache efficiency`,
setEnabled: (cfg, val) => (cfg.vmstat = val),
isEnabled: (cfg) => cfg.vmstat,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -326,14 +335,16 @@
unit: 'ms',
set: (cfg, val) => (cfg.vmstatPeriodMs = val),
get: (cfg) => cfg.vmstatPeriodMs,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Dropdown, {
title: 'Select counters',
cssClass: '.multicolumn',
options: vmstatOpts,
set: (cfg, val) => (cfg.vmstatCounters = val),
get: (cfg) => cfg.vmstatCounters,
- } as DropdownAttrs),
+ recCfg,
+ }),
),
);
}
diff --git a/ui/src/frontend/recording/power_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/power_settings.ts
similarity index 89%
rename from ui/src/frontend/recording/power_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/power_settings.ts
index 318904e..bf88217 100644
--- a/ui/src/frontend/recording/power_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/power_settings.ts
@@ -13,12 +13,13 @@
// limitations under the License.
import m from 'mithril';
-import {globals} from '../globals';
-import {Probe, ProbeAttrs, Slider, SliderAttrs} from '../record_widgets';
+import {globals} from '../../frontend/globals';
+import {Probe, Slider} from './record_widgets';
import {POLL_INTERVAL_MS, RecordingSectionAttrs} from './recording_sections';
export class PowerSettings implements m.ClassComponent<RecordingSectionAttrs> {
view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+ const recCfg = attrs.recState.recordConfig;
const DOC_URL = 'https://perfetto.dev/docs/data-sources/battery-counters';
const descr = [
m(
@@ -33,6 +34,7 @@
m('span', ')'),
),
];
+ // TODO(primiano): figure out a better story for isInternalUser.
if (globals.isInternalUser) {
descr.push(
m(
@@ -61,7 +63,8 @@
descr,
setEnabled: (cfg, val) => (cfg.batteryDrain = val),
isEnabled: (cfg) => cfg.batteryDrain,
- } as ProbeAttrs,
+ recCfg,
+ },
m(Slider, {
title: 'Poll interval',
cssClass: '.thin',
@@ -69,7 +72,8 @@
unit: 'ms',
set: (cfg, val) => (cfg.batteryDrainPollMs = val),
get: (cfg) => cfg.batteryDrainPollMs,
- } as SliderAttrs),
+ recCfg,
+ }),
),
m(Probe, {
title: 'Board voltages & frequencies',
@@ -77,7 +81,8 @@
descr: 'Tracks voltage and frequency changes from board sensors',
setEnabled: (cfg, val) => (cfg.boardSensors = val),
isEnabled: (cfg) => cfg.boardSensors,
- } as ProbeAttrs),
+ recCfg,
+ }),
);
}
}
diff --git a/ui/src/frontend/record_config.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_config.ts
similarity index 96%
rename from ui/src/frontend/record_config.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_config.ts
index 0201dc1..ae41d9c 100644
--- a/ui/src/frontend/record_config.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_config.ts
@@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {exists} from '../base/utils';
-import {getDefaultRecordingTargets, RecordingTarget} from '../common/state';
+import {exists} from '../../base/utils';
+import {getDefaultRecordingTargets, RecordingTarget} from './state';
import {
createEmptyRecordConfig,
NamedRecordConfig,
NAMED_RECORD_CONFIG_SCHEMA,
RecordConfig,
RECORD_CONFIG_SCHEMA,
-} from '../controller/record_config_types';
+} from './record_config_types';
const LOCAL_STORAGE_RECORD_CONFIGS_KEY = 'recordConfigs';
const LOCAL_STORAGE_AUTOSAVE_CONFIG_KEY = 'autosaveConfig';
@@ -156,7 +156,7 @@
export const recordConfigStore = new RecordConfigStore();
export class AutosaveConfigStore {
- config: RecordConfig;
+ private config: RecordConfig;
// Whether the current config is a default one or has been saved before.
// Used to determine whether the button to load "last started config" should
diff --git a/ui/src/controller/record_config_types.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_config_types.ts
similarity index 100%
rename from ui/src/controller/record_config_types.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_config_types.ts
diff --git a/ui/src/controller/record_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller.ts
similarity index 84%
rename from ui/src/controller/record_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_controller.ts
index e7989e4..8062650 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller.ts
@@ -13,22 +13,19 @@
// limitations under the License.
import {Message, Method, rpc, RPCImplCallback} from 'protobufjs';
-import {isString} from '../base/object_utils';
-import {base64Encode} from '../base/string_utils';
-import {Actions} from '../common/actions';
-import {TRACE_SUFFIX} from '../common/constants';
-import {genTraceConfig} from '../common/recordingV2/recording_config_utils';
-import {TargetInfo} from '../common/recordingV2/recording_interfaces_v2';
+import {isString} from '../../base/object_utils';
+import {base64Encode} from '../../base/string_utils';
+import {TRACE_SUFFIX} from '../../public/trace';
+import {genTraceConfig} from './recordingV2/recording_config_utils';
+import {TargetInfo} from './recordingV2/recording_interfaces_v2';
import {
AdbRecordingTarget,
isAdbTarget,
isChromeTarget,
isWindowsTarget,
RecordingTarget,
-} from '../common/state';
-import {globals} from '../frontend/globals';
-import {publishBufferUsage, publishTrackData} from '../frontend/publish';
-import {ConsumerPort, TraceConfig} from '../protos';
+} from './state';
+import {ConsumerPort, TraceConfig} from '../../protos';
import {AdbOverWebUsb} from './adb';
import {AdbConsumerPort} from './adb_shell_controller';
import {AdbSocketConsumerPort} from './adb_socket_controller';
@@ -42,10 +39,11 @@
isGetTraceStatsResponse,
isReadBuffersResponse,
} from './consumer_port_types';
-import {Controller} from './controller';
import {RecordConfig} from './record_config_types';
import {Consumer, RpcConsumerPort} from './record_controller_interfaces';
-import {AppImpl} from '../core/app_impl';
+import {RecordingManager} from './recording_manager';
+import {scheduleFullRedraw} from '../../widgets/raf';
+import {App} from '../../public/app';
type RPCImplMethod = Method | rpc.ServiceMethod<Message<{}>, Message<{}>>;
@@ -190,7 +188,9 @@
return [...message(json, 0)].join('');
}
-export class RecordController extends Controller<'main'> implements Consumer {
+export class RecordController implements Consumer {
+ private app: App;
+ private recMgr: RecordingManager;
private config: RecordConfig | null = null;
private readonly extensionPort: MessagePort;
private recordingInProgress = false;
@@ -207,34 +207,32 @@
// char, it is the 'targetOS'
private controllerPromises = new Map<string, Promise<RpcConsumerPort>>();
- constructor(args: {extensionPort: MessagePort}) {
- super('main');
+ constructor(app: App, recMgr: RecordingManager, extensionPort: MessagePort) {
+ this.app = app;
+ this.recMgr = recMgr;
this.consumerPort = ConsumerPort.create(this.rpcImpl.bind(this));
- this.extensionPort = args.extensionPort;
+ this.extensionPort = extensionPort;
}
- run() {
+ private get state() {
+ return this.recMgr.state;
+ }
+
+ refreshOnStateChange() {
// TODO(eseckler): Use ConsumerPort's QueryServiceState instead
// of posting a custom extension message to retrieve the category list.
- if (globals.state.fetchChromeCategories && !this.fetchedCategories) {
+ scheduleFullRedraw();
+ if (this.state.fetchChromeCategories && !this.fetchedCategories) {
this.fetchedCategories = true;
- if (globals.state.extensionInstalled) {
+ if (this.state.extensionInstalled) {
this.extensionPort.postMessage({method: 'GetCategories'});
}
- globals.dispatch(Actions.setFetchChromeCategories({fetch: false}));
+ this.recMgr.setFetchChromeCategories(false);
}
- if (
- globals.state.recordConfig === this.config &&
- globals.state.recordingInProgress === this.recordingInProgress
- ) {
- return;
- }
- this.config = globals.state.recordConfig;
- const configProto = genConfigProto(
- this.config,
- globals.state.recordingTarget,
- );
+ this.config = this.state.recordConfig;
+
+ const configProto = genConfigProto(this.config, this.state.recordingTarget);
const configProtoText = toPbtxt(configProto);
const configProtoBase64 = base64Encode(configProto);
const commandline = `
@@ -245,23 +243,18 @@
`;
const traceConfig = convertToRecordingV2Input(
this.config,
- globals.state.recordingTarget,
+ this.state.recordingTarget,
);
- // TODO(hjd): This should not be TrackData after we unify the stores.
- publishTrackData({
- id: 'config',
- data: {
- commandline,
- pbBase64: configProtoBase64,
- pbtxt: configProtoText,
- traceConfig,
- },
- });
+ this.state.recordCmd = {
+ commandline,
+ pbBase64: configProtoBase64,
+ pbtxt: configProtoText,
+ };
// If the recordingInProgress boolean state is different, it means that we
// have to start or stop recording a trace.
- if (globals.state.recordingInProgress === this.recordingInProgress) return;
- this.recordingInProgress = globals.state.recordingInProgress;
+ if (this.state.recordingInProgress === this.recordingInProgress) return;
+ this.recordingInProgress = this.state.recordingInProgress;
if (this.recordingInProgress) {
this.startRecordTrace(traceConfig);
@@ -309,7 +302,7 @@
} else if (isGetTraceStatsResponse(data)) {
const percentage = this.getBufferUsagePercentage(data);
if (percentage) {
- publishBufferUsage({percentage});
+ this.recMgr.state.bufferUsage = percentage;
}
} else if (isFreeBuffersResponse(data)) {
// No action required.
@@ -322,16 +315,14 @@
onTraceComplete() {
this.consumerPort.freeBuffers({});
- globals.dispatch(Actions.setRecordingStatus({status: undefined}));
- if (globals.state.recordingCancelled) {
- globals.dispatch(
- Actions.setLastRecordingError({error: 'Recording cancelled.'}),
- );
+ this.recMgr.setRecordingStatus(undefined);
+ if (this.state.recordingCancelled) {
+ this.recMgr.setLastRecordingError('Recording cancelled.');
this.traceBuffer = [];
return;
}
const trace = this.generateTrace();
- AppImpl.instance.openTraceFromBuffer({
+ this.app.openTraceFromBuffer({
title: 'Recorded trace',
buffer: trace.buffer,
fileName: `recorded_trace${this.recordedTraceSuffix}`,
@@ -367,14 +358,12 @@
onError(message: string) {
// TODO(octaviant): b/204998302
console.error('Error in record controller: ', message);
- globals.dispatch(
- Actions.setLastRecordingError({error: message.substr(0, 150)}),
- );
- globals.dispatch(Actions.stopRecording({}));
+ this.recMgr.setLastRecordingError(message.substring(0, 150));
+ this.recMgr.stopRecording();
}
onStatus(message: string) {
- globals.dispatch(Actions.setRecordingStatus({status: message}));
+ this.recMgr.setRecordingStatus(message);
}
// Depending on the recording target, different implementation of the
@@ -409,8 +398,8 @@
const socketAccess = await this.hasSocketAccess(target);
controller = socketAccess
- ? new AdbSocketConsumerPort(this.adb, this)
- : new AdbConsumerPort(this.adb, this);
+ ? new AdbSocketConsumerPort(this.adb, this, this.recMgr.state)
+ : new AdbConsumerPort(this.adb, this, this.recMgr.state);
} else {
throw Error(`No device connected`);
}
@@ -444,7 +433,7 @@
_callback: RPCImplCallback,
) {
try {
- const state = globals.state;
+ const state = this.state;
// TODO(hjd): This is a bit weird. We implicitly send each RPC message to
// whichever target is currently selected (creating that target if needed)
// it would be nicer if the setup/teardown was more explicit.
diff --git a/ui/src/controller/record_controller_interfaces.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller_interfaces.ts
similarity index 97%
rename from ui/src/controller/record_controller_interfaces.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_controller_interfaces.ts
index e9662fd..f29940a 100644
--- a/ui/src/controller/record_controller_interfaces.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller_interfaces.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TRACE_SUFFIX} from '../common/constants';
+import {TRACE_SUFFIX} from '../../public/trace';
import {ConsumerPortResponse} from './consumer_port_types';
export type ErrorCallback = (_: string) => void;
diff --git a/ui/src/controller/record_controller_jsdomtest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller_jsdomtest.ts
similarity index 99%
rename from ui/src/controller/record_controller_jsdomtest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_controller_jsdomtest.ts
index 442e4b8..1035369 100644
--- a/ui/src/controller/record_controller_jsdomtest.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_controller_jsdomtest.ts
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists} from '../base/logging';
-import {TraceConfig} from '../protos';
+import {assertExists} from '../../base/logging';
+import {TraceConfig} from '../../protos';
import {createEmptyRecordConfig} from './record_config_types';
import {genConfigProto, toPbtxt} from './record_controller';
diff --git a/ui/src/frontend/record_page.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_page.ts
similarity index 62%
rename from ui/src/frontend/record_page.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_page.ts
index 49f2f75..021a5db 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_page.ts
@@ -13,9 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {Actions} from '../common/actions';
import {
- AdbRecordingTarget,
getDefaultRecordingTargets,
hasActiveProbes,
isAdbTarget,
@@ -28,42 +26,32 @@
LoadedConfig,
MAX_TIME,
RecordingTarget,
-} from '../common/state';
-import {AdbOverWebUsb} from '../controller/adb';
-import {
- createEmptyRecordConfig,
- RecordConfig,
-} from '../controller/record_config_types';
-import {featureFlags} from '../core/feature_flags';
-import {raf} from '../core/raf_scheduler';
-import {globals} from './globals';
-import {PageAttrs} from '../core/router';
+} from './state';
+import {AdbOverWebUsb} from './adb';
+import {RECORD_CONFIG_SCHEMA, RecordConfig} from './record_config_types';
+import {PageAttrs} from '../../public/page';
import {
autosaveConfigStore,
recordConfigStore,
recordTargetStore,
} from './record_config';
import {CodeSnippet} from './record_widgets';
-import {AdvancedSettings} from './recording/advanced_settings';
-import {AndroidSettings} from './recording/android_settings';
-import {ChromeSettings} from './recording/chrome_settings';
-import {CpuSettings} from './recording/cpu_settings';
-import {GpuSettings} from './recording/gpu_settings';
-import {LinuxPerfSettings} from './recording/linux_perf_settings';
-import {MemorySettings} from './recording/memory_settings';
-import {PowerSettings} from './recording/power_settings';
-import {RecordingSectionAttrs} from './recording/recording_sections';
-import {RecordingSettings} from './recording/recording_settings';
-import {EtwSettings} from './recording/etw_settings';
-import {createPermalink} from './permalink';
-import {AppImpl} from '../core/app_impl';
-
-export const PERSIST_CONFIG_FLAG = featureFlags.register({
- id: 'persistConfigsUI',
- name: 'Config persistence UI',
- description: 'Show experimental config persistence UI on the record page.',
- defaultValue: true,
-});
+import {AdvancedSettings} from './advanced_settings';
+import {AndroidSettings} from './android_settings';
+import {ChromeSettings} from './chrome_settings';
+import {CpuSettings} from './cpu_settings';
+import {GpuSettings} from './gpu_settings';
+import {LinuxPerfSettings} from './linux_perf_settings';
+import {MemorySettings} from './memory_settings';
+import {PowerSettings} from './power_settings';
+import {RecordingSettings} from './recording_settings';
+import {EtwSettings} from './etw_settings';
+import {RecordingManager} from './recording_manager';
+import {scheduleFullRedraw} from '../../widgets/raf';
+import {App} from '../../public/app';
+import {GcsUploader, BUCKET_NAME, MIME_JSON} from '../../base/gcs_uploader';
+import {showModal} from '../../widgets/modal';
+import {CopyableLink} from '../../widgets/copyable_link';
export const RECORDING_SECTIONS = [
'buffers',
@@ -80,28 +68,28 @@
'advanced',
];
-function RecordHeader() {
+function RecordHeader(recMgr: RecordingManager) {
return m(
'.record-header',
m(
'.top-part',
m(
'.target-and-status',
- RecordingPlatformSelection(),
- RecordingStatusLabel(),
- ErrorLabel(),
+ RecordingPlatformSelection(recMgr),
+ RecordingStatusLabel(recMgr),
+ ErrorLabel(recMgr),
),
- recordingButtons(),
+ recordingButtons(recMgr),
),
- RecordingNotes(),
+ RecordingNotes(recMgr),
);
}
-function RecordingPlatformSelection() {
- if (globals.state.recordingInProgress) return [];
+function RecordingPlatformSelection(recMgr: RecordingManager) {
+ if (recMgr.state.recordingInProgress) return [];
- const availableAndroidDevices = globals.state.availableAdbDevices;
- const recordingTarget = globals.state.recordingTarget;
+ const availableAndroidDevices = recMgr.state.availableAdbDevices;
+ const recordingTarget = recMgr.state.recordingTarget;
const targets = [];
for (const {os, name} of getDefaultRecordingTargets()) {
@@ -125,7 +113,7 @@
{
selectedIndex,
onchange: (e: Event) => {
- onTargetChange((e.target as HTMLSelectElement).value);
+ onTargetChange(recMgr, (e.target as HTMLSelectElement).value);
},
onupdate: (select) => {
// Work around mithril bug
@@ -144,7 +132,7 @@
),
m(
'.chip',
- {onclick: addAndroidDevice},
+ {onclick: () => addAndroidDevice(recMgr)},
m('button', 'Add ADB Device'),
m('i.material-icons', 'add'),
),
@@ -152,38 +140,36 @@
}
// |target| can be the TargetOs or the android serial.
-function onTargetChange(target: string) {
+function onTargetChange(recMgr: RecordingManager, target: string) {
const recordingTarget: RecordingTarget =
- globals.state.availableAdbDevices.find((d) => d.serial === target) ||
+ recMgr.state.availableAdbDevices.find((d) => d.serial === target) ||
getDefaultRecordingTargets().find((t) => t.os === target) ||
getDefaultRecordingTargets()[0];
if (isChromeTarget(recordingTarget)) {
- globals.dispatch(Actions.setFetchChromeCategories({fetch: true}));
+ recMgr.setFetchChromeCategories(true);
}
- globals.dispatch(Actions.setRecordingTarget({target: recordingTarget}));
+ recMgr.setRecordingTarget(recordingTarget);
recordTargetStore.save(target);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
-function Instructions(cssClass: string) {
+function Instructions(recMgr: RecordingManager, cssClass: string) {
return m(
`.record-section.instructions${cssClass}`,
m('header', 'Recording command'),
- PERSIST_CONFIG_FLAG.get()
- ? m(
- 'button.permalinkconfig',
- {
- onclick: () => createPermalink({mode: 'RECORDING_OPTS'}),
- },
- 'Share recording settings',
- )
- : null,
- RecordingSnippet(),
- BufferUsageProgressBar(),
- m('.buttons', StopCancelButtons()),
- recordingLog(),
+ m(
+ 'button.permalinkconfig',
+ {
+ onclick: () => uploadRecordingConfig(recMgr.state.recordConfig),
+ },
+ 'Share recording settings',
+ ),
+ RecordingSnippet(recMgr),
+ BufferUsageProgressBar(recMgr),
+ m('.buttons', StopCancelButtons(recMgr)),
+ recordingLog(recMgr),
);
}
@@ -197,6 +183,7 @@
}
export function loadConfigButton(
+ recMgr: RecordingManager,
config: RecordConfig,
configType: LoadedConfig,
): m.Vnode {
@@ -205,23 +192,25 @@
{
class: 'config-button',
title: 'Apply configuration settings',
- disabled: loadedConfigEqual(configType, globals.state.lastLoadedConfig),
+ disabled: loadedConfigEqual(configType, recMgr.state.lastLoadedConfig),
onclick: () => {
- globals.dispatch(Actions.setRecordConfig({config, configType}));
- raf.scheduleFullRedraw();
+ recMgr.setRecordConfig(config, configType);
+ scheduleFullRedraw();
},
},
m('i.material-icons', 'file_upload'),
);
}
-export function displayRecordConfigs() {
+export function displayRecordConfigs(recMgr: RecordingManager) {
const configs = [];
if (autosaveConfigStore.hasSavedConfig) {
configs.push(
m('.config', [
m('span.title-config', m('strong', 'Latest started recording')),
- loadConfigButton(autosaveConfigStore.get(), {type: 'AUTOMATIC'}),
+ loadConfigButton(recMgr, autosaveConfigStore.get(), {
+ type: 'AUTOMATIC',
+ }),
]),
);
}
@@ -229,7 +218,10 @@
configs.push(
m('.config', [
m('span.title-config', item.title),
- loadConfigButton(item.config, {type: 'NAMED', name: item.title}),
+ loadConfigButton(recMgr, item.config, {
+ type: 'NAMED',
+ name: item.title,
+ }),
m(
'button',
{
@@ -242,16 +234,14 @@
)
) {
recordConfigStore.overwrite(
- globals.state.recordConfig,
+ recMgr.state.recordConfig,
item.key,
);
- globals.dispatch(
- Actions.setRecordConfig({
- config: item.config,
- configType: {type: 'NAMED', name: item.title},
- }),
- );
- raf.scheduleFullRedraw();
+ recMgr.setRecordConfig(item.config, {
+ type: 'NAMED',
+ name: item.title,
+ });
+ scheduleFullRedraw();
}
},
},
@@ -264,7 +254,7 @@
title: 'Remove configuration',
onclick: () => {
recordConfigStore.delete(item.key);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
},
m('i.material-icons', 'delete'),
@@ -288,7 +278,7 @@
},
};
-export function Configurations(cssClass: string) {
+export function Configurations(recMgr: RecordingManager, cssClass: string) {
const canSave = recordConfigStore.canSave(ConfigTitleState.getTitle());
return m(
`.record-section${cssClass}`,
@@ -299,7 +289,7 @@
placeholder: 'Title for config',
oninput() {
ConfigTitleState.setTitle(this.value);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
}),
m(
@@ -312,10 +302,10 @@
: 'Duplicate name, saving disabled',
onclick: () => {
recordConfigStore.save(
- globals.state.recordConfig,
+ recMgr.state.recordConfig,
ConfigTitleState.getTitle(),
);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
ConfigTitleState.clearTitle();
},
},
@@ -332,27 +322,22 @@
'Current configuration will be cleared. ' + 'Are you sure?',
)
) {
- globals.dispatch(
- Actions.setRecordConfig({
- config: createEmptyRecordConfig(),
- configType: {type: 'NONE'},
- }),
- );
- raf.scheduleFullRedraw();
+ recMgr.clearRecordConfig();
+ scheduleFullRedraw();
}
},
},
m('i.material-icons', 'delete_forever'),
),
]),
- displayRecordConfigs(),
+ displayRecordConfigs(recMgr),
);
}
-function BufferUsageProgressBar() {
- if (!globals.state.recordingInProgress) return [];
+function BufferUsageProgressBar(recMgr: RecordingManager) {
+ if (!recMgr.state.recordingInProgress) return [];
- const bufferUsage = globals.bufferUsage ?? 0.0;
+ const bufferUsage = recMgr.state.bufferUsage;
// Buffer usage is not available yet on Android.
if (bufferUsage === 0) return [];
@@ -363,7 +348,7 @@
);
}
-function RecordingNotes() {
+function RecordingNotes(recMgr: RecordingManager) {
const sideloadUrl =
'https://perfetto.dev/docs/contributing/build-instructions#get-the-code';
const linuxUrl = 'https://perfetto.dev/docs/quickstart/linux-tracing';
@@ -446,14 +431,14 @@
'Please add at least one to get a non-empty trace.',
);
- if (!hasActiveProbes(globals.state.recordConfig)) {
+ if (!hasActiveProbes(recMgr.state.recordConfig)) {
notes.push(msgZeroProbes);
}
- if (isAdbTarget(globals.state.recordingTarget)) {
+ if (isAdbTarget(recMgr.state.recordingTarget)) {
notes.push(msgRecordingNotSupported);
}
- switch (globals.state.recordingTarget.os) {
+ switch (recMgr.state.recordingTarget.os) {
case 'Q':
break;
case 'P':
@@ -466,30 +451,29 @@
notes.push(msgLinux);
break;
case 'C':
- if (!globals.state.extensionInstalled) notes.push(msgChrome);
+ if (!recMgr.state.extensionInstalled) notes.push(msgChrome);
break;
case 'CrOS':
- if (!globals.state.extensionInstalled) notes.push(msgChrome);
+ if (!recMgr.state.extensionInstalled) notes.push(msgChrome);
break;
case 'Win':
- if (!globals.state.extensionInstalled) notes.push(msgWinEtw);
+ if (!recMgr.state.extensionInstalled) notes.push(msgWinEtw);
break;
default:
}
- if (globals.state.recordConfig.mode === 'LONG_TRACE') {
+ if (recMgr.state.recordConfig.mode === 'LONG_TRACE') {
notes.unshift(msgLongTraces);
}
return notes.length > 0 ? m('div', notes) : [];
}
-function RecordingSnippet() {
- const target = globals.state.recordingTarget;
+function RecordingSnippet(recMgr: RecordingManager) {
+ const target = recMgr.state.recordingTarget;
// We don't need commands to start tracing on chrome
if (isChromeTarget(target)) {
- return globals.state.extensionInstalled &&
- !globals.state.recordingInProgress
+ return recMgr.state.extensionInstalled && !recMgr.state.recordingInProgress
? m(
'div',
m(
@@ -500,17 +484,13 @@
)
: [];
}
- return m(CodeSnippet, {text: getRecordCommand(target)});
+ return m(CodeSnippet, {text: getRecordCommand(recMgr, target)});
}
-function getRecordCommand(target: RecordingTarget) {
- const data = globals.trackDataStore.get('config') as {
- commandline: string;
- pbtxt: string;
- pbBase64: string;
- } | null;
+function getRecordCommand(recMgr: RecordingManager, target: RecordingTarget) {
+ const data = recMgr.state.recordCmd;
- const cfg = globals.state.recordConfig;
+ const cfg = recMgr.state.recordConfig;
let time = cfg.durationMs / 1000;
if (time > MAX_TIME) {
@@ -537,8 +517,8 @@
return cmd;
}
-function recordingButtons() {
- const state = globals.state;
+function recordingButtons(recMgr: RecordingManager) {
+ const state = recMgr.state;
const target = state.recordingTarget;
const recInProgress = state.recordingInProgress;
@@ -546,7 +526,7 @@
`button`,
{
class: recInProgress ? '' : 'selected',
- onclick: onStartRecordingPressed,
+ onclick: () => onStartRecordingPressed(recMgr),
},
'Start Recording',
);
@@ -557,7 +537,7 @@
if (
!recInProgress &&
isAdbTarget(target) &&
- globals.state.recordConfig.mode !== 'LONG_TRACE'
+ recMgr.state.recordConfig.mode !== 'LONG_TRACE'
) {
buttons.push(start);
}
@@ -570,57 +550,57 @@
return m('.button', buttons);
}
-function StopCancelButtons() {
- if (!globals.state.recordingInProgress) return [];
+function StopCancelButtons(recMgr: RecordingManager) {
+ if (!recMgr.state.recordingInProgress) return [];
const stop = m(
`button.selected`,
- {onclick: () => globals.dispatch(Actions.stopRecording({}))},
+ {onclick: () => recMgr.stopRecording()},
'Stop',
);
const cancel = m(
`button`,
- {onclick: () => globals.dispatch(Actions.cancelRecording({}))},
+ {onclick: () => recMgr.cancelRecording()},
'Cancel',
);
return [stop, cancel];
}
-function onStartRecordingPressed() {
+function onStartRecordingPressed(recMgr: RecordingManager) {
location.href = '#!/record/instructions';
- raf.scheduleFullRedraw();
- autosaveConfigStore.save(globals.state.recordConfig);
+ scheduleFullRedraw();
+ autosaveConfigStore.save(recMgr.state.recordConfig);
- const target = globals.state.recordingTarget;
+ const target = recMgr.state.recordingTarget;
if (
isAndroidTarget(target) ||
isChromeTarget(target) ||
isWindowsTarget(target)
) {
- AppImpl.instance.analytics.logEvent(
+ recMgr.app.analytics.logEvent(
'Record Trace',
`Record trace (${target.os})`,
);
- globals.dispatch(Actions.startRecording({}));
+ recMgr.startRecording();
}
}
-function RecordingStatusLabel() {
- const recordingStatus = globals.state.recordingStatus;
+function RecordingStatusLabel(recMgr: RecordingManager) {
+ const recordingStatus = recMgr.state.recordingStatus;
if (!recordingStatus) return [];
return m('label', recordingStatus);
}
-export function ErrorLabel() {
- const lastRecordingError = globals.state.lastRecordingError;
+export function ErrorLabel(recMgr: RecordingManager) {
+ const lastRecordingError = recMgr.state.lastRecordingError;
if (!lastRecordingError) return [];
return m('label.error-label', `Error: ${lastRecordingError}`);
}
-function recordingLog() {
- const logs = globals.recordingLog;
+function recordingLog(recMgr: RecordingManager) {
+ const logs = recMgr.state.recordingLog;
if (logs === undefined) return [];
return m('.code-snippet.no-top-bar', m('code', logs));
}
@@ -628,7 +608,7 @@
// The connection must be done in the frontend. After it, the serial ID will
// be inserted in the state, and the worker will be able to connect to the
// correct device.
-async function addAndroidDevice() {
+async function addAndroidDevice(recMgr: RecordingManager) {
let device: USBDevice;
try {
device = await new AdbOverWebUsb().findDevice();
@@ -647,92 +627,11 @@
// After the user has selected a device with the chrome UI, it will be
// available when listing all the available device from WebUSB. Therefore,
// we update the list of available devices.
- await updateAvailableAdbDevices(device.serialNumber);
+ await recMgr.updateAvailableAdbDevices(device.serialNumber);
}
-// We really should be getting the API version from the adb target, but
-// currently its too complicated to do that (== most likely, we need to finish
-// recordingV2 migration). For now, add an escape hatch to use Android S as a
-// default, given that the main features we want are gated by API level 31 and S
-// is old enough to be the default most of the time.
-const USE_ANDROID_S_AS_DEFAULT_FLAG = featureFlags.register({
- id: 'recordingPageUseSAsDefault',
- name: 'Use Android S as a default recording target',
- description: 'Use Android S as a default recording target instead of Q',
- defaultValue: false,
-});
-
-export async function updateAvailableAdbDevices(
- preferredDeviceSerial?: string,
-) {
- const devices = await new AdbOverWebUsb().getPairedDevices();
-
- let recordingTarget: AdbRecordingTarget | undefined = undefined;
-
- const availableAdbDevices: AdbRecordingTarget[] = [];
- devices.forEach((d) => {
- if (d.productName && d.serialNumber) {
- // TODO(nicomazz): At this stage, we can't know the OS version, so we
- // assume it is 'Q'. This can create problems with devices with an old
- // version of perfetto. The os detection should be done after the adb
- // connection, from adb_record_controller
- availableAdbDevices.push({
- name: d.productName,
- serial: d.serialNumber,
- os: USE_ANDROID_S_AS_DEFAULT_FLAG.get() ? 'S' : 'Q',
- });
- if (preferredDeviceSerial && preferredDeviceSerial === d.serialNumber) {
- recordingTarget = availableAdbDevices[availableAdbDevices.length - 1];
- }
- }
- });
-
- globals.dispatch(
- Actions.setAvailableAdbDevices({devices: availableAdbDevices}),
- );
- selectAndroidDeviceIfAvailable(availableAdbDevices, recordingTarget);
- raf.scheduleFullRedraw();
- return availableAdbDevices;
-}
-
-function selectAndroidDeviceIfAvailable(
- availableAdbDevices: AdbRecordingTarget[],
- recordingTarget?: RecordingTarget,
-) {
- if (!recordingTarget) {
- recordingTarget = globals.state.recordingTarget;
- }
- const deviceConnected = isAdbTarget(recordingTarget);
- const connectedDeviceDisconnected =
- deviceConnected &&
- availableAdbDevices.find(
- (e) => e.serial === (recordingTarget as AdbRecordingTarget).serial,
- ) === undefined;
-
- if (availableAdbDevices.length) {
- // If there's an Android device available and the current selection isn't
- // one, select the Android device by default. If the current device isn't
- // available anymore, but another Android device is, select the other
- // Android device instead.
- if (!deviceConnected || connectedDeviceDisconnected) {
- recordingTarget = availableAdbDevices[0];
- }
-
- globals.dispatch(Actions.setRecordingTarget({target: recordingTarget}));
- return;
- }
-
- // If the currently selected device was disconnected, reset the recording
- // target to the default one.
- if (connectedDeviceDisconnected) {
- globals.dispatch(
- Actions.setRecordingTarget({target: getDefaultRecordingTargets()[0]}),
- );
- }
-}
-
-function recordMenu(routePage: string) {
- const target = globals.state.recordingTarget;
+function recordMenu(recMgr: RecordingManager, routePage: string) {
+ const target = recMgr.state.recordingTarget;
const chromeProbe = m(
'a[href="#!/record/chrome"]',
m(
@@ -814,7 +713,7 @@
m('.sub', 'Context switch, Thread state'),
),
);
- const recInProgress = globals.state.recordingInProgress;
+ const recInProgress = recMgr.state.recordingInProgress;
const probes = [];
if (isLinuxTarget(target)) {
@@ -840,7 +739,7 @@
'.record-menu',
{
class: recInProgress ? 'disabled' : '',
- onclick: () => raf.scheduleFullRedraw(),
+ onclick: () => scheduleFullRedraw(),
},
m('header', 'Trace config'),
m(
@@ -863,22 +762,20 @@
m('.sub', 'Manually record trace'),
),
),
- PERSIST_CONFIG_FLAG.get()
- ? m(
- 'a[href="#!/record/config"]',
- {
- onclick: () => {
- recordConfigStore.reloadFromLocalStorage();
- },
- },
- m(
- `li${routePage === 'config' ? '.active' : ''}`,
- m('i.material-icons', 'save'),
- m('.title', 'Saved configs'),
- m('.sub', 'Manage local configs'),
- ),
- )
- : null,
+ m(
+ 'a[href="#!/record/config"]',
+ {
+ onclick: () => {
+ recordConfigStore.reloadFromLocalStorage();
+ },
+ },
+ m(
+ `li${routePage === 'config' ? '.active' : ''}`,
+ m('i.material-icons', 'save'),
+ m('.title', 'Saved configs'),
+ m('.sub', 'Manage local configs'),
+ ),
+ ),
),
m('header', 'Probes'),
m('ul', probes),
@@ -889,24 +786,57 @@
return routePage === section ? '.active' : '';
}
-export class RecordPage implements m.ClassComponent<PageAttrs> {
- view({attrs}: m.CVnode<PageAttrs>) {
+export interface RecordPageAttrs extends PageAttrs {
+ app: App;
+ recMgr: RecordingManager;
+}
+
+export class RecordPage implements m.ClassComponent<RecordPageAttrs> {
+ private readonly recMgr: RecordingManager;
+ private lastSubpage: string | undefined = undefined;
+
+ constructor({attrs}: m.CVnode<RecordPageAttrs>) {
+ this.recMgr = attrs.recMgr;
+ }
+
+ oninit({attrs}: m.CVnode<RecordPageAttrs>) {
+ this.lastSubpage = attrs.subpage;
+ if (attrs.subpage !== undefined && attrs.subpage.startsWith('/share/')) {
+ const hash = attrs.subpage.substring(7);
+ loadRecordConfig(this.recMgr, hash);
+ attrs.app.navigate('#!/record/instructions');
+ }
+ }
+
+ view({attrs}: m.CVnode<RecordPageAttrs>) {
+ if (attrs.subpage !== this.lastSubpage) {
+ this.lastSubpage = attrs.subpage;
+ // TODO(primiano): this is a hack necesasry to retrigger the generation of
+ // the record cmdline. Refactor this code once record v1 vs v2 is gone.
+ this.recMgr.setRecordConfig(this.recMgr.state.recordConfig);
+ }
+
const pages: m.Children = [];
// we need to remove the `/` character from the route
let routePage = attrs.subpage ? attrs.subpage.substr(1) : '';
if (!RECORDING_SECTIONS.includes(routePage)) {
routePage = 'buffers';
}
- pages.push(recordMenu(routePage));
+ pages.push(recordMenu(this.recMgr, routePage));
pages.push(
m(RecordingSettings, {
dataSources: [],
cssClass: maybeGetActiveCss(routePage, 'buffers'),
- } as RecordingSectionAttrs),
+ recState: this.recMgr.state,
+ }),
);
- pages.push(Instructions(maybeGetActiveCss(routePage, 'instructions')));
- pages.push(Configurations(maybeGetActiveCss(routePage, 'config')));
+ pages.push(
+ Instructions(this.recMgr, maybeGetActiveCss(routePage, 'instructions')),
+ );
+ pages.push(
+ Configurations(this.recMgr, maybeGetActiveCss(routePage, 'config')),
+ );
const settingsSections = new Map([
['cpu', CpuSettings],
@@ -924,22 +854,59 @@
m(component, {
dataSources: [],
cssClass: maybeGetActiveCss(routePage, section),
- } as RecordingSectionAttrs),
+ recState: this.recMgr.state,
+ }),
);
}
- if (isChromeTarget(globals.state.recordingTarget)) {
- globals.dispatch(Actions.setFetchChromeCategories({fetch: true}));
+ if (isChromeTarget(this.recMgr.state.recordingTarget)) {
+ this.recMgr.setFetchChromeCategories(true);
}
return m(
'.record-page',
- globals.state.recordingInProgress ? m('.hider') : [],
+ this.recMgr.state.recordingInProgress ? m('.hider') : [],
m(
'.record-container',
- RecordHeader(),
- m('.record-container-content', recordMenu(routePage), pages),
+ RecordHeader(this.recMgr),
+ m(
+ '.record-container-content',
+ recordMenu(this.recMgr, routePage),
+ pages,
+ ),
),
);
}
}
+
+export async function uploadRecordingConfig(recordConfig: RecordConfig) {
+ const json = JSON.stringify(recordConfig);
+ const uploader: GcsUploader = new GcsUploader(json, {
+ mimeType: MIME_JSON,
+ });
+ await uploader.waitForCompletion();
+ const hash = uploader.uploadedFileName;
+ const url = `${self.location.origin}/#!/record/share/${hash}`;
+ showModal({
+ title: 'Shareable record settings',
+ content: m(CopyableLink, {url}),
+ });
+}
+
+export async function loadRecordConfig(recMgr: RecordingManager, hash: string) {
+ const url = `https://storage.googleapis.com/${BUCKET_NAME}/${hash}`;
+ const response = await fetch(url);
+ if (!response.ok) {
+ showModal({title: 'Load failed', content: `Could not fetch ${url}`});
+ return;
+ }
+ const text = await response.text();
+ const json = JSON.parse(text);
+ const res = RECORD_CONFIG_SCHEMA.safeParse(json);
+ if (!res.success) {
+ throw new Error(
+ 'Failed to deserialize record settings ' + res.error.toString(),
+ );
+ }
+ recMgr.setRecordConfig(res.data);
+}
diff --git a/ui/src/frontend/record_page_v2.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_page_v2.ts
similarity index 77%
rename from ui/src/frontend/record_page_v2.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_page_v2.ts
index 49b1d71..3559332 100644
--- a/ui/src/frontend/record_page_v2.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_page_v2.ts
@@ -14,52 +14,52 @@
import m from 'mithril';
import {Attributes} from 'mithril';
-import {assertExists} from '../base/logging';
-import {RecordingConfigUtils} from '../common/recordingV2/recording_config_utils';
+import {assertExists} from '../../base/logging';
+import {RecordingConfigUtils} from './recordingV2/recording_config_utils';
import {
ChromeTargetInfo,
RecordingTargetV2,
TargetInfo,
-} from '../common/recordingV2/recording_interfaces_v2';
+} from './recordingV2/recording_interfaces_v2';
import {
RecordingPageController,
RecordingState,
-} from '../common/recordingV2/recording_page_controller';
-import {
- EXTENSION_NAME,
- EXTENSION_URL,
-} from '../common/recordingV2/recording_utils';
-import {targetFactoryRegistry} from '../common/recordingV2/target_factory_registry';
-import {raf} from '../core/raf_scheduler';
-import {globals} from './globals';
-import {PageAttrs} from '../core/router';
+} from './recordingV2/recording_page_controller';
+import {EXTENSION_NAME, EXTENSION_URL} from './recordingV2/recording_utils';
+import {targetFactoryRegistry} from './recordingV2/target_factory_registry';
+import {PageAttrs} from '../../public/page';
import {recordConfigStore} from './record_config';
import {
Configurations,
+ loadRecordConfig,
maybeGetActiveCss,
- PERSIST_CONFIG_FLAG,
RECORDING_SECTIONS,
+ uploadRecordingConfig,
} from './record_page';
import {CodeSnippet} from './record_widgets';
-import {AdvancedSettings} from './recording/advanced_settings';
-import {AndroidSettings} from './recording/android_settings';
-import {ChromeSettings} from './recording/chrome_settings';
-import {CpuSettings} from './recording/cpu_settings';
-import {EtwSettings} from './recording/etw_settings';
-import {GpuSettings} from './recording/gpu_settings';
-import {LinuxPerfSettings} from './recording/linux_perf_settings';
-import {MemorySettings} from './recording/memory_settings';
-import {PowerSettings} from './recording/power_settings';
-import {RecordingSectionAttrs} from './recording/recording_sections';
-import {RecordingSettings} from './recording/recording_settings';
-import {FORCE_RESET_MESSAGE} from './recording/recording_ui_utils';
-import {showAddNewTargetModal} from './recording/reset_target_modal';
-import {createPermalink} from './permalink';
+import {AdvancedSettings} from './advanced_settings';
+import {AndroidSettings} from './android_settings';
+import {ChromeSettings} from './chrome_settings';
+import {CpuSettings} from './cpu_settings';
+import {EtwSettings} from './etw_settings';
+import {GpuSettings} from './gpu_settings';
+import {LinuxPerfSettings} from './linux_perf_settings';
+import {MemorySettings} from './memory_settings';
+import {PowerSettings} from './power_settings';
+import {RecordingSettings} from './recording_settings';
+import {FORCE_RESET_MESSAGE} from './recording_ui_utils';
+import {showAddNewTargetModal} from './reset_target_modal';
+import {RecordingManager} from './recording_manager';
+import {RecordConfig} from './record_config_types';
+import {App} from '../../public/app';
+import {scheduleFullRedraw} from '../../widgets/raf';
const START_RECORDING_MESSAGE = 'Start Recording';
-const controller = new RecordingPageController();
-const recordConfigUtils = new RecordingConfigUtils();
+// TODO(primiano): this is needs to be rewritten, but then i'm going to rewrite
+// the whole record_page_v2 so not worth cleaning up now.
+let controller: RecordingPageController;
+let recordConfigUtils: RecordingConfigUtils;
// Options for displaying a target selection menu.
export interface TargetSelectionOptions {
@@ -76,11 +76,11 @@
return ['CHROME', 'CHROME_OS', 'WINDOWS'].includes(targetInfo.targetType);
}
-function RecordHeader() {
+function RecordHeader(recMgr: RecordingManager) {
const platformSelection = RecordingPlatformSelection();
- const statusLabel = RecordingStatusLabel();
- const buttons = RecordingButton();
- const notes = RecordingNotes();
+ const statusLabel = RecordingStatusLabel(recMgr);
+ const buttons = RecordingButton(recMgr.state.recordConfig);
+ const notes = RecordingNotes(recMgr.state.recordConfig);
if (!platformSelection && !statusLabel && !buttons && !notes) {
// The header should not be displayed when it has no content.
return undefined;
@@ -163,13 +163,13 @@
// This will display status messages which are informative, but do not require
// user action, such as: "Recording in progress for X seconds" in the recording
// page header.
-function RecordingStatusLabel() {
- const recordingStatus = globals.state.recordingStatus;
+function RecordingStatusLabel(recMgr: RecordingManager) {
+ const recordingStatus = recMgr.state.recordingStatus;
if (!recordingStatus) return undefined;
return m('label', recordingStatus);
}
-function Instructions(cssClass: string) {
+function Instructions(recCfg: RecordConfig, cssClass: string) {
if (controller.getState() < RecordingState.TARGET_SELECTED) {
return undefined;
}
@@ -179,16 +179,14 @@
return m(
`.record-section.instructions${cssClass}`,
m('header', 'Recording command'),
- PERSIST_CONFIG_FLAG.get()
- ? m(
- 'button.permalinkconfig',
- {
- onclick: () => createPermalink({mode: 'RECORDING_OPTS'}),
- },
- 'Share recording settings',
- )
- : null,
- RecordingSnippet(targetInfo),
+ m(
+ 'button.permalinkconfig',
+ {
+ onclick: () => uploadRecordingConfig(recCfg),
+ },
+ 'Share recording settings',
+ ),
+ RecordingSnippet(recCfg, targetInfo),
BufferUsageProgressBar(),
m('.buttons', StopCancelButtons()),
);
@@ -213,7 +211,7 @@
);
}
-function RecordingNotes() {
+function RecordingNotes(recCfg: RecordConfig) {
if (controller.getState() !== RecordingState.TARGET_INFO_DISPLAYED) {
return undefined;
}
@@ -258,10 +256,8 @@
);
if (
- !recordConfigUtils.fetchLatestRecordCommand(
- globals.state.recordConfig,
- targetInfo,
- ).hasDataSources
+ !recordConfigUtils.fetchLatestRecordCommand(recCfg, targetInfo)
+ .hasDataSources
) {
notes.push(
m(
@@ -305,14 +301,14 @@
default:
}
- if (globals.state.recordConfig.mode === 'LONG_TRACE') {
+ if (recCfg.mode === 'LONG_TRACE') {
notes.unshift(msgLongTraces);
}
return notes.length > 0 ? m('div', notes) : undefined;
}
-function RecordingSnippet(targetInfo: TargetInfo) {
+function RecordingSnippet(recCfg: RecordConfig, targetInfo: TargetInfo) {
// We don't need commands to start tracing on chrome
if (isChromeTargetInfo(targetInfo)) {
if (controller.getState() > RecordingState.AUTH_P2) {
@@ -329,12 +325,15 @@
),
);
}
- return m(CodeSnippet, {text: getRecordCommand(targetInfo)});
+ return m(CodeSnippet, {text: getRecordCommand(recCfg, targetInfo)});
}
-function getRecordCommand(targetInfo: TargetInfo): string {
+function getRecordCommand(
+ recCfg: RecordConfig,
+ targetInfo: TargetInfo,
+): string {
const recordCommand = recordConfigUtils.fetchLatestRecordCommand(
- globals.state.recordConfig,
+ recCfg,
targetInfo,
);
@@ -362,7 +361,7 @@
return cmd;
}
-function RecordingButton() {
+function RecordingButton(recCfg: RecordConfig) {
if (
controller.getState() !== RecordingState.TARGET_INFO_DISPLAYED ||
!controller.canCreateTracingSession()
@@ -373,7 +372,7 @@
// We know we have a target because we checked the state.
const targetInfo = assertExists(controller.getTargetInfo());
const hasDataSources = recordConfigUtils.fetchLatestRecordCommand(
- globals.state.recordConfig,
+ recCfg,
targetInfo,
).hasDataSources;
if (!hasDataSources) {
@@ -523,7 +522,7 @@
controller.getState() > RecordingState.TARGET_INFO_DISPLAYED
? 'disabled'
: '',
- onclick: () => raf.scheduleFullRedraw(),
+ onclick: () => scheduleFullRedraw(),
},
m('header', 'Trace config'),
m(
@@ -546,31 +545,29 @@
m('.sub', 'Manually record trace'),
),
),
- PERSIST_CONFIG_FLAG.get()
- ? m(
- 'a[href="#!/record/config"]',
- {
- onclick: () => {
- recordConfigStore.reloadFromLocalStorage();
- },
- },
- m(
- `li${routePage === 'config' ? '.active' : ''}`,
- m('i.material-icons', 'save'),
- m('.title', 'Saved configs'),
- m('.sub', 'Manage local configs'),
- ),
- )
- : null,
+ m(
+ 'a[href="#!/record/config"]',
+ {
+ onclick: () => {
+ recordConfigStore.reloadFromLocalStorage();
+ },
+ },
+ m(
+ `li${routePage === 'config' ? '.active' : ''}`,
+ m('i.material-icons', 'save'),
+ m('.title', 'Saved configs'),
+ m('.sub', 'Manage local configs'),
+ ),
+ ),
),
m('header', 'Probes'),
m('ul', probes),
);
}
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function getRecordContainer(subpage?: string): m.Vnode<any, any> {
- const components: m.Children[] = [RecordHeader()];
+function getRecordContainer(recMgr: RecordingManager, subpage?: string) {
+ const recCfg = recMgr.state.recordConfig;
+ const components: m.Children[] = [RecordHeader(recMgr)];
if (controller.getState() === RecordingState.NO_TARGET) {
components.push(m('.full-centered', 'Please connect a valid target.'));
return m('.record-container', components);
@@ -610,10 +607,13 @@
m(RecordingSettings, {
dataSources: [],
cssClass: maybeGetActiveCss(routePage, 'buffers'),
- } as RecordingSectionAttrs),
+ recState: recMgr.state,
+ }),
);
- pages.push(Instructions(maybeGetActiveCss(routePage, 'instructions')));
- pages.push(Configurations(maybeGetActiveCss(routePage, 'config')));
+ pages.push(
+ Instructions(recCfg, maybeGetActiveCss(routePage, 'instructions')),
+ );
+ pages.push(Configurations(recMgr, maybeGetActiveCss(routePage, 'config')));
const settingsSections = new Map([
['cpu', CpuSettings],
@@ -631,7 +631,8 @@
m(component, {
dataSources: controller.getTargetInfo()?.dataSources || [],
cssClass: maybeGetActiveCss(routePage, section),
- } as RecordingSectionAttrs),
+ recState: recMgr.state,
+ }),
);
}
@@ -639,18 +640,43 @@
return m('.record-container', components);
}
-export class RecordPageV2 implements m.ClassComponent<PageAttrs> {
- oninit(): void {
- controller.initFactories();
+export interface RecordPageV2Attrs extends PageAttrs {
+ app: App;
+ recCtl: RecordingPageController;
+ recMgr: RecordingManager;
+}
+
+export class RecordPageV2 implements m.ClassComponent<RecordPageV2Attrs> {
+ private lastSubpage: string | undefined = undefined;
+
+ constructor({attrs}: m.CVnode<RecordPageV2Attrs>) {
+ controller ??= attrs.recCtl;
+ recordConfigUtils ??= new RecordingConfigUtils();
}
- view({attrs}: m.CVnode<PageAttrs>) {
+ oninit({attrs}: m.CVnode<RecordPageV2Attrs>) {
+ this.lastSubpage = attrs.subpage;
+ if (attrs.subpage !== undefined && attrs.subpage.startsWith('/share/')) {
+ const hash = attrs.subpage.substring(7);
+ loadRecordConfig(attrs.recMgr, hash);
+ attrs.app.navigate('#!/record/instructions');
+ }
+ }
+
+ view({attrs}: m.CVnode<RecordPageV2Attrs>) {
+ if (attrs.subpage !== this.lastSubpage) {
+ this.lastSubpage = attrs.subpage;
+ // TODO(primiano): this is a hack necesasry to retrigger the generation of
+ // the record cmdline. Refactor this code once record v1 vs v2 is gone.
+ attrs.recMgr.setRecordConfig(attrs.recMgr.state.recordConfig);
+ }
+
return m(
'.record-page',
controller.getState() > RecordingState.TARGET_INFO_DISPLAYED
? m('.hider')
: [],
- getRecordContainer(attrs.subpage),
+ getRecordContainer(attrs.recMgr, attrs.subpage),
);
}
}
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/plugins/dev.perfetto.RecordTrace/record_widgets.ts
similarity index 82%
rename from ui/src/frontend/record_widgets.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/record_widgets.ts
index 2eeb0df..325237b 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/record_widgets.ts
@@ -12,15 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Draft, produce} from 'immer';
import m from 'mithril';
-import {copyToClipboard} from '../base/clipboard';
-import {assertExists} from '../base/logging';
-import {Actions} from '../common/actions';
-import {RecordConfig} from '../controller/record_config_types';
-import {globals} from './globals';
+import {copyToClipboard} from '../../base/clipboard';
+import {assertExists} from '../../base/logging';
+import {RecordConfig} from './record_config_types';
+import {assetSrc} from '../../base/assets';
+import {scheduleFullRedraw} from '../../widgets/raf';
-export declare type Setter<T> = (draft: Draft<RecordConfig>, val: T) => void;
+export declare type Setter<T> = (cfg: RecordConfig, val: T) => void;
export declare type Getter<T> = (cfg: RecordConfig) => T;
function defaultSort(a: string, b: string) {
@@ -51,6 +50,7 @@
// +---------------------------------------------------------------------------+
export interface ProbeAttrs {
+ recCfg: RecordConfig;
title: string;
img: string | null;
compact?: boolean;
@@ -62,19 +62,17 @@
export class Probe implements m.ClassComponent<ProbeAttrs> {
view({attrs, children}: m.CVnode<ProbeAttrs>) {
const onToggle = (enabled: boolean) => {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- attrs.setEnabled(draft, enabled);
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ attrs.setEnabled(attrs.recCfg, enabled);
+ scheduleFullRedraw();
};
- const enabled = attrs.isEnabled(globals.state.recordConfig);
+ const enabled = attrs.isEnabled(attrs.recCfg);
return m(
`.probe${attrs.compact ? '.compact' : ''}${enabled ? '.enabled' : ''}`,
attrs.img &&
m('img', {
- src: `${globals.root}assets/${attrs.img}`,
+ src: assetSrc(`assets/${attrs.img}`),
onclick: () => onToggle(!enabled),
}),
m(
@@ -99,18 +97,20 @@
}
export function CompactProbe(args: {
+ recCfg: RecordConfig;
title: string;
isEnabled: Getter<boolean>;
setEnabled: Setter<boolean>;
}) {
return m(Probe, {
+ recCfg: args.recCfg,
title: args.title,
img: null,
compact: true,
descr: '',
isEnabled: args.isEnabled,
setEnabled: args.setEnabled,
- } as ProbeAttrs);
+ });
}
// +-------------------------------------------------------------+
@@ -118,6 +118,7 @@
// +-------------------------------------------------------------+
export interface ToggleAttrs {
+ recCfg: RecordConfig;
title: string;
descr: string;
cssClass?: string;
@@ -128,13 +129,11 @@
export class Toggle implements m.ClassComponent<ToggleAttrs> {
view({attrs}: m.CVnode<ToggleAttrs>) {
const onToggle = (enabled: boolean) => {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- attrs.setEnabled(draft, enabled);
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ attrs.setEnabled(attrs.recCfg, enabled);
+ scheduleFullRedraw();
};
- const enabled = attrs.isEnabled(globals.state.recordConfig);
+ const enabled = attrs.isEnabled(attrs.recCfg);
return m(
`.toggle${enabled ? '.enabled' : ''}${attrs.cssClass ?? ''}`,
@@ -158,6 +157,7 @@
// +---------------------------------------------------------------------------+
export interface SliderAttrs {
+ recCfg: RecordConfig;
title: string;
icon?: string;
cssClass?: string;
@@ -174,10 +174,8 @@
export class Slider implements m.ClassComponent<SliderAttrs> {
onValueChange(attrs: SliderAttrs, newVal: number) {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- attrs.set(draft, newVal);
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ attrs.set(attrs.recCfg, newVal);
+ scheduleFullRedraw();
}
onTimeValueChange(attrs: SliderAttrs, hms: string) {
@@ -195,7 +193,7 @@
view({attrs}: m.CVnode<SliderAttrs>) {
const id = attrs.title.replace(/[^a-z0-9]/gim, '_').toLowerCase();
const maxIdx = attrs.values.length - 1;
- const val = attrs.get(globals.state.recordConfig);
+ const val = attrs.get(attrs.recCfg);
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
let min = attrs.min || 1;
if (attrs.zeroIsDefault) {
@@ -251,6 +249,7 @@
// +---------------------------------------------------------------------------+
export interface DropdownAttrs {
+ recCfg: RecordConfig;
title: string;
cssClass?: string;
options: Map<string, string>;
@@ -276,15 +275,13 @@
const item = assertExists(dom.selectedOptions.item(i));
selKeys.push(item.value);
}
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- attrs.set(draft, selKeys);
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ attrs.set(attrs.recCfg, selKeys);
+ scheduleFullRedraw();
}
view({attrs}: m.CVnode<DropdownAttrs>) {
const options: m.Children = [];
- const selItems = attrs.get(globals.state.recordConfig);
+ const selItems = attrs.get(attrs.recCfg);
let numSelected = 0;
const entries = [...attrs.options.entries()];
const f = attrs.sort === undefined ? defaultSort : attrs.sort;
@@ -317,6 +314,7 @@
// +---------------------------------------------------------------------------+
export interface TextareaAttrs {
+ recCfg: RecordConfig;
placeholder: string;
docsLink?: string;
cssClass?: string;
@@ -327,10 +325,8 @@
export class Textarea implements m.ClassComponent<TextareaAttrs> {
onChange(attrs: TextareaAttrs, dom: HTMLTextAreaElement) {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- attrs.set(draft, dom.value);
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ attrs.set(attrs.recCfg, dom.value);
+ scheduleFullRedraw();
}
view({attrs}: m.CVnode<TextareaAttrs>) {
@@ -345,7 +341,7 @@
onchange: (e: Event) =>
this.onChange(attrs, e.target as HTMLTextAreaElement),
placeholder: attrs.placeholder,
- value: attrs.get(globals.state.recordConfig),
+ value: attrs.get(attrs.recCfg),
}),
);
}
@@ -383,6 +379,7 @@
}
type CategoriesCheckboxListParams = CategoryGetter & {
+ recCfg: RecordConfig;
categories: Map<string, string>;
title: string;
};
@@ -395,21 +392,19 @@
value: string,
enabled: boolean,
) {
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- const values = attrs.get(draft);
- const index = values.indexOf(value);
- if (enabled && index === -1) {
- values.push(value);
- }
- if (!enabled && index !== -1) {
- values.splice(index, 1);
- }
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ const values = attrs.get(attrs.recCfg);
+ const index = values.indexOf(value);
+ if (enabled && index === -1) {
+ values.push(value);
+ }
+ if (!enabled && index !== -1) {
+ values.splice(index, 1);
+ }
+ scheduleFullRedraw();
}
view({attrs}: m.CVnode<CategoriesCheckboxListParams>) {
- const enabled = new Set(attrs.get(globals.state.recordConfig));
+ const enabled = new Set(attrs.get(attrs.recCfg));
return m(
'.categories-list',
m(
@@ -419,10 +414,7 @@
'button.config-button',
{
onclick: () => {
- const config = produce(globals.state.recordConfig, (draft) => {
- attrs.set(draft, Array.from(attrs.categories.keys()));
- });
- globals.dispatch(Actions.setRecordConfig({config}));
+ attrs.set(attrs.recCfg, Array.from(attrs.categories.keys()));
},
},
'All',
@@ -431,10 +423,7 @@
'button.config-button',
{
onclick: () => {
- const config = produce(globals.state.recordConfig, (draft) => {
- attrs.set(draft, []);
- });
- globals.dispatch(Actions.setRecordConfig({config}));
+ attrs.set(attrs.recCfg, []);
},
},
'None',
diff --git a/ui/src/common/recordingV2/adb_connection_impl.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_impl.ts
similarity index 94%
rename from ui/src/common/recordingV2/adb_connection_impl.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_impl.ts
index 99ef224..33e0dc1 100644
--- a/ui/src/common/recordingV2/adb_connection_impl.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_impl.ts
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer} from '../../base/deferred';
-import {ArrayBufferBuilder} from '../../base/array_buffer_builder';
+import {defer} from '../../../base/deferred';
+import {ArrayBufferBuilder} from '../../../base/array_buffer_builder';
import {AdbFileHandler} from './adb_file_handler';
import {
AdbConnection,
@@ -21,7 +21,7 @@
OnDisconnectCallback,
OnMessageCallback,
} from './recording_interfaces_v2';
-import {utf8Decode} from '../../base/string_utils';
+import {utf8Decode} from '../../../base/string_utils';
export abstract class AdbConnectionImpl implements AdbConnection {
// onStatus and onDisconnect are set to callbacks passed from the caller.
diff --git a/ui/src/common/recordingV2/adb_connection_over_websocket.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_websocket.ts
similarity index 98%
rename from ui/src/common/recordingV2/adb_connection_over_websocket.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_websocket.ts
index 160b257..9c9d139 100644
--- a/ui/src/common/recordingV2/adb_connection_over_websocket.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_websocket.ts
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer, Deferred} from '../../base/deferred';
-import {utf8Decode} from '../../base/string_utils';
+import {defer, Deferred} from '../../../base/deferred';
+import {utf8Decode} from '../../../base/string_utils';
import {AdbConnectionImpl} from './adb_connection_impl';
import {RecordingError} from './recording_error_handling';
import {
diff --git a/ui/src/common/recordingV2/adb_connection_over_webusb.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_webusb.ts
similarity index 98%
rename from ui/src/common/recordingV2/adb_connection_over_webusb.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_webusb.ts
index 713d8b3..715d366 100644
--- a/ui/src/common/recordingV2/adb_connection_over_webusb.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_connection_over_webusb.ts
@@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer, Deferred} from '../../base/deferred';
-import {assertExists, assertFalse, assertTrue} from '../../base/logging';
-import {isString} from '../../base/object_utils';
-import {utf8Decode, utf8Encode} from '../../base/string_utils';
-import {CmdType} from '../../controller/adb_interfaces';
+import {defer, Deferred} from '../../../base/deferred';
+import {assertExists, assertFalse, assertTrue} from '../../../base/logging';
+import {isString} from '../../../base/object_utils';
+import {utf8Decode, utf8Encode} from '../../../base/string_utils';
+import {CmdType} from '../adb_interfaces';
import {AdbConnectionImpl} from './adb_connection_impl';
import {AdbKeyManager, maybeStoreKey} from './auth/adb_key_manager';
import {RecordingError, wrapRecordingError} from './recording_error_handling';
diff --git a/ui/src/common/recordingV2/adb_file_handler.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_file_handler.ts
similarity index 94%
rename from ui/src/common/recordingV2/adb_file_handler.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_file_handler.ts
index 1016fe7..078726f 100644
--- a/ui/src/common/recordingV2/adb_file_handler.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/adb_file_handler.ts
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer, Deferred} from '../../base/deferred';
-import {assertFalse} from '../../base/logging';
-import {ArrayBufferBuilder} from '../../base/array_buffer_builder';
+import {defer, Deferred} from '../../../base/deferred';
+import {assertFalse} from '../../../base/logging';
+import {ArrayBufferBuilder} from '../../../base/array_buffer_builder';
import {RecordingError} from './recording_error_handling';
import {ByteStream} from './recording_interfaces_v2';
import {
BINARY_PUSH_FAILURE,
BINARY_PUSH_UNKNOWN_RESPONSE,
} from './recording_utils';
-import {utf8Decode} from '../../base/string_utils';
+import {utf8Decode} from '../../../base/string_utils';
// https://cs.android.com/android/platform/superproject/+/main:packages/
// modules/adb/file_sync_protocol.h;l=144
diff --git a/ui/src/common/recordingV2/auth/adb_auth.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_auth.ts
similarity index 97%
rename from ui/src/common/recordingV2/auth/adb_auth.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_auth.ts
index aec8752..7ed275e 100644
--- a/ui/src/common/recordingV2/auth/adb_auth.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_auth.ts
@@ -13,12 +13,12 @@
// limitations under the License.
import {BigInteger, RSAKey} from 'jsbn-rsa';
-import {assertExists, assertTrue} from '../../../base/logging';
+import {assertExists, assertTrue} from '../../../../base/logging';
import {
base64Decode,
base64Encode,
hexEncode,
-} from '../../../base/string_utils';
+} from '../../../../base/string_utils';
import {RecordingError} from '../recording_error_handling';
const WORD_SIZE = 4;
diff --git a/ui/src/common/recordingV2/auth/adb_key_manager.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_key_manager.ts
similarity index 96%
rename from ui/src/common/recordingV2/auth/adb_key_manager.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_key_manager.ts
index becd039..0ce297b 100644
--- a/ui/src/common/recordingV2/auth/adb_key_manager.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/adb_key_manager.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {globals} from '../../../frontend/globals';
+import {assetSrc} from '../../../../base/assets';
import {AdbKey} from './adb_auth';
function isPasswordCredential(
@@ -37,7 +37,7 @@
id: 'webusb-adb-key',
password: key.serializeKey(),
name: 'WebUSB ADB Key',
- iconURL: `${globals.root}assets/favicon.png`,
+ iconURL: assetSrc('assets/favicon.png'),
});
// The 'Save password?' Chrome dialogue only appears if the key is
// not already stored in Chrome.
diff --git a/ui/src/common/recordingV2/auth/credentials_interfaces.d.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/credentials_interfaces.d.ts
similarity index 100%
rename from ui/src/common/recordingV2/auth/credentials_interfaces.d.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/auth/credentials_interfaces.d.ts
diff --git a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/chrome_traced_tracing_session.ts
similarity index 95%
rename from ui/src/common/recordingV2/chrome_traced_tracing_session.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/chrome_traced_tracing_session.ts
index f8ecd03..9461190 100644
--- a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/chrome_traced_tracing_session.ts
@@ -12,28 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer, Deferred} from '../../base/deferred';
-import {assertExists, assertTrue} from '../../base/logging';
-import {binaryDecode, binaryEncode} from '../../base/string_utils';
+import {defer, Deferred} from '../../../base/deferred';
+import {assertExists, assertTrue} from '../../../base/logging';
+import {binaryDecode, binaryEncode} from '../../../base/string_utils';
import {
ChromeExtensionMessage,
isChromeExtensionError,
isChromeExtensionStatus,
isGetCategoriesResponse,
-} from '../../controller/chrome_proxy_record_controller';
+} from '../chrome_proxy_record_controller';
import {
isDisableTracingResponse,
isEnableTracingResponse,
isFreeBuffersResponse,
isGetTraceStatsResponse,
isReadBuffersResponse,
-} from '../../controller/consumer_port_types';
+} from '../consumer_port_types';
import {
EnableTracingRequest,
IBufferStats,
ISlice,
TraceConfig,
-} from '../../protos';
+} from '../../../protos';
import {RecordingError} from './recording_error_handling';
import {
TracingSession,
diff --git a/ui/src/common/recordingV2/host_os_byte_stream.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/host_os_byte_stream.ts
similarity index 97%
rename from ui/src/common/recordingV2/host_os_byte_stream.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/host_os_byte_stream.ts
index 3c43630..a03b791 100644
--- a/ui/src/common/recordingV2/host_os_byte_stream.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/host_os_byte_stream.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {defer} from '../../base/deferred';
+import {defer} from '../../../base/deferred';
import {
ByteStream,
OnStreamCloseCallback,
diff --git a/ui/src/common/recordingV2/recording_config_utils.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils.ts
similarity index 98%
rename from ui/src/common/recordingV2/recording_config_utils.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils.ts
index bc3262e..e4eca50 100644
--- a/ui/src/common/recordingV2/recording_config_utils.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils.ts
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {isString} from '../../base/object_utils';
-import {base64Encode} from '../../base/string_utils';
-import {exists} from '../../base/utils';
-import {RecordConfig} from '../../controller/record_config_types';
+import {isString} from '../../../base/object_utils';
+import {base64Encode} from '../../../base/string_utils';
+import {exists} from '../../../base/utils';
+import {RecordConfig} from '../record_config_types';
import {
AndroidLogConfig,
AndroidLogId,
@@ -41,7 +41,7 @@
TraceConfig,
TrackEventConfig,
VmstatCounters,
-} from '../../protos';
+} from '../../../protos';
import {TargetInfo} from './recording_interfaces_v2';
import PerfClock = PerfEvents.PerfClock;
import Timebase = PerfEvents.Timebase;
@@ -463,7 +463,7 @@
if (uiCfg.androidStatsdPushedAtoms.length > 0) {
ds.config.statsdTracingConfig.pushAtomId =
- uiCfg.androidStatsdPushedAtoms.map((atom) => atom as any as AtomId);
+ uiCfg.androidStatsdPushedAtoms.map((atom) => atom as unknown as AtomId);
}
const needPulledAtomConfig =
@@ -471,7 +471,7 @@
uiCfg.androidStatsdPulledAtoms.length > 0;
if (needPulledAtomConfig) {
- let pullAtomConfig = new StatsdPullAtomConfig();
+ const pullAtomConfig = new StatsdPullAtomConfig();
if (uiCfg.androidStatsdRawPulledAtoms.length > 0) {
for (const line of uiCfg.androidStatsdRawPulledAtoms.split('\n')) {
if (line.trim().length > 0) {
@@ -479,8 +479,9 @@
}
}
}
- pullAtomConfig.pullAtomId =
- uiCfg.androidStatsdPulledAtoms.map((atom) => atom as any as AtomId);
+ pullAtomConfig.pullAtomId = uiCfg.androidStatsdPulledAtoms.map(
+ (atom) => atom as unknown as AtomId,
+ );
pullAtomConfig.pullFrequencyMs =
uiCfg.androidStatsdPulledAtomPullFrequencyMs;
if (uiCfg.androidStatsdPulledAtomPackages.length > 0) {
diff --git a/ui/src/common/recordingV2/recording_config_utils_unittest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils_unittest.ts
similarity index 96%
rename from ui/src/common/recordingV2/recording_config_utils_unittest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils_unittest.ts
index 67ac112..dd96a69 100644
--- a/ui/src/common/recordingV2/recording_config_utils_unittest.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_config_utils_unittest.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {createEmptyRecordConfig} from '../../controller/record_config_types';
+import {createEmptyRecordConfig} from '../record_config_types';
import {genTraceConfig} from './recording_config_utils';
import {AndroidTargetInfo} from './recording_interfaces_v2';
diff --git a/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_error_handling.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_error_handling.ts
new file mode 100644
index 0000000..ba86e65
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_error_handling.ts
@@ -0,0 +1,263 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {getErrorMessage} from '../../../base/errors';
+import {showModal} from '../../../widgets/modal';
+import {OnMessageCallback} from './recording_interfaces_v2';
+import {
+ ALLOW_USB_DEBUGGING,
+ BINARY_PUSH_FAILURE,
+ BINARY_PUSH_UNKNOWN_RESPONSE,
+ EXTENSION_NOT_INSTALLED,
+ EXTENSION_URL,
+ NO_DEVICE_SELECTED,
+ PARSING_UNABLE_TO_DECODE_METHOD,
+ PARSING_UNKNWON_REQUEST_ID,
+ PARSING_UNRECOGNIZED_MESSAGE,
+ PARSING_UNRECOGNIZED_PORT,
+ WEBSOCKET_UNABLE_TO_CONNECT,
+} from './recording_utils';
+
+// The pattern for handling recording error can have the following nesting in
+// case of errors:
+// A. wrapRecordingError -> wraps a promise
+// B. onFailure -> has user defined logic and calls showRecordingModal
+// C. showRecordingModal -> shows UX for a given error; this is not called
+// directly by wrapRecordingError, because we want the caller (such as the
+// UI) to dictate the UX
+
+// This method takes a promise and a callback to be execute in case the promise
+// fails. It then awaits the promise and executes the callback in case of
+// failure. In the recording code it is used to wrap:
+// 1. Acessing the WebUSB API.
+// 2. Methods returning promises which can be rejected. For instance:
+// a) When the user clicks 'Add a new device' but then doesn't select a valid
+// device.
+// b) When the user starts a tracing session, but cancels it before they
+// authorize the session on the device.
+export async function wrapRecordingError<T>(
+ promise: Promise<T>,
+ onFailure: OnMessageCallback,
+): Promise<T | undefined> {
+ try {
+ return await promise;
+ } catch (e) {
+ // Sometimes the message is wrapped in an Error object, sometimes not, so
+ // we make sure we transform it into a string.
+ const errorMessage = getErrorMessage(e);
+ onFailure(errorMessage);
+ return undefined;
+ }
+}
+
+// Shows a modal for every known type of error which can arise during recording.
+// In this way, errors occuring at different levels of the recording process
+// can be handled in a central location.
+export function showRecordingModal(message: string): void {
+ if (
+ [
+ 'Unable to claim interface.',
+ 'The specified endpoint is not part of a claimed and selected ' +
+ 'alternate interface.',
+ // thrown when calling the 'reset' method on a WebUSB device.
+ 'Unable to reset the device.',
+ ].some((partOfMessage) => message.includes(partOfMessage))
+ ) {
+ showWebUSBErrorV2();
+ } else if (
+ [
+ 'A transfer error has occurred.',
+ 'The device was disconnected.',
+ 'The transfer was cancelled.',
+ ].some((partOfMessage) => message.includes(partOfMessage)) ||
+ isDeviceDisconnectedError(message)
+ ) {
+ showConnectionLostError();
+ } else if (message === ALLOW_USB_DEBUGGING) {
+ showAllowUSBDebugging();
+ } else if (
+ isMessageComposedOf(message, [
+ BINARY_PUSH_FAILURE,
+ BINARY_PUSH_UNKNOWN_RESPONSE,
+ ])
+ ) {
+ showFailedToPushBinary(message.substring(message.indexOf(':') + 1));
+ } else if (message === NO_DEVICE_SELECTED) {
+ showNoDeviceSelected();
+ } else if (WEBSOCKET_UNABLE_TO_CONNECT === message) {
+ showWebsocketConnectionIssue(message);
+ } else if (message === EXTENSION_NOT_INSTALLED) {
+ showExtensionNotInstalled();
+ } else if (
+ isMessageComposedOf(message, [
+ PARSING_UNKNWON_REQUEST_ID,
+ PARSING_UNABLE_TO_DECODE_METHOD,
+ PARSING_UNRECOGNIZED_PORT,
+ PARSING_UNRECOGNIZED_MESSAGE,
+ ])
+ ) {
+ showIssueParsingTheTracedResponse(message);
+ } else {
+ throw new Error(`${message}`);
+ }
+}
+
+function isDeviceDisconnectedError(message: string) {
+ return (
+ message.includes('Device with serial') &&
+ message.includes('was disconnected.')
+ );
+}
+
+function isMessageComposedOf(message: string, issues: string[]) {
+ for (const issue of issues) {
+ if (message.includes(issue)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Exception thrown by the Recording logic.
+export class RecordingError extends Error {}
+
+function showWebUSBErrorV2() {
+ showModal({
+ title: 'A WebUSB error occurred',
+ content: m(
+ 'div',
+ m(
+ 'span',
+ `Is adb already running on the host? Run this command and
+ try again.`,
+ ),
+ m('br'),
+ m('.modal-bash', '> adb kill-server'),
+ m('br'),
+ // The statement below covers the following edge case:
+ // 1. 'adb server' is running on the device.
+ // 2. The user selects the new Android target, so we try to fetch the
+ // OS version and do QSS.
+ // 3. The error modal is shown.
+ // 4. The user runs 'adb kill-server'.
+ // At this point we don't have a trigger to try fetching the OS version
+ // + QSS again. Therefore, the user will need to refresh the page.
+ m(
+ 'span',
+ "If after running 'adb kill-server', you don't see " +
+ "a 'Start Recording' button on the page and you don't see " +
+ "'Allow USB debugging' on the device, " +
+ 'you will need to reload this page.',
+ ),
+ m('br'),
+ m('br'),
+ m('span', 'For details see '),
+ m('a', {href: 'http://b/159048331', target: '_blank'}, 'b/159048331'),
+ ),
+ });
+}
+
+function showConnectionLostError(): void {
+ showModal({
+ title: 'Connection with the ADB device lost',
+ content: m(
+ 'div',
+ m('span', `Please connect the device again to restart the recording.`),
+ m('br'),
+ ),
+ });
+}
+
+function showAllowUSBDebugging(): void {
+ showModal({
+ title: 'Could not connect to the device',
+ content: m(
+ 'div',
+ m('span', 'Please allow USB debugging on the device.'),
+ m('br'),
+ ),
+ });
+}
+
+function showNoDeviceSelected(): void {
+ showModal({
+ title: 'No device was selected for recording',
+ content: m(
+ 'div',
+ m(
+ 'span',
+ `If you want to connect to an ADB device,
+ please select it from the list.`,
+ ),
+ m('br'),
+ ),
+ });
+}
+
+function showExtensionNotInstalled(): void {
+ showModal({
+ title: 'Perfetto Chrome extension not installed',
+ content: m(
+ 'div',
+ m(
+ '.note',
+ `To trace Chrome from the Perfetto UI, you need to install our `,
+ m('a', {href: EXTENSION_URL, target: '_blank'}, 'Chrome extension'),
+ ' and then reload this page.',
+ ),
+ m('br'),
+ ),
+ });
+}
+
+function showIssueParsingTheTracedResponse(message: string): void {
+ showModal({
+ title:
+ 'A problem was encountered while connecting to' +
+ ' the Perfetto tracing service',
+ content: m('div', m('span', message), m('br')),
+ });
+}
+
+function showFailedToPushBinary(message: string): void {
+ showModal({
+ title: 'Failed to push a binary to the device',
+ content: m(
+ 'div',
+ m(
+ 'span',
+ 'This can happen if your Android device has an OS version lower ' +
+ 'than Q. Perfetto tried to push the latest version of its ' +
+ 'embedded binary but failed.',
+ ),
+ m('br'),
+ m('br'),
+ m('span', 'Error message:'),
+ m('br'),
+ m('span', message),
+ ),
+ });
+}
+
+function showWebsocketConnectionIssue(message: string): void {
+ showModal({
+ title: 'Unable to connect to the device via websocket',
+ content: m(
+ 'div',
+ m('div', 'trace_processor_shell --httpd is unreachable or crashed.'),
+ m('pre', message),
+ ),
+ });
+}
diff --git a/ui/src/common/recordingV2/recording_interfaces_v2.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_interfaces_v2.ts
similarity index 99%
rename from ui/src/common/recordingV2/recording_interfaces_v2.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_interfaces_v2.ts
index 954a145..c8a030e 100644
--- a/ui/src/common/recordingV2/recording_interfaces_v2.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_interfaces_v2.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TraceConfig} from '../../protos';
+import {TraceConfig} from '../../../protos';
// TargetFactory connects, disconnects and keeps track of targets.
// There is one factory for AndroidWebusb, AndroidWebsocket, Chrome etc.
diff --git a/ui/src/common/recordingV2/recording_page_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_page_controller.ts
similarity index 93%
rename from ui/src/common/recordingV2/recording_page_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_page_controller.ts
index 5bbe067..76617d5 100644
--- a/ui/src/common/recordingV2/recording_page_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_page_controller.ts
@@ -12,20 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists, assertTrue} from '../../base/logging';
-import {currentDateHourAndMinute} from '../../base/time';
-import {AppImpl} from '../../core/app_impl';
-import {raf} from '../../core/raf_scheduler';
-import {globals} from '../../frontend/globals';
-import {autosaveConfigStore} from '../../frontend/record_config';
+import {assertExists, assertTrue} from '../../../base/logging';
+import {currentDateHourAndMinute} from '../../../base/time';
+import {RecordingManager} from '../recording_manager';
+import {autosaveConfigStore} from '../record_config';
import {
DEFAULT_ADB_WEBSOCKET_URL,
DEFAULT_TRACED_WEBSOCKET_URL,
-} from '../../frontend/recording/recording_ui_utils';
-import {couldNotClaimInterface} from '../../frontend/recording/reset_interface_modal';
-import {TraceConfig} from '../../protos';
-import {Actions} from '../actions';
-import {TRACE_SUFFIX} from '../constants';
+} from '../recording_ui_utils';
+import {couldNotClaimInterface} from '../reset_interface_modal';
+import {TraceConfig} from '../../../protos';
+import {TRACE_SUFFIX} from '../../../public/trace';
import {genTraceConfig} from './recording_config_utils';
import {RecordingError, showRecordingModal} from './recording_error_handling';
import {
@@ -48,6 +45,8 @@
HostOsTargetFactory,
} from './target_factories/host_os_target_factory';
import {targetFactoryRegistry} from './target_factory_registry';
+import {scheduleFullRedraw} from '../../../widgets/raf';
+import {App} from '../../../public/app';
// The recording page can be in any of these states. It can transition between
// states:
@@ -251,6 +250,9 @@
// Keeps track of the state the Ui is in. Has methods which are executed on
// user actions such as starting/stopping/cancelling a tracing session.
export class RecordingPageController {
+ private app: App;
+ private recMgr: RecordingManager;
+
// State of the recording page. This is set by user actions and/or automatic
// transitions. This is queried by the UI in order to
private state: RecordingState = RecordingState.NO_TARGET;
@@ -266,6 +268,11 @@
// transitions don't override one another in async functions.
private stateGeneration = 0;
+ constructor(app: App, recMgr: RecordingManager) {
+ this.app = app;
+ this.recMgr = recMgr;
+ }
+
getBufferUsagePercentage(): number {
return this.bufferUsagePercentage;
}
@@ -290,8 +297,8 @@
throw new RecordingError('Recording page state transition out of order.');
}
this.setState(state);
- globals.dispatch(Actions.setRecordingStatus({status: undefined}));
- raf.scheduleFullRedraw();
+ this.recMgr.setRecordingStatus(undefined);
+ scheduleFullRedraw();
}
maybeClearRecordingState(tracingSessionWrapper: TracingSessionWrapper): void {
@@ -307,7 +314,7 @@
if (this.tracingSessionWrapper !== tracingSessionWrapper) {
return;
}
- AppImpl.instance.openTraceFromBuffer({
+ this.app.openTraceFromBuffer({
title: 'Recorded trace',
buffer: trace.buffer,
fileName: `trace_${currentDateHourAndMinute()}${TRACE_SUFFIX}`,
@@ -322,7 +329,7 @@
// For the 'Recording in progress for 7000ms we don't show a
// modal.'
if (message.startsWith(RECORDING_IN_PROGRESS)) {
- globals.dispatch(Actions.setRecordingStatus({status: message}));
+ this.recMgr.setRecordingStatus(message);
} else {
// For messages such as 'Please allow USB debugging on your
// device, which require a user action, we show a modal.
@@ -385,11 +392,11 @@
if (!this.target) {
this.setState(RecordingState.NO_TARGET);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
return;
}
this.setState(RecordingState.TARGET_SELECTED);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
this.tracingSessionWrapper = this.createTracingSessionWrapper(this.target);
this.tracingSessionWrapper.fetchTargetInfo();
@@ -422,15 +429,18 @@
onStartRecordingPressed(): void {
assertTrue(RecordingState.TARGET_INFO_DISPLAYED === this.state);
location.href = '#!/record/instructions';
- autosaveConfigStore.save(globals.state.recordConfig);
+ autosaveConfigStore.save(this.recMgr.state.recordConfig);
const target = this.getTarget();
const targetInfo = target.getInfo();
- AppImpl.instance.analytics.logEvent(
+ this.app.analytics.logEvent(
'Record Trace',
`Record trace (${targetInfo.targetType})`,
);
- const traceConfig = genTraceConfig(globals.state.recordConfig, targetInfo);
+ const traceConfig = genTraceConfig(
+ this.recMgr.state.recordConfig,
+ targetInfo,
+ );
this.tracingSessionWrapper = this.createTracingSessionWrapper(target);
this.tracingSessionWrapper.start(traceConfig);
@@ -476,7 +486,7 @@
// We redraw if:
// 1. We received a correct buffer usage value.
// 2. We receive a RecordingError.
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
initFactories() {
@@ -523,7 +533,7 @@
// If the change happens for an existing target, the controller keeps the
// currently selected target in focus.
if (this.target && allTargets.includes(this.target)) {
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
return;
}
// If the change happens to a new target or the controller does not have a
@@ -541,10 +551,10 @@
this.bufferUsagePercentage = 0;
this.tracingSessionWrapper = undefined;
this.setState(RecordingState.TARGET_INFO_DISPLAYED);
- globals.dispatch(Actions.setRecordingStatus({status: undefined}));
+ this.recMgr.setRecordingStatus(undefined);
// Redrawing because this method has changed the RecordingState, which will
// affect the display of the record_page.
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
private setState(state: RecordingState) {
diff --git a/ui/src/common/recordingV2/recording_utils.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_utils.ts
similarity index 100%
rename from ui/src/common/recordingV2/recording_utils.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/recording_utils.ts
diff --git a/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_websocket_target_factory.ts
similarity index 96%
rename from ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_websocket_target_factory.ts
index 21097eb..03cda1f 100644
--- a/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_websocket_target_factory.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {RECORDING_V2_FLAG} from '../../../core/feature_flags';
import {
OnTargetChangeCallback,
RecordingTargetV2,
@@ -22,7 +21,6 @@
buildAbdWebsocketCommand,
WEBSOCKET_CLOSED_ABNORMALLY_CODE,
} from '../recording_utils';
-import {targetFactoryRegistry} from '../target_factory_registry';
import {AndroidWebsocketTarget} from '../targets/android_websocket_target';
export const ANDROID_WEBSOCKET_TARGET_FACTORY = 'AndroidWebsocketTargetFactory';
@@ -268,8 +266,3 @@
this.onTargetChange = onTargetChange;
}
}
-
-// We only want to instantiate this class if Recording V2 is enabled.
-if (RECORDING_V2_FLAG.get()) {
- targetFactoryRegistry.register(new AndroidWebsocketTargetFactory());
-}
diff --git a/ui/src/common/recordingV2/target_factories/android_websocket_target_factory_unittest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_websocket_target_factory_unittest.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/android_websocket_target_factory_unittest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_websocket_target_factory_unittest.ts
diff --git a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_webusb_target_factory.ts
similarity index 89%
rename from ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_webusb_target_factory.ts
index d27ab07..a969c31 100644
--- a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/android_webusb_target_factory.ts
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {getErrorMessage} from '../../../base/errors';
-import {assertExists} from '../../../base/logging';
-import {RECORDING_V2_FLAG} from '../../../core/feature_flags';
+import {getErrorMessage} from '../../../../base/errors';
+import {assertExists} from '../../../../base/logging';
import {AdbKeyManager} from '../auth/adb_key_manager';
import {RecordingError} from '../recording_error_handling';
import {
@@ -23,7 +22,6 @@
TargetFactory,
} from '../recording_interfaces_v2';
import {ADB_DEVICE_FILTER, findInterfaceAndEndpoint} from '../recording_utils';
-import {targetFactoryRegistry} from '../target_factory_registry';
import {AndroidWebusbTarget} from '../targets/android_webusb_target';
export const ANDROID_WEBUSB_TARGET_FACTORY = 'AndroidWebusbTargetFactory';
@@ -155,11 +153,3 @@
return deviceValidity;
}
}
-
-// We only want to instantiate this class if:
-// 1. The browser implements the USB functionality.
-// 2. Recording V2 is enabled.
-// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
-if (navigator.usb && RECORDING_V2_FLAG.get()) {
- targetFactoryRegistry.register(new AndroidWebusbTargetFactory(navigator.usb));
-}
diff --git a/ui/src/common/recordingV2/target_factories/chrome_target_factory.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/chrome_target_factory.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/chrome_target_factory.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/chrome_target_factory.ts
diff --git a/ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/chrome_target_factory_unittest.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/chrome_target_factory_unittest.ts
diff --git a/ui/src/common/recordingV2/target_factories/host_os_target_factory.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/host_os_target_factory.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/host_os_target_factory.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/host_os_target_factory.ts
diff --git a/ui/src/common/recordingV2/target_factories/index.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/index.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/index.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/index.ts
diff --git a/ui/src/common/recordingV2/target_factories/virtual_target_factory.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/virtual_target_factory.ts
similarity index 100%
rename from ui/src/common/recordingV2/target_factories/virtual_target_factory.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factories/virtual_target_factory.ts
diff --git a/ui/src/common/recordingV2/target_factory_registry.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factory_registry.ts
similarity index 96%
rename from ui/src/common/recordingV2/target_factory_registry.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factory_registry.ts
index e8de655..b34070d 100644
--- a/ui/src/common/recordingV2/target_factory_registry.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/target_factory_registry.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Registry} from '../../base/registry';
+import {Registry} from '../../../base/registry';
import {RecordingTargetV2, TargetFactory} from './recording_interfaces_v2';
export class TargetFactoryRegistry extends Registry<TargetFactory> {
diff --git a/ui/src/common/recordingV2/targets/android_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_target.ts
similarity index 96%
rename from ui/src/common/recordingV2/targets/android_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_target.ts
index 926846d..0bac1e4 100644
--- a/ui/src/common/recordingV2/targets/android_target.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_target.ts
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {fetchWithTimeout} from '../../../base/http_utils';
-import {exists} from '../../../base/utils';
-import {VERSION} from '../../../gen/perfetto_version';
+import {fetchWithTimeout} from '../../../../base/http_utils';
+import {exists} from '../../../../base/utils';
+import {VERSION} from '../../../../gen/perfetto_version';
import {AdbConnectionImpl} from '../adb_connection_impl';
import {
DataSource,
diff --git a/ui/src/common/recordingV2/targets/android_virtual_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_virtual_target.ts
similarity index 100%
rename from ui/src/common/recordingV2/targets/android_virtual_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_virtual_target.ts
diff --git a/ui/src/common/recordingV2/targets/android_websocket_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_websocket_target.ts
similarity index 100%
rename from ui/src/common/recordingV2/targets/android_websocket_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_websocket_target.ts
diff --git a/ui/src/common/recordingV2/targets/android_webusb_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_webusb_target.ts
similarity index 96%
rename from ui/src/common/recordingV2/targets/android_webusb_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_webusb_target.ts
index e70a19a..dc6e64d 100644
--- a/ui/src/common/recordingV2/targets/android_webusb_target.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/android_webusb_target.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists} from '../../../base/logging';
+import {assertExists} from '../../../../base/logging';
import {AdbConnectionOverWebusb} from '../adb_connection_over_webusb';
import {AdbKeyManager} from '../auth/adb_key_manager';
import {OnTargetChangeCallback, TargetInfo} from '../recording_interfaces_v2';
diff --git a/ui/src/common/recordingV2/targets/chrome_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/chrome_target.ts
similarity index 100%
rename from ui/src/common/recordingV2/targets/chrome_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/chrome_target.ts
diff --git a/ui/src/common/recordingV2/targets/host_os_target.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/host_os_target.ts
similarity index 100%
rename from ui/src/common/recordingV2/targets/host_os_target.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/targets/host_os_target.ts
diff --git a/ui/src/common/recordingV2/traced_tracing_session.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/traced_tracing_session.ts
similarity index 98%
rename from ui/src/common/recordingV2/traced_tracing_session.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/traced_tracing_session.ts
index c0ba444..8687432 100644
--- a/ui/src/common/recordingV2/traced_tracing_session.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/traced_tracing_session.ts
@@ -13,8 +13,8 @@
// limitations under the License.
import protobuf from 'protobufjs/minimal';
-import {defer, Deferred} from '../../base/deferred';
-import {assertExists, assertFalse, assertTrue} from '../../base/logging';
+import {defer, Deferred} from '../../../base/deferred';
+import {assertExists, assertFalse, assertTrue} from '../../../base/logging';
import {
DisableTracingRequest,
DisableTracingResponse,
@@ -33,7 +33,7 @@
ReadBuffersRequest,
ReadBuffersResponse,
TraceConfig,
-} from '../../protos';
+} from '../../../protos';
import {RecordingError} from './recording_error_handling';
import {
ByteStream,
@@ -50,7 +50,7 @@
PARSING_UNRECOGNIZED_PORT,
RECORDING_IN_PROGRESS,
} from './recording_utils';
-import {exists} from '../../base/utils';
+import {exists} from '../../../base/utils';
// See wire_protocol.proto for more details.
const WIRE_PROTOCOL_HEADER_SIZE = 4;
diff --git a/ui/src/common/recordingV2/websocket_menu_controller.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/websocket_menu_controller.ts
similarity index 97%
rename from ui/src/common/recordingV2/websocket_menu_controller.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/websocket_menu_controller.ts
index 8b800a7..2da8f5b 100644
--- a/ui/src/common/recordingV2/websocket_menu_controller.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recordingV2/websocket_menu_controller.ts
@@ -16,7 +16,7 @@
ADB_ENDPOINT,
DEFAULT_WEBSOCKET_URL,
TRACED_ENDPOINT,
-} from '../../frontend/recording/recording_ui_utils';
+} from '../recording_ui_utils';
import {TargetFactory} from './recording_interfaces_v2';
import {
ANDROID_WEBSOCKET_TARGET_FACTORY,
diff --git a/ui/src/plugins/dev.perfetto.RecordTrace/recording_manager.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recording_manager.ts
new file mode 100644
index 0000000..be29691
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recording_manager.ts
@@ -0,0 +1,233 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {createEmptyState} from './empty_state';
+import {
+ AdbRecordingTarget,
+ LoadedConfig,
+ RecordingState,
+ RecordingTarget,
+ getDefaultRecordingTargets,
+ isAdbTarget,
+} from './state';
+import {AdbOverWebUsb} from './adb';
+import {isGetCategoriesResponse} from './chrome_proxy_record_controller';
+import {RecordConfig, createEmptyRecordConfig} from './record_config_types';
+import {RecordController} from './record_controller';
+import {scheduleFullRedraw} from '../../widgets/raf';
+import {App} from '../../public/app';
+import {targetFactoryRegistry} from './recordingV2/target_factory_registry';
+import {AndroidWebsocketTargetFactory} from './recordingV2/target_factories/android_websocket_target_factory';
+import {AndroidWebusbTargetFactory} from './recordingV2/target_factories/android_webusb_target_factory';
+import {exists} from '../../base/utils';
+
+const EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
+
+// TODO(primiano): this class and RecordController should be merged. I'm keeping
+// them separate for now to reduce scope of refactorings.
+export class RecordingManager {
+ readonly app: App;
+ private _state: RecordingState = createEmptyState();
+ private recCtl: RecordController;
+
+ constructor(app: App, useRecordingV2: boolean) {
+ this.app = app;
+ const extensionLocalChannel = new MessageChannel();
+ this.recCtl = new RecordController(app, this, extensionLocalChannel.port1);
+ this.setupExtentionPort(extensionLocalChannel);
+
+ if (useRecordingV2) {
+ targetFactoryRegistry.register(new AndroidWebsocketTargetFactory());
+ if (exists(navigator.usb)) {
+ targetFactoryRegistry.register(
+ new AndroidWebusbTargetFactory(navigator.usb),
+ );
+ }
+ } else {
+ this.updateAvailableAdbDevices();
+ try {
+ navigator.usb.addEventListener('connect', () =>
+ this.updateAvailableAdbDevices(),
+ );
+ navigator.usb.addEventListener('disconnect', () =>
+ this.updateAvailableAdbDevices(),
+ );
+ } catch (e) {
+ console.error('WebUSB API not supported');
+ }
+ }
+ }
+
+ clearRecordConfig(): void {
+ this._state.recordConfig = createEmptyRecordConfig();
+ this._state.lastLoadedConfig = {type: 'NONE'};
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setRecordConfig(config: RecordConfig, configType?: LoadedConfig): void {
+ this._state.recordConfig = config;
+ this._state.lastLoadedConfig = configType || {type: 'NONE'};
+ this.recCtl.refreshOnStateChange();
+ }
+
+ startRecording(): void {
+ this._state.recordingInProgress = true;
+ this._state.lastRecordingError = undefined;
+ this._state.recordingCancelled = false;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ stopRecording(): void {
+ this._state.recordingInProgress = false;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ cancelRecording(): void {
+ this._state.recordingInProgress = false;
+ this._state.recordingCancelled = true;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setRecordingTarget(target: RecordingTarget): void {
+ this._state.recordingTarget = target;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setFetchChromeCategories(fetch: boolean): void {
+ this._state.fetchChromeCategories = fetch;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setAvailableAdbDevices(devices: AdbRecordingTarget[]): void {
+ this._state.availableAdbDevices = devices;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setLastRecordingError(error?: string): void {
+ this._state.lastRecordingError = error;
+ this._state.recordingStatus = undefined;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ setRecordingStatus(status?: string): void {
+ this._state.recordingStatus = status;
+ this._state.lastRecordingError = undefined;
+ this.recCtl.refreshOnStateChange();
+ }
+
+ get state() {
+ return this._state;
+ }
+
+ private setupExtentionPort(extensionLocalChannel: MessageChannel) {
+ // We proxy messages between the extension and the controller because the
+ // controller's worker can't access chrome.runtime.
+ const extensionPort =
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
+ window.chrome && chrome.runtime
+ ? chrome.runtime.connect(EXTENSION_ID)
+ : undefined;
+
+ this._state.extensionInstalled = extensionPort !== undefined;
+
+ if (extensionPort) {
+ // Send messages to keep-alive the extension port.
+ const interval = setInterval(() => {
+ extensionPort.postMessage({
+ method: 'ExtensionVersion',
+ });
+ }, 25000);
+ extensionPort.onDisconnect.addListener((_) => {
+ this._state.extensionInstalled = false;
+ clearInterval(interval);
+ void chrome.runtime.lastError; // Needed to not receive an error log.
+ });
+ // This forwards the messages from the extension to the controller.
+ extensionPort.onMessage.addListener(
+ (message: object, _port: chrome.runtime.Port) => {
+ if (isGetCategoriesResponse(message)) {
+ this._state.chromeCategories = message.categories;
+ scheduleFullRedraw();
+ return;
+ }
+ extensionLocalChannel.port2.postMessage(message);
+ },
+ );
+ }
+
+ // This forwards the messages from the controller to the extension
+ extensionLocalChannel.port2.onmessage = ({data}) => {
+ if (extensionPort) extensionPort.postMessage(data);
+ };
+ }
+
+ async updateAvailableAdbDevices(preferredDeviceSerial?: string) {
+ const devices = await new AdbOverWebUsb().getPairedDevices();
+
+ let recordingTarget: AdbRecordingTarget | undefined = undefined;
+
+ const availableAdbDevices: AdbRecordingTarget[] = [];
+ devices.forEach((d) => {
+ if (d.productName && d.serialNumber) {
+ availableAdbDevices.push({
+ name: d.productName,
+ serial: d.serialNumber,
+ os: 'S',
+ });
+ if (preferredDeviceSerial && preferredDeviceSerial === d.serialNumber) {
+ recordingTarget = availableAdbDevices[availableAdbDevices.length - 1];
+ }
+ }
+ });
+
+ this.setAvailableAdbDevices(availableAdbDevices);
+ this.selectAndroidDeviceIfAvailable(availableAdbDevices, recordingTarget);
+ scheduleFullRedraw();
+ return availableAdbDevices;
+ }
+
+ private selectAndroidDeviceIfAvailable(
+ availableAdbDevices: AdbRecordingTarget[],
+ recordingTarget?: RecordingTarget,
+ ) {
+ if (!recordingTarget) {
+ recordingTarget = this.state.recordingTarget;
+ }
+ const deviceConnected = isAdbTarget(recordingTarget);
+ const connectedDeviceDisconnected =
+ deviceConnected &&
+ availableAdbDevices.find(
+ (e) => e.serial === (recordingTarget as AdbRecordingTarget).serial,
+ ) === undefined;
+
+ if (availableAdbDevices.length) {
+ // If there's an Android device available and the current selection isn't
+ // one, select the Android device by default. If the current device isn't
+ // available anymore, but another Android device is, select the other
+ // Android device instead.
+ if (!deviceConnected || connectedDeviceDisconnected) {
+ recordingTarget = availableAdbDevices[0];
+ }
+
+ this.setRecordingTarget(recordingTarget);
+ return;
+ }
+
+ // If the currently selected device was disconnected, reset the recording
+ // target to the default one.
+ if (connectedDeviceDisconnected) {
+ this.setRecordingTarget(getDefaultRecordingTargets()[0]);
+ }
+ }
+}
diff --git a/ui/src/frontend/recording/recording_multiple_choice.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recording_multiple_choice.ts
similarity index 93%
rename from ui/src/frontend/recording/recording_multiple_choice.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recording_multiple_choice.ts
index 27a83fc..0e34f5c 100644
--- a/ui/src/frontend/recording/recording_multiple_choice.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recording_multiple_choice.ts
@@ -16,9 +16,9 @@
import {
RecordingTargetV2,
TargetFactory,
-} from '../../common/recordingV2/recording_interfaces_v2';
-import {RecordingPageController} from '../../common/recordingV2/recording_page_controller';
-import {RECORDING_MODAL_DIALOG_KEY} from '../../common/recordingV2/recording_utils';
+} from './recordingV2/recording_interfaces_v2';
+import {RecordingPageController} from './recordingV2/recording_page_controller';
+import {RECORDING_MODAL_DIALOG_KEY} from './recordingV2/recording_utils';
import {closeModal} from '../../widgets/modal';
interface RecordingMultipleChoiceAttrs {
diff --git a/ui/src/frontend/recording/recording_sections.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recording_sections.ts
similarity index 85%
rename from ui/src/frontend/recording/recording_sections.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recording_sections.ts
index f0e3fa1..c83b9e0 100644
--- a/ui/src/frontend/recording/recording_sections.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recording_sections.ts
@@ -12,9 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {DataSource} from '../../common/recordingV2/recording_interfaces_v2';
+import {DataSource} from './recordingV2/recording_interfaces_v2';
+import {RecordingState} from './state';
export interface RecordingSectionAttrs {
+ recState: RecordingState;
dataSources: DataSource[];
cssClass: string;
}
diff --git a/ui/src/frontend/recording/recording_settings.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recording_settings.ts
similarity index 76%
rename from ui/src/frontend/recording/recording_settings.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recording_settings.ts
index a818fb3..e3058be 100644
--- a/ui/src/frontend/recording/recording_settings.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/recording_settings.ts
@@ -12,13 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {produce} from 'immer';
import m from 'mithril';
-import {Actions} from '../../common/actions';
-import {RecordMode} from '../../common/state';
-import {globals} from '../globals';
-import {Slider, SliderAttrs} from '../record_widgets';
+import {RecordMode} from './state';
+import {Slider} from './record_widgets';
import {RecordingSectionAttrs} from './recording_sections';
+import {assetSrc} from '../../base/assets';
export class RecordingSettings
implements m.ClassComponent<RecordingSectionAttrs>
@@ -28,24 +26,21 @@
const M = (x: number) => x * 1000 * 60;
const H = (x: number) => x * 1000 * 60 * 60;
- const cfg = globals.state.recordConfig;
+ const recCfg = attrs.recState.recordConfig;
const recButton = (mode: RecordMode, title: string, img: string) => {
const checkboxArgs = {
- checked: cfg.mode === mode,
+ checked: recCfg.mode === mode,
onchange: (e: InputEvent) => {
const checked = (e.target as HTMLInputElement).checked;
if (!checked) return;
- const traceCfg = produce(globals.state.recordConfig, (draft) => {
- draft.mode = mode;
- });
- globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+ recCfg.mode = mode;
},
};
return m(
- `label${cfg.mode === mode ? '.selected' : ''}`,
+ `label${recCfg.mode === mode ? '.selected' : ''}`,
m(`input[type=radio][name=rec_mode]`, checkboxArgs),
- m(`img[src=${globals.root}assets/${img}]`),
+ m(`img[src=${assetSrc(`assets/${img}`)}]`),
m('span', title),
);
};
@@ -67,7 +62,8 @@
unit: 'MB',
set: (cfg, val) => (cfg.bufferSizeMb = val),
get: (cfg) => cfg.bufferSizeMb,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Max duration',
@@ -77,25 +73,28 @@
unit: 'h:m:s',
set: (cfg, val) => (cfg.durationMs = val),
get: (cfg) => cfg.durationMs,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Max file size',
icon: 'save',
- cssClass: cfg.mode !== 'LONG_TRACE' ? '.hide' : '',
+ cssClass: recCfg.mode !== 'LONG_TRACE' ? '.hide' : '',
values: [5, 25, 50, 100, 500, 1000, 1000 * 5, 1000 * 10],
unit: 'MB',
set: (cfg, val) => (cfg.maxFileSizeMb = val),
get: (cfg) => cfg.maxFileSizeMb,
- } as SliderAttrs),
+ recCfg,
+ }),
m(Slider, {
title: 'Flush on disk every',
- cssClass: cfg.mode !== 'LONG_TRACE' ? '.hide' : '',
+ cssClass: recCfg.mode !== 'LONG_TRACE' ? '.hide' : '',
icon: 'av_timer',
values: [100, 250, 500, 1000, 2500, 5000],
unit: 'ms',
set: (cfg, val) => (cfg.fileWritePeriodMs = val),
get: (cfg) => cfg.fileWritePeriodMs || 0,
- } as SliderAttrs),
+ recCfg,
+ }),
);
}
}
diff --git a/ui/src/frontend/recording/recording_ui_utils.ts b/ui/src/plugins/dev.perfetto.RecordTrace/recording_ui_utils.ts
similarity index 100%
rename from ui/src/frontend/recording/recording_ui_utils.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/recording_ui_utils.ts
diff --git a/ui/src/frontend/recording/reset_interface_modal.ts b/ui/src/plugins/dev.perfetto.RecordTrace/reset_interface_modal.ts
similarity index 100%
rename from ui/src/frontend/recording/reset_interface_modal.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/reset_interface_modal.ts
diff --git a/ui/src/frontend/recording/reset_target_modal.ts b/ui/src/plugins/dev.perfetto.RecordTrace/reset_target_modal.ts
similarity index 91%
rename from ui/src/frontend/recording/reset_target_modal.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/reset_target_modal.ts
index 4d3feb3..4d3d048 100644
--- a/ui/src/frontend/recording/reset_target_modal.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/reset_target_modal.ts
@@ -13,19 +13,19 @@
// limitations under the License.
import m from 'mithril';
-import {RecordingPageController} from '../../common/recordingV2/recording_page_controller';
+import {RecordingPageController} from './recordingV2/recording_page_controller';
import {
EXTENSION_URL,
RECORDING_MODAL_DIALOG_KEY,
-} from '../../common/recordingV2/recording_utils';
+} from './recordingV2/recording_utils';
import {
CHROME_TARGET_FACTORY,
ChromeTargetFactory,
-} from '../../common/recordingV2/target_factories/chrome_target_factory';
-import {targetFactoryRegistry} from '../../common/recordingV2/target_factory_registry';
-import {WebsocketMenuController} from '../../common/recordingV2/websocket_menu_controller';
+} from './recordingV2/target_factories/chrome_target_factory';
+import {targetFactoryRegistry} from './recordingV2/target_factory_registry';
+import {WebsocketMenuController} from './recordingV2/websocket_menu_controller';
import {closeModal, showModal} from '../../widgets/modal';
-import {CodeSnippet} from '../record_widgets';
+import {CodeSnippet} from './record_widgets';
import {RecordingMultipleChoice} from './recording_multiple_choice';
const RUN_WEBSOCKET_CMD =
diff --git a/ui/src/common/state.ts b/ui/src/plugins/dev.perfetto.RecordTrace/state.ts
similarity index 69%
rename from ui/src/common/state.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/state.ts
index 39f9c5b..b94074b 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/state.ts
@@ -12,112 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {RecordConfig} from '../controller/record_config_types';
-import {TraceSource} from '../core/trace_source';
-
-/**
- * A plain js object, holding objects of type |Class| keyed by string id.
- * We use this instead of using |Map| object since it is simpler and faster to
- * serialize for use in postMessage.
- */
-export interface ObjectById<Class extends {id: string}> {
- [id: string]: Class;
-}
-
-// Same as ObjectById but the key parameter is called `key` rather than `id`.
-export interface ObjectByKey<Class extends {key: string}> {
- [key: string]: Class;
-}
+import {RecordConfig} from './record_config_types';
export const MAX_TIME = 180;
-// 3: TrackKindPriority and related sorting changes.
-// 5: Move a large number of items off frontendLocalState and onto state.
-// 6: Common PivotTableConfig and pivot table specific PivotTableState.
-// 7: Split Chrome categories in two and add 'symbolize ksyms' flag.
-// 8: Rename several variables
-// "[...]HeapProfileFlamegraph[...]" -> "[...]Flamegraph[...]".
-// 9: Add a field to track last loaded recording profile name
-// 10: Change last loaded profile tracking type to accommodate auto-save.
-// 11: Rename updateChromeCategories to fetchChromeCategories.
-// 12: Add a field to cache mapping from UI track ID to trace track ID in order
-// to speed up flow arrows rendering.
-// 13: FlamegraphState changed to support area selection.
-// 14: Changed the type of uiTrackIdByTraceTrackId from `Map` to an object with
-// typed key/value because a `Map` does not preserve type during
-// serialisation+deserialisation.
-// 15: Added state for Pivot Table V2
-// 16: Added boolean tracking if the flamegraph modal was dismissed
-// 17:
-// - add currentEngineId to track the id of the current engine
-// - remove nextNoteId, nextAreaId and use nextId as a unique counter for all
-// indexing except the indexing of the engines
-// 18: areaSelection change see b/235869542
-// 19: Added visualisedArgs state.
-// 20: Refactored thread sorting order.
-// 21: Updated perf sample selection to include a ts range instead of single ts
-// 22: Add log selection kind.
-// 23: Add log filtering criteria for Android log entries.
-// 24: Store only a single Engine.
-// 25: Move omnibox state off VisibleState.
-// 26: Add tags for filtering Android log entries.
-// 27. Add a text entry for filtering Android log entries.
-// 28. Add a boolean indicating if non matching log entries are hidden.
-// 29. Add ftrace state. <-- Borked, state contains a non-serializable object.
-// 30. Convert ftraceFilter.excludedNames from Set<string> to string[].
-// 31. Convert all timestamps to bigints.
-// 32. Add pendingDeeplink.
-// 33. Add plugins state.
-// 34. Add additional pendingDeeplink fields (query, pid).
-// 35. Add force to OmniboxState
-// 36. Remove metrics
-// 37. Add additional pendingDeeplink fields (visStart, visEnd).
-// 38. Add track tags.
-// 39. Ported cpu_slice, ftrace, and android_log tracks to plugin tracks. Track
-// state entries now require a URI and old track implementations are no
-// longer registered.
-// 40. Ported counter, process summary/sched, & cpu_freq to plugin tracks.
-// 41. Ported all remaining tracks.
-// 42. Rename trackId -> trackKey.
-// 43. Remove visibleTracks.
-// 44. Add TabsV2 state.
-// 45. Remove v1 tracks.
-// 46. Remove trackKeyByTrackId.
-// 47. Selection V2
-// 48. Rename legacySelection -> selection and introduce new Selection type.
-// 49. Remove currentTab, which is only relevant to TabsV1.
-// 50. Remove ftrace filter state.
-// 51. Changed structure of FlamegraphState.expandedCallsiteByViewingOption.
-// 52. Update track group state - don't make the summary track the first track.
-// 53. Remove android log state.
-// 54. Remove traceTime.
-// 55. Rename TrackGroupState.id -> TrackGroupState.key.
-// 56. Renamed chrome slice to thread slice everywhere.
-// 57. Remove flamegraph related code from state.
-// 58. Remove area map.
-// 59. Deprecate old area selection type.
-// 60. Deprecate old note selection type.
-// 61. Remove params/state from TrackState.
-export const STATE_VERSION = 61;
-
-export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
-
-export interface EngineConfig {
- id: string;
- source: TraceSource;
-}
-
-export interface QueryConfig {
- id: string;
- engineId?: string;
- query: string;
-}
-
-export interface Pagination {
- offset: number;
- count: number;
-}
-
export interface RecordingTarget {
name: string;
os: TargetOs;
@@ -145,38 +43,20 @@
| LoadedConfigAutomatic
| LoadedConfigNamed;
-export interface PendingDeeplinkState {
- ts?: string;
- dur?: string;
- tid?: string;
- pid?: string;
- query?: string;
- visStart?: string;
- visEnd?: string;
+export interface RecordCommand {
+ commandline: string;
+ pbtxt: string;
+ pbBase64: string;
}
-export interface State {
- version: number;
-
+export interface RecordingState {
/**
* State of the ConfigEditor.
*/
recordConfig: RecordConfig;
- displayConfigAsPbtxt: boolean;
lastLoadedConfig: LoadedConfig;
/**
- * Open traces.
- */
- engine?: EngineConfig;
-
- debugTrackId?: string;
- lastTrackReloadRequest?: number;
-
- // Show track perf debugging overlay
- perfDebug: boolean;
-
- /**
* Trace recording
*/
recordingInProgress: boolean;
@@ -190,11 +70,9 @@
fetchChromeCategories: boolean;
chromeCategories: string[] | undefined;
- trackFilterTerm: string | undefined;
-
- // TODO(primiano): this is a hack to force-re-run controllers required for the
- // controller->managers migration. Remove once controllers are gone.
- forceRunControllers: number;
+ bufferUsage: number;
+ recordingLog: string;
+ recordCmd?: RecordCommand;
}
export declare type RecordMode =
@@ -219,7 +97,7 @@
}
export function isAndroidTarget(target: RecordingTarget) {
- return ['Q', 'P', 'O'].includes(target.os);
+ return ['Q', 'P', 'O', 'S'].includes(target.os);
}
export function isChromeTarget(target: RecordingTarget) {
diff --git a/ui/src/core/trace_config_utils.ts b/ui/src/plugins/dev.perfetto.RecordTrace/trace_config_utils.ts
similarity index 96%
rename from ui/src/core/trace_config_utils.ts
rename to ui/src/plugins/dev.perfetto.RecordTrace/trace_config_utils.ts
index e05f711..c7697dd 100644
--- a/ui/src/core/trace_config_utils.ts
+++ b/ui/src/plugins/dev.perfetto.RecordTrace/trace_config_utils.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EnableTracingRequest, TraceConfig} from '../protos';
+import {EnableTracingRequest, TraceConfig} from '../../protos';
// In this file are contained a few functions to simplify the proto parsing.
diff --git a/ui/src/plugins/dev.perfetto.Sched/active_cpu_count.ts b/ui/src/plugins/dev.perfetto.Sched/active_cpu_count.ts
index 328d386..02ff0b2 100644
--- a/ui/src/plugins/dev.perfetto.Sched/active_cpu_count.ts
+++ b/ui/src/plugins/dev.perfetto.Sched/active_cpu_count.ts
@@ -33,10 +33,7 @@
private readonly cpuType?: CPUType;
constructor(ctx: TrackContext, trace: Trace, cpuType?: CPUType) {
- super({
- trace,
- uri: ctx.trackUri,
- });
+ super(trace, ctx.trackUri);
this.cpuType = cpuType;
}
diff --git a/ui/src/plugins/dev.perfetto.Sched/index.ts b/ui/src/plugins/dev.perfetto.Sched/index.ts
index a72c0f3..8be599d 100644
--- a/ui/src/plugins/dev.perfetto.Sched/index.ts
+++ b/ui/src/plugins/dev.perfetto.Sched/index.ts
@@ -17,7 +17,10 @@
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
import {ActiveCPUCountTrack, CPUType} from './active_cpu_count';
-import {RunnableThreadCountTrack} from './runnable_thread_count';
+import {
+ RunnableThreadCountTrack,
+ UninterruptibleSleepThreadCountTrack,
+} from './thread_count';
import {getSchedTable} from './table';
import {extensions} from '../../public/lib/extensions';
@@ -28,10 +31,7 @@
ctx.tracks.registerTrack({
uri: runnableThreadCountUri,
title: 'Runnable thread count',
- track: new RunnableThreadCountTrack({
- trace: ctx,
- uri: runnableThreadCountUri,
- }),
+ track: new RunnableThreadCountTrack(ctx, runnableThreadCountUri),
});
ctx.commands.registerCommand({
id: 'dev.perfetto.Sched.AddRunnableThreadCountTrackCommand',
@@ -40,6 +40,26 @@
addPinnedTrack(ctx, runnableThreadCountUri, 'Runnable thread count'),
});
+ const uninterruptibleSleepThreadCountUri = `/uninterruptible_sleep_thread_count`;
+ ctx.tracks.registerTrack({
+ uri: uninterruptibleSleepThreadCountUri,
+ title: 'Uninterruptible Sleep thread count',
+ track: new UninterruptibleSleepThreadCountTrack(
+ ctx,
+ uninterruptibleSleepThreadCountUri,
+ ),
+ });
+ ctx.commands.registerCommand({
+ id: 'dev.perfetto.Sched.AddUninterruptibleSleepThreadCountTrackCommand',
+ name: 'Add track: uninterruptible sleep thread count',
+ callback: () =>
+ addPinnedTrack(
+ ctx,
+ uninterruptibleSleepThreadCountUri,
+ 'Uninterruptible Sleep thread count',
+ ),
+ });
+
const uri = uriForActiveCPUCountTrack();
const title = 'Active CPU count';
ctx.tracks.registerTrack({
diff --git a/ui/src/plugins/dev.perfetto.Sched/runnable_thread_count.ts b/ui/src/plugins/dev.perfetto.Sched/thread_count.ts
similarity index 71%
rename from ui/src/plugins/dev.perfetto.Sched/runnable_thread_count.ts
rename to ui/src/plugins/dev.perfetto.Sched/thread_count.ts
index 9b5e9c5..73e7ee2 100644
--- a/ui/src/plugins/dev.perfetto.Sched/runnable_thread_count.ts
+++ b/ui/src/plugins/dev.perfetto.Sched/thread_count.ts
@@ -16,11 +16,11 @@
BaseCounterTrack,
CounterOptions,
} from '../../frontend/base_counter_track';
-import {NewTrackArgs} from '../../frontend/track';
+import {Trace} from '../../public/trace';
-export class RunnableThreadCountTrack extends BaseCounterTrack {
- constructor(args: NewTrackArgs) {
- super(args);
+abstract class ThreadCountTrack extends BaseCounterTrack {
+ constructor(trace: Trace, uri: string) {
+ super(trace, uri);
}
protected getDefaultCounterOptions(): CounterOptions {
@@ -35,7 +35,9 @@
`INCLUDE PERFETTO MODULE sched.thread_level_parallelism`,
);
}
+}
+export class RunnableThreadCountTrack extends ThreadCountTrack {
getSqlSource() {
return `
select
@@ -45,3 +47,14 @@
`;
}
}
+
+export class UninterruptibleSleepThreadCountTrack extends ThreadCountTrack {
+ getSqlSource() {
+ return `
+ select
+ ts,
+ uninterruptible_sleep_thread_count as value
+ from sched_uninterruptible_sleep_thread_count
+ `;
+ }
+}
diff --git a/ui/src/plugins/dev.perfetto.Screenshots/index.ts b/ui/src/plugins/dev.perfetto.Screenshots/index.ts
index 68c7d64..7cadbf7 100644
--- a/ui/src/plugins/dev.perfetto.Screenshots/index.ts
+++ b/ui/src/plugins/dev.perfetto.Screenshots/index.ts
@@ -35,10 +35,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new ScreenshotsTrack({
- trace: ctx,
- uri,
- }),
+ track: new ScreenshotsTrack(ctx, uri),
tags: {
kind: ScreenshotsTrack.kind,
},
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/index.ts b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
new file mode 100644
index 0000000..f2fde8d
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/index.ts
@@ -0,0 +1,35 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {assetSrc} from '../../base/assets';
+import {assertExists} from '../../base/logging';
+import {PerfettoPlugin} from '../../public/plugin';
+import {SqlModules} from './sql_modules';
+import {SQL_MODULES_DOCS_SCHEMA, SqlModulesImpl} from './sql_modules_impl';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.SqlModules';
+ private sqlModules?: SqlModules;
+
+ async onTraceLoad() {
+ const resp = await fetch(assetSrc('stdlib_docs.json'));
+ const json = await resp.json();
+ const docs = SQL_MODULES_DOCS_SCHEMA.parse(json);
+ this.sqlModules = new SqlModulesImpl(docs);
+ }
+
+ getSqlModules() {
+ return assertExists(this.sqlModules);
+ }
+}
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
new file mode 100644
index 0000000..9f0503a
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules.ts
@@ -0,0 +1,90 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Handles the access to all of the Perfetto SQL modules accessible to Trace
+// Processor.
+export interface SqlModules {
+ // Returns names of all tables/views between all loaded Perfetto SQL modules.
+ listTables(): string[];
+
+ // Returns Perfetto SQL table/view if it was loaded in one of the Perfetto
+ // SQL module.
+ getTable(tableName: string): SqlTable | undefined;
+}
+
+// Handles the access to a specific Perfetto SQL Package. Package consists of
+// Perfetto SQL modules.
+export interface SqlPackage {
+ readonly name: string;
+ readonly modules: SqlModule[];
+ listTables(): string[];
+ getTable(tableName: string): SqlTable | undefined;
+}
+
+// Handles the access to a specific Perfetto SQL module.
+export interface SqlModule {
+ readonly includeKey: string;
+ readonly dataObjects: SqlTable[];
+ readonly functions: SqlFunction[];
+ readonly tableFunctions: SqlTableFunction[];
+ readonly macros: SqlMacro[];
+}
+
+// The definition of Perfetto SQL table/view.
+export interface SqlTable {
+ readonly name: string;
+ readonly description: string;
+ readonly type: string;
+ readonly columns: SqlColumn[];
+}
+
+// The definition of Perfetto SQL function.
+export interface SqlFunction {
+ readonly name: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnType: string;
+ readonly returnDesc: string;
+}
+
+// The definition of Perfetto SQL table function.
+export interface SqlTableFunction {
+ readonly name: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnCols: SqlColumn[];
+}
+
+// The definition of Perfetto SQL macro.
+export interface SqlMacro {
+ readonly name: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnType: string;
+}
+
+// The definition of Perfetto SQL column.
+export interface SqlColumn {
+ readonly name: string;
+ readonly description: string;
+ readonly type: string;
+}
+
+// The definition of Perfetto SQL argument. Can be used for functions, table
+// functions or macros.
+export interface SqlArgument {
+ readonly name: string;
+ readonly description: string;
+ readonly type: string;
+}
diff --git a/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
new file mode 100644
index 0000000..ee6fe79
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.SqlModules/sql_modules_impl.ts
@@ -0,0 +1,254 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {z} from 'zod';
+import {
+ SqlModules,
+ SqlColumn,
+ SqlFunction,
+ SqlArgument,
+ SqlMacro,
+ SqlModule,
+ SqlPackage,
+ SqlTable,
+ SqlTableFunction,
+} from './sql_modules';
+
+export class SqlModulesImpl implements SqlModules {
+ readonly packages: SqlPackage[];
+
+ constructor(docs: SqlModulesDocsSchema) {
+ this.packages = docs.map((json) => new StdlibPackageImpl(json));
+ }
+
+ listTables(): string[] {
+ const tables: string[] = [];
+ for (const stdlibPackage of this.packages) {
+ tables.concat(stdlibPackage.listTables());
+ }
+ return tables;
+ }
+
+ getTable(tableName: string): SqlTable | undefined {
+ for (const stdlibPackage of this.packages) {
+ const maybeTable = stdlibPackage.getTable(tableName);
+ if (maybeTable) {
+ return maybeTable;
+ }
+ }
+ return undefined;
+ }
+}
+
+export class StdlibPackageImpl implements SqlPackage {
+ readonly name: string;
+ readonly modules: SqlModule[];
+
+ constructor(docs: DocsPackageSchemaType) {
+ this.name = docs.name;
+ this.modules = [];
+ for (const moduleJson of docs.modules) {
+ this.modules.push(new StdlibModuleImpl(moduleJson));
+ }
+ }
+
+ getTable(tableName: string): SqlTable | undefined {
+ for (const module of this.modules) {
+ for (const dataObj of module.dataObjects) {
+ if (dataObj.name == tableName) {
+ return dataObj;
+ }
+ }
+ }
+ return undefined;
+ }
+
+ listTables(): string[] {
+ return this.modules.flatMap((module) =>
+ module.dataObjects.map((dataObj) => dataObj.name),
+ );
+ }
+}
+
+export class StdlibModuleImpl implements SqlModule {
+ readonly includeKey: string;
+ readonly dataObjects: SqlTable[];
+ readonly functions: SqlFunction[];
+ readonly tableFunctions: SqlTableFunction[];
+ readonly macros: SqlMacro[];
+
+ constructor(docs: DocsModuleSchemaType) {
+ this.includeKey = docs.module_name;
+ this.dataObjects = docs.data_objects.map(
+ (json) => new StdlibDataObjectImpl(json),
+ );
+ this.functions = docs.functions.map((json) => new StdlibFunctionImpl(json));
+ this.tableFunctions = docs.table_functions.map(
+ (json) => new StdlibTableFunctionImpl(json),
+ );
+ this.macros = docs.macros.map((json) => new StdlibMacroImpl(json));
+ }
+}
+
+class StdlibMacroImpl implements SqlMacro {
+ readonly name: string;
+ readonly summaryDesc: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnType: string;
+
+ constructor(docs: DocsMacroSchemaType) {
+ this.name = docs.name;
+ this.summaryDesc = docs.summary_desc;
+ this.description = docs.desc;
+ this.returnType = docs.return_type;
+ this.args = [];
+ this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+ }
+}
+
+class StdlibTableFunctionImpl implements SqlTableFunction {
+ readonly name: string;
+ readonly summaryDesc: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnCols: SqlColumn[];
+
+ constructor(docs: DocsTableFunctionSchemaType) {
+ this.name = docs.name;
+ this.summaryDesc = docs.summary_desc;
+ this.description = docs.desc;
+ this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+ this.returnCols = docs.cols.map((json) => new StdlibColumnImpl(json));
+ }
+}
+
+class StdlibFunctionImpl implements SqlFunction {
+ readonly name: string;
+ readonly summaryDesc: string;
+ readonly description: string;
+ readonly args: SqlArgument[];
+ readonly returnType: string;
+ readonly returnDesc: string;
+
+ constructor(docs: DocsFunctionSchemaType) {
+ this.name = docs.name;
+ this.summaryDesc = docs.summary_desc;
+ this.description = docs.desc;
+ this.returnType = docs.return_type;
+ this.returnDesc = docs.return_desc;
+ this.args = docs.args.map((json) => new StdlibFunctionArgImpl(json));
+ }
+}
+
+class StdlibDataObjectImpl implements SqlTable {
+ name: string;
+ description: string;
+ type: string;
+ columns: SqlColumn[];
+
+ constructor(docs: DocsDataObjectSchemaType) {
+ this.name = docs.name;
+ this.description = docs.desc;
+ this.type = docs.type;
+ this.columns = docs.cols.map((json) => new StdlibColumnImpl(json));
+ }
+}
+
+class StdlibColumnImpl implements SqlColumn {
+ name: string;
+ type: string;
+ description: string;
+
+ constructor(docs: DocsArgOrColSchemaType) {
+ this.type = docs.type;
+ this.description = docs.desc;
+ this.name = docs.name;
+ }
+}
+
+class StdlibFunctionArgImpl implements SqlArgument {
+ name: string;
+ description: string;
+ type: string;
+
+ constructor(docs: DocsArgOrColSchemaType) {
+ this.type = docs.type;
+ this.description = docs.desc;
+ this.name = docs.name;
+ }
+}
+
+const ARG_OR_COL_SCHEMA = z.object({
+ name: z.string(),
+ type: z.string(),
+ desc: z.string(),
+});
+type DocsArgOrColSchemaType = z.infer<typeof ARG_OR_COL_SCHEMA>;
+
+const DATA_OBJECT_SCHEMA = z.object({
+ name: z.string(),
+ desc: z.string(),
+ summary_desc: z.string(),
+ type: z.string(),
+ cols: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsDataObjectSchemaType = z.infer<typeof DATA_OBJECT_SCHEMA>;
+
+const FUNCTION_SCHEMA = z.object({
+ name: z.string(),
+ desc: z.string(),
+ summary_desc: z.string(),
+ args: z.array(ARG_OR_COL_SCHEMA),
+ return_type: z.string(),
+ return_desc: z.string(),
+});
+type DocsFunctionSchemaType = z.infer<typeof FUNCTION_SCHEMA>;
+
+const TABLE_FUNCTION_SCHEMA = z.object({
+ name: z.string(),
+ desc: z.string(),
+ summary_desc: z.string(),
+ args: z.array(ARG_OR_COL_SCHEMA),
+ cols: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsTableFunctionSchemaType = z.infer<typeof TABLE_FUNCTION_SCHEMA>;
+
+const MACRO_SCHEMA = z.object({
+ name: z.string(),
+ desc: z.string(),
+ summary_desc: z.string(),
+ return_desc: z.string(),
+ return_type: z.string(),
+ args: z.array(ARG_OR_COL_SCHEMA),
+});
+type DocsMacroSchemaType = z.infer<typeof MACRO_SCHEMA>;
+
+const MODULE_SCHEMA = z.object({
+ module_name: z.string(),
+ data_objects: z.array(DATA_OBJECT_SCHEMA),
+ functions: z.array(FUNCTION_SCHEMA),
+ table_functions: z.array(TABLE_FUNCTION_SCHEMA),
+ macros: z.array(MACRO_SCHEMA),
+});
+type DocsModuleSchemaType = z.infer<typeof MODULE_SCHEMA>;
+
+const PACKAGE_SCHEMA = z.object({
+ name: z.string(),
+ modules: z.array(MODULE_SCHEMA),
+});
+type DocsPackageSchemaType = z.infer<typeof PACKAGE_SCHEMA>;
+
+export const SQL_MODULES_DOCS_SCHEMA = z.array(PACKAGE_SCHEMA);
+export type SqlModulesDocsSchema = z.infer<typeof SQL_MODULES_DOCS_SCHEMA>;
diff --git a/ui/src/plugins/dev.perfetto.ThreadState/index.ts b/ui/src/plugins/dev.perfetto.ThreadState/index.ts
index a106654..155b35f 100644
--- a/ui/src/plugins/dev.perfetto.ThreadState/index.ts
+++ b/ui/src/plugins/dev.perfetto.ThreadState/index.ts
@@ -84,13 +84,7 @@
chips: removeFalsyValues([
isKernelThread === 0 && isMainThread === 1 && 'main thread',
]),
- track: new ThreadStateTrack(
- {
- trace: ctx,
- uri,
- },
- utid,
- ),
+ track: new ThreadStateTrack(ctx, uri, utid),
});
const group = getOrCreateGroupForThread(ctx.workspace, utid);
diff --git a/ui/src/plugins/dev.perfetto.ThreadState/table.ts b/ui/src/plugins/dev.perfetto.ThreadState/table.ts
index 9c86c5c..c93fb40 100644
--- a/ui/src/plugins/dev.perfetto.ThreadState/table.ts
+++ b/ui/src/plugins/dev.perfetto.ThreadState/table.ts
@@ -27,7 +27,7 @@
return {
name: 'thread_state',
columns: [
- new ThreadStateIdColumn('id', {notNull: true}),
+ new ThreadStateIdColumn('threadStateSqlId', {notNull: true}),
new TimestampColumn('ts'),
new DurationColumn('dur'),
new StandardColumn('state'),
@@ -46,11 +46,11 @@
},
{title: 'upid (process)', notNull: true},
),
- new StandardColumn('io_wait', {aggregationType: 'nominal'}),
- new StandardColumn('blocked_function'),
- new ThreadColumn('waker_utid', {title: 'Waker thread'}),
- new ThreadStateIdColumn('waker_id'),
- new StandardColumn('irq_context', {aggregationType: 'nominal'}),
+ new StandardColumn('ioWait', {aggregationType: 'nominal'}),
+ new StandardColumn('blockedFunction'),
+ new ThreadColumn('wakerUtid', {title: 'Waker thread'}),
+ new ThreadStateIdColumn('wakerId'),
+ new StandardColumn('irqContext', {aggregationType: 'nominal'}),
new StandardColumn('ucpu', {
aggregationType: 'nominal',
startsHidden: true,
diff --git a/ui/src/plugins/dev.perfetto.ThreadState/thread_state_details_panel.ts b/ui/src/plugins/dev.perfetto.ThreadState/thread_state_details_panel.ts
index b790f1d..0a2287b7 100644
--- a/ui/src/plugins/dev.perfetto.ThreadState/thread_state_details_panel.ts
+++ b/ui/src/plugins/dev.perfetto.ThreadState/thread_state_details_panel.ts
@@ -52,7 +52,7 @@
}
export class ThreadStateDetailsPanel implements TrackEventDetailsPanel {
- private state?: ThreadState;
+ private threadState?: ThreadState;
private relatedStates?: RelatedThreadStates;
constructor(
@@ -62,9 +62,9 @@
async load() {
const id = this.id;
- this.state = await getThreadState(this.trace.engine, id);
+ this.threadState = await getThreadState(this.trace.engine, id);
- if (!this.state) {
+ if (!this.threadState) {
return;
}
@@ -72,8 +72,8 @@
relatedStates.prev = (
await getThreadStateFromConstraints(this.trace.engine, {
filters: [
- `ts + dur = ${this.state.ts}`,
- `utid = ${this.state.thread?.utid}`,
+ `ts + dur = ${this.threadState.ts}`,
+ `utid = ${this.threadState.thread?.utid}`,
],
limit: 1,
})
@@ -81,22 +81,30 @@
relatedStates.next = (
await getThreadStateFromConstraints(this.trace.engine, {
filters: [
- `ts = ${this.state.ts + this.state.dur}`,
- `utid = ${this.state.thread?.utid}`,
+ `ts = ${this.threadState.ts + this.threadState.dur}`,
+ `utid = ${this.threadState.thread?.utid}`,
],
limit: 1,
})
)[0];
- if (this.state.wakerId !== undefined) {
+ if (this.threadState.wakerId !== undefined) {
relatedStates.waker = await getThreadState(
this.trace.engine,
- this.state.wakerId,
+ this.threadState.wakerId,
+ );
+ } else if (
+ this.threadState.state == 'Running' &&
+ relatedStates.prev.wakerId != undefined
+ ) {
+ relatedStates.waker = await getThreadState(
+ this.trace.engine,
+ relatedStates.prev.wakerId,
);
}
// note: this might be valid even if there is no |waker| slice, in the case
// of an interrupt wakeup while in the idle process (which is omitted from
// the thread_state table).
- relatedStates.wakerInterruptCtx = this.state.wakerInterruptCtx;
+ relatedStates.wakerInterruptCtx = this.threadState.wakerInterruptCtx;
relatedStates.wakee = await getThreadStateFromConstraints(
this.trace.engine,
@@ -121,7 +129,7 @@
m(
Section,
{title: 'Details'},
- this.state && this.renderTree(this.state),
+ this.threadState && this.renderTree(this.threadState),
),
m(
Section,
@@ -133,33 +141,37 @@
}
private renderLoadingText() {
- if (!this.state) {
+ if (!this.threadState) {
return 'Loading';
}
return this.id;
}
- private renderTree(state: ThreadState) {
- const thread = state.thread;
- const process = state.thread?.process;
+ private renderTree(threadState: ThreadState) {
+ const thread = threadState.thread;
+ const process = threadState.thread?.process;
return m(
Tree,
m(TreeNode, {
left: 'Start time',
- right: m(Timestamp, {ts: state.ts}),
+ right: m(Timestamp, {ts: threadState.ts}),
}),
m(TreeNode, {
left: 'Duration',
- right: m(DurationWidget, {dur: state.dur}),
+ right: m(DurationWidget, {dur: threadState.dur}),
}),
m(TreeNode, {
left: 'State',
- right: this.renderState(state.state, state.cpu, state.schedSqlId),
+ right: this.renderState(
+ threadState.state,
+ threadState.cpu,
+ threadState.schedSqlId,
+ ),
}),
- state.blockedFunction &&
+ threadState.blockedFunction &&
m(TreeNode, {
left: 'Blocked function',
- right: state.blockedFunction,
+ right: threadState.blockedFunction,
}),
process &&
m(TreeNode, {
@@ -167,9 +179,14 @@
right: getProcessName(process),
}),
thread && m(TreeNode, {left: 'Thread', right: getThreadName(thread)}),
+ threadState.priority !== undefined &&
+ m(TreeNode, {
+ left: 'Priority',
+ right: threadState.priority,
+ }),
m(TreeNode, {
left: 'SQL ID',
- right: m(SqlRef, {table: 'thread_state', id: state.threadStateSqlId}),
+ right: m(SqlRef, {table: 'thread_state', id: threadState.id}),
}),
);
}
@@ -197,18 +214,18 @@
}
private renderRelatedThreadStates(): m.Children {
- if (this.state === undefined || this.relatedStates === undefined) {
+ if (this.threadState === undefined || this.relatedStates === undefined) {
return 'Loading';
}
- const startTs = this.state.ts;
+ const startTs = this.threadState.ts;
const renderRef = (state: ThreadState, name?: string) =>
m(ThreadStateRef, {
- id: state.threadStateSqlId,
+ id: state.id,
name,
});
- const nameForNextOrPrev = (state: ThreadState) =>
- `${state.state} for ${renderDuration(state.dur)}`;
+ const nameForNextOrPrev = (threadState: ThreadState) =>
+ `${threadState.state} for ${renderDuration(threadState.dur)}`;
const renderWaker = (related: RelatedThreadStates) => {
// Could be absent if:
@@ -294,7 +311,7 @@
onclick: () => {
this.trace.commands.runCommand(
CRITICAL_PATH_LITE_CMD,
- this.state?.thread?.utid,
+ this.threadState?.thread?.utid,
);
},
}),
@@ -305,7 +322,7 @@
onclick: () => {
this.trace.commands.runCommand(
CRITICAL_PATH_CMD,
- this.state?.thread?.utid,
+ this.threadState?.thread?.utid,
);
},
}),
@@ -313,6 +330,6 @@
}
isLoading() {
- return this.state === undefined || this.relatedStates === undefined;
+ return this.threadState === undefined || this.relatedStates === undefined;
}
}
diff --git a/ui/src/plugins/dev.perfetto.ThreadState/thread_state_track.ts b/ui/src/plugins/dev.perfetto.ThreadState/thread_state_track.ts
index 3935465..4fdb791 100644
--- a/ui/src/plugins/dev.perfetto.ThreadState/thread_state_track.ts
+++ b/ui/src/plugins/dev.perfetto.ThreadState/thread_state_track.ts
@@ -22,12 +22,12 @@
SLICE_LAYOUT_FLAT_DEFAULTS,
SliceLayout,
} from '../../frontend/slice_layout';
-import {NewTrackArgs} from '../../frontend/track';
import {NUM_NULL, STR} from '../../trace_processor/query_result';
import {Slice} from '../../public/track';
import {translateState} from '../../trace_processor/sql_utils/thread_state';
import {TrackEventDetails, TrackEventSelection} from '../../public/selection';
import {ThreadStateDetailsPanel} from './thread_state_details_panel';
+import {Trace} from '../../public/trace';
export const THREAD_STATE_ROW = {
...BASE_ROW,
@@ -41,10 +41,11 @@
protected sliceLayout: SliceLayout = {...SLICE_LAYOUT_FLAT_DEFAULTS};
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
private utid: number,
) {
- super(args);
+ super(trace, uri);
}
// This is used by the base class to call iter().
diff --git a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
index aef7a76..37206a3 100644
--- a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
+++ b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
@@ -272,6 +272,7 @@
private onmessage(msg: MessageEvent) {
if (this._ctx === undefined) return; // Trace unloaded
if (!('perfettoSync' in msg.data)) return;
+ this._ctx.scheduleFullRedraw('force');
const msgData = msg.data as SyncMessage;
const sync = msgData.perfettoSync;
switch (sync.cmd) {
diff --git a/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts b/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts
new file mode 100644
index 0000000..f019331
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.TraceInfoPage/index.ts
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {TraceInfoPage} from './trace_info_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.TraceInfoPage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/info', page: TraceInfoPage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Info and stats',
+ href: '#!/info',
+ icon: 'info',
+ sortOrder: 10,
+ });
+ }
+}
diff --git a/ui/src/frontend/trace_info_page.ts b/ui/src/plugins/dev.perfetto.TraceInfoPage/trace_info_page.ts
similarity index 95%
rename from ui/src/frontend/trace_info_page.ts
rename to ui/src/plugins/dev.perfetto.TraceInfoPage/trace_info_page.ts
index a15304e..686b976 100644
--- a/ui/src/frontend/trace_info_page.ts
+++ b/ui/src/plugins/dev.perfetto.TraceInfoPage/trace_info_page.ts
@@ -13,12 +13,11 @@
// limitations under the License.
import m from 'mithril';
-import {raf} from '../core/raf_scheduler';
-import {Engine, EngineAttrs} from '../trace_processor/engine';
-import {PageWithTraceAttrs} from './pages';
-import {QueryResult, UNKNOWN} from '../trace_processor/query_result';
-import {assertExists} from '../base/logging';
-import {TraceImplAttrs} from '../core/trace_impl';
+import {Engine, EngineAttrs} from '../../trace_processor/engine';
+import {QueryResult, UNKNOWN} from '../../trace_processor/query_result';
+import {assertExists} from '../../base/logging';
+import {TraceAttrs} from '../../public/trace';
+import {PageWithTraceAttrs} from '../../public/page';
/**
* Extracts and copies fields from a source object based on the keys present in
@@ -102,8 +101,6 @@
data.push(pickFields(it, statsSpec));
}
this.data = data;
-
- raf.scheduleFullRedraw();
});
}
@@ -140,8 +137,8 @@
}
}
-class LoadingErrors implements m.ClassComponent<TraceImplAttrs> {
- view({attrs}: m.CVnode<TraceImplAttrs>) {
+class LoadingErrors implements m.ClassComponent<TraceAttrs> {
+ view({attrs}: m.CVnode<TraceAttrs>) {
const errors = attrs.trace.loadingErrors;
if (errors.length === 0) return;
return m(
@@ -194,7 +191,6 @@
tableRows.push(pickFields(it, traceMetadataRowSpec));
}
this.data = tableRows;
- raf.scheduleFullRedraw();
});
}
@@ -272,7 +268,6 @@
data.push(pickFields(it, androidGameInterventionRowSpec));
}
this.data = data;
- raf.scheduleFullRedraw();
});
}
@@ -386,7 +381,6 @@
}
this.packageList = packageList;
- raf.scheduleFullRedraw();
}
view() {
diff --git a/ui/src/plugins/dev.perfetto.TraceMetadata/index.ts b/ui/src/plugins/dev.perfetto.TraceMetadata/index.ts
index 8849ee5..a10afe1 100644
--- a/ui/src/plugins/dev.perfetto.TraceMetadata/index.ts
+++ b/ui/src/plugins/dev.perfetto.TraceMetadata/index.ts
@@ -15,7 +15,7 @@
import {NUM} from '../../trace_processor/query_result';
import {Trace} from '../../public/trace';
import {PerfettoPlugin} from '../../public/plugin';
-import {SimpleSliceTrack} from '../../frontend/simple_slice_track';
+import {createQuerySliceTrack} from '../../public/lib/tracks/query_slice_track';
import {TrackNode} from '../../public/workspace';
export default class implements PerfettoPlugin {
@@ -30,21 +30,16 @@
}
const uri = `/clock_snapshots`;
const title = 'Clock Snapshots';
- const track = new SimpleSliceTrack(
- ctx,
- {trackUri: uri},
- {
- data: {
- sqlSource: `
- select ts, 0 as dur, 'Snapshot' as name
- from clock_snapshot
+ const track = await createQuerySliceTrack({
+ trace: ctx,
+ uri,
+ data: {
+ sqlSource: `
+ select ts, 0 as dur, 'Snapshot' as name
+ from clock_snapshot
`,
- columns: ['ts', 'dur', 'name'],
- },
- columns: {ts: 'ts', dur: 'dur', name: 'name'},
- argColumns: [],
},
- );
+ });
ctx.tracks.registerTrack({
uri,
title,
diff --git a/ui/src/plugins/dev.perfetto.VizPage/index.ts b/ui/src/plugins/dev.perfetto.VizPage/index.ts
new file mode 100644
index 0000000..9476479
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.VizPage/index.ts
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {PerfettoPlugin} from '../../public/plugin';
+import {Trace} from '../../public/trace';
+import {VizPage} from './viz_page';
+
+export default class implements PerfettoPlugin {
+ static readonly id = 'dev.perfetto.VizPage';
+
+ async onTraceLoad(trace: Trace): Promise<void> {
+ trace.pages.registerPage({route: '/viz', page: VizPage});
+ trace.sidebar.addMenuItem({
+ section: 'current_trace',
+ text: 'Viz',
+ href: '#!/viz',
+ icon: 'area_chart',
+ sortOrder: 2,
+ });
+ }
+}
diff --git a/ui/src/frontend/viz_page.ts b/ui/src/plugins/dev.perfetto.VizPage/viz_page.ts
similarity index 68%
rename from ui/src/frontend/viz_page.ts
rename to ui/src/plugins/dev.perfetto.VizPage/viz_page.ts
index 1a9e9c4..a838a99 100644
--- a/ui/src/frontend/viz_page.ts
+++ b/ui/src/plugins/dev.perfetto.VizPage/viz_page.ts
@@ -13,35 +13,32 @@
// limitations under the License.
import m from 'mithril';
-import {raf} from '../core/raf_scheduler';
-import {Editor} from '../widgets/editor';
-import {VegaView} from '../widgets/vega_view';
-import {PageWithTraceAttrs} from './pages';
-import {assertExists} from '../base/logging';
-import {Engine} from '../trace_processor/engine';
+import {Editor} from '../../widgets/editor';
+import {VegaView} from '../../widgets/vega_view';
+import {PageWithTraceAttrs} from '../../public/page';
+import {Engine} from '../../trace_processor/engine';
let SPEC = '';
export class VizPage implements m.ClassComponent<PageWithTraceAttrs> {
- private engine?: Engine;
+ private engine: Engine;
- oninit({attrs}: m.CVnode<PageWithTraceAttrs>) {
+ constructor({attrs}: m.CVnode<PageWithTraceAttrs>) {
this.engine = attrs.trace.engine.getProxy('VizPage');
}
- view() {
- const engine = assertExists(this.engine);
+ view({attrs}: m.CVnode<PageWithTraceAttrs>) {
return m(
'.viz-page',
m(VegaView, {
spec: SPEC,
- engine: engine,
+ engine: this.engine,
data: {},
}),
m(Editor, {
onUpdate: (text: string) => {
SPEC = text;
- raf.scheduleFullRedraw();
+ attrs.trace.scheduleFullRedraw();
},
}),
);
diff --git a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts b/ui/src/plugins/dev.perfetto.WidgetsPage/index.ts
similarity index 61%
copy from ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts
copy to ui/src/plugins/dev.perfetto.WidgetsPage/index.ts
index 2df958f..44fade1 100644
--- a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts
+++ b/ui/src/plugins/dev.perfetto.WidgetsPage/index.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 The Android Open Source Project
+// Copyright (C) 2024 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.
@@ -14,15 +14,23 @@
import {App} from '../../public/app';
import {PerfettoPlugin} from '../../public/plugin';
+import {WidgetsPage} from './widgets_page';
-// This is just an example plugin, used to prove that the plugin system works.
export default class implements PerfettoPlugin {
- static readonly id = 'dev.perfetto.ExampleSimpleCommand';
- static onActivate(ctx: App): void {
- ctx.commands.registerCommand({
- id: 'dev.perfetto.ExampleSimpleCommand#LogHelloWorld',
- name: 'Log "Hello, world!"',
- callback: () => console.log('Hello, world!'),
+ static readonly id = 'dev.perfetto.WidgetsPage';
+
+ static onActivate(app: App): void {
+ app.pages.registerPage({
+ route: '/widgets',
+ page: WidgetsPage,
+ traceless: true,
+ });
+ app.sidebar.addMenuItem({
+ section: 'navigation',
+ text: 'Widgets',
+ href: '#!/widgets',
+ icon: 'widgets',
+ sortOrder: 99,
});
}
}
diff --git a/ui/src/frontend/tables/table_showcase.ts b/ui/src/plugins/dev.perfetto.WidgetsPage/table_showcase.ts
similarity index 97%
rename from ui/src/frontend/tables/table_showcase.ts
rename to ui/src/plugins/dev.perfetto.WidgetsPage/table_showcase.ts
index c0da7c1..00936bc 100644
--- a/ui/src/frontend/tables/table_showcase.ts
+++ b/ui/src/plugins/dev.perfetto.WidgetsPage/table_showcase.ts
@@ -19,7 +19,7 @@
stringColumn,
Table,
TableData,
-} from './table';
+} from '../../widgets/table';
// This file serves as an example of a table component present in the widgets
// showcase. Since table is somewhat complicated component that requires some
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
similarity index 92%
rename from ui/src/frontend/widgets_page.ts
rename to ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
index 3c93f1f..a5ff6d7 100644
--- a/ui/src/frontend/widgets_page.ts
+++ b/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
@@ -13,51 +13,51 @@
// limitations under the License.
import m from 'mithril';
-import {classNames} from '../base/classnames';
-import {Hotkey, Platform} from '../base/hotkeys';
-import {isString} from '../base/object_utils';
-import {Icons} from '../base/semantic_icons';
-import {raf} from '../core/raf_scheduler';
-import {Anchor} from '../widgets/anchor';
-import {Button} from '../widgets/button';
-import {Callout} from '../widgets/callout';
-import {Checkbox} from '../widgets/checkbox';
-import {Editor} from '../widgets/editor';
-import {EmptyState} from '../widgets/empty_state';
-import {Form, FormLabel} from '../widgets/form';
-import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
-import {Icon} from '../widgets/icon';
-import {Menu, MenuDivider, MenuItem, PopupMenu2} from '../widgets/menu';
-import {showModal} from '../widgets/modal';
+import {classNames} from '../../base/classnames';
+import {Hotkey, Platform} from '../../base/hotkeys';
+import {isString} from '../../base/object_utils';
+import {Icons} from '../../base/semantic_icons';
+import {Anchor} from '../../widgets/anchor';
+import {Button} from '../../widgets/button';
+import {Callout} from '../../widgets/callout';
+import {Checkbox} from '../../widgets/checkbox';
+import {Editor} from '../../widgets/editor';
+import {EmptyState} from '../../widgets/empty_state';
+import {Form, FormLabel} from '../../widgets/form';
+import {HotkeyGlyphs} from '../../widgets/hotkey_glyphs';
+import {Icon} from '../../widgets/icon';
+import {Menu, MenuDivider, MenuItem, PopupMenu2} from '../../widgets/menu';
+import {showModal} from '../../widgets/modal';
import {
MultiSelect,
MultiSelectDiff,
PopupMultiSelect,
-} from '../widgets/multiselect';
-import {Popup, PopupPosition} from '../widgets/popup';
-import {Portal} from '../widgets/portal';
-import {Select} from '../widgets/select';
-import {Spinner} from '../widgets/spinner';
-import {Switch} from '../widgets/switch';
-import {TextInput} from '../widgets/text_input';
-import {MultiParagraphText, TextParagraph} from '../widgets/text_paragraph';
-import {LazyTreeNode, Tree, TreeNode} from '../widgets/tree';
-import {VegaView} from '../widgets/vega_view';
-import {PageAttrs} from '../core/router';
-import {PopupMenuButton} from './popup_menu';
-import {TableShowcase} from './tables/table_showcase';
-import {TreeTable, TreeTableAttrs} from './widgets/treetable';
-import {Intent} from '../widgets/common';
+} from '../../widgets/multiselect';
+import {Popup, PopupPosition} from '../../widgets/popup';
+import {Portal} from '../../widgets/portal';
+import {Select} from '../../widgets/select';
+import {Spinner} from '../../widgets/spinner';
+import {Switch} from '../../widgets/switch';
+import {TextInput} from '../../widgets/text_input';
+import {MultiParagraphText, TextParagraph} from '../../widgets/text_paragraph';
+import {LazyTreeNode, Tree, TreeNode} from '../../widgets/tree';
+import {VegaView} from '../../widgets/vega_view';
+import {PageAttrs} from '../../public/page';
+import {TableShowcase} from './table_showcase';
+import {TreeTable, TreeTableAttrs} from '../../frontend/widgets/treetable';
+import {Intent} from '../../widgets/common';
import {
VirtualTable,
VirtualTableAttrs,
VirtualTableRow,
-} from '../widgets/virtual_table';
-import {TagInput} from '../widgets/tag_input';
-import {SegmentedButtons} from '../widgets/segmented_buttons';
-import {MiddleEllipsis} from '../widgets/middle_ellipsis';
-import {Chip, ChipBar} from '../widgets/chip';
-import {TrackWidget} from '../widgets/track_widget';
+} from '../../widgets/virtual_table';
+import {TagInput} from '../../widgets/tag_input';
+import {SegmentedButtons} from '../../widgets/segmented_buttons';
+import {MiddleEllipsis} from '../../widgets/middle_ellipsis';
+import {Chip, ChipBar} from '../../widgets/chip';
+import {TrackWidget} from '../../widgets/track_widget';
+import {scheduleFullRedraw} from '../../widgets/raf';
+import {CopyableLink} from '../../widgets/copyable_link';
const DATA_ENGLISH_LETTER_FREQUENCY = {
table: [
@@ -310,7 +310,7 @@
intent: Intent.Primary,
onclick: () => {
portalOpen = !portalOpen;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
}),
portalOpen &&
@@ -362,7 +362,7 @@
label: 'Close Popup',
onclick: () => {
popupOpen = !popupOpen;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
}),
);
@@ -498,7 +498,7 @@
label: key,
onchange: () => {
this.optValues[key] = !Boolean(this.optValues[key]);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
});
}
@@ -512,7 +512,7 @@
value: this.optValues[key],
oninput: (e: Event) => {
this.optValues[key] = (e.target as HTMLInputElement).value;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
}),
);
@@ -530,7 +530,7 @@
this.optValues[key] = Number.parseInt(
(e.target as HTMLInputElement).value,
);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
}),
);
@@ -550,7 +550,7 @@
onchange: (e: Event) => {
const el = e.target as HTMLSelectElement;
this.optValues[key] = el.value;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
},
optionElements,
@@ -642,14 +642,14 @@
onTagAdd: (tag) => {
tags.push(tag);
tagInputValue = '';
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
onChange: (value) => {
tagInputValue = value;
},
onTagRemove: (index) => {
tags.splice(index, 1);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
});
},
@@ -666,7 +666,7 @@
selectedOption: selectedIdx,
onOptionSelected: (num) => {
selectedIdx = num;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
});
},
@@ -685,6 +685,7 @@
icon: arg(icon, 'send'),
rightIcon: arg(rightIcon, 'arrow_forward'),
label: arg(label, 'Button', ''),
+ onclick: () => alert('button pressed'),
...rest,
}),
initialOpts: {
@@ -782,6 +783,17 @@
},
}),
m(WidgetShowcase, {
+ label: 'CopyableLink',
+ renderWidget: ({noicon}) =>
+ m(CopyableLink, {
+ noicon: arg(noicon, true),
+ url: 'https://perfetto.dev/docs/',
+ }),
+ initialOpts: {
+ noicon: false,
+ },
+ }),
+ m(WidgetShowcase, {
label: 'Table',
renderWidget: () => m(TableShowcase),
initialOpts: {},
@@ -854,7 +866,7 @@
diffs.forEach(({id, checked}) => {
options[id] = checked;
});
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
...rest,
}),
@@ -881,7 +893,7 @@
diffs.forEach(({id, checked}) => {
options[id] = checked;
});
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
...rest,
}),
@@ -892,30 +904,6 @@
},
}),
m(WidgetShowcase, {
- label: 'PopupMenu',
- renderWidget: () => {
- return m(PopupMenuButton, {
- icon: 'description',
- items: [
- {itemType: 'regular', text: 'New', callback: () => {}},
- {itemType: 'regular', text: 'Open', callback: () => {}},
- {itemType: 'regular', text: 'Save', callback: () => {}},
- {itemType: 'regular', text: 'Delete', callback: () => {}},
- {
- itemType: 'group',
- text: 'Share',
- itemId: 'foo',
- children: [
- {itemType: 'regular', text: 'Friends', callback: () => {}},
- {itemType: 'regular', text: 'Family', callback: () => {}},
- {itemType: 'regular', text: 'Everyone', callback: () => {}},
- ],
- },
- ],
- });
- },
- }),
- m(WidgetShowcase, {
label: 'Menu',
renderWidget: () =>
m(
@@ -1285,7 +1273,7 @@
offset: rowOffset,
rows,
};
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
},
};
return m(VirtualTable, attrs);
@@ -1414,7 +1402,7 @@
},
view: function (vnode: m.Vnode<{}, {progress: number}>) {
vnode.state.progress = (vnode.state.progress + 1) % 100;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
return m(
'div',
m('div', 'You should see an animating progress bar'),
diff --git a/ui/src/plugins/org.chromium.ChromeCriticalUserInteractions/index.ts b/ui/src/plugins/org.chromium.ChromeCriticalUserInteractions/index.ts
index 6b96209..a2b7981 100644
--- a/ui/src/plugins/org.chromium.ChromeCriticalUserInteractions/index.ts
+++ b/ui/src/plugins/org.chromium.ChromeCriticalUserInteractions/index.ts
@@ -39,10 +39,10 @@
kind: CriticalUserInteractionTrack.kind,
},
title: 'Chrome Interactions',
- track: new CriticalUserInteractionTrack({
- trace: ctx,
- uri: CriticalUserInteractionTrack.kind,
- }),
+ track: new CriticalUserInteractionTrack(
+ ctx,
+ CriticalUserInteractionTrack.kind,
+ ),
});
}
}
diff --git a/ui/src/plugins/org.chromium.ChromeTasks/track.ts b/ui/src/plugins/org.chromium.ChromeTasks/track.ts
index ff2f361..030af9e 100644
--- a/ui/src/plugins/org.chromium.ChromeTasks/track.ts
+++ b/ui/src/plugins/org.chromium.ChromeTasks/track.ts
@@ -25,9 +25,9 @@
constructor(
trace: Trace,
uri: string,
- private utid: Utid,
+ private readonly utid: Utid,
) {
- super({trace, uri});
+ super(trace, uri);
}
getSqlDataSource(): CustomSqlTableDefConfig {
diff --git a/ui/src/plugins/org.kernel.LinuxKernelSubsystems/index.ts b/ui/src/plugins/org.kernel.LinuxKernelSubsystems/index.ts
index 0bca424..8d7ff66 100644
--- a/ui/src/plugins/org.kernel.LinuxKernelSubsystems/index.ts
+++ b/ui/src/plugins/org.kernel.LinuxKernelSubsystems/index.ts
@@ -66,14 +66,7 @@
ctx.tracks.registerTrack({
uri,
title,
- track: new AsyncSliceTrack(
- {
- trace: ctx,
- uri,
- },
- 0,
- [trackId],
- ),
+ track: new AsyncSliceTrack(ctx, uri, 0, [trackId]),
tags: {
kind: SLICE_TRACK_KIND,
trackIds: [trackId],
diff --git a/ui/src/plugins/org.kernel.SuspendResumeLatency/index.ts b/ui/src/plugins/org.kernel.SuspendResumeLatency/index.ts
index 96b0ddb..973af15 100644
--- a/ui/src/plugins/org.kernel.SuspendResumeLatency/index.ts
+++ b/ui/src/plugins/org.kernel.SuspendResumeLatency/index.ts
@@ -14,7 +14,6 @@
import {NUM, STR_NULL} from '../../trace_processor/query_result';
import {AsyncSliceTrack} from '../dev.perfetto.AsyncSlices/async_slice_track';
-import {NewTrackArgs} from '../../frontend/track';
import {PerfettoPlugin} from '../../public/plugin';
import {Trace} from '../../public/trace';
import {TrackNode} from '../../public/workspace';
@@ -31,12 +30,13 @@
// TODO(stevegolton): Remove this?
class SuspendResumeSliceTrack extends AsyncSliceTrack {
constructor(
- args: NewTrackArgs,
+ trace: Trace,
+ uri: string,
maxDepth: number,
trackIds: number[],
private readonly threads: ThreadMap,
) {
- super(args, maxDepth, trackIds);
+ super(trace, uri, maxDepth, trackIds);
}
onSliceClick(args: OnSliceClickArgs<Slice>) {
@@ -96,12 +96,7 @@
trackIds,
kind: SLICE_TRACK_KIND,
},
- track: new SuspendResumeSliceTrack(
- {uri, trace: ctx},
- maxDepth,
- trackIds,
- threads,
- ),
+ track: new SuspendResumeSliceTrack(ctx, uri, maxDepth, trackIds, threads),
});
// Display the track in the UI.
diff --git a/ui/src/plugins/org.kernel.Wattson/index.ts b/ui/src/plugins/org.kernel.Wattson/index.ts
index 539366d..ebbe946 100644
--- a/ui/src/plugins/org.kernel.Wattson/index.ts
+++ b/ui/src/plugins/org.kernel.Wattson/index.ts
@@ -94,10 +94,7 @@
readonly queryKey: string;
constructor(trace: Trace, uri: string, queryKey: string) {
- super({
- trace,
- uri,
- });
+ super(trace, uri);
this.queryKey = queryKey;
}
diff --git a/ui/src/public/app.ts b/ui/src/public/app.ts
index 6d52dc7..0c8321b 100644
--- a/ui/src/public/app.ts
+++ b/ui/src/public/app.ts
@@ -18,6 +18,9 @@
import {SidebarManager} from './sidebar';
import {Analytics} from './analytics';
import {PluginManager} from './plugin';
+import {Trace} from './trace';
+import {PageManager} from './page';
+import {FeatureFlagManager} from './feature_flag';
/**
* The API endpoint to interact programmaticaly with the UI before a trace has
@@ -34,6 +37,8 @@
readonly omnibox: OmniboxManager;
readonly analytics: Analytics;
readonly plugins: PluginManager;
+ readonly pages: PageManager;
+ readonly featureFlags: FeatureFlagManager;
/**
* The parsed querystring passed when starting the app, before any navigation
@@ -41,14 +46,26 @@
*/
readonly initialRouteArgs: RouteArgs;
- readonly rootUrl: string;
+ /**
+ * Returns the current trace object, if any. The instance being returned is
+ * bound to the same plugin of App.pluginId.
+ */
+ readonly trace?: Trace;
// TODO(primiano): this should be needed in extremely rare cases. We should
// probably switch to mithril auto-redraw at some point.
- scheduleFullRedraw(): void;
+ scheduleFullRedraw(force?: 'force'): void;
/**
* Navigate to a new page.
*/
navigate(newHash: string): void;
+
+ openTraceFromFile(file: File): void;
+ openTraceFromUrl(url: string): void;
+ openTraceFromBuffer(args: {
+ buffer: ArrayBuffer;
+ title: string;
+ fileName: string;
+ }): void;
}
diff --git a/ui/src/public/debug_tracks.ts b/ui/src/public/debug_tracks.ts
index c8e6ac5..15577e3 100644
--- a/ui/src/public/debug_tracks.ts
+++ b/ui/src/public/debug_tracks.ts
@@ -14,4 +14,4 @@
// TODO(primiano): in near future the code to create debug tracks from an App
// context will be moved here. For now i'm just re-exporting the function as-is.
-export {addDebugSliceTrack} from './lib/debug_tracks/debug_tracks';
+export {addDebugSliceTrack} from './lib/tracks/debug_tracks';
diff --git a/ui/src/public/feature_flag.ts b/ui/src/public/feature_flag.ts
new file mode 100644
index 0000000..c82d38a
--- /dev/null
+++ b/ui/src/public/feature_flag.ts
@@ -0,0 +1,64 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export interface FeatureFlagManager {
+ register(settings: FlagSettings): Flag;
+}
+
+export interface FlagSettings {
+ id: string;
+ defaultValue: boolean;
+ description: string;
+ name?: string;
+ devOnly?: boolean;
+}
+
+export interface Flag {
+ // A unique identifier for this flag ("magicSorting")
+ readonly id: string;
+
+ // The name of the flag the user sees ("New track sorting algorithm")
+ readonly name: string;
+
+ // A longer description which is displayed to the user.
+ // "Sort tracks using an embedded tfLite model based on your expression
+ // while waiting for the trace to load."
+ readonly description: string;
+
+ // Whether the flag defaults to true or false.
+ // If !flag.isOverridden() then flag.get() === flag.defaultValue
+ readonly defaultValue: boolean;
+
+ // Get the current value of the flag.
+ get(): boolean;
+
+ // Override the flag and persist the new value.
+ set(value: boolean): void;
+
+ // If the flag has been overridden.
+ // Note: A flag can be overridden to its default value.
+ isOverridden(): boolean;
+
+ // Reset the flag to its default setting.
+ reset(): void;
+
+ // Get the current state of the flag.
+ overriddenState(): OverrideState;
+}
+
+export enum OverrideState {
+ DEFAULT = 'DEFAULT',
+ TRUE = 'OVERRIDE_TRUE',
+ FALSE = 'OVERRIDE_FALSE',
+}
diff --git a/ui/src/public/lib/debug_tracks/counter_track.ts b/ui/src/public/lib/debug_tracks/counter_track.ts
deleted file mode 100644
index d9c2f7c..0000000
--- a/ui/src/public/lib/debug_tracks/counter_track.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-import {BaseCounterTrack} from '../../../frontend/base_counter_track';
-import {TrackContext} from '../../track';
-import {Button} from '../../../widgets/button';
-import {Icons} from '../../../base/semantic_icons';
-import {Trace} from '../../trace';
-
-export class DebugCounterTrack extends BaseCounterTrack {
- private readonly sqlTableName: string;
-
- constructor(trace: Trace, ctx: TrackContext, tableName: string) {
- super({
- trace,
- uri: ctx.trackUri,
- });
- this.sqlTableName = tableName;
- }
-
- getSqlSource(): string {
- return `select * from ${this.sqlTableName}`;
- }
-
- getTrackShellButtons(): m.Children {
- return m(Button, {
- onclick: () => {
- this.trace.workspace.findTrackByUri(this.uri)?.remove();
- },
- icon: Icons.Close,
- title: 'Close',
- compact: true,
- });
- }
-}
diff --git a/ui/src/public/lib/debug_tracks/debug_tracks.ts b/ui/src/public/lib/debug_tracks/debug_tracks.ts
deleted file mode 100644
index ab367b1..0000000
--- a/ui/src/public/lib/debug_tracks/debug_tracks.ts
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {DebugSliceTrack} from './slice_track';
-import {
- createPerfettoTable,
- matchesSqlValue,
- sqlValueToReadableString,
-} from '../../../trace_processor/sql_utils';
-import {DebugCounterTrack} from './counter_track';
-import {ARG_PREFIX} from './details_tab';
-import {TrackNode} from '../../workspace';
-import {Trace} from '../../trace';
-
-let trackCounter = 0; // For reproducible ids.
-
-// Names of the columns of the underlying view to be used as
-// ts / dur / name / pivot.
-export interface SliceColumns {
- ts: string;
- dur: string;
- name: string;
-}
-
-let debugTrackCount = 0;
-
-export interface SqlDataSource {
- // SQL source selecting the necessary data.
- sqlSource: string;
-
- // Optional: Rename columns from the query result.
- // If omitted, original column names from the query are used instead.
- // The caller is responsible for ensuring that the number of items in this
- // list matches the number of columns returned by sqlSource.
- columns?: string[];
-}
-
-// Creates actions to add a debug track. The actions must be dispatched to
-// have an effect. Use this variant if you want to create many tracks at
-// once or want to tweak the actions once produced. Otherwise, use
-// addDebugSliceTrack().
-function addDebugTrack(trace: Trace, trackName: string, uri: string): void {
- const debugTrackId = ++debugTrackCount;
- const title = trackName.trim() || `Debug Track ${debugTrackId}`;
- const track = new TrackNode({uri, title});
- trace.workspace.addChildFirst(track);
- track.pin();
-}
-
-export async function addPivotedTracks(
- trace: Trace,
- data: SqlDataSource,
- trackName: string,
- pivotColumn: string,
- createTrack: (
- trace: Trace,
- data: SqlDataSource,
- trackName: string,
- ) => Promise<void>,
-) {
- const iter = (
- await trace.engine.query(`
- with all_vals as (${data.sqlSource})
- select DISTINCT ${pivotColumn} from all_vals
- order by ${pivotColumn}
- `)
- ).iter({});
-
- for (; iter.valid(); iter.next()) {
- await createTrack(
- trace,
- {
- sqlSource: `select * from
- (${data.sqlSource})
- where ${pivotColumn} ${matchesSqlValue(iter.get(pivotColumn))}`,
- },
- `${trackName.trim() || 'Pivot Track'}: ${sqlValueToReadableString(iter.get(pivotColumn))}`,
- );
- }
-}
-
-// Adds a debug track immediately. Use createDebugSliceTrackActions() if you
-// want to create many tracks at once.
-export async function addDebugSliceTrack(
- trace: Trace,
- data: SqlDataSource,
- trackName: string,
- sliceColumns: SliceColumns,
- argColumns: string[],
-): Promise<void> {
- const cnt = trackCounter++;
- // Create a new table from the debug track definition. This will be used as
- // the backing data source for our track and its details panel.
- const tableName = `__debug_slice_${cnt}`;
-
- await createPerfettoTable(
- trace.engine,
- tableName,
- createDebugSliceTrackTableExpr(data, sliceColumns, argColumns),
- );
-
- const uri = `debug.slice.${cnt}`;
- trace.tracks.registerTrack({
- uri,
- title: trackName,
- track: new DebugSliceTrack(trace, {trackUri: uri}, tableName),
- });
-
- // Create the actions to add this track to the tracklist
- addDebugTrack(trace, trackName, uri);
-}
-
-function createDebugSliceTrackTableExpr(
- data: SqlDataSource,
- sliceColumns: SliceColumns,
- argColumns: string[],
-): string {
- const dataColumns =
- data.columns !== undefined ? `(${data.columns.join(', ')})` : '';
- const dur = sliceColumns.dur === '0' ? 0 : sliceColumns.dur;
- return `
- with data${dataColumns} as (
- ${data.sqlSource}
- ),
- prepared_data as (
- select
- ${sliceColumns.ts} as ts,
- ifnull(cast(${dur} as int), -1) as dur,
- printf('%s', ${sliceColumns.name}) as name
- ${argColumns.length > 0 ? ',' : ''}
- ${argColumns.map((c) => `${c} as ${ARG_PREFIX}${c}`).join(',\n')}
- from data
- )
- select
- row_number() over (order by ts) as id,
- *
- from prepared_data
- order by ts
- `;
-}
-
-// Names of the columns of the underlying view to be used as ts / dur / name.
-export interface CounterColumns {
- ts: string;
- value: string;
-}
-
-export interface CounterDebugTrackConfig {
- data: SqlDataSource;
- columns: CounterColumns;
-}
-
-export interface CounterDebugTrackCreateConfig {
- pinned?: boolean; // default true
- closeable?: boolean; // default true
-}
-
-// Adds a debug track immediately. Use createDebugCounterTrackActions() if you
-// want to create many tracks at once.
-export async function addDebugCounterTrack(
- trace: Trace,
- data: SqlDataSource,
- trackName: string,
- columns: CounterColumns,
-): Promise<void> {
- const cnt = trackCounter++;
- // Create a new table from the debug track definition. This will be used as
- // the backing data source for our track and its details panel.
- const tableName = `__debug_counter_${cnt}`;
-
- await createPerfettoTable(
- trace.engine,
- tableName,
- createDebugCounterTrackTableExpr(data, columns),
- );
-
- const uri = `debug.counter.${cnt}`;
- trace.tracks.registerTrack({
- uri,
- title: trackName,
- track: new DebugCounterTrack(trace, {trackUri: uri}, tableName),
- });
-
- // Create the actions to add this track to the tracklist
- addDebugTrack(trace, trackName, uri);
-}
-
-function createDebugCounterTrackTableExpr(
- data: SqlDataSource,
- columns: CounterColumns,
-): string {
- return `
- with data as (
- ${data.sqlSource}
- )
- select
- ${columns.ts} as ts,
- ${columns.value} as value
- from data
- order by ts
- `;
-}
diff --git a/ui/src/public/lib/debug_tracks/slice_track.ts b/ui/src/public/lib/debug_tracks/slice_track.ts
deleted file mode 100644
index 692f9b5..0000000
--- a/ui/src/public/lib/debug_tracks/slice_track.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (C) 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import m from 'mithril';
-import {
- CustomSqlTableDefConfig,
- CustomSqlTableSliceTrack,
-} from '../../../frontend/tracks/custom_sql_table_slice_track';
-import {TrackContext} from '../../track';
-import {Button} from '../../../widgets/button';
-import {Icons} from '../../../base/semantic_icons';
-import {Trace} from '../../trace';
-import {TrackEventSelection} from '../../selection';
-import {DebugSliceDetailsPanel} from './details_tab';
-
-export class DebugSliceTrack extends CustomSqlTableSliceTrack {
- private readonly sqlTableName: string;
-
- constructor(trace: Trace, ctx: TrackContext, tableName: string) {
- super({
- trace,
- uri: ctx.trackUri,
- });
- this.sqlTableName = tableName;
- }
-
- async getSqlDataSource(): Promise<CustomSqlTableDefConfig> {
- return {
- sqlTableName: this.sqlTableName,
- };
- }
-
- getTrackShellButtons(): m.Children {
- return m(Button, {
- onclick: () => {
- this.trace.workspace.findTrackByUri(this.uri)?.remove();
- },
- icon: Icons.Close,
- title: 'Close',
- compact: true,
- });
- }
-
- detailsPanel(sel: TrackEventSelection) {
- return new DebugSliceDetailsPanel(this.trace, this.tableName, sel.eventId);
- }
-}
diff --git a/ui/src/public/lib/extensions.ts b/ui/src/public/lib/extensions.ts
index 59c75b7..00b9555 100644
--- a/ui/src/public/lib/extensions.ts
+++ b/ui/src/public/lib/extensions.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {type addDebugSliceTrack} from '../debug_tracks';
-import {type addDebugCounterTrack} from './debug_tracks/debug_tracks';
+import {type addDebugCounterTrack} from './tracks/debug_tracks';
import {type addSqlTableTab} from '../../frontend/sql_table_tab';
import {type addVisualizedArgTracks} from '../../frontend/visualized_args_tracks';
import {type addQueryResultsTab} from './query_table/query_result_tab';
diff --git a/ui/src/public/lib/query_table/query_result_tab.ts b/ui/src/public/lib/query_table/query_result_tab.ts
index 30b053d..bf30cd0 100644
--- a/ui/src/public/lib/query_table/query_result_tab.ts
+++ b/ui/src/public/lib/query_table/query_result_tab.ts
@@ -17,7 +17,7 @@
import {assertExists} from '../../../base/logging';
import {QueryResponse, runQuery} from './queries';
import {QueryError} from '../../../trace_processor/query_result';
-import {AddDebugTrackMenu} from '../debug_tracks/add_debug_track_menu';
+import {AddDebugTrackMenu} from '../tracks/add_debug_track_menu';
import {Button} from '../../../widgets/button';
import {PopupMenu2} from '../../../widgets/menu';
import {PopupPosition} from '../../../widgets/popup';
diff --git a/ui/src/public/lib/stdlib_docs.ts b/ui/src/public/lib/stdlib_docs.ts
deleted file mode 100644
index 79980c7..0000000
--- a/ui/src/public/lib/stdlib_docs.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {App} from '../app';
-
-// Fetch the stdlib docs
-export async function getStdlibDocs(app: App): Promise<string> {
- const resp = await fetch(`${app.rootUrl}/stdlib_docs.json`);
- const json = await resp.json();
- return JSON.parse(json);
-}
diff --git a/ui/src/public/lib/debug_tracks/add_debug_track_menu.ts b/ui/src/public/lib/tracks/add_debug_track_menu.ts
similarity index 84%
rename from ui/src/public/lib/debug_tracks/add_debug_track_menu.ts
rename to ui/src/public/lib/tracks/add_debug_track_menu.ts
index 440591b..19eef19 100644
--- a/ui/src/public/lib/debug_tracks/add_debug_track_menu.ts
+++ b/ui/src/public/lib/tracks/add_debug_track_menu.ts
@@ -18,14 +18,13 @@
import {Select} from '../../../widgets/select';
import {TextInput} from '../../../widgets/text_input';
import {
- CounterColumns,
- SliceColumns,
- SqlDataSource,
addDebugCounterTrack,
addDebugSliceTrack,
addPivotedTracks,
} from './debug_tracks';
import {Trace} from '../../trace';
+import {SliceColumnMapping, SqlDataSource} from './query_slice_track';
+import {CounterColumnMapping} from './query_counter_track';
interface AddDebugTrackMenuAttrs {
dataSource: Required<SqlDataSource>;
@@ -177,7 +176,7 @@
onSubmit: () => {
switch (this.trackType) {
case 'slice':
- const sliceColumns: SliceColumns = {
+ const sliceColumns: SliceColumnMapping = {
ts: this.renderParams.ts,
dur: this.renderParams.dur,
name: this.renderParams.name,
@@ -189,26 +188,26 @@
this.name,
this.renderParams.pivot,
async (ctx, data, trackName) =>
- addDebugSliceTrack(
- ctx,
+ addDebugSliceTrack({
+ trace: ctx,
data,
- trackName,
- sliceColumns,
- this.columns,
- ),
+ title: trackName,
+ columns: sliceColumns,
+ argColumns: this.columns,
+ }),
);
} else {
- addDebugSliceTrack(
- vnode.attrs.trace,
- vnode.attrs.dataSource,
- this.name,
- sliceColumns,
- this.columns,
- );
+ addDebugSliceTrack({
+ trace: vnode.attrs.trace,
+ data: vnode.attrs.dataSource,
+ title: this.name,
+ columns: sliceColumns,
+ argColumns: this.columns,
+ });
}
break;
case 'counter':
- const counterColumns: CounterColumns = {
+ const counterColumns: CounterColumnMapping = {
ts: this.renderParams.ts,
value: this.renderParams.value,
};
@@ -220,15 +219,20 @@
this.name,
this.renderParams.pivot,
async (ctx, data, trackName) =>
- addDebugCounterTrack(ctx, data, trackName, counterColumns),
+ addDebugCounterTrack({
+ trace: ctx,
+ data,
+ title: trackName,
+ columns: counterColumns,
+ }),
);
} else {
- addDebugCounterTrack(
- vnode.attrs.trace,
- vnode.attrs.dataSource,
- this.name,
- counterColumns,
- );
+ addDebugCounterTrack({
+ trace: vnode.attrs.trace,
+ data: vnode.attrs.dataSource,
+ title: this.name,
+ columns: counterColumns,
+ });
}
break;
}
diff --git a/ui/src/public/lib/tracks/debug_tracks.ts b/ui/src/public/lib/tracks/debug_tracks.ts
new file mode 100644
index 0000000..e1caad0
--- /dev/null
+++ b/ui/src/public/lib/tracks/debug_tracks.ts
@@ -0,0 +1,133 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {
+ matchesSqlValue,
+ sqlValueToReadableString,
+} from '../../../trace_processor/sql_utils';
+import {TrackNode} from '../../workspace';
+import {Trace} from '../../trace';
+import {
+ createQuerySliceTrack,
+ SliceColumnMapping,
+ SqlDataSource,
+} from './query_slice_track';
+import {
+ CounterColumnMapping,
+ createQueryCounterTrack,
+} from './query_counter_track';
+
+let trackCounter = 0; // For reproducible ids.
+
+export async function addPivotedTracks(
+ trace: Trace,
+ data: SqlDataSource,
+ trackName: string,
+ pivotColumn: string,
+ createTrack: (
+ trace: Trace,
+ data: SqlDataSource,
+ trackName: string,
+ ) => Promise<void>,
+) {
+ const iter = (
+ await trace.engine.query(`
+ with all_vals as (${data.sqlSource})
+ select DISTINCT ${pivotColumn} from all_vals
+ order by ${pivotColumn}
+ `)
+ ).iter({});
+
+ for (; iter.valid(); iter.next()) {
+ await createTrack(
+ trace,
+ {
+ sqlSource: `select * from
+ (${data.sqlSource})
+ where ${pivotColumn} ${matchesSqlValue(iter.get(pivotColumn))}`,
+ },
+ `${trackName.trim() || 'Pivot Track'}: ${sqlValueToReadableString(iter.get(pivotColumn))}`,
+ );
+ }
+}
+
+export interface DebugSliceTrackArgs {
+ readonly trace: Trace;
+ readonly data: SqlDataSource;
+ readonly title?: string;
+ readonly columns?: Partial<SliceColumnMapping>;
+ readonly argColumns?: string[];
+}
+
+/**
+ * Adds a new debug slice track to the workspace.
+ *
+ * See {@link createQuerySliceTrack} for details about the configuration args.
+ *
+ * A debug slice track is a track based on a query which is:
+ * - Based on a query.
+ * - Uses automatic slice layout.
+ * - Automatically added to the top of the current workspace.
+ * - Pinned.
+ * - Has a close button.
+ */
+export async function addDebugSliceTrack(args: DebugSliceTrackArgs) {
+ const trace = args.trace;
+ const cnt = trackCounter++;
+ const uri = `debugSliceTrack/${cnt}`;
+ const title = args.title?.trim() || `Debug Slice Track ${cnt}`;
+
+ // Create & register the track renderer
+ const track = await createQuerySliceTrack({...args, uri});
+ trace.tracks.registerTrack({uri, title, track});
+
+ // Create the track node and pin it
+ const trackNode = new TrackNode({uri, title, removable: true});
+ trace.workspace.addChildFirst(trackNode);
+ trackNode.pin();
+}
+
+export interface DebugCounterTrackArgs {
+ readonly trace: Trace;
+ readonly data: SqlDataSource;
+ readonly title?: string;
+ readonly columns?: Partial<CounterColumnMapping>;
+}
+
+/**
+ * Adds a new debug counter track to the workspace.
+ *
+ * See {@link createQueryCounterTrack} for details about the configuration args.
+ *
+ * A debug counter track is a track based on a query which is:
+ * - Based on a query.
+ * - Automatically added to the top of the current workspace.
+ * - Pinned.
+ * - Has a close button.
+ */
+export async function addDebugCounterTrack(args: DebugCounterTrackArgs) {
+ const trace = args.trace;
+ const cnt = trackCounter++;
+ const uri = `debugCounterTrack/${cnt}`;
+ const title = args.title?.trim() || `Debug Counter Track ${cnt}`;
+
+ // Create & register the track renderer
+ const track = await createQueryCounterTrack({...args, uri});
+ trace.tracks.registerTrack({uri, title, track});
+
+ // Create the track node and pin it
+ const trackNode = new TrackNode({uri, title, removable: true});
+ trace.workspace.addChildFirst(trackNode);
+ trackNode.pin();
+}
diff --git a/ui/src/public/lib/tracks/query_counter_track.ts b/ui/src/public/lib/tracks/query_counter_track.ts
new file mode 100644
index 0000000..e412f7c
--- /dev/null
+++ b/ui/src/public/lib/tracks/query_counter_track.ts
@@ -0,0 +1,118 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {createPerfettoTable} from '../../../trace_processor/sql_utils';
+import {Trace} from '../../trace';
+import {sqlNameSafe} from '../../../base/string_utils';
+import {
+ BaseCounterTrack,
+ CounterOptions,
+} from '../../../frontend/base_counter_track';
+import {Engine} from '../../../trace_processor/engine';
+
+export interface QueryCounterTrackArgs {
+ // The trace object used to run queries.
+ readonly trace: Trace;
+
+ // A unique, reproducible ID for this track.
+ readonly uri: string;
+
+ // The query and optional column remapping.
+ readonly data: SqlDataSource;
+
+ // Optional: Which columns should be used for ts, and value. If omitted,
+ // the defaults 'ts', and 'value' will be used.
+ readonly columns?: Partial<CounterColumnMapping>;
+
+ // Optional: Display options for the counter track.
+ readonly options?: Partial<CounterOptions>;
+}
+
+export interface SqlDataSource {
+ // SQL source selecting the necessary data.
+ readonly sqlSource: string;
+
+ // Optional: Rename columns from the query result.
+ // If omitted, original column names from the query are used instead.
+ // The caller is responsible for ensuring that the number of items in this
+ // list matches the number of columns returned by sqlSource.
+ readonly columns?: string[];
+}
+
+export interface CounterColumnMapping {
+ readonly ts: string;
+ readonly value: string;
+}
+
+/**
+ * Creates a counter track based on a query.
+ *
+ * The query must provide the following columns:
+ * - ts: INTEGER - The timestamp of each sample.
+ * - value: REAL | INTEGER - The value of each sample.
+ *
+ * The column names don't have to be 'ts' and 'value', and can be remapped if
+ * convenient using the config.columns parameter.
+ */
+export async function createQueryCounterTrack(args: QueryCounterTrackArgs) {
+ const tableName = `__query_counter_track_${sqlNameSafe(args.uri)}`;
+ await createPerfettoTableForTrack(
+ args.trace.engine,
+ tableName,
+ args.data,
+ args.columns,
+ );
+ return new SqlTableCounterTrack(
+ args.trace,
+ args.uri,
+ tableName,
+ args.options,
+ );
+}
+
+async function createPerfettoTableForTrack(
+ engine: Engine,
+ tableName: string,
+ data: SqlDataSource,
+ columnMapping: Partial<CounterColumnMapping> = {},
+) {
+ const {ts = 'ts', value = 'value'} = columnMapping;
+ const query = `
+ with data as (
+ ${data.sqlSource}
+ )
+ select
+ ${ts} as ts,
+ ${value} as value
+ from data
+ order by ts
+ `;
+
+ return await createPerfettoTable(engine, tableName, query);
+}
+
+class SqlTableCounterTrack extends BaseCounterTrack {
+ constructor(
+ trace: Trace,
+ uri: string,
+ private readonly sqlTableName: string,
+ options?: Partial<CounterOptions>,
+ ) {
+ super(trace, uri, options);
+ }
+
+ getSqlSource(): string {
+ return `select * from ${this.sqlTableName}`;
+ }
+}
diff --git a/ui/src/public/lib/tracks/query_slice_track.ts b/ui/src/public/lib/tracks/query_slice_track.ts
new file mode 100644
index 0000000..c8065fb
--- /dev/null
+++ b/ui/src/public/lib/tracks/query_slice_track.ts
@@ -0,0 +1,156 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {
+ CustomSqlTableDefConfig,
+ CustomSqlTableSliceTrack,
+} from '../../../frontend/tracks/custom_sql_table_slice_track';
+import {
+ ARG_PREFIX,
+ SqlTableSliceTrackDetailsPanel,
+} from './sql_table_slice_track_details_tab';
+import {createPerfettoTable} from '../../../trace_processor/sql_utils';
+import {Trace} from '../../trace';
+import {TrackEventSelection} from '../../selection';
+import {sqlNameSafe} from '../../../base/string_utils';
+import {Engine} from '../../../trace_processor/engine';
+
+export interface QuerySliceTrackArgs {
+ // The trace object used to run queries.
+ readonly trace: Trace;
+
+ // A unique, reproducible ID for this track.
+ readonly uri: string;
+
+ // The query and optional column remapping.
+ readonly data: SqlDataSource;
+
+ // Optional: Which columns should be used for ts, dur, and name. If omitted,
+ // the defaults 'ts', 'dur', and 'name' will be used.
+ readonly columns?: Partial<SliceColumnMapping>;
+
+ // Optional: A list of column names which are displayed in the details panel
+ // when a slice is selected.
+ readonly argColumns?: string[];
+}
+
+export interface SqlDataSource {
+ // SQL source selecting the necessary data.
+ readonly sqlSource: string;
+
+ // Optional: Rename columns from the query result.
+ // If omitted, original column names from the query are used instead.
+ // The caller is responsible for ensuring that the number of items in this
+ // list matches the number of columns returned by sqlSource.
+ readonly columns?: string[];
+}
+
+export interface SliceColumnMapping {
+ readonly ts: string;
+ readonly dur: string;
+ readonly name: string;
+}
+
+/**
+ * Creates a slice track based on a query with automatic slice layout.
+ *
+ * The query must provide the following columns:
+ * - ts: INTEGER - The timestamp of the start of each slice.
+ * - dur: INTEGER - The length of each slice.
+ * - name: TEXT - A name to show on each slice, which is also used to derive the
+ * color.
+ *
+ * The column names don't have to be 'ts', 'dur', and 'name' and can be remapped
+ * if convenient using the config.columns parameter.
+ *
+ * An optional set of columns can be provided which will be displayed in the
+ * details panel when a slice is selected.
+ *
+ * The layout (vertical depth) of each slice will be determined automatically to
+ * avoid overlapping slices.
+ */
+export async function createQuerySliceTrack(args: QuerySliceTrackArgs) {
+ const tableName = `__query_slice_track_${sqlNameSafe(args.uri)}`;
+ await createPerfettoTableForTrack(
+ args.trace.engine,
+ tableName,
+ args.data,
+ args.columns,
+ args.argColumns,
+ );
+ return new SqlTableSliceTrack(args.trace, args.uri, tableName);
+}
+
+async function createPerfettoTableForTrack(
+ engine: Engine,
+ tableName: string,
+ data: SqlDataSource,
+ columns: Partial<SliceColumnMapping> = {},
+ argColumns: string[] = [],
+) {
+ const {ts = 'ts', dur = 'dur', name = 'name'} = columns;
+
+ // If the view has clashing names (e.g. "name" coming from joining two
+ // different tables, we will see names like "name_1", "name_2", but they
+ // won't be addressable from the SQL. So we explicitly name them through a
+ // list of columns passed to CTE.
+ const dataColumns =
+ data.columns !== undefined ? `(${data.columns.join(', ')})` : '';
+
+ const query = `
+ with data${dataColumns} as (
+ ${data.sqlSource}
+ ),
+ prepared_data as (
+ select
+ ${ts} as ts,
+ ifnull(cast(${dur} as int), -1) as dur,
+ printf('%s', ${name}) as name
+ ${argColumns.length > 0 ? ',' : ''}
+ ${argColumns.map((c) => `${c} as ${ARG_PREFIX}${c}`).join(',\n')}
+ from data
+ )
+ select
+ row_number() over (order by ts) as id,
+ *
+ from prepared_data
+ order by ts
+ `;
+
+ return await createPerfettoTable(engine, tableName, query);
+}
+
+class SqlTableSliceTrack extends CustomSqlTableSliceTrack {
+ constructor(
+ trace: Trace,
+ uri: string,
+ private readonly sqlTableName: string,
+ ) {
+ super(trace, uri);
+ }
+
+ override async getSqlDataSource(): Promise<CustomSqlTableDefConfig> {
+ return {
+ sqlTableName: this.sqlTableName,
+ };
+ }
+
+ override detailsPanel({eventId}: TrackEventSelection) {
+ return new SqlTableSliceTrackDetailsPanel(
+ this.trace,
+ this.sqlTableName,
+ eventId,
+ );
+ }
+}
diff --git a/ui/src/public/lib/tracks/slice_layout.ts b/ui/src/public/lib/tracks/slice_layout.ts
new file mode 100644
index 0000000..d328d1c
--- /dev/null
+++ b/ui/src/public/lib/tracks/slice_layout.ts
@@ -0,0 +1,81 @@
+// 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export interface SliceLayoutBase {
+ readonly padding: number; // vertical pixel padding between slices and track.
+ readonly rowSpacing: number; // Spacing between rows.
+
+ // A *guess* at the depth
+ readonly depthGuess?: number;
+
+ // True iff the track is flat (all slices have the same depth
+ // we have an optimisation for this).
+ readonly isFlat?: boolean;
+
+ readonly titleSizePx?: number;
+ readonly subtitleSizePx?: number;
+}
+
+export const SLICE_LAYOUT_BASE_DEFAULTS: SliceLayoutBase = Object.freeze({
+ padding: 3,
+ rowSpacing: 0,
+});
+
+export interface SliceLayoutFixed extends SliceLayoutBase {
+ readonly heightMode: 'FIXED';
+ readonly fixedHeight: number; // Outer height of the track.
+}
+
+export const SLICE_LAYOUT_FIXED_DEFAULTS: SliceLayoutFixed = Object.freeze({
+ ...SLICE_LAYOUT_BASE_DEFAULTS,
+ heightMode: 'FIXED',
+ fixedHeight: 30,
+});
+
+export interface SliceLayoutFitContent extends SliceLayoutBase {
+ readonly heightMode: 'FIT_CONTENT';
+ readonly sliceHeight: number; // Only when heightMode = 'FIT_CONTENT'.
+}
+
+export const SLICE_LAYOUT_FIT_CONTENT_DEFAULTS: SliceLayoutFitContent =
+ Object.freeze({
+ ...SLICE_LAYOUT_BASE_DEFAULTS,
+ heightMode: 'FIT_CONTENT',
+ sliceHeight: 18,
+ });
+
+export interface SliceLayoutFlat extends SliceLayoutBase {
+ readonly heightMode: 'FIXED';
+ readonly fixedHeight: number; // Outer height of the track.
+ readonly depthGuess: 0;
+ readonly isFlat: true;
+}
+
+export const SLICE_LAYOUT_FLAT_DEFAULTS: SliceLayoutFlat = Object.freeze({
+ ...SLICE_LAYOUT_BASE_DEFAULTS,
+ depthGuess: 0,
+ isFlat: true,
+ heightMode: 'FIXED',
+ fixedHeight: 18,
+ titleSizePx: 10,
+ padding: 3,
+});
+
+export type SliceLayout =
+ | SliceLayoutFixed
+ | SliceLayoutFitContent
+ | SliceLayoutFlat;
+
+export const DEFAULT_SLICE_LAYOUT: SliceLayout =
+ SLICE_LAYOUT_FIT_CONTENT_DEFAULTS;
diff --git a/ui/src/public/lib/debug_tracks/details_tab.ts b/ui/src/public/lib/tracks/sql_table_slice_track_details_tab.ts
similarity index 97%
rename from ui/src/public/lib/debug_tracks/details_tab.ts
rename to ui/src/public/lib/tracks/sql_table_slice_track_details_tab.ts
index c0c9130..621d9d3 100644
--- a/ui/src/public/lib/debug_tracks/details_tab.ts
+++ b/ui/src/public/lib/tracks/sql_table_slice_track_details_tab.ts
@@ -78,7 +78,7 @@
return children;
}
-export class DebugSliceDetailsPanel implements TrackEventDetailsPanel {
+export class SqlTableSliceTrackDetailsPanel implements TrackEventDetailsPanel {
private data?: {
name: string;
ts: time;
@@ -243,7 +243,7 @@
'Name': this.data['name'] as string,
'Start time': m(Timestamp, {ts: timeFromSql(this.data['ts'])}),
'Duration': m(DurationWidget, {dur: durationFromSql(this.data['dur'])}),
- 'Debug slice id': `${this.tableName}[${this.eventId}]`,
+ 'Slice id': `${this.tableName}[${this.eventId}]`,
});
details.push(this.renderThreadStateInfo());
details.push(this.renderSliceInfo());
@@ -256,7 +256,7 @@
return m(
DetailsShell,
{
- title: 'Debug Slice',
+ title: 'Slice',
},
m(
GridLayout,
diff --git a/ui/src/public/page.ts b/ui/src/public/page.ts
new file mode 100644
index 0000000..08efbc0
--- /dev/null
+++ b/ui/src/public/page.ts
@@ -0,0 +1,71 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {Trace} from './trace';
+
+/**
+ * Allows to register custom page endpoints that response to given routes, e.g.
+ * /viewer, /record etc.
+ */
+export interface PageManager {
+ /**
+ * Example usage:
+ * registerPage({route: '/foo', page: FooPage})
+ * class FooPage implements m.ClassComponent<PageWithTrace> {
+ * view({attrs}: m.CVnode<PageWithTrace>) {
+ * return m('div', ...
+ * onclick: () => attrs.trace.timeline.zoom(...);
+ * )
+ * }
+ * }
+ */
+ registerPage(pageHandler: PageHandler): Disposable;
+}
+
+/**
+ * Mithril attrs for pages that don't require a Trace object. These pages are
+ * always accessible, even before a trace is loaded.
+ */
+export interface PageAttrs {
+ subpage?: string;
+ trace?: Trace;
+}
+
+/**
+ * Mithril attrs for pages that require a Trace object. These pages are
+ * reachable only after a trace is loaded. Trying to access the route without a
+ * trace loaded results in the HomePage (route: '/') to be displayed instead.
+ */
+export interface PageWithTraceAttrs extends PageAttrs {
+ trace: Trace;
+}
+
+export type PageHandler<PWT = m.ComponentTypes<PageWithTraceAttrs>> = {
+ route: string; // e.g. '/' (default route), '/viewer'
+ pluginId?: string; // Not needed, the internal impl will fill it.
+} & (
+ | {
+ // If true, the route will be available even when there is no trace
+ // loaded. The component needs to deal with a possibly undefined attr.
+ traceless: true;
+ page: m.ComponentTypes<PageAttrs>;
+ }
+ | {
+ // If is omitted, the route will be available only when a trace is loaded.
+ // The component is guarranteed to get a defined Trace in its attrs.
+ traceless?: false;
+ page: PWT;
+ }
+);
diff --git a/ui/src/public/plugin.ts b/ui/src/public/plugin.ts
index d0602c3..2360acf 100644
--- a/ui/src/public/plugin.ts
+++ b/ui/src/public/plugin.ts
@@ -75,4 +75,5 @@
export interface PluginManager {
getPlugin<T extends PerfettoPlugin>(plugin: PerfettoPluginStatic<T>): T;
+ metricVisualisations(): MetricVisualisation[];
}
diff --git a/ui/src/public/sidebar.ts b/ui/src/public/sidebar.ts
index 3112610..caba71d 100644
--- a/ui/src/public/sidebar.ts
+++ b/ui/src/public/sidebar.ts
@@ -12,14 +12,34 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Indicates the potential enabled states the sidebar can be in.
-export type SidebarEnabled = 'ENABLED' | 'DISABLED';
+// For now sections are fixed and cannot be extended by plugins.
+export const SIDEBAR_SECTIONS = {
+ navigation: {
+ title: 'Navigation',
+ summary: 'Open or record a new trace',
+ },
+ current_trace: {
+ title: 'Current Trace',
+ summary: 'Actions on the current trace',
+ },
+ convert_trace: {
+ title: 'Convert trace',
+ summary: 'Convert to other formats',
+ },
+ example_traces: {
+ title: 'Example Traces',
+ summary: 'Open an example trace',
+ },
+ support: {
+ title: 'Support',
+ summary: 'Documentation & Bugs',
+ },
+} as const;
-// Indicates the potential visibility states the sidebar can be in.
-export type SidebarVisibility = 'HIDDEN' | 'VISIBLE';
+export type SidebarSections = keyof typeof SIDEBAR_SECTIONS;
export interface SidebarManager {
- readonly sidebarEnabled: SidebarEnabled;
+ readonly enabled: boolean;
/**
* Adds a new menu item to the sidebar.
@@ -31,24 +51,65 @@
/**
* Gets the current visibility of the sidebar.
*/
- get sidebarVisibility(): SidebarVisibility;
+ get visible(): boolean;
/**
* Toggles the visibility of the sidebar. Can only be called when
* `sidebarEnabled` returns `ENABLED`.
*/
- toggleSidebarVisbility(): void;
+ toggleVisibility(): void;
}
-export interface SidebarMenuItem {
- readonly commandId: string;
- readonly group:
- | 'navigation'
- | 'current_trace'
- | 'convert_trace'
- | 'example_traces'
- | 'support';
- when?(): boolean;
- readonly icon: string;
- readonly priority?: number;
-}
+export type SidebarMenuItem = {
+ readonly section: SidebarSections;
+ readonly sortOrder?: number;
+
+ // The properties below can be mutated by passing a callback rather than a
+ // direct value. The callback is invoked on every render frame, keep it cheap.
+ // readonly text: string | (() => string);
+ readonly icon?: string | (() => string);
+ readonly tooltip?: string | (() => string);
+ readonly cssClass?: string | (() => string); // Without trailing '.'.
+
+ // If false or omitted the item works normally.
+ // If true the item is striken through and the action/href will be a no-op.
+ // If a string, the item acts as disabled and clicking on it shows a popup
+ // that shows the returned text (the string has "disabled reason" semantic);
+ readonly disabled?: string | boolean | (() => string | boolean);
+
+ // One of the three following arguments must be specified.
+} & (
+ | {
+ /** The text of the menu entry. Required. */
+ readonly text: string | (() => string);
+
+ /**
+ * The URL to navigate to. It can be either:
+ * - A local route (e.g. ''#!/query').
+ * - An absolute URL (e.g. 'https://example.com'). In this case the link will
+ * be open in a target=_blank new tag.
+ */
+ readonly href: string;
+ }
+ | {
+ /** The text of the menu entry. Required. */
+ readonly text: string | (() => string);
+
+ /**
+ * The function that will be invoked when clicking. If the function returns
+ * a promise, a spinner will be drawn next to the sidebar entry until the
+ * promise resolves.
+ */
+ readonly action: () => unknown | Promise<unknown>;
+
+ /** Optional. If omitted href = '#'. */
+ readonly href?: string;
+ }
+ | {
+ /** Optional. If omitted uses the command name. */
+ readonly text?: string | (() => string);
+
+ /** The ID of the command that will be invoked when clicking */
+ readonly commandId: string;
+ }
+);
diff --git a/ui/src/public/trace.ts b/ui/src/public/trace.ts
index e95de2e..95db546 100644
--- a/ui/src/public/trace.ts
+++ b/ui/src/public/trace.ts
@@ -63,6 +63,11 @@
// the trace. It will throw if traceInfo.downloadable === false.
getTraceFile(): Promise<Blob>;
+ // List of errors that were encountered while loading the trace by the TS
+ // code. These are on top of traceInfo.importErrors, which is a summary of
+ // what TraceProcessor reports on the stats table at import time.
+ get loadingErrors(): ReadonlyArray<string>;
+
// When the trace is opened via postMessage deep-linking, returns the sub-set
// of postMessageData.pluginArgs[pluginId] for the current plugin. If not
// present returns undefined.
@@ -91,3 +96,5 @@
export interface TraceAttrs {
trace: Trace;
}
+
+export const TRACE_SUFFIX = '.perfetto-trace';
diff --git a/ui/src/public/track.ts b/ui/src/public/track.ts
index 94ac9e7..6d1b1dc 100644
--- a/ui/src/public/track.ts
+++ b/ui/src/public/track.ts
@@ -20,6 +20,7 @@
import {ColorScheme} from './color_scheme';
import {TrackEventDetailsPanel} from './details_panel';
import {TrackEventDetails, TrackEventSelection} from './selection';
+import {Dataset} from '../trace_processor/dataset';
export interface TrackManager {
/**
@@ -175,6 +176,12 @@
onMouseOut?(): void;
/**
+ * Optional: Returns a dataset that represents the events displayed on this
+ * track.
+ */
+ getDataset?(): Dataset | undefined;
+
+ /**
* Optional: Get details of a track event given by eventId on this track.
*/
getSelectionDetails?(eventId: number): Promise<TrackEventDetails | undefined>;
diff --git a/ui/src/public/workspace.ts b/ui/src/public/workspace.ts
index fb3410f..a2bab1f 100644
--- a/ui/src/public/workspace.ts
+++ b/ui/src/public/workspace.ts
@@ -233,6 +233,7 @@
sortOrder: number;
collapsed: boolean;
isSummary: boolean;
+ removable: boolean;
}
/**
@@ -273,6 +274,11 @@
// vertical space.
public isSummary: boolean;
+ // If true, this node will be removable by the user. It will show a little
+ // close button in the track shell which the user can press to remove the
+ // track from the workspace.
+ public removable: boolean;
+
protected _collapsed = true;
constructor(args?: Partial<TrackNodeArgs>) {
@@ -286,6 +292,7 @@
sortOrder,
collapsed = true,
isSummary = false,
+ removable = false,
} = args ?? {};
this.id = id;
@@ -295,6 +302,7 @@
this.sortOrder = sortOrder;
this.isSummary = isSummary;
this._collapsed = collapsed;
+ this.removable = removable;
}
/**
@@ -489,7 +497,11 @@
*/
pinTrack(track: TrackNode): void {
// Make a lightweight clone of this track - just the uri and the title.
- const cloned = new TrackNode({uri: track.uri, title: track.title});
+ const cloned = new TrackNode({
+ uri: track.uri,
+ title: track.title,
+ removable: track.removable,
+ });
this.pinnedRoot.addChildLast(cloned);
}
diff --git a/ui/src/test/ftrace_tracks_and_tab.test.ts b/ui/src/test/ftrace_tracks_and_tab.test.ts
index e84f45b..7bed34b 100644
--- a/ui/src/test/ftrace_tracks_and_tab.test.ts
+++ b/ui/src/test/ftrace_tracks_and_tab.test.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {test, expect, Page} from '@playwright/test';
+import {test, Page} from '@playwright/test';
import {PerfettoTestHelper} from './perfetto_ui_test_helper';
test.describe.configure({mode: 'serial'});
@@ -28,14 +28,12 @@
test('ftrace tracks', async () => {
await page.click('h1[ref="Ftrace Events"]');
- await pth.waitForPerfettoIdle();
- await expect(page).toHaveScreenshot('ftrace_events.png');
+ await pth.waitForIdleAndScreenshot('ftrace_events.png');
});
test('ftrace tab', async () => {
await page.mouse.move(0, 0);
await page.click('button[title="More Tabs"]');
await page.getByRole('button', {name: 'Ftrace Events'}).click();
- await pth.waitForPerfettoIdle();
- await expect(page).toHaveScreenshot('ftrace_tab.png');
+ await pth.waitForIdleAndScreenshot('ftrace_tab.png');
});
diff --git a/ui/src/test/load_and_tracks.test.ts b/ui/src/test/load_and_tracks.test.ts
index 6078c67..f02611c 100644
--- a/ui/src/test/load_and_tracks.test.ts
+++ b/ui/src/test/load_and_tracks.test.ts
@@ -31,9 +31,9 @@
});
test('info and stats', async () => {
- await page.locator('.sidebar #info_and_stats').click();
+ await pth.navigate('#!/info');
await pth.waitForIdleAndScreenshot('into_and_stats.png');
- await page.locator('.sidebar #show_timeline').click();
+ await pth.navigate('#!/viewer');
await pth.waitForIdleAndScreenshot('back_to_timeline.png');
});
diff --git a/ui/src/test/local_cache_key.test.ts b/ui/src/test/local_cache_key.test.ts
index 2acd534..3816ea1 100644
--- a/ui/src/test/local_cache_key.test.ts
+++ b/ui/src/test/local_cache_key.test.ts
@@ -24,7 +24,7 @@
'#!/?url=http://127.0.0.1:10000/test/data/perf_sample_annotations.pftrace',
);
const cacheKey1 = page.url().match(/local_cache_key=([a-z0-9-]+)/)![1];
- await expect(page).toHaveScreenshot('trace_1.png');
+ await pth.waitForIdleAndScreenshot('trace_1.png');
// Open second trace.
await pth.navigate(
@@ -32,13 +32,13 @@
);
const cacheKey2 = page.url().match(/local_cache_key=([a-z0-9-]+)/)![1];
expect(cacheKey1).not.toEqual(cacheKey2);
- await expect(page).toHaveScreenshot('trace_2.png');
+ await pth.waitForIdleAndScreenshot('trace_2.png');
// Navigate back to the first trace. A confirmation dialog will be shown
await pth.navigate('#!/viewer?local_cache_key=' + cacheKey1);
- await expect(page).toHaveScreenshot('confirmation_dialog.png');
+ await pth.waitForIdleAndScreenshot('confirmation_dialog.png');
await page.locator('button.modal-btn-primary').click();
await pth.waitForPerfettoIdle();
- await expect(page).toHaveScreenshot('back_to_trace_1.png');
+ await pth.waitForIdleAndScreenshot('back_to_trace_1.png');
});
diff --git a/ui/src/test/perfetto_ui_test_helper.ts b/ui/src/test/perfetto_ui_test_helper.ts
index 2fe5569..20e1f82 100644
--- a/ui/src/test/perfetto_ui_test_helper.ts
+++ b/ui/src/test/perfetto_ui_test_helper.ts
@@ -79,7 +79,7 @@
) {
await this.page.mouse.move(0, 0); // Move mouse out of the way.
await this.waitForPerfettoIdle();
- await expect(this.page).toHaveScreenshot(screenshotName, opts);
+ await expect.soft(this.page).toHaveScreenshot(screenshotName, opts);
}
locateTrackGroup(name: string): Locator {
diff --git a/ui/src/test/queries.test.ts b/ui/src/test/queries.test.ts
index db215a7..3882063 100644
--- a/ui/src/test/queries.test.ts
+++ b/ui/src/test/queries.test.ts
@@ -59,7 +59,7 @@
});
test('query page', async () => {
- await page.locator('.sidebar #query__sql_').click();
+ await pth.navigate('#!/query');
await pth.waitForPerfettoIdle();
const textbox = page.locator('.pf-editor div[role=textbox]');
for (let i = 1; i <= 3; i++) {
diff --git a/ui/src/trace_processor/dataset.ts b/ui/src/trace_processor/dataset.ts
new file mode 100644
index 0000000..25c64cb
--- /dev/null
+++ b/ui/src/trace_processor/dataset.ts
@@ -0,0 +1,290 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {assertUnreachable} from '../base/logging';
+import {getOrCreate} from '../base/utils';
+import {ColumnType, SqlValue} from './query_result';
+
+/**
+ * A dataset defines a set of rows in TraceProcessor and a schema of the
+ * resultant columns. Dataset implementations describe how to get the data in
+ * different ways - e.g. 'source' datasets define a dataset as a table name (or
+ * select statement) + filters, whereas a 'union' dataset defines a dataset as
+ * the union of other datasets.
+ *
+ * The idea is that users can build arbitrarily complex trees of datasets, then
+ * at any point call `optimize()` to create the smallest possible tree that
+ * represents the same dataset, and `query()` which produces a select statement
+ * for the resultant dataset.
+ *
+ * Users can also use the `schema` property and `implements()` to get and test
+ * the schema of a given dataset.
+ */
+export interface Dataset {
+ /**
+ * Get or calculate the resultant schema of this dataset.
+ */
+ readonly schema: DatasetSchema;
+
+ /**
+ * Produce a query for this dataset.
+ *
+ * @param schema - The schema to use for extracting columns - if undefined,
+ * the most specific possible schema is evaluated from the dataset first and
+ * used instead.
+ */
+ query(schema?: DatasetSchema): string;
+
+ /**
+ * Optimizes a dataset into the smallest possible expression.
+ *
+ * For example by combining elements of union data sets that have the same src
+ * and similar filters into a single set.
+ *
+ * For example, the following 'union' dataset...
+ *
+ * ```
+ * {
+ * union: [
+ * {
+ * src: 'foo',
+ * schema: {
+ * 'a': NUM,
+ * 'b': NUM,
+ * },
+ * filter: {col: 'a', eq: 1},
+ * },
+ * {
+ * src: 'foo',
+ * schema: {
+ * 'a': NUM,
+ * 'b': NUM,
+ * },
+ * filter: {col: 'a', eq: 2},
+ * },
+ * ]
+ * }
+ * ```
+ *
+ * ...will be combined into a single 'source' dataset...
+ *
+ * ```
+ * {
+ * src: 'foo',
+ * schema: {
+ * 'a': NUM,
+ * 'b': NUM,
+ * },
+ * filter: {col: 'a', in: [1, 2]},
+ * },
+ * ```
+ */
+ optimize(): Dataset;
+
+ /**
+ * Returns true if this dataset implements a given schema.
+ *
+ * @param schema - The schema to test against.
+ */
+ implements(schema: DatasetSchema): boolean;
+}
+
+/**
+ * Defines a list of columns and types that define the shape of the data
+ * represented by a dataset.
+ */
+export type DatasetSchema = Record<string, ColumnType>;
+
+/**
+ * A filter used to express that a column must equal a value.
+ */
+interface EqFilter {
+ readonly col: string;
+ readonly eq: SqlValue;
+}
+
+/**
+ * A filter used to express that column must be one of a set of values.
+ */
+interface InFilter {
+ readonly col: string;
+ readonly in: ReadonlyArray<SqlValue>;
+}
+
+/**
+ * Union of all filter types.
+ */
+type Filter = EqFilter | InFilter;
+
+/**
+ * Named arguments for a SourceDataset.
+ */
+interface SourceDatasetConfig {
+ readonly src: string;
+ readonly schema: DatasetSchema;
+ readonly filter?: Filter;
+}
+
+/**
+ * Defines a dataset with a source SQL select statement of table name, a
+ * schema describing the columns, and an optional filter.
+ */
+export class SourceDataset implements Dataset {
+ readonly src: string;
+ readonly schema: DatasetSchema;
+ readonly filter?: Filter;
+
+ constructor(config: SourceDatasetConfig) {
+ this.src = config.src;
+ this.schema = config.schema;
+ this.filter = config.filter;
+ }
+
+ query(schema?: DatasetSchema) {
+ schema = schema ?? this.schema;
+ const cols = Object.keys(schema);
+ const whereClause = this.filterToQuery();
+ return `select ${cols.join(', ')} from (${this.src}) ${whereClause}`.trim();
+ }
+
+ optimize() {
+ // Cannot optimize SourceDataset
+ return this;
+ }
+
+ implements(schema: DatasetSchema) {
+ return Object.entries(schema).every(([name, kind]) => {
+ return name in this.schema && this.schema[name] === kind;
+ });
+ }
+
+ private filterToQuery() {
+ const filter = this.filter;
+ if (filter === undefined) {
+ return '';
+ }
+ if ('eq' in filter) {
+ return `where ${filter.col} = ${filter.eq}`;
+ } else if ('in' in filter) {
+ return `where ${filter.col} in (${filter.in.join(',')})`;
+ } else {
+ assertUnreachable(filter);
+ }
+ }
+}
+
+/**
+ * A dataset that represents the union of multiple datasets.
+ */
+export class UnionDataset implements Dataset {
+ constructor(readonly union: ReadonlyArray<Dataset>) {}
+
+ get schema(): DatasetSchema {
+ // Find the minimal set of columns that are supported by all datasets of
+ // the union
+ let sch: Record<string, ColumnType> | undefined = undefined;
+ this.union.forEach((ds) => {
+ const dsSchema = ds.schema;
+ if (sch === undefined) {
+ // First time just use this one
+ sch = dsSchema;
+ } else {
+ const newSch: Record<string, ColumnType> = {};
+ for (const [key, kind] of Object.entries(sch)) {
+ if (key in dsSchema && dsSchema[key] === kind) {
+ newSch[key] = kind;
+ }
+ }
+ sch = newSch;
+ }
+ });
+ return sch ?? {};
+ }
+
+ query(schema?: DatasetSchema): string {
+ schema = schema ?? this.schema;
+ return this.union
+ .map((dataset) => dataset.query(schema))
+ .join(' union all ');
+ }
+
+ optimize(): Dataset {
+ // Recursively optimize each dataset of this union
+ const optimizedUnion = this.union.map((ds) => ds.optimize());
+
+ // Find all source datasets and combine then based on src
+ const combinedSrcSets = new Map<string, SourceDataset[]>();
+ const otherDatasets: Dataset[] = [];
+ for (const e of optimizedUnion) {
+ if (e instanceof SourceDataset) {
+ const set = getOrCreate(combinedSrcSets, e.src, () => []);
+ set.push(e);
+ } else {
+ otherDatasets.push(e);
+ }
+ }
+
+ const mergedSrcSets = Array.from(combinedSrcSets.values()).map(
+ (srcGroup) => {
+ if (srcGroup.length === 1) return srcGroup[0];
+
+ // Combine schema across all members in the union
+ const combinedSchema = srcGroup.reduce((acc, e) => {
+ Object.assign(acc, e.schema);
+ return acc;
+ }, {} as DatasetSchema);
+
+ // Merge filters for the same src
+ const inFilters: InFilter[] = [];
+ for (const {filter} of srcGroup) {
+ if (filter) {
+ if ('eq' in filter) {
+ inFilters.push({col: filter.col, in: [filter.eq]});
+ } else {
+ inFilters.push(filter);
+ }
+ }
+ }
+
+ const mergedFilter = mergeFilters(inFilters);
+ return new SourceDataset({
+ src: srcGroup[0].src,
+ schema: combinedSchema,
+ filter: mergedFilter,
+ });
+ },
+ );
+
+ const finalUnion = [...mergedSrcSets, ...otherDatasets];
+
+ if (finalUnion.length === 1) {
+ return finalUnion[0];
+ } else {
+ return new UnionDataset(finalUnion);
+ }
+ }
+
+ implements(schema: DatasetSchema) {
+ return Object.entries(schema).every(([name, kind]) => {
+ return name in this.schema && this.schema[name] === kind;
+ });
+ }
+}
+
+function mergeFilters(filters: InFilter[]): InFilter | undefined {
+ if (filters.length === 0) return undefined;
+ const col = filters[0].col;
+ const values = new Set(filters.flatMap((filter) => filter.in));
+ return {col, in: Array.from(values)};
+}
diff --git a/ui/src/trace_processor/dataset_unittest.ts b/ui/src/trace_processor/dataset_unittest.ts
new file mode 100644
index 0000000..2bd4e53
--- /dev/null
+++ b/ui/src/trace_processor/dataset_unittest.ts
@@ -0,0 +1,228 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {SourceDataset, UnionDataset} from './dataset';
+import {LONG, NUM, STR} from './query_result';
+
+test('get query for simple dataset', () => {
+ const dataset = new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM},
+ });
+
+ expect(dataset.query()).toEqual('select id from (slice)');
+});
+
+test("get query for simple dataset with 'eq' filter", () => {
+ const dataset = new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM},
+ filter: {
+ col: 'id',
+ eq: 123,
+ },
+ });
+
+ expect(dataset.query()).toEqual('select id from (slice) where id = 123');
+});
+
+test("get query for simple dataset with an 'in' filter", () => {
+ const dataset = new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM},
+ filter: {
+ col: 'id',
+ in: [123, 456],
+ },
+ });
+
+ expect(dataset.query()).toEqual(
+ 'select id from (slice) where id in (123,456)',
+ );
+});
+
+test('get query for union dataset', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM},
+ filter: {
+ col: 'id',
+ eq: 123,
+ },
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM},
+ filter: {
+ col: 'id',
+ eq: 456,
+ },
+ }),
+ ]);
+
+ expect(dataset.query()).toEqual(
+ 'select id from (slice) where id = 123 union all select id from (slice) where id = 456',
+ );
+});
+
+test('doesImplement', () => {
+ const dataset = new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM, ts: LONG},
+ });
+
+ expect(dataset.implements({id: NUM})).toBe(true);
+ expect(dataset.implements({id: NUM, ts: LONG})).toBe(true);
+ expect(dataset.implements({id: NUM, ts: LONG, name: STR})).toBe(false);
+ expect(dataset.implements({id: LONG})).toBe(false);
+});
+
+test('find the schema of a simple dataset', () => {
+ const dataset = new SourceDataset({
+ src: 'slice',
+ schema: {id: NUM, ts: LONG},
+ });
+
+ expect(dataset.schema).toMatchObject({id: NUM, ts: LONG});
+});
+
+test('find the schema of a union where source sets differ in their names', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: NUM},
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {bar: NUM},
+ }),
+ ]);
+
+ expect(dataset.schema).toMatchObject({});
+});
+
+test('find the schema of a union with differing source sets', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: NUM},
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: LONG},
+ }),
+ ]);
+
+ expect(dataset.schema).toMatchObject({});
+});
+
+test('find the schema of a union with one column in common', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: NUM, bar: NUM},
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: NUM, baz: NUM},
+ }),
+ ]);
+
+ expect(dataset.schema).toMatchObject({foo: NUM});
+});
+
+test('optimize a union dataset', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ eq: 123,
+ },
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ eq: 456,
+ },
+ }),
+ ]);
+
+ expect(dataset.optimize()).toEqual({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ in: [123, 456],
+ },
+ });
+});
+
+test('optimize a union dataset with different types of filters', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ eq: 123,
+ },
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ in: [456, 789],
+ },
+ }),
+ ]);
+
+ expect(dataset.optimize()).toEqual({
+ src: 'slice',
+ schema: {},
+ filter: {
+ col: 'track_id',
+ in: [123, 456, 789],
+ },
+ });
+});
+
+test('optimize a union dataset with different schemas', () => {
+ const dataset = new UnionDataset([
+ new SourceDataset({
+ src: 'slice',
+ schema: {foo: NUM},
+ }),
+ new SourceDataset({
+ src: 'slice',
+ schema: {bar: NUM},
+ }),
+ ]);
+
+ expect(dataset.optimize()).toEqual({
+ src: 'slice',
+ // The resultant schema is the combination of the union's member's schemas,
+ // as we know the source is the same as we know we can get all of the 'seen'
+ // columns from the source.
+ schema: {
+ foo: NUM,
+ bar: NUM,
+ },
+ });
+});
diff --git a/ui/src/trace_processor/engine.ts b/ui/src/trace_processor/engine.ts
index ccb8a03..58a3705 100644
--- a/ui/src/trace_processor/engine.ts
+++ b/ui/src/trace_processor/engine.ts
@@ -208,7 +208,7 @@
let isFinalResponse = true;
switch (rpc.response) {
- case TPM.TPM_APPEND_TRACE_DATA:
+ case TPM.TPM_APPEND_TRACE_DATA: {
const appendResult = assertExists(rpc.appendResult);
const pendingPromise = assertExists(this.pendingParses.shift());
if (exists(appendResult.error) && appendResult.error.length > 0) {
@@ -217,9 +217,17 @@
pendingPromise.resolve();
}
break;
- case TPM.TPM_FINALIZE_TRACE_DATA:
- assertExists(this.pendingEOFs.shift()).resolve();
+ }
+ case TPM.TPM_FINALIZE_TRACE_DATA: {
+ const finalizeResult = assertExists(rpc.finalizeDataResult);
+ const pendingPromise = assertExists(this.pendingEOFs.shift());
+ if (exists(finalizeResult.error) && finalizeResult.error.length > 0) {
+ pendingPromise.reject(finalizeResult.error);
+ } else {
+ pendingPromise.resolve();
+ }
break;
+ }
case TPM.TPM_RESET_TRACE_PROCESSOR:
assertExists(this.pendingResetTraceProcessors.shift()).resolve();
break;
diff --git a/ui/src/trace_processor/query_result.ts b/ui/src/trace_processor/query_result.ts
index c65f874..b12e06c 100644
--- a/ui/src/trace_processor/query_result.ts
+++ b/ui/src/trace_processor/query_result.ts
@@ -281,6 +281,9 @@
// first result.
firstRow<T extends Row>(spec: T): T;
+ // Like firstRow() but returns undefined if no rows are available.
+ maybeFirstRow<T extends Row>(spec: T): T | undefined;
+
// If != undefined the query errored out and error() contains the message.
error(): string | undefined;
@@ -406,6 +409,14 @@
return impl as {} as RowIterator<T> as T;
}
+ maybeFirstRow<T extends Row>(spec: T): T | undefined {
+ const impl = new RowIteratorImplWithRowData(spec, this);
+ if (!impl.valid()) {
+ return undefined;
+ }
+ return impl as {} as RowIterator<T> as T;
+ }
+
// Can be called only once.
waitAllRows(): Promise<QueryResult> {
assertTrue(this.allRowsPromise === undefined);
@@ -937,6 +948,9 @@
firstRow<T extends Row>(spec: T) {
return this.impl.firstRow(spec);
}
+ maybeFirstRow<T extends Row>(spec: T) {
+ return this.impl.maybeFirstRow(spec);
+ }
waitAllRows() {
return this.impl.waitAllRows();
}
diff --git a/ui/src/trace_processor/sql_utils/thread_state.ts b/ui/src/trace_processor/sql_utils/thread_state.ts
index 92834fe..cc10a5f 100644
--- a/ui/src/trace_processor/sql_utils/thread_state.ts
+++ b/ui/src/trace_processor/sql_utils/thread_state.ts
@@ -85,7 +85,7 @@
// Single thread state slice, corresponding to a row of |thread_slice| table.
export interface ThreadState {
// Id into |thread_state| table.
- threadStateSqlId: ThreadStateSqlId;
+ id: ThreadStateSqlId;
// Id of the corresponding entry in the |sched| table.
schedSqlId?: SchedSqlId;
// Timestamp of the beginning of this thread state in nanoseconds.
@@ -108,6 +108,8 @@
// unset even for runnable states, if the trace was recorded without
// interrupt information.
wakerInterruptCtx?: boolean;
+ // Kernel priority of this thread state.
+ priority?: number;
}
// Gets a list of thread state objects from Trace Processor with given
@@ -117,61 +119,63 @@
constraints: SQLConstraints,
): Promise<ThreadState[]> {
const query = await engine.query(`
- SELECT
- thread_state.id as threadStateSqlId,
- (select sched.id
- from sched
- where sched.ts=thread_state.ts and sched.utid=thread_state.utid
- limit 1
- ) as schedSqlId,
- ts,
- thread_state.dur as dur,
- thread_state.cpu as cpu,
- state,
- thread_state.blocked_function as blockedFunction,
- io_wait as ioWait,
- thread_state.utid as utid,
- waker_utid as wakerUtid,
- waker_id as wakerId,
- irq_context as wakerInterruptCtx
- FROM thread_state
+ WITH raw AS (
+ SELECT
+ ts.id,
+ sched.id AS sched_id,
+ ts.ts,
+ ts.dur,
+ ts.cpu,
+ ts.state,
+ ts.blocked_function,
+ ts.io_wait,
+ ts.utid,
+ ts.waker_utid,
+ ts.waker_id,
+ ts.irq_context,
+ sched.priority
+ FROM thread_state ts
+ LEFT JOIN sched USING (utid, ts)
+ )
+ SELECT * FROM raw
+
${constraintsToQuerySuffix(constraints)}`);
const it = query.iter({
- threadStateSqlId: NUM,
- schedSqlId: NUM_NULL,
+ id: NUM,
+ sched_id: NUM_NULL,
ts: LONG,
dur: LONG,
cpu: NUM_NULL,
state: STR_NULL,
- blockedFunction: STR_NULL,
- ioWait: NUM_NULL,
+ blocked_function: STR_NULL,
+ io_wait: NUM_NULL,
utid: NUM,
- wakerUtid: NUM_NULL,
- wakerId: NUM_NULL,
- wakerInterruptCtx: NUM_NULL,
+ waker_utid: NUM_NULL,
+ waker_id: NUM_NULL,
+ irq_context: NUM_NULL,
+ priority: NUM_NULL,
});
const result: ThreadState[] = [];
for (; it.valid(); it.next()) {
- const ioWait = it.ioWait === null ? undefined : it.ioWait > 0;
+ const ioWait = it.io_wait === null ? undefined : it.io_wait > 0;
- // TODO(altimin): Consider fetcing thread / process info using a single
+ // TODO(altimin): Consider fetching thread / process info using a single
// query instead of one per row.
result.push({
- threadStateSqlId: it.threadStateSqlId as ThreadStateSqlId,
- schedSqlId: fromNumNull(it.schedSqlId) as SchedSqlId | undefined,
+ id: it.id as ThreadStateSqlId,
+ schedSqlId: fromNumNull(it.sched_id) as SchedSqlId | undefined,
ts: Time.fromRaw(it.ts),
dur: it.dur,
cpu: fromNumNull(it.cpu),
state: translateState(it.state ?? undefined, ioWait),
- blockedFunction: it.blockedFunction ?? undefined,
+ blockedFunction: it.blocked_function ?? undefined,
thread: await getThreadInfo(engine, asUtid(it.utid)),
- wakerUtid: asUtid(it.wakerUtid ?? undefined),
- wakerId: asThreadStateSqlId(it.wakerId ?? undefined),
- wakerInterruptCtx: fromNumNull(it.wakerInterruptCtx) as
- | boolean
- | undefined,
+ wakerUtid: asUtid(it.waker_id ?? undefined),
+ wakerId: asThreadStateSqlId(it.waker_id ?? undefined),
+ wakerInterruptCtx: fromNumNull(it.irq_context) as boolean | undefined,
+ priority: fromNumNull(it.priority),
});
}
return result;
diff --git a/ui/src/trace_processor/wasm_engine_proxy.ts b/ui/src/trace_processor/wasm_engine_proxy.ts
index 2f2e6a3..123cb86 100644
--- a/ui/src/trace_processor/wasm_engine_proxy.ts
+++ b/ui/src/trace_processor/wasm_engine_proxy.ts
@@ -12,15 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {assetSrc} from '../base/assets';
import {assertExists, assertTrue} from '../base/logging';
import {EngineBase} from '../trace_processor/engine';
-let bundlePath: string;
let idleWasmWorker: Worker;
-export function initWasm(root: string) {
- bundlePath = root + 'engine_bundle.js';
- idleWasmWorker = new Worker(bundlePath);
+export function initWasm() {
+ idleWasmWorker = new Worker(assetSrc('engine_bundle.js'));
}
/**
@@ -49,7 +48,7 @@
// around. The latency is hidden by the fact that the user usually takes few
// seconds until they click on "open trace file" and pick a file.
this.worker = assertExists(idleWasmWorker);
- idleWasmWorker = new Worker(bundlePath);
+ idleWasmWorker = new Worker(assetSrc('engine_bundle.js'));
this.worker.postMessage(port1, [port1]);
this.port.onmessage = this.onMessage.bind(this);
}
diff --git a/ui/src/widgets/copyable_link.ts b/ui/src/widgets/copyable_link.ts
new file mode 100644
index 0000000..9244107
--- /dev/null
+++ b/ui/src/widgets/copyable_link.ts
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+import {copyToClipboard} from '../base/clipboard';
+import {Anchor} from './anchor';
+
+interface CopyableLinkAttrs {
+ url: string;
+ text?: string; // Will use url if omitted.
+ noicon?: boolean;
+}
+
+export class CopyableLink implements m.ClassComponent<CopyableLinkAttrs> {
+ view({attrs}: m.CVnode<CopyableLinkAttrs>) {
+ const url = attrs.url;
+ return m(
+ 'div',
+ m(
+ Anchor,
+ {
+ href: url,
+ title: 'Click to copy the URL into the clipboard',
+ target: '_blank',
+ icon: attrs.noicon ? undefined : 'content_copy',
+ onclick: (e: Event) => {
+ e.preventDefault();
+ copyToClipboard(url);
+ },
+ },
+ attrs.text ?? url,
+ ),
+ );
+ }
+}
diff --git a/ui/src/widgets/editor.ts b/ui/src/widgets/editor.ts
index 58ea153..1187be0 100644
--- a/ui/src/widgets/editor.ts
+++ b/ui/src/widgets/editor.ts
@@ -21,6 +21,7 @@
import {assertExists} from '../base/logging';
import {DragGestureHandler} from '../base/drag_gesture_handler';
import {DisposableStack} from '../base/disposable_stack';
+import {scheduleFullRedraw} from './raf';
export interface EditorAttrs {
// Initial state for the editor.
@@ -64,6 +65,7 @@
text = selectedText;
}
onExecute(text);
+ scheduleFullRedraw('force');
return true;
},
});
@@ -75,6 +77,7 @@
view.update([tr]);
const text = view.state.doc.toString();
onUpdate(text);
+ scheduleFullRedraw('force');
};
}
diff --git a/ui/src/widgets/flamegraph.ts b/ui/src/widgets/flamegraph.ts
index a7252f5..3c831dd 100644
--- a/ui/src/widgets/flamegraph.ts
+++ b/ui/src/widgets/flamegraph.ts
@@ -966,7 +966,7 @@
}
return addFilter(state, {
kind: 'SHOW_STACK',
- filter: filter.split(': ', 2)[1],
+ filter: filter,
});
}
diff --git a/ui/src/widgets/hotkey_context.ts b/ui/src/widgets/hotkey_context.ts
index f4d702a..767683e 100644
--- a/ui/src/widgets/hotkey_context.ts
+++ b/ui/src/widgets/hotkey_context.ts
@@ -14,6 +14,7 @@
import m from 'mithril';
import {checkHotkey, Hotkey} from '../base/hotkeys';
+import {scheduleFullRedraw} from './raf';
export interface HotkeyConfig {
hotkey: Hotkey;
@@ -58,6 +59,7 @@
if (checkHotkey(hotkey, e)) {
e.preventDefault();
callback();
+ scheduleFullRedraw('force');
}
});
}
diff --git a/ui/src/widgets/modal.ts b/ui/src/widgets/modal.ts
index e32f329..c07e6fe 100644
--- a/ui/src/widgets/modal.ts
+++ b/ui/src/widgets/modal.ts
@@ -14,8 +14,8 @@
import m from 'mithril';
import {defer} from '../base/deferred';
-import {scheduleFullRedraw} from './raf';
import {Icon} from './icon';
+import {scheduleFullRedraw} from './raf';
// This module deals with modal dialogs. Unlike most components, here we want to
// render the DOM elements outside of the corresponding vdom tree. For instance
@@ -79,7 +79,10 @@
export class Modal implements m.ClassComponent<ModalAttrs> {
onbeforeremove(vnode: m.VnodeDOM<ModalAttrs>) {
const removePromise = defer<void>();
- vnode.dom.addEventListener('animationend', () => removePromise.resolve());
+ vnode.dom.addEventListener('animationend', () => {
+ scheduleFullRedraw('force');
+ removePromise.resolve();
+ });
vnode.dom.classList.add('modal-fadeout');
// Retuning `removePromise` will cause Mithril to defer the actual component
@@ -94,7 +97,6 @@
// in turn will: (1) call the user's original attrs.onClose; (2) resolve
// the promise returned by showModal().
vnode.attrs.onClose();
- scheduleFullRedraw();
}
}
@@ -223,7 +225,7 @@
},
};
currentModal = attrs;
- scheduleFullRedraw();
+ redrawModal();
return returnedClosePromise;
}
@@ -232,7 +234,7 @@
// evident why a redraw is requested.
export function redrawModal() {
if (currentModal !== undefined) {
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
@@ -251,7 +253,7 @@
return;
}
currentModal = undefined;
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
export function getCurrentModalKey(): string | undefined {
diff --git a/ui/src/widgets/popup.ts b/ui/src/widgets/popup.ts
index ed16695..ac8b563 100644
--- a/ui/src/widgets/popup.ts
+++ b/ui/src/widgets/popup.ts
@@ -352,13 +352,13 @@
if (this.isOpen) {
this.isOpen = false;
this.onChange(this.isOpen);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
private togglePopup() {
this.isOpen = !this.isOpen;
this.onChange(this.isOpen);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
diff --git a/ui/src/widgets/portal.ts b/ui/src/widgets/portal.ts
index 734fee6..91c3608 100644
--- a/ui/src/widgets/portal.ts
+++ b/ui/src/widgets/portal.ts
@@ -46,13 +46,22 @@
export class Portal implements m.ClassComponent<PortalAttrs> {
private portalElement?: HTMLElement;
private containerElement?: Element;
+ private contentComponent: m.Component;
+
+ constructor({children}: m.CVnode<PortalAttrs>) {
+ // Create a temporary component that we can mount in oncreate, and unmount
+ // in onremove, but inject the new portal content (children) into it each
+ // render cycle. This is initialized here rather than in oncreate to avoid
+ // having to make it optional or use assertExists().
+ this.contentComponent = {view: () => children};
+ }
view() {
// Dummy element renders nothing but permits DOM access in lifecycle hooks.
return m('span', {style: {display: 'none'}});
}
- oncreate({attrs, children, dom}: m.VnodeDOM<PortalAttrs, this>) {
+ oncreate({attrs, dom}: m.CVnodeDOM<PortalAttrs>) {
const {
onContentMount = () => {},
onBeforeContentMount = (): MountOptions => ({}),
@@ -65,16 +74,21 @@
container.appendChild(this.portalElement);
this.applyPortalProps(attrs);
- m.render(this.portalElement, children);
+ m.mount(this.portalElement, this.contentComponent);
onContentMount(this.portalElement);
}
- onupdate({attrs, children}: m.VnodeDOM<PortalAttrs, this>) {
+ onbeforeupdate({children}: m.CVnode<PortalAttrs>) {
+ // Update the mounted content's view function to return the latest portal
+ // content passed in via children, without changing the component itself.
+ this.contentComponent.view = () => children;
+ }
+
+ onupdate({attrs}: m.CVnodeDOM<PortalAttrs>) {
const {onContentUpdate = () => {}} = attrs;
if (this.portalElement) {
this.applyPortalProps(attrs);
- m.render(this.portalElement, children);
onContentUpdate(this.portalElement);
}
}
@@ -86,14 +100,14 @@
}
}
- onremove({attrs}: m.VnodeDOM<PortalAttrs, this>) {
+ onremove({attrs}: m.CVnodeDOM<PortalAttrs>) {
const {onContentUnmount = () => {}} = attrs;
const container = this.containerElement ?? document.body;
if (this.portalElement) {
if (container.contains(this.portalElement)) {
onContentUnmount(this.portalElement);
// Rendering null ensures previous vnodes are removed properly.
- m.render(this.portalElement, null);
+ m.mount(this.portalElement, null);
container.removeChild(this.portalElement);
}
}
diff --git a/ui/src/widgets/raf.ts b/ui/src/widgets/raf.ts
index 20afb61..dc0d3ab 100644
--- a/ui/src/widgets/raf.ts
+++ b/ui/src/widgets/raf.ts
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-let FULL_REDRAW_FUNCTION = () => {};
+let FULL_REDRAW_FUNCTION = (_force?: 'force') => {};
export function setScheduleFullRedraw(func: () => void) {
FULL_REDRAW_FUNCTION = func;
}
-export function scheduleFullRedraw() {
- FULL_REDRAW_FUNCTION();
+export function scheduleFullRedraw(force?: 'force') {
+ FULL_REDRAW_FUNCTION(force);
}
diff --git a/ui/src/frontend/tables/table.ts b/ui/src/widgets/table.ts
similarity index 77%
rename from ui/src/frontend/tables/table.ts
rename to ui/src/widgets/table.ts
index b145c4d..1389907 100644
--- a/ui/src/frontend/tables/table.ts
+++ b/ui/src/widgets/table.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {allUnique, range} from '../../base/array_utils';
+import {allUnique, range} from '../base/array_utils';
import {
compareUniversal,
comparingBy,
@@ -21,18 +21,29 @@
SortableValue,
SortDirection,
withDirection,
-} from '../../base/comparison_utils';
-import {raf} from '../../core/raf_scheduler';
-import {
- menuItem,
- PopupMenuButton,
- popupMenuIcon,
- PopupMenuItem,
-} from '../popup_menu';
+} from '../base/comparison_utils';
+import {scheduleFullRedraw} from './raf';
+import {MenuItem, PopupMenu2} from './menu';
+import {Button} from './button';
+
+// For a table column that can be sorted; the standard popup icon should
+// reflect the current sorting direction. This function returns an icon
+// corresponding to optional SortDirection according to which the column is
+// sorted. (Optional because column might be unsorted)
+export function popupMenuIcon(sortDirection?: SortDirection) {
+ switch (sortDirection) {
+ case undefined:
+ return 'more_horiz';
+ case 'DESC':
+ return 'arrow_drop_down';
+ case 'ASC':
+ return 'arrow_drop_up';
+ }
+}
export interface ColumnDescriptorAttrs<T> {
// Context menu items displayed on the column header.
- contextMenu?: PopupMenuItem[];
+ contextMenu?: m.Child[];
// Unique column ID, used to identify which column is currently sorted.
columnId?: string;
@@ -49,7 +60,7 @@
name: string;
render: (row: T) => m.Child;
id: string;
- contextMenu?: PopupMenuItem[];
+ contextMenu?: m.Child[];
ordering?: ComparisonFn<T>;
constructor(
@@ -81,7 +92,7 @@
export function numberColumn<T>(
name: string,
getter: (t: T) => number,
- contextMenu?: PopupMenuItem[],
+ contextMenu?: m.Child[],
): ColumnDescriptor<T> {
return new ColumnDescriptor<T>(name, getter, {contextMenu, sortKey: getter});
}
@@ -89,7 +100,7 @@
export function stringColumn<T>(
name: string,
getter: (t: T) => string,
- contextMenu?: PopupMenuItem[],
+ contextMenu?: m.Child[],
): ColumnDescriptor<T> {
return new ColumnDescriptor<T>(name, getter, {contextMenu, sortKey: getter});
}
@@ -136,13 +147,13 @@
if (this._sortingInfo !== undefined) {
this.reorder(this._sortingInfo);
}
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
resetOrder() {
this.permutation = range(this.data.length);
this._sortingInfo = undefined;
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
get sortingInfo(): SortingInfo<T> | undefined {
@@ -157,7 +168,7 @@
info.direction,
),
);
- raf.scheduleFullRedraw();
+ scheduleFullRedraw();
}
}
@@ -191,33 +202,42 @@
if (column.ordering !== undefined) {
const ordering = column.ordering;
currDirection = directionOnIndex(column.id, vnode.attrs.data.sortingInfo);
- const newItems: PopupMenuItem[] = [];
+ const newItems: m.Child[] = [];
if (currDirection !== 'ASC') {
newItems.push(
- menuItem('Sort ascending', () => {
- vnode.attrs.data.reorder({
- columnId: column.id,
- direction: 'ASC',
- ordering,
- });
+ m(MenuItem, {
+ label: 'Sort ascending',
+ onclick: () => {
+ vnode.attrs.data.reorder({
+ columnId: column.id,
+ direction: 'ASC',
+ ordering,
+ });
+ },
}),
);
}
if (currDirection !== 'DESC') {
newItems.push(
- menuItem('Sort descending', () => {
- vnode.attrs.data.reorder({
- columnId: column.id,
- direction: 'DESC',
- ordering,
- });
+ m(MenuItem, {
+ label: 'Sort descending',
+ onclick: () => {
+ vnode.attrs.data.reorder({
+ columnId: column.id,
+ direction: 'DESC',
+ ordering,
+ });
+ },
}),
);
}
if (currDirection !== undefined) {
newItems.push(
- menuItem('Restore original order', () => {
- vnode.attrs.data.resetOrder();
+ m(MenuItem, {
+ label: 'Restore original order',
+ onclick: () => {
+ vnode.attrs.data.resetOrder();
+ },
}),
);
}
@@ -227,12 +247,14 @@
return m(
'td',
column.name,
- items === undefined
- ? null
- : m(PopupMenuButton, {
- icon: popupMenuIcon(currDirection),
- items,
- }),
+ items &&
+ m(
+ PopupMenu2,
+ {
+ trigger: m(Button, {icon: popupMenuIcon(currDirection)}),
+ },
+ items,
+ ),
);
}
diff --git a/ui/src/widgets/vega_view.ts b/ui/src/widgets/vega_view.ts
index 7cbf533..1a8cb43 100644
--- a/ui/src/widgets/vega_view.ts
+++ b/ui/src/widgets/vega_view.ts
@@ -228,7 +228,7 @@
}
this._status = Status.Done;
this.pending = undefined;
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
private handleError(pending: Promise<vega.View>, err: unknown) {
@@ -242,7 +242,7 @@
private setError(err: unknown) {
this._status = Status.Error;
this._error = getErrorMessage(err);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
[Symbol.dispose]() {
diff --git a/ui/src/widgets/virtual_scroll_helper.ts b/ui/src/widgets/virtual_scroll_helper.ts
index 475c360..6172c94 100644
--- a/ui/src/widgets/virtual_scroll_helper.ts
+++ b/ui/src/widgets/virtual_scroll_helper.ts
@@ -14,6 +14,7 @@
import {DisposableStack} from '../base/disposable_stack';
import {Bounds2D, Rect2D} from '../base/geom';
+import {scheduleFullRedraw} from './raf';
export interface VirtualScrollHelperOpts {
overdrawPx: number;
@@ -46,6 +47,7 @@
this._data.forEach((data) =>
recalculatePuckRect(sliderElement, containerElement, data),
);
+ scheduleFullRedraw('force');
};
containerElement.addEventListener('scroll', recalculateRects, {