Merge "Trace Redaction - Add missing test file" into main
diff --git a/Android.bp b/Android.bp
index 722da60..ebc715d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6041,6 +6041,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -9177,6 +9178,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -9219,6 +9221,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/log_message.gen.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.cc",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.gen.cc",
@@ -9261,6 +9264,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/log_message.gen.h",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.h",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.gen.h",
@@ -9299,6 +9303,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -9340,6 +9345,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -9381,6 +9387,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/log_message.pb.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.cc",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.pb.cc",
@@ -9422,6 +9429,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/log_message.pb.h",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.h",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.pb.h",
@@ -9460,6 +9468,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -9502,6 +9511,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.cc",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.pbzero.cc",
@@ -9544,6 +9554,7 @@
"external/perfetto/protos/perfetto/trace/track_event/counter_descriptor.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/track_event/pixel_modem.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.h",
"external/perfetto/protos/perfetto/trace/track_event/screenshot.pbzero.h",
@@ -9712,6 +9723,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -12369,6 +12381,7 @@
"src/trace_processor/perfetto_sql/stdlib/prelude/casts.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/prelude/trace_bounds.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",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
@@ -13870,6 +13883,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
@@ -15151,70 +15165,38 @@
":perfetto_include_perfetto_trace_processor_basic_types",
":perfetto_include_perfetto_trace_processor_storage",
":perfetto_include_perfetto_trace_processor_trace_processor",
- ":perfetto_protos_perfetto_common_cpp_gen",
":perfetto_protos_perfetto_common_zero_gen",
- ":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
- ":perfetto_protos_perfetto_config_cpp_gen",
- ":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
- ":perfetto_protos_perfetto_config_gpu_cpp_gen",
":perfetto_protos_perfetto_config_gpu_zero_gen",
- ":perfetto_protos_perfetto_config_inode_file_cpp_gen",
":perfetto_protos_perfetto_config_inode_file_zero_gen",
- ":perfetto_protos_perfetto_config_interceptors_cpp_gen",
":perfetto_protos_perfetto_config_interceptors_zero_gen",
- ":perfetto_protos_perfetto_config_power_cpp_gen",
":perfetto_protos_perfetto_config_power_zero_gen",
- ":perfetto_protos_perfetto_config_process_stats_cpp_gen",
":perfetto_protos_perfetto_config_process_stats_zero_gen",
- ":perfetto_protos_perfetto_config_profiling_cpp_gen",
":perfetto_protos_perfetto_config_profiling_zero_gen",
- ":perfetto_protos_perfetto_config_statsd_cpp_gen",
":perfetto_protos_perfetto_config_statsd_zero_gen",
- ":perfetto_protos_perfetto_config_sys_stats_cpp_gen",
":perfetto_protos_perfetto_config_sys_stats_zero_gen",
- ":perfetto_protos_perfetto_config_system_info_cpp_gen",
":perfetto_protos_perfetto_config_system_info_zero_gen",
- ":perfetto_protos_perfetto_config_track_event_cpp_gen",
":perfetto_protos_perfetto_config_track_event_zero_gen",
":perfetto_protos_perfetto_config_zero_gen",
- ":perfetto_protos_perfetto_trace_android_cpp_gen",
":perfetto_protos_perfetto_trace_android_zero_gen",
- ":perfetto_protos_perfetto_trace_chrome_cpp_gen",
":perfetto_protos_perfetto_trace_chrome_zero_gen",
- ":perfetto_protos_perfetto_trace_etw_cpp_gen",
":perfetto_protos_perfetto_trace_etw_zero_gen",
- ":perfetto_protos_perfetto_trace_filesystem_cpp_gen",
":perfetto_protos_perfetto_trace_filesystem_zero_gen",
- ":perfetto_protos_perfetto_trace_ftrace_cpp_gen",
":perfetto_protos_perfetto_trace_ftrace_zero_gen",
- ":perfetto_protos_perfetto_trace_gpu_cpp_gen",
":perfetto_protos_perfetto_trace_gpu_zero_gen",
- ":perfetto_protos_perfetto_trace_interned_data_cpp_gen",
":perfetto_protos_perfetto_trace_interned_data_zero_gen",
- ":perfetto_protos_perfetto_trace_minimal_cpp_gen",
":perfetto_protos_perfetto_trace_minimal_zero_gen",
- ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen",
":perfetto_protos_perfetto_trace_non_minimal_zero_gen",
- ":perfetto_protos_perfetto_trace_perfetto_cpp_gen",
":perfetto_protos_perfetto_trace_perfetto_zero_gen",
- ":perfetto_protos_perfetto_trace_power_cpp_gen",
":perfetto_protos_perfetto_trace_power_zero_gen",
":perfetto_protos_perfetto_trace_processor_zero_gen",
- ":perfetto_protos_perfetto_trace_profiling_cpp_gen",
":perfetto_protos_perfetto_trace_profiling_zero_gen",
- ":perfetto_protos_perfetto_trace_ps_cpp_gen",
":perfetto_protos_perfetto_trace_ps_zero_gen",
- ":perfetto_protos_perfetto_trace_statsd_cpp_gen",
":perfetto_protos_perfetto_trace_statsd_zero_gen",
- ":perfetto_protos_perfetto_trace_sys_stats_cpp_gen",
":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
- ":perfetto_protos_perfetto_trace_system_info_cpp_gen",
":perfetto_protos_perfetto_trace_system_info_zero_gen",
- ":perfetto_protos_perfetto_trace_track_event_cpp_gen",
":perfetto_protos_perfetto_trace_track_event_zero_gen",
- ":perfetto_protos_perfetto_trace_translation_cpp_gen",
":perfetto_protos_perfetto_trace_translation_zero_gen",
":perfetto_src_base_base",
":perfetto_src_protozero_protozero",
@@ -15257,70 +15239,38 @@
"libz",
],
generated_headers: [
- "perfetto_protos_perfetto_common_cpp_gen_headers",
"perfetto_protos_perfetto_common_zero_gen_headers",
- "perfetto_protos_perfetto_config_android_cpp_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
- "perfetto_protos_perfetto_config_cpp_gen_headers",
- "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
- "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
"perfetto_protos_perfetto_config_gpu_zero_gen_headers",
- "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
"perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
- "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers",
"perfetto_protos_perfetto_config_interceptors_zero_gen_headers",
- "perfetto_protos_perfetto_config_power_cpp_gen_headers",
"perfetto_protos_perfetto_config_power_zero_gen_headers",
- "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
"perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
- "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
"perfetto_protos_perfetto_config_profiling_zero_gen_headers",
- "perfetto_protos_perfetto_config_statsd_cpp_gen_headers",
"perfetto_protos_perfetto_config_statsd_zero_gen_headers",
- "perfetto_protos_perfetto_config_sys_stats_cpp_gen_headers",
"perfetto_protos_perfetto_config_sys_stats_zero_gen_headers",
- "perfetto_protos_perfetto_config_system_info_cpp_gen_headers",
"perfetto_protos_perfetto_config_system_info_zero_gen_headers",
- "perfetto_protos_perfetto_config_track_event_cpp_gen_headers",
"perfetto_protos_perfetto_config_track_event_zero_gen_headers",
"perfetto_protos_perfetto_config_zero_gen_headers",
- "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
"perfetto_protos_perfetto_trace_android_zero_gen_headers",
- "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
"perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
- "perfetto_protos_perfetto_trace_etw_cpp_gen_headers",
"perfetto_protos_perfetto_trace_etw_zero_gen_headers",
- "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
"perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
- "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
"perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
- "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
"perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
- "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
"perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
- "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
"perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
- "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
"perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
- "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
"perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
- "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
"perfetto_protos_perfetto_trace_power_zero_gen_headers",
"perfetto_protos_perfetto_trace_processor_zero_gen_headers",
- "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
"perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
- "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
"perfetto_protos_perfetto_trace_ps_zero_gen_headers",
- "perfetto_protos_perfetto_trace_statsd_cpp_gen_headers",
"perfetto_protos_perfetto_trace_statsd_zero_gen_headers",
- "perfetto_protos_perfetto_trace_sys_stats_cpp_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
- "perfetto_protos_perfetto_trace_system_info_cpp_gen_headers",
"perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
- "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
"perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
- "perfetto_protos_perfetto_trace_translation_cpp_gen_headers",
"perfetto_protos_perfetto_trace_translation_zero_gen_headers",
"perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
"perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
diff --git a/BUILD b/BUILD
index d678720..f355d4e 100644
--- a/BUILD
+++ b/BUILD
@@ -2528,6 +2528,7 @@
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_sched_sched",
srcs = [
+ "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",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
@@ -5222,6 +5223,7 @@
"protos/perfetto/trace/track_event/counter_descriptor.proto",
"protos/perfetto/trace/track_event/debug_annotation.proto",
"protos/perfetto/trace/track_event/log_message.proto",
+ "protos/perfetto/trace/track_event/pixel_modem.proto",
"protos/perfetto/trace/track_event/process_descriptor.proto",
"protos/perfetto/trace/track_event/range_of_interest.proto",
"protos/perfetto/trace/track_event/screenshot.proto",
diff --git a/CHANGELOG b/CHANGELOG
index 145aa78..abdf0f9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,7 +12,11 @@
of RING_BUFFER traces, removing artifacts such as never-ending slices
starting at the beginning of the trace.
SDK:
- *
+ * "track_event" categories are disabled by default in the C API, if they
+ don't match anything in the data source config. This behavior differs from
+ the C++ API. Configs should include either `enabled_categories: "*"` or
+ `disable_categories: "*"` to explicitly specify the desired behavior that
+ work both for C and C++.
v43.2 - 2024-03-07:
diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md
index 49320e0..ea9393d 100644
--- a/docs/instrumentation/tracing-sdk.md
+++ b/docs/instrumentation/tracing-sdk.md
@@ -147,16 +147,16 @@
very little complexity.
To include your new track events in the trace, ensure that the `track_event`
-data source is included in the trace config. If you do not specify any
-categories then all non-debug categories will be included by default. However,
-you can also add just the categories you are interested in like so:
+data source is included in the trace config, with a list of enabled and disabled
+categories.
```protobuf
data_sources {
config {
name: "track_event"
track_event_config {
- enabled_categories: "rendering"
+ enabled_categories: "rendering"
+ disabled_categories: "*"
}
}
}
diff --git a/docs/instrumentation/track-events.md b/docs/instrumentation/track-events.md
index 51618fd..f06ec91 100644
--- a/docs/instrumentation/track-events.md
+++ b/docs/instrumentation/track-events.md
@@ -262,17 +262,22 @@
7. Pattern matches in disabled categories.
8. Pattern matches in disabled tags.
-If none of the steps produced a match, the category is enabled by default. In
-other words, every category is implicitly enabled unless specifically disabled.
+If none of the steps produced a match, the category:
+* is enabled by default in the C++ API
+* is disabled by default in the C API.
+
+Specifying an `enabled_categories: "*"` or `disabled_categories: "*"` helps
+achieving a consistent behavior explicitly.
+
For example:
| Setting | Needed configuration |
| ------------------------------- | -------------------------------------------- |
-| Enable just specific categories | `enabled_categories = [“foo”, “bar”, “baz”]` |
-| | `disabled_categories = [“*”]` |
-| Enable all non-slow categories | (Happens by default.) |
-| Enable specific tags | `disabled_tags = [“*”]` |
-| | `enabled_tags = [“foo”, “bar”]` |
+| Enable just specific categories | `enabled_categories = ["foo", "bar", "baz"]` |
+| | `disabled_categories = ["*"]` |
+| Enable all non-slow categories | `enabled_categories = ["*"] ` |
+| Enable specific tags | `disabled_tags = ["*"]` |
+| | `enabled_tags = ["foo", "bar"]` |
## Dynamic and test-only categories
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index f11c56d..6623597 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -3162,23 +3162,27 @@
// 7. Pattern matches in disabled categories.
// 8. Pattern matches in disabled tags.
//
- // If none of the steps produced a match, the category is enabled by default.
+ // If none of the steps produced a match:
+ // - In the C++ SDK (`perfetto::Category`), categories are enabled by
+ // default.
+ // - In the C SDK (`PerfettoTeCategory`), categories are disabled by default.
//
// Examples:
//
// - To enable all non-slow/debug categories:
//
- // No configuration needed, happens by default.
+ // enabled_categories: "*"
//
- // - To enable a specific category:
+ // - To enable specific categories:
//
- // disabled_categories = ["*"]
- // enabled_categories = ["my_category"]
+ // disabled_categories: "*"
+ // enabled_categories: "my_category"
+ // enabled_categories: "my_category2"
//
// - To enable only categories with a specific tag:
//
- // disabled_tags = ["*"]
- // enabled_tags = ["my_tag"]
+ // disabled_tags: "*"
+ // enabled_tags: "my_tag"
//
// Default: []
diff --git a/protos/perfetto/config/track_event/track_event_config.proto b/protos/perfetto/config/track_event/track_event_config.proto
index e4c0e4d..5c029e2 100644
--- a/protos/perfetto/config/track_event/track_event_config.proto
+++ b/protos/perfetto/config/track_event/track_event_config.proto
@@ -34,23 +34,27 @@
// 7. Pattern matches in disabled categories.
// 8. Pattern matches in disabled tags.
//
- // If none of the steps produced a match, the category is enabled by default.
+ // If none of the steps produced a match:
+ // - In the C++ SDK (`perfetto::Category`), categories are enabled by
+ // default.
+ // - In the C SDK (`PerfettoTeCategory`), categories are disabled by default.
//
// Examples:
//
// - To enable all non-slow/debug categories:
//
- // No configuration needed, happens by default.
+ // enabled_categories: "*"
//
- // - To enable a specific category:
+ // - To enable specific categories:
//
- // disabled_categories = ["*"]
- // enabled_categories = ["my_category"]
+ // disabled_categories: "*"
+ // enabled_categories: "my_category"
+ // enabled_categories: "my_category2"
//
// - To enable only categories with a specific tag:
//
- // disabled_tags = ["*"]
- // enabled_tags = ["my_tag"]
+ // disabled_tags: "*"
+ // enabled_tags: "my_tag"
//
// Default: []
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index c5ad849..e2ca850 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -158,13 +158,13 @@
optional string destination_thread = 3;
optional string destination_process = 4;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=15;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=15;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional string flags = 5;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=14;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=14;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional string code = 6;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=37;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=37;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional int64 data_size = 7;
}
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index b2c3392..fbd42fd 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -2115,13 +2115,13 @@
optional string destination_thread = 3;
optional string destination_process = 4;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=15;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=15;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional string flags = 5;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=14;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=14;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional string code = 6;
// From
- // https://cs.android.com/android/platform/superproject/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=37;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
+ // https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/protos/perfetto/trace/ftrace/binder.proto;l=37;drc=7b6a788162a30802f4c9d8d7a30a54e25edd30f1
optional int64 data_size = 7;
}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index ca07b6f..74e49b6 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3162,23 +3162,27 @@
// 7. Pattern matches in disabled categories.
// 8. Pattern matches in disabled tags.
//
- // If none of the steps produced a match, the category is enabled by default.
+ // If none of the steps produced a match:
+ // - In the C++ SDK (`perfetto::Category`), categories are enabled by
+ // default.
+ // - In the C SDK (`PerfettoTeCategory`), categories are disabled by default.
//
// Examples:
//
// - To enable all non-slow/debug categories:
//
- // No configuration needed, happens by default.
+ // enabled_categories: "*"
//
- // - To enable a specific category:
+ // - To enable specific categories:
//
- // disabled_categories = ["*"]
- // enabled_categories = ["my_category"]
+ // disabled_categories: "*"
+ // enabled_categories: "my_category"
+ // enabled_categories: "my_category2"
//
// - To enable only categories with a specific tag:
//
- // disabled_tags = ["*"]
- // enabled_tags = ["my_tag"]
+ // disabled_tags: "*"
+ // enabled_tags: "my_tag"
//
// Default: []
@@ -12126,6 +12130,16 @@
// End of protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto
+// Begin of protos/perfetto/trace/track_event/pixel_modem.proto
+
+// Event insights emitted by the Pixel modem.
+message PixelModemEventInsight {
+ // Opaque string containing arguments from the modem.
+ optional string detokenized_message = 1;
+}
+
+// End of protos/perfetto/trace/track_event/pixel_modem.proto
+
// Begin of protos/perfetto/trace/track_event/screenshot.proto
message Screenshot {
@@ -12211,7 +12225,7 @@
// their default track association) can be emitted as part of a
// TrackEventDefaults message.
//
-// Next reserved id: 13 (up to 15). Next id: 51.
+// Next reserved id: 13 (up to 15). Next id: 52.
message TrackEvent {
// Names of categories of the event. In the client library, categories are a
// way to turn groups of individual events on or off.
@@ -12365,6 +12379,7 @@
43;
optional ChromeActiveProcesses chrome_active_processes = 49;
optional Screenshot screenshot = 50;
+ optional PixelModemEventInsight pixel_modem_event_insight = 51;
// This field is used only if the source location represents the function that
// executes during this event.
@@ -13771,7 +13786,7 @@
// trace.
message StatsdAtom {
// Atom should be filled with an Atom proto from:
- // https://cs.android.com/android/platform/superproject/+/master:frameworks/proto_logging/stats/atoms.proto?q=f:stats%2Fatoms.proto$%20message%5C%20Atom
+ // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/proto_logging/stats/atoms.proto?q=f:stats%2Fatoms.proto$%20message%5C%20Atom
// We don't reference Atom directly here since we don't want to import
// Atom.proto and all its transitive dependencies into Perfetto.
// atom and timestamp_nanos have the same cardinality
diff --git a/protos/perfetto/trace/statsd/statsd_atom.proto b/protos/perfetto/trace/statsd/statsd_atom.proto
index 62ca960..8e487e8 100644
--- a/protos/perfetto/trace/statsd/statsd_atom.proto
+++ b/protos/perfetto/trace/statsd/statsd_atom.proto
@@ -26,7 +26,7 @@
// trace.
message StatsdAtom {
// Atom should be filled with an Atom proto from:
- // https://cs.android.com/android/platform/superproject/+/master:frameworks/proto_logging/stats/atoms.proto?q=f:stats%2Fatoms.proto$%20message%5C%20Atom
+ // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/proto_logging/stats/atoms.proto?q=f:stats%2Fatoms.proto$%20message%5C%20Atom
// We don't reference Atom directly here since we don't want to import
// Atom.proto and all its transitive dependencies into Perfetto.
// atom and timestamp_nanos have the same cardinality
diff --git a/protos/perfetto/trace/track_event/BUILD.gn b/protos/perfetto/trace/track_event/BUILD.gn
index c101c46..00a5ff5 100644
--- a/protos/perfetto/trace/track_event/BUILD.gn
+++ b/protos/perfetto/trace/track_event/BUILD.gn
@@ -35,6 +35,7 @@
"counter_descriptor.proto",
"debug_annotation.proto",
"log_message.proto",
+ "pixel_modem.proto",
"process_descriptor.proto",
"range_of_interest.proto",
"screenshot.proto",
diff --git a/protos/perfetto/trace/track_event/pixel_modem.proto b/protos/perfetto/trace/track_event/pixel_modem.proto
new file mode 100644
index 0000000..ffe9062
--- /dev/null
+++ b/protos/perfetto/trace/track_event/pixel_modem.proto
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+// Event insights emitted by the Pixel modem.
+message PixelModemEventInsight {
+ // Opaque string containing arguments from the modem.
+ optional string detokenized_message = 1;
+}
diff --git a/protos/perfetto/trace/track_event/track_event.proto b/protos/perfetto/trace/track_event/track_event.proto
index 325465d..2b9d35c 100644
--- a/protos/perfetto/trace/track_event/track_event.proto
+++ b/protos/perfetto/trace/track_event/track_event.proto
@@ -33,6 +33,7 @@
import "protos/perfetto/trace/track_event/chrome_renderer_scheduler_state.proto";
import "protos/perfetto/trace/track_event/chrome_user_event.proto";
import "protos/perfetto/trace/track_event/chrome_window_handle_event_info.proto";
+import "protos/perfetto/trace/track_event/pixel_modem.proto";
import "protos/perfetto/trace/track_event/screenshot.proto";
import "protos/perfetto/trace/track_event/source_location.proto";
@@ -103,7 +104,7 @@
// their default track association) can be emitted as part of a
// TrackEventDefaults message.
//
-// Next reserved id: 13 (up to 15). Next id: 51.
+// Next reserved id: 13 (up to 15). Next id: 52.
message TrackEvent {
// Names of categories of the event. In the client library, categories are a
// way to turn groups of individual events on or off.
@@ -257,6 +258,7 @@
43;
optional ChromeActiveProcesses chrome_active_processes = 49;
optional Screenshot screenshot = 50;
+ optional PixelModemEventInsight pixel_modem_event_insight = 51;
// This field is used only if the source location represents the function that
// executes during this event.
diff --git a/protos/third_party/statsd/shell_config.proto b/protos/third_party/statsd/shell_config.proto
index 98a3e67..49ec142 100644
--- a/protos/third_party/statsd/shell_config.proto
+++ b/protos/third_party/statsd/shell_config.proto
@@ -19,7 +19,7 @@
package perfetto.protos;
// This is a manual import of ShellSubscription:
-// https://cs.android.com/android/platform/superproject/+/main:packages/modules/StatsD/statsd/src/shell/shell_config.proto?q=f:statsD%2Fstatsd%20f:shell_config.proto
+// https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/StatsD/statsd/src/shell/shell_config.proto?q=f:statsD%2Fstatsd%20f:shell_config.proto
// and its transitive dependencies.
message StatsdShellSubscription {
diff --git a/protos/third_party/statsd/shell_data.proto b/protos/third_party/statsd/shell_data.proto
index c5180e8..3896a36 100644
--- a/protos/third_party/statsd/shell_data.proto
+++ b/protos/third_party/statsd/shell_data.proto
@@ -19,7 +19,7 @@
package perfetto.proto;
// This is a manual import of ShellData:
-// https://cs.android.com/android/platform/superproject/+/main:packages/modules/StatsD/statsd/src/shell/shell_data.proto;l=27;drc=d2e51ecdf08753688fb889b657dcba60adb994f3
+// https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/StatsD/statsd/src/shell/shell_data.proto;l=27;drc=d2e51ecdf08753688fb889b657dcba60adb994f3
// This must exactly match perfetto.protos.StatsdAtom.
message ShellData {
repeated bytes atom = 1;
diff --git a/src/profiling/memory/README.md b/src/profiling/memory/README.md
index 8e00c7f..23e6846 100644
--- a/src/profiling/memory/README.md
+++ b/src/profiling/memory/README.md
@@ -21,10 +21,10 @@
### Interceptors
bionic: uses bionic [malloc dispatch](
-https://cs.android.com/android/platform/superproject/+/main:bionic/libc/private/bionic_malloc_dispatch.h)
+https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/bionic_malloc_dispatch.h)
to intercept allocation functions on Android. This works by placing a library
on a pre-defined path, which gets [loaded by Bionic](
-https://cs.android.com/android/platform/superproject/+/main:bionic/libc/bionic/malloc_heapprofd.cpp).
+https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/malloc_heapprofd.cpp).
glibc: generates a library exposing the allocation functions. This library
should be used for `LD_PRELOAD` and uses the glibc specific symbols
diff --git a/src/shared_lib/test/api_integrationtest.cc b/src/shared_lib/test/api_integrationtest.cc
index 0dff900..1fa6278 100644
--- a/src/shared_lib/test/api_integrationtest.cc
+++ b/src/shared_lib/test/api_integrationtest.cc
@@ -1028,7 +1028,7 @@
EXPECT_FALSE(std::atomic_load(cat3.enabled));
}
-TEST_F(SharedLibTrackEventTest, TrackEventFastpathEmptyConfigEnableAllCats) {
+TEST_F(SharedLibTrackEventTest, TrackEventFastpathEmptyConfigDisablesAllCats) {
ASSERT_FALSE(std::atomic_load(cat1.enabled));
ASSERT_FALSE(std::atomic_load(cat2.enabled));
ASSERT_FALSE(std::atomic_load(cat3.enabled));
@@ -1036,9 +1036,9 @@
TracingSession tracing_session =
TracingSession::Builder().set_data_source_name("track_event").Build();
- EXPECT_TRUE(std::atomic_load(cat1.enabled));
- EXPECT_TRUE(std::atomic_load(cat2.enabled));
- EXPECT_TRUE(std::atomic_load(cat3.enabled));
+ EXPECT_FALSE(std::atomic_load(cat1.enabled));
+ EXPECT_FALSE(std::atomic_load(cat2.enabled));
+ EXPECT_FALSE(std::atomic_load(cat3.enabled));
}
TEST_F(SharedLibTrackEventTest, TrackEventFastpathOneCatEnabled) {
@@ -1058,9 +1058,12 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlCategory) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
+ EXPECT_TRUE(std::atomic_load(cat1.enabled));
PERFETTO_TE(cat1, PERFETTO_TE_INSTANT(""));
tracing_session.StopBlocking();
@@ -1194,8 +1197,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlInstant) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"));
@@ -1235,8 +1240,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventLlInstant) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
if (PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
cat1.enabled, PERFETTO_MEMORY_ORDER_RELAXED))) {
@@ -1315,8 +1322,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlInstantNoIntern) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"), PERFETTO_TE_NO_INTERN());
@@ -1345,8 +1354,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlDbgArg) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"),
PERFETTO_TE_ARG_UINT64("arg_name", 42));
@@ -1413,8 +1424,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlNamedTrack) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
PERFETTO_TE(cat1, PERFETTO_TE_INSTANT("event"),
PERFETTO_TE_NAMED_TRACK("MyTrack", 1, 2));
@@ -1453,8 +1466,10 @@
}
TEST_F(SharedLibTrackEventTest, TrackEventHlRegisteredCounter) {
- TracingSession tracing_session =
- TracingSession::Builder().set_data_source_name("track_event").Build();
+ TracingSession tracing_session = TracingSession::Builder()
+ .set_data_source_name("track_event")
+ .add_enabled_category("*")
+ .Build();
PerfettoTeRegisteredTrack my_counter_track;
PerfettoTeCounterTrackRegister(&my_counter_track, "MyCounter",
diff --git a/src/shared_lib/test/utils.cc b/src/shared_lib/test/utils.cc
index 7c0ed8d..966c7f0 100644
--- a/src/shared_lib/test/utils.cc
+++ b/src/shared_lib/test/utils.cc
@@ -68,7 +68,7 @@
perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg,
data_source_name_.c_str());
- if (!enabled_categories_.empty() && !disabled_categories_.empty()) {
+ if (!enabled_categories_.empty() || !disabled_categories_.empty()) {
perfetto_protos_TrackEventConfig te_cfg;
perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg,
&te_cfg);
diff --git a/src/shared_lib/track_event.cc b/src/shared_lib/track_event.cc
index 5a4b87c..bbafde7 100644
--- a/src/shared_lib/track_event.cc
+++ b/src/shared_lib/track_event.cc
@@ -137,8 +137,9 @@
}
}
- // If nothing matched, enable the category by default.
- return true;
+ // If nothing matched, the category is disabled by default. N.B. this behavior
+ // is different than the C++ TrackEvent API.
+ return false;
}
static bool IsRegisteredCategoryEnabled(
diff --git a/src/trace_processor/importers/proto/jit_tracker.cc b/src/trace_processor/importers/proto/jit_tracker.cc
index 4cab6f1..224f8d9 100644
--- a/src/trace_processor/importers/proto/jit_tracker.cc
+++ b/src/trace_processor/importers/proto/jit_tracker.cc
@@ -31,7 +31,6 @@
namespace perfetto::trace_processor {
JitTracker::JitTracker(TraceProcessorContext* context) : context_(context) {}
-
JitTracker::~JitTracker() = default;
JitCache* JitTracker::CreateJitCache(std::string name,
diff --git a/src/trace_processor/importers/proto/v8_module.cc b/src/trace_processor/importers/proto/v8_module.cc
index 94684e3..f90a71c 100644
--- a/src/trace_processor/importers/proto/v8_module.cc
+++ b/src/trace_processor/importers/proto/v8_module.cc
@@ -16,16 +16,21 @@
#include "src/trace_processor/importers/proto/v8_module.h"
+#include <cstdint>
#include <optional>
+#include "perfetto/base/logging.h"
#include "protos/perfetto/trace/chrome/v8.pbzero.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/importers/proto/v8_sequence_state.h"
#include "src/trace_processor/importers/proto/v8_tracker.h"
#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/v8_tables_py.h"
namespace perfetto {
@@ -33,6 +38,7 @@
namespace {
using ::perfetto::protos::pbzero::TracePacket;
+using ::perfetto::protos::pbzero::V8CodeDefaults;
using ::perfetto::protos::pbzero::V8CodeMove;
using ::perfetto::protos::pbzero::V8InternalCode;
using ::perfetto::protos::pbzero::V8JsCode;
@@ -85,6 +91,54 @@
}
}
+template <typename CodeDecoder>
+std::optional<UniqueTid> V8Module::GetUtid(
+ PacketSequenceStateGeneration& generation,
+ IsolateId isolate_id,
+ const CodeDecoder& code) {
+ auto* pid = isolate_to_pid_.Find(isolate_id);
+ if (!pid) {
+ tables::ProcessTable::Id upid(
+ context_->storage->v8_isolate_table().FindById(isolate_id)->upid());
+ pid = isolate_to_pid_
+ .Insert(isolate_id,
+ context_->storage->process_table().FindById(upid)->pid())
+ .first;
+ }
+
+ if (code.has_tid()) {
+ return context_->process_tracker->UpdateThread(code.tid(), *pid);
+ }
+
+ if (auto tid = GetDefaultTid(generation); tid.has_value()) {
+ return context_->process_tracker->UpdateThread(*tid, *pid);
+ }
+
+ return std::nullopt;
+}
+
+std::optional<uint32_t> V8Module::GetDefaultTid(
+ PacketSequenceStateGeneration& generation) const {
+ auto* tp_defaults = generation.GetTracePacketDefaults();
+ if (!tp_defaults) {
+ context_->storage->IncrementStats(stats::v8_no_defaults);
+ return std::nullopt;
+ }
+ if (!tp_defaults->has_v8_code_defaults()) {
+ context_->storage->IncrementStats(stats::v8_no_defaults);
+ return std::nullopt;
+ }
+
+ V8CodeDefaults::Decoder v8_defaults(tp_defaults->v8_code_defaults());
+
+ if (!v8_defaults.has_tid()) {
+ context_->storage->IncrementStats(stats::v8_no_defaults);
+ return std::nullopt;
+ }
+
+ return v8_defaults.tid();
+}
+
void V8Module::ParseV8JsCode(protozero::ConstBytes bytes,
int64_t ts,
const TracePacketData& data) {
@@ -97,13 +151,19 @@
return;
}
+ std::optional<UniqueTid> utid =
+ GetUtid(*data.sequence_state, *v8_isolate_id, code);
+ if (!utid) {
+ return;
+ }
+
auto v8_function_id =
state.GetOrInsertJsFunction(code.v8_js_function_iid(), *v8_isolate_id);
if (!v8_function_id) {
return;
}
- v8_tracker_->AddJsCode(ts, *v8_isolate_id, *v8_function_id, code);
+ v8_tracker_->AddJsCode(ts, *utid, *v8_isolate_id, *v8_function_id, code);
}
void V8Module::ParseV8InternalCode(protozero::ConstBytes bytes,
@@ -118,7 +178,13 @@
return;
}
- v8_tracker_->AddInternalCode(ts, *v8_isolate_id, code);
+ std::optional<UniqueTid> utid =
+ GetUtid(*data.sequence_state, *v8_isolate_id, code);
+ if (!utid) {
+ return;
+ }
+
+ v8_tracker_->AddInternalCode(ts, *utid, *v8_isolate_id, code);
}
void V8Module::ParseV8WasmCode(protozero::ConstBytes bytes,
@@ -139,7 +205,13 @@
return;
}
- v8_tracker_->AddWasmCode(ts, *v8_isolate_id, *v8_wasm_script_id, code);
+ std::optional<UniqueTid> utid =
+ GetUtid(*data.sequence_state, *v8_isolate_id, code);
+ if (!utid) {
+ return;
+ }
+
+ v8_tracker_->AddWasmCode(ts, *utid, *v8_isolate_id, *v8_wasm_script_id, code);
}
void V8Module::ParseV8RegExpCode(protozero::ConstBytes bytes,
@@ -154,7 +226,13 @@
return;
}
- v8_tracker_->AddRegExpCode(ts, *v8_isolate_id, code);
+ std::optional<UniqueTid> utid =
+ GetUtid(*data.sequence_state, *v8_isolate_id, code);
+ if (!utid) {
+ return;
+ }
+
+ v8_tracker_->AddRegExpCode(ts, *utid, *v8_isolate_id, code);
}
void V8Module::ParseV8CodeMove(protozero::ConstBytes bytes,
@@ -163,7 +241,7 @@
V8SequenceState& state = *data.sequence_state->GetOrCreate<V8SequenceState>();
protos::pbzero::V8CodeMove::Decoder v8_code_move(bytes);
- std::optional<tables::V8IsolateTable::Id> isolate_id =
+ std::optional<IsolateId> isolate_id =
state.GetOrInsertIsolate(v8_code_move.isolate_iid());
if (!isolate_id) {
return;
diff --git a/src/trace_processor/importers/proto/v8_module.h b/src/trace_processor/importers/proto/v8_module.h
index de3f896..ea49b32 100644
--- a/src/trace_processor/importers/proto/v8_module.h
+++ b/src/trace_processor/importers/proto/v8_module.h
@@ -18,9 +18,13 @@
#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_MODULE_H_
#include <cstdint>
+#include <optional>
+#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/protozero/field.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
namespace perfetto {
namespace protos {
@@ -74,8 +78,20 @@
int64_t ts,
const TracePacketData& data);
+ // Determine the utid for a code event.
+ // If the passed in decoder has no tid field this method will try the
+ // TracePacketDefaults.
+ template <typename CodeDecoder>
+ std::optional<UniqueTid> GetUtid(PacketSequenceStateGeneration& generation,
+ tables::V8IsolateTable::Id isolate_id,
+ const CodeDecoder& code);
+ std::optional<uint32_t> GetDefaultTid(
+ PacketSequenceStateGeneration& generation) const;
TraceProcessorContext* const context_;
V8Tracker* const v8_tracker_;
+ // Caches isolate to pid associations. Used to compute the utid for code
+ // events.
+ base::FlatHashMap<tables::V8IsolateTable::Id, uint32_t> isolate_to_pid_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/v8_sequence_state.cc b/src/trace_processor/importers/proto/v8_sequence_state.cc
index 33abb24..4f0a4368 100644
--- a/src/trace_processor/importers/proto/v8_sequence_state.cc
+++ b/src/trace_processor/importers/proto/v8_sequence_state.cc
@@ -19,7 +19,6 @@
#include "protos/perfetto/trace/chrome/v8.pbzero.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
-#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
#include "src/trace_processor/importers/proto/string_encoding_utils.h"
#include "src/trace_processor/importers/proto/v8_tracker.h"
@@ -46,8 +45,7 @@
V8SequenceState::~V8SequenceState() = default;
-std::optional<tables::V8IsolateTable::Id> V8SequenceState::GetOrInsertIsolate(
- uint64_t iid) {
+std::optional<IsolateId> V8SequenceState::GetOrInsertIsolate(uint64_t iid) {
if (auto* id = isolates_.Find(iid); id != nullptr) {
return *id;
}
@@ -64,8 +62,7 @@
}
std::optional<tables::V8JsFunctionTable::Id>
-V8SequenceState::GetOrInsertJsFunction(uint64_t iid,
- tables::V8IsolateTable::Id isolate_id) {
+V8SequenceState::GetOrInsertJsFunction(uint64_t iid, IsolateId isolate_id) {
if (auto* id = js_functions_.Find(iid); id != nullptr) {
return *id;
}
@@ -98,8 +95,7 @@
}
std::optional<tables::V8WasmScriptTable::Id>
-V8SequenceState::GetOrInsertWasmScript(uint64_t iid,
- tables::V8IsolateTable::Id isolate_id) {
+V8SequenceState::GetOrInsertWasmScript(uint64_t iid, IsolateId isolate_id) {
if (auto* id = wasm_scripts_.Find(iid); id != nullptr) {
return *id;
}
@@ -118,7 +114,7 @@
std::optional<tables::V8JsScriptTable::Id> V8SequenceState::GetOrInsertJsScript(
uint64_t iid,
- tables::V8IsolateTable::Id v8_isolate_id) {
+ IsolateId v8_isolate_id) {
if (auto* id = js_scripts_.Find(iid); id != nullptr) {
return *id;
}
@@ -157,10 +153,10 @@
base::StringView(ConvertLatin1ToUtf8(function_name.latin1())));
} else if (function_name.has_utf16_le()) {
id = storage.InternString(
- base::StringView(ConvertUtf16LeToUtf8(function_name.latin1())));
+ base::StringView(ConvertUtf16LeToUtf8(function_name.utf16_le())));
} else if (function_name.has_utf16_be()) {
id = storage.InternString(
- base::StringView(ConvertUtf16BeToUtf8(function_name.latin1())));
+ base::StringView(ConvertUtf16BeToUtf8(function_name.utf16_be())));
} else {
id = storage.InternString("");
}
diff --git a/src/trace_processor/importers/proto/v8_tracker.cc b/src/trace_processor/importers/proto/v8_tracker.cc
index 963ad5b..8bacba0 100644
--- a/src/trace_processor/importers/proto/v8_tracker.cc
+++ b/src/trace_processor/importers/proto/v8_tracker.cc
@@ -17,16 +17,28 @@
#include "src/trace_processor/importers/proto/v8_tracker.h"
#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
#include <utility>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/protozero/field.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/common/jit_cache.h"
+#include "src/trace_processor/importers/common/mapping_tracker.h"
#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/jit_tracker.h"
#include "src/trace_processor/importers/proto/string_encoding_utils.h"
+#include "src/trace_processor/storage/stats.h"
#include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/tables/metadata_tables_py.h"
+#include "src/trace_processor/tables/jit_tables_py.h"
#include "src/trace_processor/tables/v8_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
@@ -40,9 +52,40 @@
using ::perfetto::protos::pbzero::InternedV8WasmScript;
using ::perfetto::protos::pbzero::V8InternalCode;
using ::perfetto::protos::pbzero::V8JsCode;
+using ::perfetto::protos::pbzero::V8RegExpCode;
using ::perfetto::protos::pbzero::V8String;
using ::perfetto::protos::pbzero::V8WasmCode;
+bool IsInterpretedCode(const V8JsCode::Decoder& code) {
+ switch (code.tier()) {
+ case V8JsCode::TIER_IGNITION:
+ return true;
+
+ case V8JsCode::TIER_UNKNOWN:
+ case V8JsCode::TIER_SPARKPLUG:
+ case V8JsCode::TIER_MAGLEV:
+ case V8JsCode::TIER_TURBOSHAFT:
+ case V8JsCode::TIER_TURBOFAN:
+ return false;
+ }
+ PERFETTO_FATAL("Unreachable");
+}
+
+bool IsNativeCode(const V8JsCode::Decoder& code) {
+ switch (code.tier()) {
+ case V8JsCode::TIER_UNKNOWN:
+ case V8JsCode::TIER_IGNITION:
+ return false;
+
+ case V8JsCode::TIER_SPARKPLUG:
+ case V8JsCode::TIER_MAGLEV:
+ case V8JsCode::TIER_TURBOSHAFT:
+ case V8JsCode::TIER_TURBOFAN:
+ return true;
+ }
+ PERFETTO_FATAL("Unreachable");
+}
+
base::StringView JsScriptTypeToString(int32_t type) {
if (type < protos::pbzero::InternedV8JsScript_Type_MIN ||
type > protos::pbzero::InternedV8JsScript_Type_MAX) {
@@ -65,53 +108,174 @@
return name.substr(5);
}
+base::StringView JsCodeTierToString(int32_t tier) {
+ if (tier < protos::pbzero::V8JsCode_Tier_MIN ||
+ tier > protos::pbzero::V8JsCode_Tier_MAX) {
+ return "UNKNOWN";
+ }
+ base::StringView name = V8JsCode::Tier_Name(V8JsCode::Tier(tier));
+ // Remove the "TIER_" prefix
+ return name.substr(5);
+}
+
+base::StringView InternalCodeTypeToString(int32_t type) {
+ if (type < protos::pbzero::V8InternalCode_Type_MIN ||
+ type > protos::pbzero::V8InternalCode_Type_MAX) {
+ return "UNKNOWN";
+ }
+ base::StringView name = V8InternalCode::Type_Name(V8InternalCode::Type(type));
+ // Remove the "TYPE_" prefix
+ return name.substr(5);
+}
+
+base::StringView WasmCodeTierToString(int32_t tier) {
+ if (tier < protos::pbzero::V8WasmCode_Tier_MIN ||
+ tier > protos::pbzero::V8WasmCode_Tier_MAX) {
+ return "UNKNOWN";
+ }
+ base::StringView name = V8WasmCode::Tier_Name(V8WasmCode::Tier(tier));
+ // Remove the "TIER_" prefix
+ return name.substr(5);
+}
+
} // namespace
V8Tracker::V8Tracker(TraceProcessorContext* context) : context_(context) {}
V8Tracker::~V8Tracker() = default;
-tables::V8IsolateTable::Id V8Tracker::InternIsolate(
- protozero::ConstBytes bytes) {
+IsolateId V8Tracker::InternIsolate(protozero::ConstBytes bytes) {
InternedV8Isolate::Decoder isolate(bytes);
- const UniquePid upid =
- context_->process_tracker->GetOrCreateProcess(isolate.pid());
- if (auto* id =
- isolate_index_.Find(std::make_pair(upid, isolate.isolate_id()));
- id) {
+ const IsolateKey isolate_key{
+ context_->process_tracker->GetOrCreateProcess(isolate.pid()),
+ isolate.isolate_id()};
+
+ if (auto* id = isolate_index_.Find(isolate_key); id) {
return *id;
}
+ return *isolate_index_.Insert(isolate_key, CreateIsolate(isolate)).first;
+}
+
+UserMemoryMapping* V8Tracker::FindEmbeddedBlobMapping(
+ UniquePid upid,
+ AddressRange embedded_blob_code) const {
+ UserMemoryMapping* m = context_->mapping_tracker->FindUserMappingForAddress(
+ upid, embedded_blob_code.start());
+ if (!m) {
+ return nullptr;
+ }
+
+ if (m->memory_range().start() == embedded_blob_code.start() &&
+ embedded_blob_code.end() <= m->memory_range().end()) {
+ return m;
+ }
+
+ return nullptr;
+}
+
+std::pair<V8Tracker::IsolateCodeRanges, bool> V8Tracker::GetIsolateCodeRanges(
+ UniquePid upid,
+ const protos::pbzero::InternedV8Isolate::Decoder& isolate) {
// TODO(carlscab): Implement support for no code range
PERFETTO_CHECK(isolate.has_code_range());
+ IsolateCodeRanges res;
+
+ InternedV8Isolate::CodeRange::Decoder code_range_proto(isolate.code_range());
+ AddressRange code_range = AddressRange::FromStartAndSize(
+ code_range_proto.base_address(), code_range_proto.size());
+
+ res.heap_code.Add(code_range);
+ if (isolate.has_embedded_blob_code_start_address() &&
+ isolate.embedded_blob_code_size() != 0) {
+ res.embedded_blob = AddressRange::FromStartAndSize(
+ isolate.embedded_blob_code_start_address(),
+ isolate.embedded_blob_code_size());
+
+ if (UserMemoryMapping* m =
+ FindEmbeddedBlobMapping(upid, *res.embedded_blob);
+ m) {
+ res.embedded_blob = m->memory_range();
+ }
+
+ res.heap_code.Remove(*res.embedded_blob);
+ }
+
+ return {std::move(res), code_range_proto.is_process_wide()};
+}
+
+AddressRangeMap<JitCache*> V8Tracker::CreateJitCaches(
+ UniquePid upid,
+ const IsolateCodeRanges& code_ranges) {
+ JitTracker* const jit_tracker = JitTracker::GetOrCreate(context_);
+ AddressRangeMap<JitCache*> jit_caches;
+ for (const AddressRange& r : code_ranges.heap_code) {
+ jit_caches.Emplace(r, jit_tracker->CreateJitCache("v8 code", upid, r));
+ }
+ if (code_ranges.embedded_blob) {
+ jit_caches.Emplace(*code_ranges.embedded_blob,
+ jit_tracker->CreateJitCache("v8 blob", upid,
+ *code_ranges.embedded_blob));
+ }
+
+ return jit_caches;
+}
+
+AddressRangeMap<JitCache*> V8Tracker::GetOrCreateSharedJitCaches(
+ UniquePid upid,
+ const IsolateCodeRanges& code_ranges) {
+ if (auto* shared = shared_code_ranges_.Find(upid); shared) {
+ PERFETTO_CHECK(shared->code_ranges == code_ranges);
+ return shared->jit_caches;
+ }
+
+ return shared_code_ranges_
+ .Insert(upid, {code_ranges, CreateJitCaches(upid, code_ranges)})
+ .first->jit_caches;
+}
+
+IsolateId V8Tracker::CreateIsolate(
+ const InternedV8Isolate::Decoder& isolate_proto) {
+ auto v8_isolate = InsertIsolate(isolate_proto);
+ const UniquePid upid = v8_isolate.upid();
+
+ auto [code_ranges, is_process_wide] =
+ GetIsolateCodeRanges(upid, isolate_proto);
+
+ PERFETTO_CHECK(isolates_
+ .Insert(v8_isolate.id(),
+ is_process_wide
+ ? GetOrCreateSharedJitCaches(upid, code_ranges)
+ : CreateJitCaches(upid, code_ranges))
+ .second);
+
+ return v8_isolate.id();
+}
+
+tables::V8IsolateTable::ConstRowReference V8Tracker::InsertIsolate(
+ const InternedV8Isolate::Decoder& isolate) {
InternedV8Isolate::CodeRange::Decoder code_range(isolate.code_range());
-
- auto v8_isolate_id =
- context_->storage->mutable_v8_isolate_table()
- ->Insert(
- {upid, isolate.isolate_id(),
- static_cast<int64_t>(isolate.embedded_blob_code_start_address()),
- static_cast<int64_t>(isolate.embedded_blob_code_size()),
- static_cast<int64_t>(code_range.base_address()),
- static_cast<int64_t>(code_range.size()),
- code_range.is_process_wide(),
- code_range.has_embedded_blob_code_copy_start_address()
- ? std::make_optional(static_cast<int64_t>(
- code_range.embedded_blob_code_copy_start_address()))
- : std::nullopt
-
- })
- .id;
- isolate_index_.Insert(std::make_pair(upid, isolate.isolate_id()),
- v8_isolate_id);
- return v8_isolate_id;
+ return context_->storage->mutable_v8_isolate_table()
+ ->Insert(
+ {context_->process_tracker->GetOrCreateProcess(isolate.pid()),
+ isolate.isolate_id(),
+ static_cast<int64_t>(isolate.embedded_blob_code_start_address()),
+ static_cast<int64_t>(isolate.embedded_blob_code_size()),
+ static_cast<int64_t>(code_range.base_address()),
+ static_cast<int64_t>(code_range.size()),
+ code_range.is_process_wide(),
+ code_range.has_embedded_blob_code_copy_start_address()
+ ? std::make_optional(static_cast<int64_t>(
+ code_range.embedded_blob_code_copy_start_address()))
+ : std::nullopt})
+ .row_reference;
}
tables::V8JsScriptTable::Id V8Tracker::InternJsScript(
protozero::ConstBytes bytes,
- tables::V8IsolateTable::Id isolate_id) {
+ IsolateId isolate_id) {
InternedV8JsScript::Decoder script(bytes);
if (auto* id =
@@ -137,7 +301,7 @@
tables::V8WasmScriptTable::Id V8Tracker::InternWasmScript(
protozero::ConstBytes bytes,
- tables::V8IsolateTable::Id isolate_id) {
+ IsolateId isolate_id) {
InternedV8WasmScript::Decoder script(bytes);
if (auto* id = wasm_script_index_.Find(
@@ -170,9 +334,13 @@
row.is_toplevel = function.is_toplevel();
row.kind =
context_->storage->InternString(JsFunctionKindToString(function.kind()));
- // TODO(carlscab): Row and line are hard. Offset is in bytes, row and line are
- // in characters and we potentially have a multi byte encoding (UTF16). Good
- // luck!
+ // TODO(carlscab): Line and column are hard. Offset is in bytes, line and
+ // column are in characters and we potentially have a multi byte encoding
+ // (UTF16). Good luck!
+ if (function.has_byte_offset()) {
+ row.line = 1;
+ row.col = function.byte_offset();
+ }
if (auto* id = js_function_index_.Find(row); id) {
return *id;
@@ -184,35 +352,168 @@
return function_id;
}
-void V8Tracker::AddJsCode(int64_t,
- tables::V8IsolateTable::Id,
- tables::V8JsFunctionTable::Id,
- const protos::pbzero::V8JsCode::Decoder&) {
- // TODO(carlscab): Implement
+JitCache* V8Tracker::MaybeFindJitCache(IsolateId isolate_id,
+ AddressRange code_range) const {
+ if (code_range.empty()) {
+ context_->storage->IncrementStats(stats::v8_code_load_missing_code_range);
+ return nullptr;
+ }
+ auto* isolate = isolates_.Find(isolate_id);
+ PERFETTO_CHECK(isolate);
+ if (auto it = isolate->FindRangeThatContains(code_range);
+ it != isolate->end()) {
+ return it->second;
+ }
+
+ return nullptr;
}
-void V8Tracker::AddInternalCode(
- int64_t,
- tables::V8IsolateTable::Id,
- const protos::pbzero::V8InternalCode::Decoder&) {
- // TODO(carlscab): Implement
+JitCache* V8Tracker::FindJitCache(IsolateId isolate_id,
+ AddressRange code_range) const {
+ if (code_range.empty()) {
+ context_->storage->IncrementStats(stats::v8_code_load_missing_code_range);
+ return nullptr;
+ }
+ JitCache* cache = MaybeFindJitCache(isolate_id, code_range);
+ if (!cache) {
+ context_->storage->IncrementStats(stats::v8_no_code_range);
+ }
+ return cache;
}
-void V8Tracker::AddWasmCode(int64_t,
- tables::V8IsolateTable::Id,
- tables::V8WasmScriptTable::Id,
- const protos::pbzero::V8WasmCode::Decoder&) {
- // TODO(carlscab): Implement
+void V8Tracker::AddJsCode(int64_t timestamp,
+ UniqueTid utid,
+ IsolateId isolate_id,
+ tables::V8JsFunctionTable::Id function_id,
+ const V8JsCode::Decoder& code) {
+ const StringId tier =
+ context_->storage->InternString(JsCodeTierToString(code.tier()));
+
+ const AddressRange code_range = AddressRange::FromStartAndSize(
+ code.instruction_start(), code.instruction_size_bytes());
+
+ JitCache* jit_cache = nullptr;
+
+ if (IsInterpretedCode(code)) {
+ // If --interpreted_frames_native_stack is specified interpreted frames will
+ // also be emitted as native functions.
+ // TODO(carlscab): Add an additional tier to for NATIVE_IGNITION_FRAME. Int
+ // he meantime we can infer that this is the case if we have a hit in the
+ // jit cache. NOte we call MaybeFindJitCache to not log an error if there is
+ // no hit.
+ jit_cache = MaybeFindJitCache(isolate_id, code_range);
+ if (!jit_cache) {
+ context_->storage->mutable_v8_js_code_table()->Insert(
+ {std::nullopt, function_id, tier,
+ context_->storage->InternString(base::StringView(base::Base64Encode(
+ code.bytecode().data, code.bytecode().size)))});
+ return;
+ }
+ } else if (IsNativeCode(code)) {
+ jit_cache = FindJitCache(isolate_id, code_range);
+ } else {
+ context_->storage->IncrementStats(stats::v8_unknown_code_type);
+ return;
+ }
+
+ if (!jit_cache) {
+ return;
+ }
+
+ auto function =
+ *context_->storage->v8_js_function_table().FindById(function_id);
+ auto script = *context_->storage->v8_js_script_table().FindById(
+ function.v8_js_script_id());
+ const auto jit_code_id = jit_cache->LoadCode(
+ timestamp, utid, code_range, function.name(),
+ JitCache::SourceLocation{script.name(), function.line().value_or(0)},
+ code.has_machine_code()
+ ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
+ code.machine_code().size))
+ : TraceBlobView());
+
+ context_->storage->mutable_v8_js_code_table()->Insert(
+ {jit_code_id, function_id, tier});
}
-void V8Tracker::AddRegExpCode(int64_t,
- tables::V8IsolateTable::Id,
- const protos::pbzero::V8RegExpCode::Decoder&) {
- // TODO(carlscab): Implement
+void V8Tracker::AddInternalCode(int64_t timestamp,
+ UniqueTid utid,
+ IsolateId isolate_id,
+ const V8InternalCode::Decoder& code) {
+ const AddressRange code_range = AddressRange::FromStartAndSize(
+ code.instruction_start(), code.instruction_size_bytes());
+ JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
+ if (!jit_cache) {
+ return;
+ }
+
+ const StringId function_name = context_->storage->InternString(code.name());
+ const StringId type =
+ context_->storage->InternString(InternalCodeTypeToString(code.type()));
+ const auto jit_code_id = jit_cache->LoadCode(
+ timestamp, utid, code_range, function_name, std::nullopt,
+ code.has_machine_code()
+ ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
+ code.machine_code().size))
+ : TraceBlobView());
+
+ context_->storage->mutable_v8_internal_code_table()->Insert(
+ {jit_code_id, isolate_id, function_name, type});
}
-StringId V8Tracker::InternV8String(
- const protos::pbzero::V8String::Decoder& v8_string) {
+void V8Tracker::AddWasmCode(int64_t timestamp,
+ UniqueTid utid,
+ IsolateId isolate_id,
+ tables::V8WasmScriptTable::Id script_id,
+ const V8WasmCode::Decoder& code) {
+ const AddressRange code_range = AddressRange::FromStartAndSize(
+ code.instruction_start(), code.instruction_size_bytes());
+ JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
+ if (!jit_cache) {
+ return;
+ }
+
+ const StringId function_name =
+ context_->storage->InternString(code.function_name());
+ const StringId tier =
+ context_->storage->InternString(WasmCodeTierToString(code.tier()));
+
+ const auto jit_code_id = jit_cache->LoadCode(
+ timestamp, utid, code_range, function_name, std::nullopt,
+ code.has_machine_code()
+ ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
+ code.machine_code().size))
+ : TraceBlobView());
+
+ context_->storage->mutable_v8_wasm_code_table()->Insert(
+ {jit_code_id, isolate_id, script_id, function_name, tier});
+}
+
+void V8Tracker::AddRegExpCode(int64_t timestamp,
+ UniqueTid utid,
+ IsolateId isolate_id,
+ const V8RegExpCode::Decoder& code) {
+ const AddressRange code_range = AddressRange::FromStartAndSize(
+ code.instruction_start(), code.instruction_size_bytes());
+ JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
+ if (!jit_cache) {
+ return;
+ }
+
+ const StringId function_name = context_->storage->InternString("[RegExp]");
+ const StringId pattern = InternV8String(V8String::Decoder(code.pattern()));
+ const auto jit_code_id = jit_cache->LoadCode(
+ timestamp, utid, code_range, function_name, std::nullopt,
+ code.has_machine_code()
+ ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
+ code.machine_code().size))
+ : TraceBlobView());
+
+ context_->storage->mutable_v8_regexp_code_table()->Insert(
+ {jit_code_id, isolate_id, pattern});
+}
+
+StringId V8Tracker::InternV8String(const V8String::Decoder& v8_string) {
auto& storage = *context_->storage;
if (v8_string.has_latin1()) {
return storage.InternString(
@@ -221,12 +522,12 @@
if (v8_string.has_utf16_le()) {
return storage.InternString(
- base::StringView(ConvertUtf16LeToUtf8(v8_string.latin1())));
+ base::StringView(ConvertUtf16LeToUtf8(v8_string.utf16_le())));
}
if (v8_string.has_utf16_be()) {
return storage.InternString(
- base::StringView(ConvertUtf16BeToUtf8(v8_string.latin1())));
+ base::StringView(ConvertUtf16BeToUtf8(v8_string.utf16_be())));
}
return storage.InternString("");
}
diff --git a/src/trace_processor/importers/proto/v8_tracker.h b/src/trace_processor/importers/proto/v8_tracker.h
index 3d85377..b17affb 100644
--- a/src/trace_processor/importers/proto/v8_tracker.h
+++ b/src/trace_processor/importers/proto/v8_tracker.h
@@ -19,11 +19,14 @@
#include <cstddef>
#include <cstdint>
+#include <memory>
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/hash.h"
#include "perfetto/protozero/field.h"
#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "src/trace_processor/importers/common/address_range.h"
+#include "src/trace_processor/importers/proto/jit_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/tables/v8_tables_py.h"
#include "src/trace_processor/types/destructible.h"
@@ -32,6 +35,11 @@
namespace perfetto {
namespace trace_processor {
+class TraceStorage;
+class UserMemoryMapping;
+
+using IsolateId = tables::V8IsolateTable::Id;
+
// Keeps track of V8 related objects.
class V8Tracker : public Destructible {
public:
@@ -44,69 +52,39 @@
~V8Tracker() override;
- tables::V8IsolateTable::Id InternIsolate(protozero::ConstBytes bytes);
- tables::V8JsScriptTable::Id InternJsScript(
- protozero::ConstBytes bytes,
- tables::V8IsolateTable::Id isolate_id);
- tables::V8WasmScriptTable::Id InternWasmScript(
- protozero::ConstBytes bytes,
- tables::V8IsolateTable::Id isolate_id);
+ IsolateId InternIsolate(protozero::ConstBytes bytes);
+ tables::V8JsScriptTable::Id InternJsScript(protozero::ConstBytes bytes,
+ IsolateId isolate_id);
+ tables::V8WasmScriptTable::Id InternWasmScript(protozero::ConstBytes bytes,
+ IsolateId isolate_id);
tables::V8JsFunctionTable::Id InternJsFunction(
protozero::ConstBytes bytes,
StringId name,
tables::V8JsScriptTable::Id script_id);
void AddJsCode(int64_t timestamp,
- tables::V8IsolateTable::Id isolate_id,
+ UniqueTid utid,
+ IsolateId isolate_id,
tables::V8JsFunctionTable::Id function_id,
const protos::pbzero::V8JsCode::Decoder& code);
void AddInternalCode(int64_t timestamp,
- tables::V8IsolateTable::Id v8_isolate_id,
+ UniqueTid utid,
+ IsolateId v8_isolate_id,
const protos::pbzero::V8InternalCode::Decoder& code);
void AddWasmCode(int64_t timestamp,
- tables::V8IsolateTable::Id isolate_id,
+ UniqueTid utid,
+ IsolateId isolate_id,
tables::V8WasmScriptTable::Id script_id,
const protos::pbzero::V8WasmCode::Decoder& code);
void AddRegExpCode(int64_t timestamp,
- tables::V8IsolateTable::Id v8_isolate_id,
+ UniqueTid utid,
+ IsolateId v8_isolate_id,
const protos::pbzero::V8RegExpCode::Decoder& code);
private:
- explicit V8Tracker(TraceProcessorContext* context);
-
- StringId InternV8String(const protos::pbzero::V8String::Decoder& v8_string);
-
- TraceProcessorContext* const context_;
-
- struct IsolateIndexHash {
- size_t operator()(const std::pair<UniquePid, int32_t>& v) const {
- return static_cast<size_t>(base::Hasher::Combine(v.first, v.second));
- }
- };
- base::FlatHashMap<std::pair<UniquePid, int32_t>,
- tables::V8IsolateTable::Id,
- IsolateIndexHash>
- isolate_index_;
-
- struct ScriptIndexHash {
- size_t operator()(
- const std::pair<tables::V8IsolateTable::Id, int32_t>& v) const {
- return static_cast<size_t>(
- base::Hasher::Combine(v.first.value, v.second));
- }
- };
- base::FlatHashMap<std::pair<tables::V8IsolateTable::Id, int32_t>,
- tables::V8JsScriptTable::Id,
- ScriptIndexHash>
- js_script_index_;
- base::FlatHashMap<std::pair<tables::V8IsolateTable::Id, int32_t>,
- tables::V8WasmScriptTable::Id,
- ScriptIndexHash>
- wasm_script_index_;
-
struct JsFunctionHash {
size_t operator()(const tables::V8JsFunctionTable::Row& v) const {
return static_cast<size_t>(base::Hasher::Combine(
@@ -114,6 +92,94 @@
v.kind.raw_id(), v.line.value_or(0), v.col.value_or(0)));
}
};
+
+ struct IsolateCodeRanges {
+ AddressSet heap_code;
+ std::optional<AddressRange> embedded_blob;
+
+ bool operator==(const IsolateCodeRanges& o) const {
+ return heap_code == o.heap_code && embedded_blob == o.embedded_blob;
+ }
+ };
+
+ struct SharedCodeRanges {
+ IsolateCodeRanges code_ranges;
+ AddressRangeMap<JitCache*> jit_caches;
+ };
+
+ // V8 internal isolate_id and upid uniquely identify an isolate in a trace.
+ struct IsolateKey {
+ struct Hasher {
+ size_t operator()(const IsolateKey& v) const {
+ return base::Hasher::Combine(v.upid, v.isolate_id);
+ }
+ };
+
+ bool operator==(const IsolateKey& other) const {
+ return upid == other.upid && isolate_id == other.isolate_id;
+ }
+
+ bool operator!=(const IsolateKey& other) const { return !(*this == other); }
+ UniquePid upid;
+ int32_t isolate_id;
+ };
+
+ struct ScriptIndexHash {
+ size_t operator()(const std::pair<IsolateId, int32_t>& v) const {
+ return static_cast<size_t>(
+ base::Hasher::Combine(v.first.value, v.second));
+ }
+ };
+
+ explicit V8Tracker(TraceProcessorContext* context);
+
+ StringId InternV8String(const protos::pbzero::V8String::Decoder& v8_string);
+
+ tables::V8IsolateTable::ConstRowReference InsertIsolate(
+ const protos::pbzero::InternedV8Isolate::Decoder& isolate);
+
+ IsolateId CreateIsolate(
+ const protos::pbzero::InternedV8Isolate::Decoder& isolate);
+
+ // Find JitCache that fully contains the given range. Returns null if not
+ // found and updates error counter.
+ JitCache* FindJitCache(IsolateId isolate_id, AddressRange code_range) const;
+ // Same as `FindJitCache` but error counter is not updated if no cache is
+ // found.
+ JitCache* MaybeFindJitCache(IsolateId isolate_id,
+ AddressRange code_range) const;
+
+ UserMemoryMapping* FindEmbeddedBlobMapping(
+ UniquePid upid,
+ AddressRange embedded_blob_code) const;
+
+ std::pair<IsolateCodeRanges, bool> GetIsolateCodeRanges(
+ UniquePid upid,
+ const protos::pbzero::InternedV8Isolate::Decoder& isolate);
+ AddressRangeMap<JitCache*> GetOrCreateSharedJitCaches(
+ UniquePid upid,
+ const IsolateCodeRanges& code_ranges);
+ AddressRangeMap<JitCache*> CreateJitCaches(
+ UniquePid upid,
+ const IsolateCodeRanges& code_ranges);
+
+ TraceProcessorContext* const context_;
+
+ base::FlatHashMap<IsolateId, AddressRangeMap<JitCache*>> isolates_;
+
+ // Multiple isolates in the same process might share the code. Keep track of
+ // those here.
+ base::FlatHashMap<UniquePid, SharedCodeRanges> shared_code_ranges_;
+
+ base::FlatHashMap<IsolateKey, IsolateId, IsolateKey::Hasher> isolate_index_;
+ base::FlatHashMap<std::pair<IsolateId, int32_t>,
+ tables::V8JsScriptTable::Id,
+ ScriptIndexHash>
+ js_script_index_;
+ base::FlatHashMap<std::pair<IsolateId, int32_t>,
+ tables::V8WasmScriptTable::Id,
+ ScriptIndexHash>
+ wasm_script_index_;
base::FlatHashMap<tables::V8JsFunctionTable::Row,
tables::V8JsFunctionTable::Id,
JsFunctionHash>
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.cc
index 4ff33cc..dda42ab 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.cc
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.cc
@@ -86,20 +86,20 @@
std::vector<Edge> roots;
bool parse_error = false;
auto root_node_ids = start.int_values(&parse_error);
- auto max_weights = end.int_values(&parse_error);
+ auto target_weights = end.int_values(&parse_error);
- for (; root_node_ids && max_weights; ++root_node_ids, ++max_weights) {
+ for (; root_node_ids && target_weights; ++root_node_ids, ++target_weights) {
roots.push_back(Edge{static_cast<uint32_t>(*root_node_ids),
- static_cast<uint32_t>(*max_weights)});
+ static_cast<uint32_t>(*target_weights)});
}
if (parse_error) {
return base::ErrStatus(
- "Failed while parsing root_node_ids or root_max_weights");
+ "Failed while parsing root_node_ids or root_target_weights");
}
- if (static_cast<bool>(root_node_ids) != static_cast<bool>(max_weights)) {
+ if (static_cast<bool>(root_node_ids) != static_cast<bool>(target_weights)) {
return base::ErrStatus(
- "dfs_weight_bounded: length of root_node_ids and root_max_weights "
+ "dfs_weight_bounded: length of root_node_ids and root_target_weights "
"columns is not the same");
}
return roots;
@@ -108,7 +108,8 @@
void DfsWeightBoundedImpl(
tables::DfsWeightBoundedTable* table,
const std::vector<Destinations>& source_to_destinations_map,
- const std::vector<Edge>& roots) {
+ const std::vector<Edge>& roots,
+ const bool is_target_weight_floor) {
struct StackState {
uint32_t id;
uint32_t weight;
@@ -131,22 +132,26 @@
continue;
}
seen_node_ids[stack_state.id] = true;
-
- // We want to greedily return all possible edges that are reachable within
- // the target weight. If an edge already fails the requirement, skip it
- // and don't include it's weight but continue the search, some other edges
- // might fit.
- if (total_weight + stack_state.weight > root.weight) {
- continue;
- }
total_weight += stack_state.weight;
+ if (!is_target_weight_floor && total_weight > root.weight) {
+ // If target weight is a ceiling weight then we don't want to include
+ // the last node that crosses the threshold.
+ break;
+ }
+
tables::DfsWeightBoundedTable::Row row;
row.root_node_id = root.id;
row.node_id = stack_state.id;
row.parent_node_id = stack_state.parent_id;
table->Insert(row);
+ if (total_weight > root.weight) {
+ // If the target weight is a floor weight, we add the last node that
+ // crossed the threshold before exiting the search.
+ break;
+ }
+
PERFETTO_DCHECK(stack_state.id < source_to_destinations_map.size());
const auto& children = source_to_destinations_map[stack_state.id];
@@ -176,24 +181,29 @@
base::StatusOr<std::unique_ptr<Table>> DfsWeightBounded::ComputeTable(
const std::vector<SqlValue>& arguments) {
- PERFETTO_CHECK(arguments.size() == 5);
+ PERFETTO_CHECK(arguments.size() == 6);
const SqlValue& raw_source_ids = arguments[0];
const SqlValue& raw_dest_ids = arguments[1];
const SqlValue& raw_edge_weights = arguments[2];
const SqlValue& raw_root_ids = arguments[3];
- const SqlValue& raw_root_max_weights = arguments[4];
+ const SqlValue& raw_root_target_weights = arguments[4];
+ const SqlValue& raw_is_target_weight_floor = arguments[5];
if (raw_source_ids.is_null() && raw_dest_ids.is_null() &&
- raw_edge_weights.is_null() && raw_root_ids.is_null() &&
- raw_root_max_weights.is_null()) {
+ raw_edge_weights.is_null()) {
+ return std::unique_ptr<Table>(
+ std::make_unique<tables::DfsWeightBoundedTable>(pool_));
+ }
+
+ if (raw_root_ids.is_null() && raw_root_target_weights.is_null()) {
return std::unique_ptr<Table>(
std::make_unique<tables::DfsWeightBoundedTable>(pool_));
}
if (raw_source_ids.is_null() || raw_dest_ids.is_null() ||
raw_edge_weights.is_null() || raw_root_ids.is_null() ||
- raw_root_max_weights.is_null()) {
+ raw_root_target_weights.is_null()) {
return base::ErrStatus(
"dfs_weight_bounded: either all arguments should be null or none "
"should be");
@@ -214,9 +224,9 @@
return base::ErrStatus(
"dfs_weight_bounded: root_ids should be a repeated field");
}
- if (raw_root_max_weights.type != SqlValue::kBytes) {
+ if (raw_root_target_weights.type != SqlValue::kBytes) {
return base::ErrStatus(
- "dfs_weight_bounded: root_max_weights should be a repeated field");
+ "dfs_weight_bounded: root_target_weights should be a repeated field");
}
protos::pbzero::ProtoBuilderResult::Decoder proto_source_ids(
@@ -263,25 +273,27 @@
protos::pbzero::RepeatedBuilderResult::Decoder root_ids(
proto_root_ids.repeated());
- protos::pbzero::ProtoBuilderResult::Decoder proto_root_max_weights(
- static_cast<const uint8_t*>(raw_root_max_weights.AsBytes()),
- raw_root_max_weights.bytes_count);
- if (!proto_root_max_weights.is_repeated()) {
+ protos::pbzero::ProtoBuilderResult::Decoder proto_root_target_weights(
+ static_cast<const uint8_t*>(raw_root_target_weights.AsBytes()),
+ raw_root_target_weights.bytes_count);
+ if (!proto_root_target_weights.is_repeated()) {
return base::ErrStatus(
- "dfs_weight_bounded: root_max_weights is not generated by "
+ "dfs_weight_bounded: root_target_weights is not generated by "
"RepeatedField function");
}
- protos::pbzero::RepeatedBuilderResult::Decoder root_max_weights(
- proto_root_max_weights.repeated());
+ protos::pbzero::RepeatedBuilderResult::Decoder root_target_weights(
+ proto_root_target_weights.repeated());
+ bool is_target_weight_floor =
+ static_cast<bool>(raw_is_target_weight_floor.AsLong());
ASSIGN_OR_RETURN(auto map, ParseSourceToDestionationsMap(source_ids, dest_ids,
edge_weights));
ASSIGN_OR_RETURN(auto roots,
- ParseRootToMaxWeightMap(root_ids, root_max_weights));
+ ParseRootToMaxWeightMap(root_ids, root_target_weights));
auto table = std::make_unique<tables::DfsWeightBoundedTable>(pool_);
- DfsWeightBoundedImpl(table.get(), map, roots);
+ DfsWeightBoundedImpl(table.get(), map, roots, is_target_weight_floor);
return std::unique_ptr<Table>(std::move(table));
}
diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.h b/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.h
index f1af772..92caf25 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/dfs_weight_bounded.h
@@ -54,6 +54,13 @@
// uint32 values corresponding to the max sum of edge weights inclusive,
// at which point the DFS from the |root_node_ids| stops. This number of
// values should be the same as |root_node_ids|.
+// 6) |is_target_weight_floor|: Whether the target_weight is a floor weight or
+// ceiling weight.
+// If it's floor, the search stops right after we exceed the target weight,
+// and we include the node that pushed just passed the target. If ceiling,
+// the search stops right before the target weight and the node that would
+// have pushed us passed the target is not included.
+
//
// Returns:
// A table with the nodes reachable from the start node, their "parent" in
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 0570082..e129565 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
+++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/tables.py
@@ -206,6 +206,9 @@
C("in_root_max_weights",
CppOptional(CppUint32()),
flags=ColumnFlag.HIDDEN),
+ C("in_is_target_weight_floor",
+ CppOptional(CppUint32()),
+ flags=ColumnFlag.HIDDEN),
])
# Keep this list sorted.
diff --git a/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql b/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
index 6214716..34fc1de 100644
--- a/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/graphs/search.sql
@@ -131,7 +131,7 @@
-- (
-- SELECT
-- id AS root_node_id,
--- id - COALESCE(prev_id, id) AS root_max_weight
+-- id - COALESCE(prev_id, id) AS root_target_weight
-- FROM _wakeup_chain
-- ));
-- ```
@@ -150,7 +150,7 @@
graph_table TableOrSubquery,
-- A table/view/subquery corresponding to start nodes to |graph_table| which will be the
-- roots of the reachability trees. This table must have the columns
- -- "root_node_id" and "root_max_weight" corresponding to the starting node id and the max
+ -- "root_node_id" and "root_target_weight" corresponding to the starting node id and the max
-- weight allowed on the tree.
--
-- Note: the columns must contain uint32 similar to ids in trace processor
@@ -158,7 +158,14 @@
-- implementation makes assumptions on this for performance reasons and, if
-- this criteria is not, can lead to enormous amounts of memory being
-- allocated.
- root_table TableOrSubquery
+ root_table TableOrSubquery,
+ -- Whether the target_weight is a floor weight or ceiling weight.
+ -- If it's floor, the search stops right after we exceed the target weight, and we
+ -- include the node that pushed just passed the target. If ceiling, the search stops
+ -- right before the target weight and the node that would have pushed us passed the
+ -- target is not included.
+ is_target_weight_floor Expr
+
)
-- The returned table has the schema (root_node_id, node_id UINT32, parent_node_id UINT32).
-- |root_node_id| is the id of the starting node under which this edge was encountered.
@@ -175,6 +182,7 @@
(SELECT RepeatedField(dest_node_id) FROM __temp_graph_table),
(SELECT RepeatedField(edge_weight) FROM __temp_graph_table),
(SELECT RepeatedField(root_node_id) FROM __temp_root_table),
- (SELECT RepeatedField(root_max_weight) FROM __temp_root_table)
+ (SELECT RepeatedField(root_target_weight) FROM __temp_root_table),
+ $is_target_weight_floor
) dt
);
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql b/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
index 2326aab..28edf7f 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql
@@ -160,4 +160,5 @@
_subtree_native_size_bytes(t.id) AS dominated_native_size_bytes,
d.depth
FROM _heap_graph_dominator_tree t
-JOIN _heap_graph_dominator_tree_depth d USING(id);
+JOIN _heap_graph_dominator_tree_depth d USING(id)
+ORDER BY id;
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
index bd2dc94..d9b8f42 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
@@ -17,6 +17,7 @@
perfetto_sql_source_set("sched") {
deps = [ "utilization" ]
sources = [
+ "runnable.sql",
"states.sql",
"thread_executing_span.sql",
"thread_level_parallelism.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql b/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql
new file mode 100644
index 0000000..38956b1
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/runnable.sql
@@ -0,0 +1,53 @@
+--
+-- 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.
+--
+
+-- Previous runnable slice on the same thread.
+-- For each "Running" thread state finds:
+-- - 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.
+ id INT,
+ -- Previous runnable `thread_state.id`.
+ prev_runnable_id INT,
+ -- Previous runnable `thread_state.id` with valid waker
+ -- thread.
+ prev_wakeup_runnable_id INT
+) AS
+WITH running_and_runnable AS (
+ SELECT
+ id,
+ state,
+ MAX(id)
+ FILTER (WHERE state != 'Running')
+ OVER utid_part AS prev_runnable_id,
+ MAX(id)
+ FILTER (WHERE
+ waker_utid IS NOT NULL
+ AND (irq_context IS NULL OR irq_context != 1))
+ OVER utid_part AS prev_wakeup_runnable_id
+ FROM thread_state
+ -- Optimal operation for state IN (R, R+, Running)
+ WHERE state GLOB 'R*' AND dur != -1
+ WINDOW utid_part AS (PARTITION BY utid ORDER BY id)
+)
+SELECT
+ id,
+ prev_runnable_id,
+ prev_wakeup_runnable_id
+FROM running_and_runnable
+WHERE state = 'Running'
+ORDER BY id;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql b/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql
index 348d261..ffafd6d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql
@@ -180,6 +180,7 @@
USING (utid);
-- Mapping from running thread state to runnable
+-- TODO(zezeozue): Switch to use `sched_previous_runnable_on_thread`.
CREATE PERFETTO TABLE _waker_map
AS
WITH x AS (
diff --git a/src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql b/src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql
index 4335a7e..34c3948 100644
--- a/src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/stack_trace/jit.sql
@@ -15,6 +15,7 @@
-- Represents a jitted code snippet.
+-- TODO(carlscab): Make public
CREATE PERFETTO VIEW _jit_code (
-- Unique jit code id.
jit_code_id UINT,
@@ -47,8 +48,9 @@
FROM __intrinsic_jit_code;
-- Represents a jitted frame.
+-- TODO(carlscab): Make public
CREATE PERFETTO VIEW _jit_frame (
- -- Jitted code snipped the frame is in (joins with jit_code.jit_code_id).
+ -- Jitted code snipped the frame is in (joins with _jit_code.jit_code_id).
jit_code_id UINT,
-- Jitted frame (joins with stack_profile_frame.id).
frame_id UINT
diff --git a/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql b/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql
index 6765272..124f530 100644
--- a/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/v8/jit.sql
@@ -76,7 +76,8 @@
CREATE PERFETTO VIEW v8_js_script (
-- Unique V8 JS script id.
v8_js_script_id UINT,
- -- V8 isolate this script belongs to (joinable with v8_isolate.v8_isolate_id).
+ -- V8 isolate this script belongs to (joinable with
+ -- `v8_isolate.v8_isolate_id`).
v8_isolate_id UINT,
-- Script id used by the V8 engine.
internal_script_id UINT,
@@ -102,7 +103,8 @@
CREATE PERFETTO VIEW v8_wasm_script (
-- Unique V8 WASM script id.
v8_wasm_script_id UINT,
- -- V8 Isolate this script belongs to (joinable with v8_isolate.v8_isolate_id).
+ -- V8 Isolate this script belongs to (joinable with
+ -- `v8_isolate.v8_isolate_id`).
v8_isolate_id UINT,
-- Script id used by the V8 engine.
internal_script_id UINT,
@@ -128,7 +130,7 @@
-- Function name.
name STRING,
-- Script where the function is defined (joinable with
- -- v8_js_script.v8_js_script_id).
+ -- `v8_js_script.v8_js_script_id`).
v8_js_script_id UINT,
-- Whether this function represents the top level script.
is_toplevel BOOL,
@@ -149,3 +151,108 @@
col
FROM
__intrinsic_v8_js_function;
+
+
+-- Represents a v8 code snippet for a Javascript function. A given function can
+-- have multiple code snippets (e.g. for different compilation tiers, or as the
+-- function moves around the heap).
+-- TODO(carlscab): Make public once `_jit_code` is public too
+CREATE PERFETTO VIEW _v8_js_code(
+ -- Unique id
+ id UINT,
+ -- Associated jit code. Set for all tiers except IGNITION. Joinable with
+ -- `_jit_code.jit_code_id`.
+ jit_code_id UINT,
+ -- JS function for this snippet. Joinable with
+ -- `v8_js_function.v8_js_function_id`.
+ v8_js_function_id UINT,
+ -- Compilation tier
+ tier STRING,
+ -- V8 VM bytecode. Set only for the IGNITION tier.
+ bytecode BYTES
+) AS
+SELECT
+ id,
+ jit_code_id,
+ v8_js_function_id,
+ tier,
+ base64_decode(bytecode_base64) AS bytecode
+FROM
+ __intrinsic_v8_js_code;
+
+
+-- Represents a v8 code snippet for a v8 internal function.
+-- TODO(carlscab): Make public once `_jit_code` is public too
+CREATE PERFETTO VIEW _v8_internal_code(
+ -- Unique id
+ id UINT,
+ -- Associated jit code. Joinable with `_jit_code.jit_code_id`.
+ jit_code_id UINT,
+ -- V8 Isolate this code was created in. Joinable with
+ -- `v8_isolate.v8_isolate_id`.
+ v8_isolate_id UINT,
+ -- Function name.
+ function_name STRING,
+ -- Type of internal code.
+ code_type STRING
+) AS
+SELECT
+ id,
+ jit_code_id,
+ v8_isolate_id,
+ function_name,
+ code_type
+FROM
+ __intrinsic_v8_internal_code;
+
+-- Represents the code associated to a WASM function.
+-- TODO(carlscab): Make public once `_jit_code` is public too
+CREATE PERFETTO VIEW _v8_wasm_code(
+ -- Unique id
+ id UINT,
+ -- Associated jit code. Joinable with `_jit_code.jit_code_id`.
+ jit_code_id UINT,
+ -- V8 Isolate this code was created in. Joinable with
+ -- `v8_isolate.v8_isolate_id`.
+ v8_isolate_id UINT,
+ -- Script where the function is defined. Joinable with
+ -- `v8_wasm_script.v8_wasm_script_id`.
+ v8_wasm_script_id UINT,
+ -- Function name.
+ function_name STRING,
+ -- Compilation tier.
+ tier STRING,
+ -- Offset into the WASM module where the function starts.
+ code_offset_in_module INT
+ ) AS
+SELECT
+ id,
+ jit_code_id,
+ v8_isolate_id,
+ v8_wasm_script_id,
+ function_name,
+ tier,
+ code_offset_in_module
+FROM
+ __intrinsic_v8_wasm_code;
+
+-- Represents the code associated to a regular expression
+-- TODO(carlscab): Make public once `_jit_code` is public too
+CREATE PERFETTO VIEW _v8_regexp_code(
+ -- Unique id
+ id UINT,
+ -- Associated jit code. Joinable with `_jit_code.jit_code_id`.
+ jit_code_id UINT,
+ -- V8 Isolate this code was created in. Joinable with
+ -- `v8_isolate.v8_isolate_id`.
+ v8_isolate_id UINT,
+ -- The pattern the this regular expression was compiled from.
+ pattern STRING
+) AS
+SELECT
+ id,
+ jit_code_id,
+ v8_isolate_id,
+ pattern
+FROM
+ __intrinsic_v8_regexp_code;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 7085055..7495cfc 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -289,6 +289,15 @@
F(v8_intern_errors, \
kSingle, kDataLoss, kAnalysis, \
"Failed to resolve V8 interned data."), \
+ F(v8_no_defaults, \
+ kSingle, kDataLoss, kAnalysis, \
+ "Failed to resolve V8 default data."), \
+ F(v8_no_code_range, \
+ kSingle, kError, kAnalysis, \
+ "V8 isolate had no code range."), \
+ F(v8_unknown_code_type, kSingle, kError, kAnalysis, ""), \
+ F(v8_code_load_missing_code_range, kSingle, kError, kAnalysis, \
+ "V8 load had no code range or an empty one. Event ignored."), \
F(winscope_sf_layers_parse_errors, kSingle, kInfo, kAnalysis, \
"SurfaceFlinger layers snapshot has unknown fields, which results in " \
"some arguments missing. You may need a newer version of trace " \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index ec014d7..c2158d7 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -761,6 +761,30 @@
tables::V8JsFunctionTable* mutable_v8_js_function_table() {
return &v8_js_function_table_;
}
+ const tables::V8JsCodeTable& v8_js_code_table() const {
+ return v8_js_code_table_;
+ }
+ tables::V8JsCodeTable* mutable_v8_js_code_table() {
+ return &v8_js_code_table_;
+ }
+ const tables::V8InternalCodeTable& v8_internal_code_table() const {
+ return v8_internal_code_table_;
+ }
+ tables::V8InternalCodeTable* mutable_v8_internal_code_table() {
+ return &v8_internal_code_table_;
+ }
+ const tables::V8WasmCodeTable& v8_wasm_code_table() const {
+ return v8_wasm_code_table_;
+ }
+ tables::V8WasmCodeTable* mutable_v8_wasm_code_table() {
+ return &v8_wasm_code_table_;
+ }
+ const tables::V8RegexpCodeTable& v8_regexp_code_table() const {
+ return v8_regexp_code_table_;
+ }
+ tables::V8RegexpCodeTable* mutable_v8_regexp_code_table() {
+ return &v8_regexp_code_table_;
+ }
const tables::JitCodeTable& jit_code_table() const { return jit_code_table_; }
tables::JitCodeTable* mutable_jit_code_table() { return &jit_code_table_; }
@@ -1075,6 +1099,10 @@
tables::V8JsScriptTable v8_js_script_table_{&string_pool_};
tables::V8WasmScriptTable v8_wasm_script_table_{&string_pool_};
tables::V8JsFunctionTable v8_js_function_table_{&string_pool_};
+ tables::V8JsCodeTable v8_js_code_table_{&string_pool_};
+ tables::V8InternalCodeTable v8_internal_code_table_{&string_pool_};
+ tables::V8WasmCodeTable v8_wasm_code_table_{&string_pool_};
+ tables::V8RegexpCodeTable v8_regexp_code_table_{&string_pool_};
// Jit tables
tables::JitCodeTable jit_code_table_{&string_pool_};
@@ -1134,6 +1162,9 @@
struct std::hash<::perfetto::trace_processor::tables::HeapGraphObjectTable::Id>
: std::hash<::perfetto::trace_processor::BaseId> {};
template <>
+struct std::hash<::perfetto::trace_processor::tables::V8IsolateTable::Id>
+ : std::hash<::perfetto::trace_processor::BaseId> {};
+template <>
struct std::hash<::perfetto::trace_processor::tables::JitCodeTable::Id>
: std::hash<::perfetto::trace_processor::BaseId> {};
diff --git a/src/trace_processor/tables/jit_tables.py b/src/trace_processor/tables/jit_tables.py
index aff2c61..44707a6 100644
--- a/src/trace_processor/tables/jit_tables.py
+++ b/src/trace_processor/tables/jit_tables.py
@@ -24,6 +24,7 @@
from python.generators.trace_processor_table.public import CppString
from python.generators.trace_processor_table.public import CppUint32
from python.generators.trace_processor_table.public import CppTableId
+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 TableDoc
from .profiler_tables import STACK_PROFILE_FRAME_TABLE
@@ -33,7 +34,7 @@
class_name='JitCodeTable',
sql_name='__intrinsic_jit_code',
columns=[
- C('create_ts', CppInt64()),
+ C('create_ts', CppInt64(), ColumnFlag.SORTED),
C('estimated_delete_ts', CppOptional(CppInt64())),
C('utid', CppUint32()),
C('start_address', CppInt64()),
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 16c2665..826316c 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -129,6 +129,10 @@
V8JsScriptTable::~V8JsScriptTable() = default;
V8WasmScriptTable::~V8WasmScriptTable() = default;
V8JsFunctionTable::~V8JsFunctionTable() = default;
+V8JsCodeTable::~V8JsCodeTable() = default;
+V8InternalCodeTable::~V8InternalCodeTable() = default;
+V8WasmCodeTable::~V8WasmCodeTable() = default;
+V8RegexpCodeTable::~V8RegexpCodeTable() = default;
// winscope_tables_py.h
SurfaceFlingerLayersSnapshotTable::~SurfaceFlingerLayersSnapshotTable() =
diff --git a/src/trace_processor/tables/v8_tables.py b/src/trace_processor/tables/v8_tables.py
index 76a8cda..28ed330 100644
--- a/src/trace_processor/tables/v8_tables.py
+++ b/src/trace_processor/tables/v8_tables.py
@@ -20,7 +20,6 @@
from python.generators.trace_processor_table.public import Alias
from python.generators.trace_processor_table.public import Column as C
from python.generators.trace_processor_table.public import ColumnDoc
-from python.generators.trace_processor_table.public import ColumnFlag
from python.generators.trace_processor_table.public import CppInt32
from python.generators.trace_processor_table.public import CppInt64
from python.generators.trace_processor_table.public import CppOptional
@@ -30,7 +29,7 @@
from python.generators.trace_processor_table.public import CppUint32 as CppBool
from python.generators.trace_processor_table.public import Table
from python.generators.trace_processor_table.public import TableDoc
-from .profiler_tables import STACK_PROFILE_FRAME_TABLE
+from .jit_tables import JIT_CODE_TABLE
V8_ISOLATE = Table(
python_module=__file__,
@@ -156,10 +155,165 @@
),
)
+V8_JS_CODE = Table(
+ python_module=__file__,
+ class_name='V8JsCodeTable',
+ sql_name='__intrinsic_v8_js_code',
+ columns=[
+ C('jit_code_id', CppOptional(CppTableId(JIT_CODE_TABLE))),
+ C('v8_js_function_id', CppTableId(V8_JS_FUNCTION)),
+ C('tier', CppString()),
+ C('bytecode_base64', CppOptional(CppString())),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents a v8 code snippet for a Javascript function. A given
+ function can have multiple code snippets (e.g. for different
+ compilation tiers, or as the function moves around the heap)
+ """,
+ group='v8',
+ columns={
+ 'jit_code_id':
+ ColumnDoc(
+ doc="""
+ Set for all tiers except IGNITION.
+ """,
+ joinable='__intrinsic_jit_code.id',
+ ),
+ 'v8_js_function_id':
+ ColumnDoc(
+ doc='JS function for this snippet.',
+ joinable='__intrinsic_v8_js_function.id',
+ ),
+ 'tier':
+ 'Compilation tier',
+ 'bytecode_base64':
+ 'Set only for the IGNITION tier (base64 encoded)',
+ },
+ ),
+)
+
+V8_INTERNAL_CODE = Table(
+ python_module=__file__,
+ class_name='V8InternalCodeTable',
+ sql_name='__intrinsic_v8_internal_code',
+ columns=[
+ C('jit_code_id', CppTableId(JIT_CODE_TABLE)),
+ C('v8_isolate_id', CppTableId(V8_ISOLATE)),
+ C('function_name', CppString()),
+ C('code_type', CppString()),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents a v8 code snippet for a v8 internal function.
+ """,
+ group='v8',
+ columns={
+ 'jit_code_id':
+ ColumnDoc(
+ doc='Associated JitCode.',
+ joinable='__intrinsic_jit_code.id',
+ ),
+ 'v8_isolate_id':
+ ColumnDoc(
+ doc="""
+ V8 Isolate this code was created in.
+ """,
+ joinable='__intrinsic_v8_isolate.id'),
+ 'function_name':
+ 'Function name.',
+ 'code_type':
+ 'Type of internal function (e.g. BYTECODE_HANDLER, BUILTIN)',
+ },
+ ),
+)
+
+V8_WASM_CODE = Table(
+ python_module=__file__,
+ class_name='V8WasmCodeTable',
+ sql_name='__intrinsic_v8_wasm_code',
+ columns=[
+ C('jit_code_id', CppTableId(JIT_CODE_TABLE)),
+ C('v8_isolate_id', CppTableId(V8_ISOLATE)),
+ C('v8_wasm_script_id', CppTableId(V8_WASM_SCRIPT)),
+ C('function_name', CppString()),
+ C('tier', CppString()),
+ C('code_offset_in_module', CppInt32()),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents the code associated to a WASM function
+ """,
+ group='v8',
+ columns={
+ 'jit_code_id':
+ ColumnDoc(
+ doc='Associated JitCode.',
+ joinable='__intrinsic_jit_code.id',
+ ),
+ 'v8_isolate_id':
+ ColumnDoc(
+ doc="""
+ V8 Isolate this code was created in.
+ """,
+ joinable='__intrinsic_v8_isolate.id'),
+ 'v8_wasm_script_id':
+ ColumnDoc(
+ doc="""
+ Script where the function is defined.
+ """,
+ joinable='v8_wasm_script.id',
+ ),
+ 'function_name':
+ 'Function name.',
+ 'tier':
+ 'Compilation tier',
+ 'code_offset_in_module':
+ """Offset into the WASM module where the function starts""",
+ },
+ ),
+)
+
+V8_REGEXP_CODE = Table(
+ python_module=__file__,
+ class_name='V8RegexpCodeTable',
+ sql_name='__intrinsic_v8_regexp_code',
+ columns=[
+ C('jit_code_id', CppTableId(JIT_CODE_TABLE)),
+ C('v8_isolate_id', CppTableId(V8_ISOLATE)),
+ C('pattern', CppString()),
+ ],
+ tabledoc=TableDoc(
+ doc="""
+ Represents the code associated to a regular expression
+ """,
+ group='v8',
+ columns={
+ 'jit_code_id':
+ ColumnDoc(
+ doc='Associated JitCode.',
+ joinable='__intrinsic_jit_code.id',
+ ),
+ 'v8_isolate_id':
+ ColumnDoc(
+ doc="""
+ V8 Isolate this code was created in.
+ """,
+ joinable='__intrinsic_v8_isolate.id'),
+ 'pattern':
+ """The pattern the this regular expression was compiled from""",
+ },
+ ),
+)
+
# Keep this list sorted.
ALL_TABLES = [
V8_ISOLATE,
V8_JS_SCRIPT,
V8_WASM_SCRIPT,
V8_JS_FUNCTION,
+ V8_JS_CODE,
+ V8_INTERNAL_CODE,
+ V8_WASM_CODE,
+ V8_REGEXP_CODE,
]
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index edca26a..1b3dea3 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -853,6 +853,13 @@
RegisterStaticTable(storage->v8_js_script_table());
RegisterStaticTable(storage->v8_wasm_script_table());
RegisterStaticTable(storage->v8_js_function_table());
+ RegisterStaticTable(storage->v8_js_code_table());
+ RegisterStaticTable(storage->v8_internal_code_table());
+ RegisterStaticTable(storage->v8_wasm_code_table());
+ RegisterStaticTable(storage->v8_regexp_code_table());
+
+ RegisterStaticTable(storage->jit_code_table());
+ RegisterStaticTable(storage->jit_frame_table());
RegisterStaticTable(storage->jit_code_table());
RegisterStaticTable(storage->jit_frame_table());
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index 455ce10..3630898 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -61,9 +61,7 @@
"../../include/perfetto/ext/base",
"../../include/perfetto/protozero:protozero",
"../../include/perfetto/trace_processor:storage",
- "../../protos/perfetto/trace:non_minimal_cpp",
"../../protos/perfetto/trace:non_minimal_zero",
- "../../protos/perfetto/trace/android:cpp",
"../../protos/perfetto/trace/android:zero",
"../../protos/perfetto/trace/ftrace:zero",
"../../protos/perfetto/trace/ps:zero",
diff --git a/src/trace_redaction/prune_package_list.cc b/src/trace_redaction/prune_package_list.cc
index a85a18f..de6331b 100644
--- a/src/trace_redaction/prune_package_list.cc
+++ b/src/trace_redaction/prune_package_list.cc
@@ -18,15 +18,29 @@
#include <string>
+#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
-#include "protos/perfetto/trace/android/packages_list.gen.h"
-#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/android/packages_list.pbzero.h"
+#include "src/trace_redaction/proto_util.h"
namespace perfetto::trace_redaction {
+namespace {
-PrunePackageList::PrunePackageList() = default;
-PrunePackageList::~PrunePackageList() = default;
+bool ShouldKeepPackageInfo(protozero::Field package_info, uint64_t uid) {
+ PERFETTO_DCHECK(package_info.id() ==
+ protos::pbzero::PackagesList::kPackagesFieldNumber);
+
+ protozero::ProtoDecoder decoder(package_info.as_bytes());
+ auto uid_field = decoder.FindField(
+ protos::pbzero::PackagesList::PackageInfo::kUidFieldNumber);
+
+ return uid_field.valid() &&
+ NormalizeUid(uid_field.as_uint64()) == NormalizeUid(uid);
+}
+
+} // namespace
base::Status PrunePackageList::Transform(const Context& context,
std::string* packet) const {
@@ -34,30 +48,47 @@
return base::ErrStatus("PrunePackageList: missing package uid.");
}
+ protozero::ProtoDecoder packet_decoder(*packet);
+
protos::pbzero::TracePacket::Decoder trace_packet_decoder(*packet);
- if (!trace_packet_decoder.has_packages_list()) {
+ auto package_list = packet_decoder.FindField(
+ protos::pbzero::TracePacket::kPackagesListFieldNumber);
+
+ if (!package_list.valid()) {
return base::OkStatus();
}
- auto normalized_uid = NormalizeUid(context.package_uid.value());
+ auto uid = context.package_uid.value();
- protos::gen::TracePacket mutable_packet;
- mutable_packet.ParseFromString(*packet);
+ protozero::HeapBuffered<protos::pbzero::TracePacket> packet_message;
- auto* packages = mutable_packet.mutable_packages_list()->mutable_packages();
+ for (auto packet_field = packet_decoder.ReadField(); packet_field.valid();
+ packet_field = packet_decoder.ReadField()) {
+ if (packet_field.id() !=
+ protos::pbzero::TracePacket::kPackagesListFieldNumber) {
+ proto_util::AppendField(packet_field, packet_message.get());
+ continue;
+ }
- // Remove all entries that don't match the uid. After this, one or more
- // packages will be left in the list (multiple packages can share a uid).
- packages->erase(
- std::remove_if(
- packages->begin(), packages->end(),
- [normalized_uid](const protos::gen::PackagesList::PackageInfo& info) {
- return NormalizeUid(info.uid()) != normalized_uid;
- }),
- packages->end());
+ auto* package_list_message = packet_message->set_packages_list();
- packet->assign(mutable_packet.SerializeAsString());
+ protozero::ProtoDecoder package_list_decoder(packet_field.as_bytes());
+
+ for (auto package_field = package_list_decoder.ReadField();
+ package_field.valid();
+ package_field = package_list_decoder.ReadField()) {
+ // If not packages, keep.
+ // If packages and uid matches, keep.
+ if (package_field.id() !=
+ protos::pbzero::PackagesList::kPackagesFieldNumber ||
+ ShouldKeepPackageInfo(package_field, uid)) {
+ proto_util::AppendField(package_field, package_list_message);
+ }
+ }
+ }
+
+ packet->assign(packet_message.SerializeAsString());
return base::OkStatus();
}
diff --git a/src/trace_redaction/prune_package_list.h b/src/trace_redaction/prune_package_list.h
index 24d9ec2..ff5f060 100644
--- a/src/trace_redaction/prune_package_list.h
+++ b/src/trace_redaction/prune_package_list.h
@@ -28,9 +28,6 @@
// Returns `base::ErrStatus()` if `Context.package_uid` was not set.
class PrunePackageList final : public TransformPrimitive {
public:
- PrunePackageList();
- ~PrunePackageList() override;
-
base::Status Transform(const Context& context,
std::string* packet) const override;
};
diff --git a/src/traceconv/trace_to_hprof.cc b/src/traceconv/trace_to_hprof.cc
index 74f515a..38c3329 100644
--- a/src/traceconv/trace_to_hprof.cc
+++ b/src/traceconv/trace_to_hprof.cc
@@ -32,7 +32,7 @@
// Spec
// http://hg.openjdk.java.net/jdk6/jdk6/jdk/raw-file/tip/src/share/demo/jvmti/hprof/manual.html#Basic_Type
// Parser
-// https://cs.android.com/android/platform/superproject/+/main:art/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
+// https://cs.android.com/android/platform/superproject/main/+/main:art/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
namespace perfetto {
namespace trace_to_text {
diff --git a/test/cts/heapprofd_test_cts.cc b/test/cts/heapprofd_test_cts.cc
index fec7aea..c4eb97f 100644
--- a/test/cts/heapprofd_test_cts.cc
+++ b/test/cts/heapprofd_test_cts.cc
@@ -24,6 +24,7 @@
#include <string_view>
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/android_utils.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "src/base/test/test_task_runner.h"
@@ -81,9 +82,19 @@
const std::string& path) {
tmp_dir_.TrackFile("contents.txt");
tempfile_ = tmp_dir_.AbsolutePath("contents.txt");
- cmd_ = std::string("content read --uri content://") + app +
- std::string("/") + path + " >" + tempfile_;
+
+ std::optional<int32_t> sdk =
+ base::StringToInt32(base::GetAndroidProp("ro.build.version.sdk"));
+ bool multiuser_support = sdk && *sdk >= 34;
+ cmd_ = "content read";
+ if (multiuser_support) {
+ // This command is available only starting from android U.
+ cmd_ += " --user `cmd user get-main-user`";
+ }
+ cmd_ += std::string(" --uri content://") + app + std::string("/") + path;
+ cmd_ += " >" + tempfile_;
}
+
std::optional<int64_t> ReadInt64() {
if (system(cmd_.c_str()) != 0) {
return std::nullopt;
diff --git a/test/trace_processor/diff_tests/parser/chrome/tests_v8.py b/test/trace_processor/diff_tests/parser/chrome/tests_v8.py
index 08f1b65..d4a0dc4 100644
--- a/test/trace_processor/diff_tests/parser/chrome/tests_v8.py
+++ b/test/trace_processor/diff_tests/parser/chrome/tests_v8.py
@@ -60,3 +60,43 @@
query=_no_duplicates_query(V8_WASM_SCRIPT),
out=Csv(""""count"\n1\n"""),
)
+
+ def test_no_code_overlaps(self):
+ return DiffTestBlueprint(
+ trace=DataPath('parser/v8.code.trace.pb.gz'),
+ query="""
+INCLUDE PERFETTO MODULE stack_trace.jit;
+WITH
+ view AS (
+ SELECT
+ jit_code_id,
+ start_address AS start,
+ start_address + size AS end,
+ create_ts,
+ estimated_delete_ts,
+ t.upid AS upid
+ FROM
+ _JIT_CODE AS c, thread AS t
+ USING (utid)
+ -- Prevent the cross join below from blowing up
+ WHERE jit_code_id < 10000
+ )
+SELECT COUNT(*) AS num_overlaps
+FROM
+ view AS v1, view AS v2
+WHERE
+ -- Prevent comparison with self
+ v1.jit_code_id <> v2.jit_code_id
+ -- Code ranges in same process
+ AND v1.upid = v2.upid
+ -- Address overlap
+ AND v1.start < v2.end
+ AND v2.start < v1.end
+ -- Time overlap
+ AND (v2.estimated_delete_ts IS NULL OR v1.create_ts < v2.estimated_delete_ts)
+ AND (v1.estimated_delete_ts IS NULL OR v2.create_ts < v1.estimated_delete_ts)
+""",
+ out=Csv(""""num_overlaps"
+0
+"""),
+ )
diff --git a/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py b/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
index 48cc56f..37f5d52 100644
--- a/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
+++ b/test/trace_processor/diff_tests/stdlib/graphs/search_tests.py
@@ -154,7 +154,7 @@
2,"[NULL]"
"""))
- def test_weight_bounded_dfs(self):
+ def test_weight_bounded_dfs_floor(self):
return DiffTestBlueprint(
trace=DataPath('counters.json'),
query="""
@@ -174,7 +174,7 @@
VALUES (5, 6, 0);
CREATE PERFETTO TABLE roots AS
- SELECT 0 AS root_node_id, 0 AS root_max_weight
+ SELECT 0 AS root_node_id, 0 AS root_target_weight
UNION ALL
VALUES (1, 2)
UNION ALL
@@ -182,7 +182,7 @@
UNION ALL
VALUES (2, 0);
- SELECT * FROM graph_reachable_weight_bounded_dfs!(foo, roots);
+ SELECT * FROM graph_reachable_weight_bounded_dfs!(foo, roots, 1);
""",
out=Csv("""
"root_node_id","node_id","parent_node_id"
@@ -190,8 +190,50 @@
1,1,"[NULL]"
1,2,1
1,3,1
- 1,5,3
- 1,6,5
+ 1,4,3
+ 3,3,"[NULL]"
+ 3,4,3
+ 3,5,3
+ 3,6,5
+ 2,2,"[NULL]"
+ """))
+
+ def test_weight_bounded_dfs_ceiling(self):
+ return DiffTestBlueprint(
+ trace=DataPath('counters.json'),
+ query="""
+ INCLUDE PERFETTO MODULE graphs.search;
+
+ CREATE PERFETTO TABLE foo AS
+ SELECT 0 AS source_node_id, 0 AS dest_node_id, 0 AS edge_weight
+ UNION ALL
+ VALUES (1, 2, 1)
+ UNION ALL
+ VALUES (1, 3, 1)
+ UNION ALL
+ VALUES (3, 4, 1)
+ UNION ALL
+ VALUES (3, 5, 0)
+ UNION ALL
+ VALUES (5, 6, 0);
+
+ CREATE PERFETTO TABLE roots AS
+ SELECT 0 AS root_node_id, 0 AS root_target_weight
+ UNION ALL
+ VALUES (1, 2)
+ UNION ALL
+ VALUES (3, 1)
+ UNION ALL
+ VALUES (2, 0);
+
+ SELECT * FROM graph_reachable_weight_bounded_dfs!(foo, roots, 0);
+ """,
+ out=Csv("""
+ "root_node_id","node_id","parent_node_id"
+ 0,0,"[NULL]"
+ 1,1,"[NULL]"
+ 1,2,1
+ 1,3,1
3,3,"[NULL]"
3,4,3
3,5,3
diff --git a/test/trace_processor/diff_tests/stdlib/sched/tests.py b/test/trace_processor/diff_tests/stdlib/sched/tests.py
index bb24f1b..3527185 100644
--- a/test/trace_processor/diff_tests/stdlib/sched/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/sched/tests.py
@@ -13,8 +13,8 @@
# 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 Path, 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
@@ -263,3 +263,29 @@
"S",3868233011
"x",35240577
"""))
+
+ def test_sched_previous_runnable_on_thread(self):
+ return DiffTestBlueprint(
+ trace=DataPath('android_boot.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE sched.runnable;
+
+ SELECT *
+ FROM sched_previous_runnable_on_thread
+ WHERE prev_wakeup_runnable_id IS NOT NULL
+ ORDER BY id DESC
+ LIMIT 10;
+ """,
+ out=Csv("""
+ "id","prev_runnable_id","prev_wakeup_runnable_id"
+ 538199,538191,538191
+ 538197,538191,538191
+ 538195,538191,538191
+ 538190,538136,538136
+ 538188,538088,533235
+ 538184,538176,524613
+ 538181,538178,537492
+ 538179,524619,524619
+ 538177,537492,537492
+ 538175,538174,524613
+ """))
diff --git a/ui/src/base/logging.ts b/ui/src/base/logging.ts
index 10fbbff..74cbe44 100644
--- a/ui/src/base/logging.ts
+++ b/ui/src/base/logging.ts
@@ -60,7 +60,7 @@
if (err instanceof ErrorEvent) {
errType = 'ERROR';
- errMsg = err.message;
+ errMsg = `${err.error}`;
errorObj = err.error;
} else if (err instanceof PromiseRejectionEvent) {
errType = 'PROMISE_REJ';
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 40fcef3..4f16d80 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -80,6 +80,7 @@
trackSortKey: TrackSortKey;
trackGroup?: string;
params?: unknown;
+ closeable?: boolean;
}
export interface PostedTrace {
@@ -211,6 +212,7 @@
labels: track.labels,
uri: track.uri,
params: track.params,
+ closeable: track.closeable,
};
if (track.trackGroup === SCROLLING_TRACK_GROUP) {
state.scrollingTracks.push(trackKey);
@@ -639,21 +641,6 @@
}
},
- selectSlice(
- state: StateDraft,
- args: {id: number; trackKey: string; scroll?: boolean},
- ): void {
- state.selection = {
- kind: 'legacy',
- legacySelection: {
- kind: 'SLICE',
- id: args.id,
- trackKey: args.trackKey,
- },
- };
- state.pendingScrollId = args.scroll ? args.id : undefined;
- },
-
selectCounter(
state: StateDraft,
args: {leftTs: time; rightTs: time; id: number; trackKey: string},
@@ -741,6 +728,7 @@
type: args.type,
viewingOption: args.viewingOption,
focusRegex: '',
+ expandedCallsiteByViewingOption: {},
};
},
@@ -761,10 +749,15 @@
expandFlamegraphState(
state: StateDraft,
- args: {expandedCallsite?: CallsiteInfo},
+ args: {
+ expandedCallsite?: CallsiteInfo;
+ viewingOption: FlamegraphStateViewingOption;
+ },
): void {
if (state.currentFlamegraphState === null) return;
- state.currentFlamegraphState.expandedCallsite = args.expandedCallsite;
+ state.currentFlamegraphState.expandedCallsiteByViewingOption[
+ args.viewingOption
+ ] = args.expandedCallsite;
},
changeViewFlamegraphState(
@@ -835,6 +828,10 @@
};
},
+ setPendingScrollId(state: StateDraft, args: {pendingScrollId: number}): void {
+ state.pendingScrollId = args.pendingScrollId;
+ },
+
clearPendingScrollId(state: StateDraft, _: {}): void {
state.pendingScrollId = undefined;
},
@@ -853,27 +850,6 @@
};
},
- selectLog(
- state: StateDraft,
- args: {id: number; trackKey: string; scroll?: boolean},
- ): void {
- state.selection = {
- kind: 'legacy',
- legacySelection: {
- kind: 'LOG',
- id: args.id,
- trackKey: args.trackKey,
- },
- };
- state.pendingScrollId = args.scroll ? args.id : undefined;
- },
-
- deselect(state: StateDraft, _: {}): void {
- state.selection = {
- kind: 'empty',
- };
- },
-
updateLogsPagination(state: StateDraft, args: Pagination): void {
state.logsPagination = args;
},
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index 63689ca..0817ebf 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {featureFlags} from '../core/feature_flags';
import {CallsiteInfo, FlamegraphStateViewingOption, ProfileType} from './state';
interface ViewingOption {
@@ -19,6 +20,13 @@
name: string;
}
+const SHOW_HEAP_GRAPH_DOMINATOR_TREE_FLAG = featureFlags.register({
+ id: 'showHeapGraphDominatorTree',
+ name: 'Show heap graph dominator tree',
+ description: 'Show dominated size and objects tabs in Java heap graph view.',
+ defaultValue: false,
+});
+
export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {
switch (profileType) {
case ProfileType.PERF_SAMPLE:
@@ -39,7 +47,22 @@
option: FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY,
name: 'Objects',
},
- ];
+ ].concat(
+ SHOW_HEAP_GRAPH_DOMINATOR_TREE_FLAG.get()
+ ? [
+ {
+ option:
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY,
+ name: 'Dominated size',
+ },
+ {
+ option:
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY,
+ name: 'Dominated objects',
+ },
+ ]
+ : [],
+ );
case ProfileType.HEAP_PROFILE:
return [
{
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 19b156c..92efee5 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -22,7 +22,7 @@
} from '../frontend/pivot_table_types';
import {PrimaryTrackSortKey} from '../public/index';
-import {Direction} from './event_set';
+import {Direction} from '../core/event_set';
import {
selectionToLegacySelection,
@@ -147,7 +147,8 @@
// 48. Rename legacySelection -> selection and introduce new Selection type.
// 49. Remove currentTab, which is only relevant to TabsV1.
// 50. Remove ftrace filter state.
-export const STATE_VERSION = 50;
+// 51. Changed structure of FlamegraphState.expandedCallsiteByViewingOption.
+export const STATE_VERSION = 51;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -189,6 +190,24 @@
OBJECTS_ALLOCATED_NOT_FREED_KEY = 'OBJECTS',
OBJECTS_ALLOCATED_KEY = 'ALLOC_OBJECTS',
PERF_SAMPLES_KEY = 'PERF_SAMPLES',
+ DOMINATOR_TREE_OBJ_SIZE_KEY = 'DOMINATED_OBJ_SIZE',
+ DOMINATOR_TREE_OBJ_COUNT_KEY = 'DOMINATED_OBJ_COUNT',
+}
+
+const HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS = [
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY,
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY,
+] as const;
+
+export type HeapGraphDominatorTreeViewingOption =
+ (typeof HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS)[number];
+
+export function isHeapGraphDominatorTreeViewingOption(
+ option: FlamegraphStateViewingOption,
+): option is HeapGraphDominatorTreeViewingOption {
+ return (
+ HEAP_GRAPH_DOMINATOR_TREE_VIEWING_OPTIONS as readonly FlamegraphStateViewingOption[]
+ ).includes(option);
}
export interface FlamegraphState {
@@ -199,7 +218,7 @@
type: ProfileType;
viewingOption: FlamegraphStateViewingOption;
focusRegex: string;
- expandedCallsite?: CallsiteInfo;
+ expandedCallsiteByViewingOption: {[key: string]: CallsiteInfo | undefined};
}
export interface CallsiteInfo {
@@ -262,6 +281,7 @@
trackGroup?: string;
params?: unknown;
state?: unknown;
+ closeable?: boolean;
}
export interface TrackGroupState {
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index aa123f7..a10252c 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Duration, time} from '../base/time';
+import {Duration, Time, time} from '../base/time';
import {exists} from '../base/utils';
import {Actions} from '../common/actions';
import {
@@ -26,6 +26,7 @@
FlamegraphState,
FlamegraphStateViewingOption,
ProfileType,
+ isHeapGraphDominatorTreeViewingOption,
} from '../common/state';
import {FlamegraphDetails, globals} from '../frontend/globals';
import {publishFlamegraphDetails} from '../frontend/publish';
@@ -115,6 +116,10 @@
private flamegraphDetails: FlamegraphDetails = {};
private areaSelectionHandler: AreaSelectionHandler;
private cache: TablesCache;
+ private heapGraphSelected: {upid: number; timestamp: time} = {
+ upid: -1,
+ timestamp: Time.INVALID,
+ };
constructor(private args: FlamegraphControllerArgs) {
super('main');
@@ -197,13 +202,12 @@
this.lastSelectedFlamegraphState = {...selection};
- const expandedId = selectedFlamegraphState.expandedCallsite
- ? selectedFlamegraphState.expandedCallsite.id
- : -1;
- const rootSize =
- selectedFlamegraphState.expandedCallsite === undefined
- ? undefined
- : selectedFlamegraphState.expandedCallsite.totalSize;
+ const expandedCallsite =
+ selectedFlamegraphState.expandedCallsiteByViewingOption[
+ selectedFlamegraphState.viewingOption
+ ];
+ const expandedId = expandedCallsite ? expandedCallsite.id : -1;
+ const rootSize = expandedCallsite?.totalSize;
const key = `${selectedFlamegraphState.upids};${selectedFlamegraphState.start};${selectedFlamegraphState.end}`;
@@ -238,7 +242,7 @@
this.lastSelectedFlamegraphState.viewingOption,
isInAreaSelection,
rootSize,
- this.lastSelectedFlamegraphState.expandedCallsite,
+ expandedCallsite,
);
}
} finally {
@@ -264,8 +268,10 @@
this.lastSelectedFlamegraphState.viewingOption !==
selection.viewingOption ||
this.lastSelectedFlamegraphState.focusRegex !== selection.focusRegex ||
- this.lastSelectedFlamegraphState.expandedCallsite !==
- selection.expandedCallsite)
+ this.lastSelectedFlamegraphState.expandedCallsiteByViewingOption[
+ selection.viewingOption
+ ] !==
+ selection.expandedCallsiteByViewingOption[selection.viewingOption])
);
}
@@ -311,7 +317,7 @@
if (this.flamegraphDatasets.has(key)) {
currentData = this.flamegraphDatasets.get(key)!;
} else {
- // TODO(hjd): Show loading state.
+ // TODO(b/330703412): Show loading state.
// Collecting data for drawing flamegraph for selected profile.
// Data needs to be in following format:
@@ -322,6 +328,7 @@
upids,
type,
focusRegex,
+ viewingOption,
);
currentData = await this.getFlamegraphDataFromTables(
tableName,
@@ -374,6 +381,18 @@
totalColumnName = 'cumulativeSize';
selfColumnName = 'size';
break;
+ case FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY:
+ orderBy = `where depth < ${maxDepth} order by depth,
+ cumulativeCount desc, name`;
+ totalColumnName = 'cumulativeCount';
+ selfColumnName = 'count';
+ break;
+ case FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY:
+ orderBy = `where depth < ${maxDepth} order by depth,
+ cumulativeSize desc, name`;
+ totalColumnName = 'cumulativeSize';
+ selfColumnName = 'size';
+ break;
default:
const exhaustiveCheck: never = viewingOption;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
@@ -381,21 +400,21 @@
}
const callsites = await this.args.engine.query(`
- SELECT
- id as hash,
- IFNULL(IFNULL(DEMANGLE(name), name), '[NULL]') as name,
- IFNULL(parent_id, -1) as parentHash,
- depth,
- cumulative_size as cumulativeSize,
- cumulative_alloc_size as cumulativeAllocSize,
- cumulative_count as cumulativeCount,
- cumulative_alloc_count as cumulativeAllocCount,
- map_name as mapping,
- size,
- count,
- IFNULL(source_file, '') as sourceFile,
- IFNULL(line_number, -1) as lineNumber
- from ${tableName} ${orderBy}`);
+ SELECT
+ id as hash,
+ IFNULL(IFNULL(DEMANGLE(name), name), '[NULL]') as name,
+ IFNULL(parent_id, -1) as parentHash,
+ depth,
+ cumulative_size as cumulativeSize,
+ cumulative_alloc_size as cumulativeAllocSize,
+ cumulative_count as cumulativeCount,
+ cumulative_alloc_count as cumulativeAllocCount,
+ map_name as mapping,
+ size,
+ count,
+ IFNULL(source_file, '') as sourceFile,
+ IFNULL(line_number, -1) as lineNumber
+ from ${tableName} ${orderBy}`);
const flamegraphData: CallsiteInfo[] = [];
const hashToindex: Map<number, number> = new Map();
@@ -467,6 +486,7 @@
upids: number[],
type: ProfileType,
focusRegex: string,
+ viewingOption: FlamegraphStateViewingOption,
): Promise<string> {
const flamegraphType = getFlamegraphType(type);
if (type === ProfileType.PERF_SAMPLE) {
@@ -493,6 +513,14 @@
)`,
);
}
+ if (
+ type === ProfileType.JAVA_HEAP_GRAPH &&
+ isHeapGraphDominatorTreeViewingOption(viewingOption)
+ ) {
+ return this.cache.getTableName(
+ await this.loadHeapGraphDominatorTreeQuery(upids[0], end),
+ );
+ }
return this.cache.getTableName(
`select id, name, map_name, parent_id, depth, cumulative_size,
cumulative_alloc_size, cumulative_count, cumulative_alloc_count,
@@ -508,6 +536,91 @@
);
}
+ private async loadHeapGraphDominatorTreeQuery(upid: number, timestamp: time) {
+ const selectTreeQuery = `
+ -- cache invalidate: upid ${upid}, ts ${timestamp}
+ SELECT * FROM heap_graph_type_dominated`;
+ if (
+ this.heapGraphSelected.upid === upid &&
+ this.heapGraphSelected.timestamp === timestamp
+ ) {
+ return selectTreeQuery;
+ }
+ this.heapGraphSelected = {upid, timestamp};
+ this.args.engine.query(`
+ INCLUDE PERFETTO MODULE memory.heap_graph_dominator_tree;
+
+ -- heap graph dominator tree with objects as nodes and all relavant
+ -- object self stats and dominated stats
+ CREATE PERFETTO TABLE heap_graph_object_dominated AS
+ SELECT
+ node.id,
+ node.idom_id,
+ node.dominated_obj_count,
+ node.dominated_size_bytes + node.dominated_native_size_bytes AS dominated_size,
+ node.depth,
+ obj.type_id,
+ obj.root_type,
+ obj.self_size + obj.native_size AS self_size
+ FROM memory_heap_graph_dominator_tree node
+ JOIN heap_graph_object obj USING(id)
+ WHERE obj.upid = ${upid} AND obj.graph_sample_ts = ${timestamp}
+ -- required to accelerate the recursive cte below
+ ORDER BY idom_id;
+
+ -- calculate for each object node in the dominator tree the
+ -- HASH(path of type_id's from the super root to the object)
+ CREATE PERFETTO TABLE _dominator_tree_path_hash AS
+ WITH RECURSIVE _tree_visitor(id, path, path_hash) AS (
+ SELECT
+ id,
+ CAST(type_id AS text) || '-' || IFNULL(root_type, '') AS path,
+ HASH(
+ CAST(type_id AS text) || '-' || IFNULL(root_type, '')
+ ) AS path_hash
+ FROM heap_graph_object_dominated
+ WHERE depth = 1
+ UNION ALL
+ SELECT
+ child.id,
+ parent.path || '/' || CAST(type_id AS text) AS path,
+ HASH(parent.path || '/' || CAST(type_id AS text)) AS path_hash
+ FROM heap_graph_object_dominated child
+ JOIN _tree_visitor parent ON child.idom_id = parent.id
+ )
+ SELECT * from _tree_visitor
+ ORDER BY id;
+
+ -- merge object nodes with the same path into one "class type node", so the
+ -- end result is a tree where nodes are identified by their types and the
+ -- dominator relationships are preserved.
+ CREATE PERFETTO TABLE heap_graph_type_dominated AS
+ SELECT
+ map.path_hash as id,
+ COALESCE(cls.deobfuscated_name, cls.name, '[NULL]') || IIF(
+ node.root_type IS NOT NULL,
+ ' [' || node.root_type || ']', ''
+ ) AS name,
+ IFNULL(parent_map.path_hash, -1) AS parent_id,
+ node.depth - 1 AS depth,
+ sum(dominated_size) AS cumulative_size,
+ -1 AS cumulative_alloc_size,
+ sum(dominated_obj_count) AS cumulative_count,
+ -1 AS cumulative_alloc_count,
+ '' as map_name,
+ '' as source_file,
+ -1 as line_number,
+ sum(self_size) AS size,
+ count(*) AS count
+ FROM heap_graph_object_dominated node
+ JOIN _dominator_tree_path_hash map USING(id)
+ LEFT JOIN _dominator_tree_path_hash parent_map ON node.idom_id = parent_map.id
+ JOIN heap_graph_class cls ON node.type_id = cls.id
+ GROUP BY map.path_hash, name, parent_id, depth, map_name, source_file, line_number;`);
+
+ return selectTreeQuery;
+ }
+
getMinSizeDisplayed(
flamegraphData: CallsiteInfo[],
rootSize?: number,
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index faa68e7..9e39eed 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -759,13 +759,18 @@
if (trackKey === undefined) {
return;
}
- globals.makeSelection(
- Actions.selectChromeSlice({
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: row.id,
trackKey,
- table: '',
- scroll: true,
- }),
+ table: 'slice',
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: row.id,
+ switchToCurrentSelectionTab: false,
+ },
);
}
}
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 7d1039b..b71f26a 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -72,7 +72,7 @@
const TEMPERATURE_REGEX = new RegExp('^.* Temperature$');
const TEMPERATURE_GROUP = 'Temperature';
const IRQ_GROUP = 'IRQs';
-const IRQ_REGEX = new RegExp('^Irq Cpu.*');
+const IRQ_REGEX = new RegExp('^(Irq|SoftIrq) Cpu.*');
const CHROME_TRACK_REGEX = new RegExp('^Chrome.*|^InputLatency::.*');
const CHROME_TRACK_GROUP = 'Chrome Global Tracks';
const MISC_GROUP = 'Misc Global Tracks';
diff --git a/ui/src/common/event_set.ts b/ui/src/core/event_set.ts
similarity index 100%
rename from ui/src/common/event_set.ts
rename to ui/src/core/event_set.ts
diff --git a/ui/src/common/event_set_nocompile_test.ts b/ui/src/core/event_set_nocompile_test.ts
similarity index 100%
rename from ui/src/common/event_set_nocompile_test.ts
rename to ui/src/core/event_set_nocompile_test.ts
diff --git a/ui/src/common/event_set_unittest.ts b/ui/src/core/event_set_unittest.ts
similarity index 100%
rename from ui/src/common/event_set_unittest.ts
rename to ui/src/core/event_set_unittest.ts
diff --git a/ui/src/core/selection_manager.ts b/ui/src/core/selection_manager.ts
index 961ce95..2159847 100644
--- a/ui/src/core/selection_manager.ts
+++ b/ui/src/core/selection_manager.ts
@@ -13,6 +13,8 @@
// limitations under the License.
import {duration, time} from '../base/time';
+import {Store} from '../base/store';
+import {assertUnreachable} from '../base/logging';
import {GenericSliceDetailsTabConfigBase} from './generic_slice_details_types';
export enum ProfileType {
@@ -158,8 +160,104 @@
export function selectionToLegacySelection(
selection: Selection,
): LegacySelection | null {
- if (selection.kind === 'legacy') {
- return selection.legacySelection;
+ switch (selection.kind) {
+ case 'area':
+ case 'single':
+ case 'empty':
+ return null;
+ case 'union':
+ for (const child of selection.selections) {
+ const result = selectionToLegacySelection(child);
+ if (result !== null) {
+ return result;
+ }
+ }
+ return null;
+ case 'legacy':
+ return selection.legacySelection;
+ default:
+ assertUnreachable(selection);
+ return null;
}
- return null;
+}
+
+interface SelectionState {
+ selection: Selection;
+}
+
+export class SelectionManager {
+ private store: Store<SelectionState>;
+
+ constructor(store: Store<SelectionState>) {
+ this.store = store;
+ }
+
+ clear(): void {
+ this.store.edit((draft) => {
+ draft.selection = {
+ kind: 'empty',
+ };
+ });
+ }
+
+ private addSelection(selection: Selection): void {
+ this.store.edit((draft) => {
+ switch (draft.selection.kind) {
+ case 'empty':
+ draft.selection = selection;
+ break;
+ case 'union':
+ draft.selection.selections.push(selection);
+ break;
+ case 'single':
+ case 'legacy':
+ case 'area':
+ draft.selection = {
+ kind: 'union',
+ selections: [draft.selection, selection],
+ };
+ break;
+ default:
+ assertUnreachable(draft.selection);
+ break;
+ }
+ });
+ }
+
+ // There is no matching addLegacy as we did not support multi-single
+ // selection with the legacy selection system.
+ setLegacy(legacySelection: LegacySelection): void {
+ this.clear();
+ this.addSelection({
+ kind: 'legacy',
+ legacySelection,
+ });
+ }
+
+ setEvent(
+ trackKey: string,
+ eventId: string,
+ legacySelection?: LegacySelection,
+ ) {
+ this.clear();
+ this.addEvent(trackKey, eventId, legacySelection);
+ }
+
+ addEvent(
+ trackKey: string,
+ eventId: string,
+ legacySelection?: LegacySelection,
+ ) {
+ this.addSelection({
+ kind: 'single',
+ trackKey,
+ eventId,
+ });
+ if (legacySelection) {
+ this.addSelection({
+ kind: 'legacy',
+ legacySelection,
+ });
+ }
+ }
}
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 17b0ad9..940ff31 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -554,7 +554,7 @@
name: 'Deselect',
callback: () => {
globals.timeline.deselectArea();
- globals.makeSelection(Actions.deselect({}));
+ globals.clearSelection();
globals.dispatch(Actions.removeNote({id: '0'}));
},
defaultHotkey: 'Escape',
diff --git a/ui/src/frontend/debug_tracks.ts b/ui/src/frontend/debug_tracks.ts
index 7e2ce7e..6ff6297 100644
--- a/ui/src/frontend/debug_tracks.ts
+++ b/ui/src/frontend/debug_tracks.ts
@@ -67,7 +67,6 @@
const trackConfig: DebugTrackV2Config = {
data,
columns: sliceColumns,
- closeable,
argColumns,
};
@@ -79,6 +78,7 @@
trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
trackGroup: SCROLLING_TRACK_GROUP,
params: trackConfig,
+ closeable,
}),
];
if (config?.pinned ?? true) {
@@ -117,7 +117,6 @@
export interface CounterDebugTrackConfig {
data: SqlDataSource;
columns: CounterColumns;
- closeable: boolean;
}
export interface CounterDebugTrackCreateConfig {
@@ -143,7 +142,6 @@
const params: CounterDebugTrackConfig = {
data,
columns,
- closeable,
};
const trackKey = uuidv4();
@@ -155,6 +153,7 @@
trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
trackGroup: SCROLLING_TRACK_GROUP,
params,
+ closeable,
}),
];
if (config?.pinned ?? true) {
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index 7bf69c9..fea7fb2 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -230,7 +230,9 @@
case ProfileType.JAVA_HEAP_GRAPH:
if (
viewingOption ===
- FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY
+ FlamegraphStateViewingOption.OBJECTS_ALLOCATED_NOT_FREED_KEY ||
+ viewingOption ===
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_COUNT_KEY
) {
return RENDER_OBJ_COUNT;
} else {
@@ -347,7 +349,9 @@
current.viewingOption ===
FlamegraphStateViewingOption.SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY ||
current.viewingOption ===
- FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY
+ FlamegraphStateViewingOption.ALLOC_SPACE_MEMORY_ALLOCATED_KEY ||
+ current.viewingOption ===
+ FlamegraphStateViewingOption.DOMINATOR_TREE_OBJ_SIZE_KEY
? 'B'
: '';
this.flamegraph.draw(ctx, width, height, 0, 0, unit);
@@ -355,7 +359,13 @@
private onMouseClick({x, y}: {x: number; y: number}): boolean {
const expandedCallsite = this.flamegraph.onMouseClick({x, y});
- globals.dispatch(Actions.expandFlamegraphState({expandedCallsite}));
+ globals.state.currentFlamegraphState &&
+ globals.dispatch(
+ Actions.expandFlamegraphState({
+ expandedCallsite,
+ viewingOption: globals.state.currentFlamegraphState.viewingOption,
+ }),
+ );
return true;
}
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index 02bf691..4af4015 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -67,9 +67,18 @@
const flowClickHandler = (sliceId: number, trackId: number) => {
const trackKey = globals.trackManager.trackKeyByTrackId.get(trackId);
if (trackKey) {
- globals.makeSelection(
- Actions.selectChromeSlice({id: sliceId, trackKey, table: 'slice'}),
- {switchToCurrentSelectionTab: false},
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
+ id: sliceId,
+ trackKey,
+ table: 'slice',
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: false,
+ },
);
}
};
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index d6727f9..1af1399 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -14,6 +14,7 @@
import {BigintMath} from '../base/bigint_math';
import {assertExists} from '../base/logging';
+import {createStore, Store} from '../base/store';
import {duration, Span, Time, time, TimeSpan} from '../base/time';
import {Actions, DeferredAction} from '../common/actions';
import {AggregateData} from '../common/aggregation_data';
@@ -52,8 +53,8 @@
import {horizontalScrollToTs} from './scroll_helper';
import {ServiceWorkerController} from './service_worker_controller';
import {SliceSqlId} from './sql_types';
-import {createStore, Store} from '../base/store';
import {PxSpan, TimeScale} from './time_scale';
+import {SelectionManager, LegacySelection} from '../core/selection_manager';
const INSTANT_FOCUS_DURATION = 1n;
const INCOMPLETE_SLICE_DURATION = 30_000n;
@@ -209,6 +210,14 @@
clearSearch?: boolean;
}
+// All of these control additional things we can do when doing a
+// selection.
+export interface LegacySelectionArgs {
+ clearSearch: boolean;
+ switchToCurrentSelectionTab: boolean;
+ pendingScrollId: number | undefined;
+}
+
/**
* Global accessors for state/dispatch in the frontend.
*/
@@ -217,7 +226,7 @@
private _testing = false;
private _dispatch?: Dispatch = undefined;
- private _store = createStore(createEmptyState());
+ private _store = createStore<State>(createEmptyState());
private _timeline?: Timeline = undefined;
private _serviceWorkerController?: ServiceWorkerController = undefined;
private _logging?: Analytics = undefined;
@@ -253,6 +262,7 @@
private _traceTzOffset = Time.ZERO;
private _tabManager = new TabManager();
private _trackManager = new TrackManager(this._store);
+ private _selectionManager = new SelectionManager(this._store);
private _hasFtrace: boolean = false;
scrollToTrackKey?: string | number;
@@ -311,6 +321,7 @@
this._flamegraphDetails = {};
this._cpuProfileDetails = {};
this.engines.clear();
+ this._selectionManager.clear();
}
// Only initialises the store - useful for testing.
@@ -583,18 +594,42 @@
makeSelection(action: DeferredAction<{}>, opts: MakeSelectionOpts = {}) {
const {switchToCurrentSelectionTab = true, clearSearch = true} = opts;
-
const currentSelectionTabUri = 'current_selection';
// A new selection should cancel the current search selection.
clearSearch && globals.dispatch(Actions.setSearchIndex({index: -1}));
- if (action.type !== 'deselect' && switchToCurrentSelectionTab) {
+ if (switchToCurrentSelectionTab) {
globals.dispatch(Actions.showTab({uri: currentSelectionTabUri}));
}
globals.dispatch(action);
}
+ setLegacySelection(
+ legacySelection: LegacySelection,
+ args: LegacySelectionArgs,
+ ): void {
+ this._selectionManager.setLegacy(legacySelection);
+ if (args.clearSearch) {
+ globals.dispatch(Actions.setSearchIndex({index: -1}));
+ }
+ if (args.pendingScrollId !== undefined) {
+ globals.dispatch(
+ Actions.setPendingScrollId({
+ pendingScrollId: args.pendingScrollId,
+ }),
+ );
+ }
+ if (args.switchToCurrentSelectionTab) {
+ globals.dispatch(Actions.showTab({uri: 'current_selection'}));
+ }
+ }
+
+ clearSelection(): void {
+ globals.dispatch(Actions.setSearchIndex({index: -1}));
+ this._selectionManager.clear();
+ }
+
resetForTesting() {
this._dispatch = undefined;
this._timeline = undefined;
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 60b7c8f..59f1d95 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -98,13 +98,18 @@
const trackKeyByTrackId = globals.trackManager.trackKeyByTrackId;
const trackKey = trackKeyByTrackId.get(flowPoint.trackId);
if (trackKey) {
- globals.makeSelection(
- Actions.selectChromeSlice({
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: flowPoint.sliceId,
trackKey,
table: 'slice',
- scroll: true,
- }),
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: flowPoint.sliceId,
+ switchToCurrentSelectionTab: true,
+ },
);
}
}
diff --git a/ui/src/frontend/named_slice_track.ts b/ui/src/frontend/named_slice_track.ts
index 23e0723..5eff6af 100644
--- a/ui/src/frontend/named_slice_track.ts
+++ b/ui/src/frontend/named_slice_track.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Actions} from '../common/actions';
import {getColorForSlice} from '../core/colorizer';
import {STR_NULL} from '../trace_processor/query_result';
@@ -77,16 +76,21 @@
}
onSliceClick(args: OnSliceClickArgs<T['slice']>) {
- globals.makeSelection(
- Actions.selectChromeSlice({
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: args.slice.id,
trackKey: this.trackKey,
-
// |table| here can be either 'slice' or 'annotation'. The
// AnnotationSliceTrack overrides the onSliceClick and sets this to
// 'annotation'
table: 'slice',
- }),
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: true,
+ },
);
}
}
diff --git a/ui/src/frontend/search_handler.ts b/ui/src/frontend/search_handler.ts
index fb4eb16..d9e3f0d 100644
--- a/ui/src/frontend/search_handler.ts
+++ b/ui/src/frontend/search_handler.ts
@@ -88,26 +88,46 @@
if (currentId === undefined) return;
if (source === 'cpu') {
- globals.makeSelection(
- Actions.selectSlice({id: currentId, trackKey, scroll: true}),
- {clearSearch: false},
+ globals.setLegacySelection(
+ {
+ kind: 'SLICE',
+ id: currentId,
+ trackKey,
+ },
+ {
+ clearSearch: false,
+ pendingScrollId: currentId,
+ switchToCurrentSelectionTab: true,
+ },
);
} else if (source === 'log') {
- globals.makeSelection(
- Actions.selectLog({id: currentId, trackKey, scroll: true}),
- {clearSearch: false},
+ globals.setLegacySelection(
+ {
+ kind: 'LOG',
+ id: currentId,
+ trackKey,
+ },
+ {
+ clearSearch: false,
+ pendingScrollId: currentId,
+ switchToCurrentSelectionTab: true,
+ },
);
} else {
// Search results only include slices from the slice table for now.
// When we include annotations we need to pass the correct table.
- globals.makeSelection(
- Actions.selectChromeSlice({
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: currentId,
trackKey,
table: 'slice',
- scroll: true,
- }),
- {clearSearch: false},
+ },
+ {
+ clearSearch: false,
+ pendingScrollId: currentId,
+ switchToCurrentSelectionTab: true,
+ },
);
}
}
diff --git a/ui/src/frontend/slice_track.ts b/ui/src/frontend/slice_track.ts
index cde7027..e12cd73 100644
--- a/ui/src/frontend/slice_track.ts
+++ b/ui/src/frontend/slice_track.ts
@@ -362,12 +362,18 @@
if (data === undefined) return false;
const sliceId = data.sliceIds[sliceIndex];
if (sliceId !== undefined && sliceId !== -1) {
- globals.makeSelection(
- Actions.selectChromeSlice({
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: sliceId,
trackKey: this.trackKey,
table: this.namespace,
- }),
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: true,
+ },
);
return true;
}
diff --git a/ui/src/frontend/sql/slice.ts b/ui/src/frontend/sql/slice.ts
index 31a5586..050ec4e 100644
--- a/ui/src/frontend/sql/slice.ts
+++ b/ui/src/frontend/sql/slice.ts
@@ -18,7 +18,6 @@
import {Icons} from '../../base/semantic_icons';
import {duration, Time, time} from '../../base/time';
import {exists} from '../../base/utils';
-import {Actions} from '../../common/actions';
import {EngineProxy} from '../../trace_processor/engine';
import {
LONG,
@@ -233,13 +232,19 @@
vnode.attrs.ts,
Time.fromRaw(vnode.attrs.ts + dur),
);
- globals.makeSelection(
- Actions.selectChromeSlice({
+
+ globals.setLegacySelection(
+ {
+ kind: 'CHROME_SLICE',
id: vnode.attrs.id,
trackKey,
table: 'slice',
- }),
- {switchToCurrentSelectionTab: switchTab},
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: switchTab,
+ },
);
},
},
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index ba294a8..8b0d520 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -151,7 +151,19 @@
if (trackId === undefined) {
return;
}
- globals.makeSelection(Actions.selectSlice({id, trackKey: trackId}));
+ globals.setLegacySelection(
+ {
+ kind: 'SLICE',
+ id,
+ trackKey: trackId,
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: true,
+ },
+ );
+
scrollToTrackAndTs(trackId, ts);
}
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 2e15d67..4240ef4 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -37,6 +37,7 @@
import {canvasClip} from '../common/canvas_utils';
import {TimeScale} from './time_scale';
import {getLegacySelection} from '../common/state';
+import {CloseTrackButton} from './close_track_button';
function getTitleSize(title: string): string | undefined {
const length = title.length;
@@ -330,6 +331,7 @@
tags?: TrackTags;
track?: Track;
error?: Error | undefined;
+ closeable: boolean;
// Issues a scrollTo() on this DOM element at creation time. Default: false.
revealOnCreate?: boolean;
@@ -359,6 +361,7 @@
m(TrackShell, {
buttons: [
attrs.error && m(CrashButton, {error: attrs.error}),
+ attrs.closeable && m(CloseTrackButton, {trackKey: attrs.trackKey}),
attrs.buttons,
],
title: attrs.title,
@@ -426,6 +429,7 @@
tags?: TrackTags;
trackFSM?: TrackCacheEntry;
revealOnCreate?: boolean;
+ closeable: boolean;
}
export class TrackPanel implements Panel {
@@ -452,6 +456,7 @@
trackKey: attrs.trackKey,
error: attrs.trackFSM.getError(),
track: attrs.trackFSM.track,
+ closeable: attrs.closeable,
});
}
return m(TrackComponent, {
@@ -463,12 +468,14 @@
track: attrs.trackFSM.track,
error: attrs.trackFSM.getError(),
revealOnCreate: attrs.revealOnCreate,
+ closeable: attrs.closeable,
});
} else {
return m(TrackComponent, {
trackKey: attrs.trackKey,
title: attrs.title,
revealOnCreate: attrs.revealOnCreate,
+ closeable: attrs.closeable,
});
}
}
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 6cd0fb4..719705e 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -241,6 +241,7 @@
title: trackBundle.title,
tags: trackBundle.tags,
trackFSM: trackBundle.trackFSM,
+ closeable: trackBundle.closeable,
});
},
);
@@ -270,6 +271,7 @@
title: trackBundle.title,
tags: trackBundle.tags,
trackFSM: trackBundle.trackFSM,
+ closeable: trackBundle.closeable,
});
childTracks.push(panel);
}
@@ -301,7 +303,7 @@
this.keepCurrentSelection = false;
return;
}
- globals.makeSelection(Actions.deselect({}));
+ globals.clearSelection();
},
},
m(PanelContainer, {
@@ -327,6 +329,7 @@
tags: trackBundle.tags,
trackFSM: trackBundle.trackFSM,
revealOnCreate: true,
+ closeable: trackBundle.closeable,
});
}),
kind: 'TRACKS',
@@ -348,7 +351,7 @@
// Resolve a track and its metadata through the track cache
private resolveTrack(key: string): TrackBundle {
const trackState = globals.state.tracks[key];
- const {uri, params, name, labels} = trackState;
+ const {uri, params, name, labels, closeable} = trackState;
const trackDesc = globals.trackManager.resolveTrackInfo(uri);
const trackCacheEntry =
trackDesc && globals.trackManager.resolveTrack(key, trackDesc, params);
@@ -361,6 +364,7 @@
trackFSM,
labels,
trackIds,
+ closeable: closeable ?? false,
};
}
@@ -371,6 +375,7 @@
interface TrackBundle {
title: string;
+ closeable: boolean;
trackFSM?: TrackCacheEntry;
tags?: TrackTags;
labels?: string[];
diff --git a/ui/src/plugins/dev.perfetto.Chaos/OWNERS b/ui/src/plugins/dev.perfetto.Chaos/OWNERS
new file mode 100644
index 0000000..9ee9fce
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.Chaos/OWNERS
@@ -0,0 +1 @@
+hjd@google.com
diff --git a/ui/src/plugins/dev.perfetto.Chaos/index.ts b/ui/src/plugins/dev.perfetto.Chaos/index.ts
new file mode 100644
index 0000000..c18ff7f
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.Chaos/index.ts
@@ -0,0 +1,81 @@
+// 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 {
+ Plugin,
+ PluginContext,
+ PluginContextTrace,
+ PluginDescriptor,
+ addDebugSliceTrack,
+} from '../../public';
+
+class Chaos implements Plugin {
+ onActivate(ctx: PluginContext): void {
+ ctx.registerCommand({
+ id: 'dev.perfetto.Chaos#CrashNow',
+ name: 'Chaos: crash now',
+ callback: () => {
+ throw new Error('Manual crash from dev.perfetto.Chaos#CrashNow');
+ },
+ });
+ }
+
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ ctx.registerCommand({
+ id: 'dev.perfetto.Chaos#CrashNowQuery',
+ name: 'Chaos: run crashing query',
+ callback: () => {
+ ctx.engine.query(`this is a
+ syntactically
+ invalid
+ query
+ over
+ many
+ lines
+ `);
+ },
+ });
+
+ ctx.registerCommand({
+ id: 'dev.perfetto.Chaos#AddCrashingDebugTrack',
+ name: 'Chaos: add crashing debug track',
+ callback: () => {
+ addDebugSliceTrack(
+ ctx.engine,
+ {
+ sqlSource: `
+ syntactically
+ invalid
+ query
+ over
+ many
+ `,
+ },
+ `Chaos track`,
+ {ts: 'ts', dur: 'dur', name: 'name'},
+ [],
+ );
+ },
+ });
+ }
+
+ async onTraceUnload(_: PluginContextTrace): Promise<void> {}
+
+ onDeactivate(_: PluginContext): void {}
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'dev.perfetto.Chaos',
+ plugin: Chaos,
+};
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index 19c1867..97cee3c 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -21,7 +21,7 @@
import {LegacySelection} from '../common/state';
import {PanelSize} from '../frontend/panel';
import {EngineProxy} from '../trace_processor/engine';
-import {UntypedEventSet} from '../common/event_set';
+import {UntypedEventSet} from '../core/event_set';
export {EngineProxy} from '../trace_processor/engine';
export {
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index b4c5fe0..c561cbd 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -524,7 +524,20 @@
const id = index === -1 ? undefined : data.ids[index];
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!id || this.utidHoveredInThisTrack === -1) return false;
- globals.makeSelection(Actions.selectSlice({id, trackKey: this.trackKey}));
+
+ globals.setLegacySelection(
+ {
+ kind: 'SLICE',
+ id,
+ trackKey: this.trackKey,
+ },
+ {
+ clearSearch: true,
+ pendingScrollId: undefined,
+ switchToCurrentSelectionTab: true,
+ },
+ );
+
return true;
}
}
diff --git a/ui/src/tracks/debug/counter_track.ts b/ui/src/tracks/debug/counter_track.ts
index d61d573..5b37e3d 100644
--- a/ui/src/tracks/debug/counter_track.ts
+++ b/ui/src/tracks/debug/counter_track.ts
@@ -14,10 +14,7 @@
import m from 'mithril';
-import {Actions} from '../../common/actions';
import {BaseCounterTrack} from '../../frontend/base_counter_track';
-import {globals} from '../../frontend/globals';
-import {TrackButton} from '../../frontend/track_panel';
import {TrackContext} from '../../public';
import {EngineProxy} from '../../trace_processor/engine';
import {CounterDebugTrackConfig} from '../../frontend/debug_tracks';
@@ -49,20 +46,7 @@
}
getTrackShellButtons(): m.Children {
- return [
- this.getCounterContextMenu(),
- this.config.closeable &&
- m(TrackButton, {
- action: () => {
- globals.dispatch(
- Actions.removeTracks({trackKeys: [this.trackKey]}),
- );
- },
- i: 'close',
- tooltip: 'Close',
- showButton: true,
- }),
- ];
+ return this.getCounterContextMenu();
}
getSqlSource(): string {
diff --git a/ui/src/tracks/debug/slice_track.ts b/ui/src/tracks/debug/slice_track.ts
index dbab2be..4ea16dc 100644
--- a/ui/src/tracks/debug/slice_track.ts
+++ b/ui/src/tracks/debug/slice_track.ts
@@ -12,12 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import m from 'mithril';
-
-import {Actions} from '../../common/actions';
-import {globals} from '../../frontend/globals';
import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {TrackButton} from '../../frontend/track_panel';
import {TrackContext} from '../../public';
import {EngineProxy} from '../../trace_processor/engine';
import {
@@ -38,7 +33,6 @@
export interface DebugTrackV2Config {
data: SqlDataSource;
columns: SliceColumns;
- closeable: boolean;
argColumns: string[];
}
@@ -81,21 +75,6 @@
};
}
- getTrackShellButtons(): m.Children {
- return this.config.closeable
- ? m(TrackButton, {
- action: () => {
- globals.dispatch(
- Actions.removeTracks({trackKeys: [this.trackKey]}),
- );
- },
- i: 'close',
- tooltip: 'Close',
- showButton: true,
- })
- : [];
- }
-
private async createTrackTable(
data: SqlDataSource,
sliceColumns: SliceColumns,