Merge "add support ADB_SERVER_SOCKET" into main
diff --git a/Android.bp b/Android.bp
index c3a576a..ef5737b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,6 +37,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -124,6 +126,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -305,6 +309,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -401,6 +407,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -510,6 +518,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -616,6 +626,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -754,6 +766,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -839,6 +853,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -925,6 +941,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -1003,6 +1021,7 @@
],
host_supported: true,
vendor_available: true,
+ product_available: true,
export_include_dirs: [
"include",
"include/perfetto/base/build_configs/android_tree",
@@ -1013,6 +1032,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1066,6 +1087,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1117,10 +1140,8 @@
"perfetto_defaults",
],
apex_available: [
+ "//apex_available:anyapex",
"//apex_available:platform",
- "com.android.art",
- "com.android.art.debug",
- "com.android.tethering",
],
min_sdk_version: "30",
}
@@ -1149,6 +1170,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -1227,6 +1250,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1317,6 +1342,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -1449,6 +1476,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1520,6 +1549,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1615,6 +1646,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -1733,6 +1766,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -1804,6 +1839,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -2114,6 +2151,9 @@
":perfetto_protos_perfetto_config_android_lite_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_lite_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_lite_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
@@ -2253,7 +2293,6 @@
":perfetto_src_shared_lib_test_utils",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_db",
- ":perfetto_src_trace_processor_db_overlays_overlays",
":perfetto_src_trace_processor_db_storage_storage",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -2388,6 +2427,9 @@
"perfetto_protos_perfetto_config_android_lite_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
"perfetto_protos_perfetto_config_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_lite_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_gen_headers",
"perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
"perfetto_protos_perfetto_config_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
@@ -3011,6 +3053,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -3046,6 +3089,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -3110,6 +3154,7 @@
"protos/perfetto/config/chrome/chrome_config.proto",
"protos/perfetto/config/chrome/scenario_config.proto",
"protos/perfetto/config/data_source_config.proto",
+ "protos/perfetto/config/etw/etw_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/vulkan_memory_config.proto",
@@ -3139,6 +3184,136 @@
],
}
+// GN: //protos/perfetto/config/etw:cpp
+filegroup {
+ name: "perfetto_protos_perfetto_config_etw_cpp",
+ srcs: [
+ "protos/perfetto/config/etw/etw_config.proto",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:cpp
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_cpp_gen",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_cpp",
+ ],
+ tools: [
+ "aprotoc",
+ "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_cpp)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.gen.cc",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:cpp
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_cpp_gen_headers",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_cpp",
+ ],
+ tools: [
+ "aprotoc",
+ "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_cpp)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.gen.h",
+ ],
+ export_include_dirs: [
+ ".",
+ "protos",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:lite
+filegroup {
+ name: "perfetto_protos_perfetto_config_etw_lite",
+ srcs: [
+ "protos/perfetto/config/etw/etw_config.proto",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:lite
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_lite_gen",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_lite",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_lite)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.pb.cc",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:lite
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_lite_gen_headers",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_lite",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_lite)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.pb.h",
+ ],
+ export_include_dirs: [
+ ".",
+ "protos",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:zero
+filegroup {
+ name: "perfetto_protos_perfetto_config_etw_zero",
+ srcs: [
+ "protos/perfetto/config/etw/etw_config.proto",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:zero
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_zero_gen",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_zero",
+ ],
+ tools: [
+ "aprotoc",
+ "protozero_plugin",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_zero)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.pbzero.cc",
+ ],
+}
+
+// GN: //protos/perfetto/config/etw:zero
+genrule {
+ name: "perfetto_protos_perfetto_config_etw_zero_gen_headers",
+ srcs: [
+ ":perfetto_protos_perfetto_config_etw_zero",
+ ],
+ tools: [
+ "aprotoc",
+ "protozero_plugin",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_etw_zero)",
+ out: [
+ "external/perfetto/protos/perfetto/config/etw/etw_config.pbzero.h",
+ ],
+ export_include_dirs: [
+ ".",
+ "protos",
+ ],
+}
+
// GN: //protos/perfetto/config/ftrace:cpp
filegroup {
name: "perfetto_protos_perfetto_config_ftrace_cpp",
@@ -3694,6 +3869,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -3728,6 +3904,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -4735,6 +4912,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -4770,6 +4948,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -4819,6 +4998,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -4850,6 +5030,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -4894,6 +5075,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -4927,6 +5109,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -5239,6 +5422,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5268,6 +5452,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.cc",
"external/perfetto/protos/perfetto/trace/android/network_trace.gen.cc",
"external/perfetto/protos/perfetto/trace/android/packages_list.gen.cc",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.gen.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.gen.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.gen.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.gen.cc",
@@ -5297,6 +5482,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.gen.h",
"external/perfetto/protos/perfetto/trace/android/network_trace.gen.h",
"external/perfetto/protos/perfetto/trace/android/packages_list.gen.h",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.gen.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.gen.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.gen.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.gen.h",
@@ -5321,6 +5507,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5349,6 +5536,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.cc",
"external/perfetto/protos/perfetto/trace/android/network_trace.pb.cc",
"external/perfetto/protos/perfetto/trace/android/packages_list.pb.cc",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.pb.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pb.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pb.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pb.cc",
@@ -5377,6 +5565,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.pb.h",
"external/perfetto/protos/perfetto/trace/android/network_trace.pb.h",
"external/perfetto/protos/perfetto/trace/android/packages_list.pb.h",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.pb.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pb.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pb.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pb.h",
@@ -5391,6 +5580,7 @@
genrule {
name: "perfetto_protos_perfetto_trace_android_winscope_descriptor",
srcs: [
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5419,6 +5609,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5448,6 +5639,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.cc",
"external/perfetto/protos/perfetto/trace/android/network_trace.pbzero.cc",
"external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.pbzero.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pbzero.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pbzero.cc",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pbzero.cc",
@@ -5477,6 +5669,7 @@
"external/perfetto/protos/perfetto/trace/android/initial_display_state.pbzero.h",
"external/perfetto/protos/perfetto/trace/android/network_trace.pbzero.h",
"external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/android/shell_transition.pbzero.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_common.pbzero.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_layers.pbzero.h",
"external/perfetto/protos/perfetto/trace/android/surfaceflinger_transactions.pbzero.h",
@@ -5667,6 +5860,7 @@
"protos/perfetto/config/chrome/chrome_config.proto",
"protos/perfetto/config/chrome/scenario_config.proto",
"protos/perfetto/config/data_source_config.proto",
+ "protos/perfetto/config/etw/etw_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/vulkan_memory_config.proto",
@@ -5696,6 +5890,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -5749,6 +5944,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6167,6 +6363,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6248,6 +6445,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.gen.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.cc",
@@ -6329,6 +6527,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.gen.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.h",
@@ -6406,6 +6605,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6486,6 +6686,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pb.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.cc",
@@ -6566,6 +6767,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pb.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.h",
@@ -6643,6 +6845,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6724,6 +6927,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.cc",
@@ -6805,6 +7009,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.h",
@@ -7187,6 +7392,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -7220,6 +7426,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -7267,6 +7474,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -7299,6 +7507,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -7346,6 +7555,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -7379,6 +7589,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -7432,6 +7643,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -7486,6 +7698,7 @@
":perfetto_protos_perfetto_common_cpp",
":perfetto_protos_perfetto_config_android_cpp",
":perfetto_protos_perfetto_config_cpp",
+ ":perfetto_protos_perfetto_config_etw_cpp",
":perfetto_protos_perfetto_config_ftrace_cpp",
":perfetto_protos_perfetto_config_gpu_cpp",
":perfetto_protos_perfetto_config_inode_file_cpp",
@@ -7558,6 +7771,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -7611,6 +7825,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite",
":perfetto_protos_perfetto_config_android_lite",
+ ":perfetto_protos_perfetto_config_etw_lite",
":perfetto_protos_perfetto_config_ftrace_lite",
":perfetto_protos_perfetto_config_gpu_lite",
":perfetto_protos_perfetto_config_inode_file_lite",
@@ -7683,6 +7898,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -7737,6 +7953,7 @@
srcs: [
":perfetto_protos_perfetto_common_zero",
":perfetto_protos_perfetto_config_android_zero",
+ ":perfetto_protos_perfetto_config_etw_zero",
":perfetto_protos_perfetto_config_ftrace_zero",
":perfetto_protos_perfetto_config_gpu_zero",
":perfetto_protos_perfetto_config_inode_file_zero",
@@ -10744,24 +10961,11 @@
],
}
-// GN: //src/trace_processor/db/overlays:overlays
+// GN: //src/trace_processor/db/storage:fake_storage
filegroup {
- name: "perfetto_src_trace_processor_db_overlays_overlays",
+ name: "perfetto_src_trace_processor_db_storage_fake_storage",
srcs: [
- "src/trace_processor/db/overlays/arrangement_overlay.cc",
- "src/trace_processor/db/overlays/null_overlay.cc",
- "src/trace_processor/db/overlays/selector_overlay.cc",
- "src/trace_processor/db/overlays/storage_overlay.cc",
- ],
-}
-
-// GN: //src/trace_processor/db/overlays:unittests
-filegroup {
- name: "perfetto_src_trace_processor_db_overlays_unittests",
- srcs: [
- "src/trace_processor/db/overlays/arrangement_overlay_unittest.cc",
- "src/trace_processor/db/overlays/null_overlay_unittest.cc",
- "src/trace_processor/db/overlays/selector_overlay_unittest.cc",
+ "src/trace_processor/db/storage/fake_storage.cc",
],
}
@@ -10769,9 +10973,13 @@
filegroup {
name: "perfetto_src_trace_processor_db_storage_storage",
srcs: [
+ "src/trace_processor/db/storage/arrangement_storage.cc",
+ "src/trace_processor/db/storage/dense_null_storage.cc",
"src/trace_processor/db/storage/dummy_storage.cc",
"src/trace_processor/db/storage/id_storage.cc",
+ "src/trace_processor/db/storage/null_storage.cc",
"src/trace_processor/db/storage/numeric_storage.cc",
+ "src/trace_processor/db/storage/selector_storage.cc",
"src/trace_processor/db/storage/set_id_storage.cc",
"src/trace_processor/db/storage/storage.cc",
"src/trace_processor/db/storage/string_storage.cc",
@@ -10782,8 +10990,12 @@
filegroup {
name: "perfetto_src_trace_processor_db_storage_unittests",
srcs: [
+ "src/trace_processor/db/storage/arrangement_storage_unittest.cc",
+ "src/trace_processor/db/storage/dense_null_storage_unittest.cc",
"src/trace_processor/db/storage/id_storage_unittest.cc",
+ "src/trace_processor/db/storage/null_storage_unittest.cc",
"src/trace_processor/db/storage/numeric_storage_unittest.cc",
+ "src/trace_processor/db/storage/selector_storage_unittest.cc",
"src/trace_processor/db/storage/set_id_storage_unittest.cc",
"src/trace_processor/db/storage/string_storage_unittest.cc",
],
@@ -11259,6 +11471,8 @@
filegroup {
name: "perfetto_src_trace_processor_importers_proto_winscope_full",
srcs: [
+ "src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc",
+ "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc",
"src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc",
"src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc",
"src/trace_processor/importers/proto/winscope/winscope_args_parser.cc",
@@ -13081,6 +13295,7 @@
"protos/perfetto/config/chrome/chrome_config.proto",
"protos/perfetto/config/chrome/scenario_config.proto",
"protos/perfetto/config/data_source_config.proto",
+ "protos/perfetto/config/etw/etw_config.proto",
"protos/perfetto/config/ftrace/ftrace_config.proto",
"protos/perfetto/config/gpu/gpu_counter_config.proto",
"protos/perfetto/config/gpu/vulkan_memory_config.proto",
@@ -13110,6 +13325,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -13163,6 +13379,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -13261,6 +13478,7 @@
srcs: [
":perfetto_protos_perfetto_common_lite_gen",
":perfetto_protos_perfetto_config_android_lite_gen",
+ ":perfetto_protos_perfetto_config_etw_lite_gen",
":perfetto_protos_perfetto_config_ftrace_lite_gen",
":perfetto_protos_perfetto_config_gpu_lite_gen",
":perfetto_protos_perfetto_config_inode_file_lite_gen",
@@ -13299,6 +13517,7 @@
generated_headers: [
"perfetto_protos_perfetto_common_lite_gen_headers",
"perfetto_protos_perfetto_config_android_lite_gen_headers",
+ "perfetto_protos_perfetto_config_etw_lite_gen_headers",
"perfetto_protos_perfetto_config_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_config_gpu_lite_gen_headers",
"perfetto_protos_perfetto_config_inode_file_lite_gen_headers",
@@ -13333,6 +13552,7 @@
export_generated_headers: [
"perfetto_protos_perfetto_common_lite_gen_headers",
"perfetto_protos_perfetto_config_android_lite_gen_headers",
+ "perfetto_protos_perfetto_config_etw_lite_gen_headers",
"perfetto_protos_perfetto_config_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_config_gpu_lite_gen_headers",
"perfetto_protos_perfetto_config_inode_file_lite_gen_headers",
@@ -13420,6 +13640,9 @@
":perfetto_protos_perfetto_config_android_lite_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_lite_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_lite_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
@@ -13590,8 +13813,7 @@
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_containers_unittests",
":perfetto_src_trace_processor_db_db",
- ":perfetto_src_trace_processor_db_overlays_overlays",
- ":perfetto_src_trace_processor_db_overlays_unittests",
+ ":perfetto_src_trace_processor_db_storage_fake_storage",
":perfetto_src_trace_processor_db_storage_storage",
":perfetto_src_trace_processor_db_storage_unittests",
":perfetto_src_trace_processor_db_unittests",
@@ -13773,6 +13995,9 @@
"perfetto_protos_perfetto_config_android_lite_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
"perfetto_protos_perfetto_config_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_lite_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_gen_headers",
"perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
"perfetto_protos_perfetto_config_ftrace_lite_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
@@ -13953,6 +14178,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -14078,6 +14305,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -14149,6 +14378,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -14266,6 +14497,7 @@
":perfetto_include_perfetto_trace_processor_trace_processor",
":perfetto_protos_perfetto_common_zero_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_zero_gen",
":perfetto_protos_perfetto_config_inode_file_zero_gen",
@@ -14311,7 +14543,6 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_db",
- ":perfetto_src_trace_processor_db_overlays_overlays",
":perfetto_src_trace_processor_db_storage_storage",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -14384,6 +14615,7 @@
generated_headers: [
"perfetto_protos_perfetto_common_zero_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_config_gpu_zero_gen_headers",
"perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
@@ -14461,7 +14693,7 @@
host: {
static_libs: [
"libprotobuf-cpp-full",
- "libsqlite",
+ "libsqlite_static_noicu",
"libz",
"sqlite_ext_percentile",
],
@@ -14502,6 +14734,7 @@
":perfetto_include_perfetto_trace_processor_trace_processor",
":perfetto_protos_perfetto_common_zero_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_zero_gen",
":perfetto_protos_perfetto_config_inode_file_zero_gen",
@@ -14545,7 +14778,6 @@
":perfetto_src_protozero_protozero",
":perfetto_src_trace_processor_containers_containers",
":perfetto_src_trace_processor_db_db",
- ":perfetto_src_trace_processor_db_overlays_overlays",
":perfetto_src_trace_processor_db_storage_storage",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -14613,7 +14845,7 @@
":perfetto_src_traceconv_utils",
],
static_libs: [
- "libsqlite",
+ "libsqlite_static_noicu",
"libz",
"perfetto_src_trace_processor_demangle",
"sqlite_ext_percentile",
@@ -14621,6 +14853,7 @@
generated_headers: [
"perfetto_protos_perfetto_common_zero_gen_headers",
"perfetto_protos_perfetto_config_android_zero_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_gen_headers",
"perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
"perfetto_protos_perfetto_config_gpu_zero_gen_headers",
"perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
@@ -14735,6 +14968,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -14834,6 +15069,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -14942,6 +15179,8 @@
":perfetto_protos_perfetto_config_android_cpp_gen",
":perfetto_protos_perfetto_config_android_zero_gen",
":perfetto_protos_perfetto_config_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_cpp_gen",
+ ":perfetto_protos_perfetto_config_etw_zero_gen",
":perfetto_protos_perfetto_config_ftrace_cpp_gen",
":perfetto_protos_perfetto_config_ftrace_zero_gen",
":perfetto_protos_perfetto_config_gpu_cpp_gen",
@@ -15016,6 +15255,8 @@
"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_etw_cpp_gen_headers",
+ "perfetto_protos_perfetto_config_etw_zero_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",
@@ -15242,3 +15483,29 @@
"LICENSE",
],
}
+
+// TODO(b/315118713): use list of proto file sources instead of merged proto
+gensrcs {
+ name: "perfetto_trace_javastream_protos",
+ srcs: [
+ "protos/perfetto/trace/perfetto_trace.proto",
+ ],
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ "--plugin=$(location protoc-gen-javastream) " +
+ "--javastream_out=$(genDir)/$(in) " +
+ "-Iexternal/protobuf/src " +
+ "-Iexternal/perfetto " +
+ "-I . $(in) " +
+ "&& $(location soong_zip) " +
+ "-jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+ data: [
+ ":libprotobuf-internal-protos",
+ ],
+ output_extension: "srcjar",
+}
diff --git a/Android.bp.extras b/Android.bp.extras
index 3292ad9..9c7b0b0 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -171,3 +171,29 @@
"LICENSE",
],
}
+
+// TODO(b/315118713): use list of proto file sources instead of merged proto
+gensrcs {
+ name: "perfetto_trace_javastream_protos",
+ srcs: [
+ "protos/perfetto/trace/perfetto_trace.proto",
+ ],
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ "--plugin=$(location protoc-gen-javastream) " +
+ "--javastream_out=$(genDir)/$(in) " +
+ "-Iexternal/protobuf/src " +
+ "-Iexternal/perfetto " +
+ "-I . $(in) " +
+ "&& $(location soong_zip) " +
+ "-jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+ data: [
+ ":libprotobuf-internal-protos",
+ ],
+ output_extension: "srcjar",
+}
diff --git a/BUILD b/BUILD
index 9353be2..5c8fbe6 100644
--- a/BUILD
+++ b/BUILD
@@ -172,6 +172,7 @@
":protos_perfetto_common_cpp",
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_gpu_cpp",
":protos_perfetto_config_inode_file_cpp",
@@ -217,7 +218,6 @@
":src_kernel_utils_syscall_table",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_db",
- ":src_trace_processor_db_overlays_overlays",
":src_trace_processor_db_storage_storage",
":src_trace_processor_export_json",
":src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -302,6 +302,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -379,6 +380,8 @@
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_android_zero",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_cpp",
@@ -492,6 +495,8 @@
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_android_zero",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_cpp",
@@ -854,6 +859,7 @@
"include/perfetto/tracing/console_interceptor.h",
"include/perfetto/tracing/data_source.h",
"include/perfetto/tracing/debug_annotation.h",
+ "include/perfetto/tracing/default_socket.h",
"include/perfetto/tracing/event_context.h",
"include/perfetto/tracing/interceptor.h",
"include/perfetto/tracing/internal/basic_types.h",
@@ -1283,37 +1289,31 @@
"src/trace_processor/containers/string_pool.h",
],
deps = [
+ ":protos_perfetto_common_zero",
+ ":protos_perfetto_trace_processor_zero",
":src_base_base",
],
linkstatic = True,
)
-# GN target: //src/trace_processor/db/overlays:overlays
-perfetto_filegroup(
- name = "src_trace_processor_db_overlays_overlays",
- srcs = [
- "src/trace_processor/db/overlays/arrangement_overlay.cc",
- "src/trace_processor/db/overlays/arrangement_overlay.h",
- "src/trace_processor/db/overlays/null_overlay.cc",
- "src/trace_processor/db/overlays/null_overlay.h",
- "src/trace_processor/db/overlays/selector_overlay.cc",
- "src/trace_processor/db/overlays/selector_overlay.h",
- "src/trace_processor/db/overlays/storage_overlay.cc",
- "src/trace_processor/db/overlays/storage_overlay.h",
- "src/trace_processor/db/overlays/types.h",
- ],
-)
-
# GN target: //src/trace_processor/db/storage:storage
perfetto_filegroup(
name = "src_trace_processor_db_storage_storage",
srcs = [
+ "src/trace_processor/db/storage/arrangement_storage.cc",
+ "src/trace_processor/db/storage/arrangement_storage.h",
+ "src/trace_processor/db/storage/dense_null_storage.cc",
+ "src/trace_processor/db/storage/dense_null_storage.h",
"src/trace_processor/db/storage/dummy_storage.cc",
"src/trace_processor/db/storage/dummy_storage.h",
"src/trace_processor/db/storage/id_storage.cc",
"src/trace_processor/db/storage/id_storage.h",
+ "src/trace_processor/db/storage/null_storage.cc",
+ "src/trace_processor/db/storage/null_storage.h",
"src/trace_processor/db/storage/numeric_storage.cc",
"src/trace_processor/db/storage/numeric_storage.h",
+ "src/trace_processor/db/storage/selector_storage.cc",
+ "src/trace_processor/db/storage/selector_storage.h",
"src/trace_processor/db/storage/set_id_storage.cc",
"src/trace_processor/db/storage/set_id_storage.h",
"src/trace_processor/db/storage/storage.cc",
@@ -1576,6 +1576,10 @@
perfetto_filegroup(
name = "src_trace_processor_importers_proto_winscope_full",
srcs = [
+ "src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc",
+ "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h",
+ "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc",
+ "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h",
"src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.cc",
"src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h",
"src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.cc",
@@ -3169,6 +3173,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -3235,6 +3240,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -3583,6 +3589,7 @@
deps = [
":protos_perfetto_common_cpp",
":protos_perfetto_config_android_cpp",
+ ":protos_perfetto_config_etw_cpp",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_gpu_cpp",
":protos_perfetto_config_inode_file_cpp",
@@ -3609,6 +3616,33 @@
],
)
+# GN target: //protos/perfetto/config/etw:cpp
+perfetto_cc_protocpp_library(
+ name = "protos_perfetto_config_etw_cpp",
+ deps = [
+ ":protos_perfetto_config_etw_protos",
+ ],
+)
+
+# GN target: //protos/perfetto/config/etw:source_set
+perfetto_proto_library(
+ name = "protos_perfetto_config_etw_protos",
+ srcs = [
+ "protos/perfetto/config/etw/etw_config.proto",
+ ],
+ visibility = [
+ PERFETTO_CONFIG.proto_library_visibility,
+ ],
+)
+
+# GN target: //protos/perfetto/config/etw:zero
+perfetto_cc_protozero_library(
+ name = "protos_perfetto_config_etw_zero",
+ deps = [
+ ":protos_perfetto_config_etw_protos",
+ ],
+)
+
# GN target: //protos/perfetto/config/ftrace:cpp
perfetto_cc_protocpp_library(
name = "protos_perfetto_config_ftrace_cpp",
@@ -3829,6 +3863,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -3968,6 +4003,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -3990,6 +4026,7 @@
":protos_perfetto_common_cpp",
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_gpu_cpp",
":protos_perfetto_config_inode_file_cpp",
@@ -4012,6 +4049,7 @@
":protos_perfetto_common_cpp",
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_gpu_cpp",
":protos_perfetto_config_inode_file_cpp",
@@ -4041,6 +4079,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -4259,6 +4298,7 @@
"protos/perfetto/trace/android/initial_display_state.proto",
"protos/perfetto/trace/android/network_trace.proto",
"protos/perfetto/trace/android/packages_list.proto",
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -4275,6 +4315,7 @@
perfetto_proto_library(
name = "protos_perfetto_trace_android_winscope_deps_protos",
srcs = [
+ "protos/perfetto/trace/android/shell_transition.proto",
"protos/perfetto/trace/android/surfaceflinger_common.proto",
"protos/perfetto/trace/android/surfaceflinger_layers.proto",
"protos/perfetto/trace/android/surfaceflinger_transactions.proto",
@@ -4421,6 +4462,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -4533,6 +4575,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -4554,6 +4597,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -4589,6 +4633,7 @@
deps = [
":protos_perfetto_common_protos",
":protos_perfetto_config_android_protos",
+ ":protos_perfetto_config_etw_protos",
":protos_perfetto_config_ftrace_protos",
":protos_perfetto_config_gpu_protos",
":protos_perfetto_config_inode_file_protos",
@@ -4630,6 +4675,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -5096,6 +5142,8 @@
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_android_zero",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_cpp",
@@ -5188,6 +5236,8 @@
":protos_perfetto_config_android_cpp",
":protos_perfetto_config_android_zero",
":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_etw_cpp",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_cpp",
@@ -5246,7 +5296,6 @@
srcs = [
":src_kernel_utils_syscall_table",
":src_trace_processor_db_db",
- ":src_trace_processor_db_overlays_overlays",
":src_trace_processor_db_storage_storage",
":src_trace_processor_export_json",
":src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -5332,6 +5381,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -5412,7 +5462,6 @@
":src_profiling_symbolizer_symbolizer",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_db",
- ":src_trace_processor_db_overlays_overlays",
":src_trace_processor_db_storage_storage",
":src_trace_processor_export_json",
":src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -5486,6 +5535,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -5574,6 +5624,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
@@ -5635,7 +5686,6 @@
":src_profiling_symbolizer_symbolizer",
":src_protozero_proto_ring_buffer",
":src_trace_processor_db_db",
- ":src_trace_processor_db_overlays_overlays",
":src_trace_processor_db_storage_storage",
":src_trace_processor_export_json",
":src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -5710,6 +5760,7 @@
deps = [
":protos_perfetto_common_zero",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_etw_zero",
":protos_perfetto_config_ftrace_zero",
":protos_perfetto_config_gpu_zero",
":protos_perfetto_config_inode_file_zero",
diff --git a/CHANGELOG b/CHANGELOG
index 1c5e81b..35291b7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,17 @@
*
+v40.0 - 2023-12-04:
+ Tracing service and probes:
+ * Added support for collecting battery voltage from the Android Power HAL.
+ * Added support for emitting machine id from producers on remote hosts.
+ Trace Processor:
+ * Added of flow id from trace as a column in the flow table.
+ * Fixed computation of trace_bounds table when using UI native acceleration.
+ UI:
+ * Switched to use "v2" querying and rendering system for tracks by default.
+
+
v39.0 - 2023-11-15:
Tracing service and probes:
* Added reporting of TZ offset under system_info.timezone_off_mins .
@@ -18,12 +29,12 @@
* Added support for running on Linux & Android systems configured with 16K
pagetables.
Trace Processor:
- * New android_boot metric.
+ * Added android_boot metric.
* Added new PerfettoSQL syntax (CREATE PERFETTO VIEW) for adding schemas to views.
- * Support perf.data import format.
- * Add dvfs and cpu_idle to stdlib.
+ * Added support for the perf.data import format.
+ * Added dvfs and cpu_idle to stdlib.
UI:
- * Add a new type of debug tracks: counter.
+ * Added a new type of debug tracks: counter.
* Improved visualization of timestamps for durations.
diff --git a/docs/analysis/common-queries.md b/docs/analysis/common-queries.md
index adae686..6675bc0 100644
--- a/docs/analysis/common-queries.md
+++ b/docs/analysis/common-queries.md
@@ -52,8 +52,8 @@
SELECT
slice_id,
slice_name,
- SUM(CASE state = 'R' THEN dur ELSE 0 END) AS runnable_time,
- SUM(CASE state = 'D' THEN dur ELSE 0 END) AS uninterruptible_time
+ SUM(IIF(state = 'R', dur, 0)) AS runnable_time,
+ SUM(IIF(state = 'D', dur, 0)) AS uninterruptible_time
FROM slice_thread_state_breakdown
GROUP BY slice_id;
```
diff --git a/docs/contributing/ui-plugins.md b/docs/contributing/ui-plugins.md
index 939e334..809018a 100644
--- a/docs/contributing/ui-plugins.md
+++ b/docs/contributing/ui-plugins.md
@@ -14,7 +14,7 @@
```sh
git clone https://android.googlesource.com/platform/external/perfetto/
cd perfetto
-./tool/install-build-deps --ui
+./tools/install-build-deps --ui
```
### Copy the plugin skeleton
diff --git a/docs/data-sources/cpu-scheduling.md b/docs/data-sources/cpu-scheduling.md
index b8447ff..6023136 100644
--- a/docs/data-sources/cpu-scheduling.md
+++ b/docs/data-sources/cpu-scheduling.md
@@ -126,10 +126,11 @@
NOTE: `sched_waking` and `sched_wakeup` provide nearly the same information. The
difference lies in wakeup events across CPUs, which involve
- inter-processor interrupts. The former is emitted on the source (wakee)
- CPU, the latter on the destination (waked) CPU. `sched_waking` is usually
- sufficient for latency analysis, unless you are looking into breaking down
- latency due to inter-processor signaling.
+ inter-processor interrupts. The former is always emitted on the source (wakee)
+ CPU, the latter may be executed on either the source or the destination (waked) CPU
+ depending on several factors. `sched_waking` is usually sufficient for latency
+ analysis, unless you are looking into breaking down latency due to
+ the scheduler's wake up path, such as inter-processor signaling.
When enabling `sched_waking` events, the following will appear in the UI when
selecting a CPU slice:
diff --git a/include/perfetto/base/build_config.h b/include/perfetto/base/build_config.h
index 5002529..a0e6bb9 100644
--- a/include/perfetto/base/build_config.h
+++ b/include/perfetto/base/build_config.h
@@ -129,6 +129,16 @@
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_USERDEBUG_BUILD() 0
#endif
+// Processor architecture detection. For more info on what's defined, see:
+// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+// http://www.agner.org/optimize/calling_conventions.pdf
+// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(__aarch64__) || defined(_M_ARM64)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 1
+#else
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 0
+#endif
+
// perfetto_build_flags.h contains the tweakable build flags defined via GN.
// - In GN builds (e.g., standalone, chromium, v8) this file is generated at
// build time via the gen_rule //gn/gen_buildflags.
diff --git a/include/perfetto/base/platform_handle.h b/include/perfetto/base/platform_handle.h
index 879fa85..88f6d59 100644
--- a/include/perfetto/base/platform_handle.h
+++ b/include/perfetto/base/platform_handle.h
@@ -17,6 +17,8 @@
#ifndef INCLUDE_PERFETTO_BASE_PLATFORM_HANDLE_H_
#define INCLUDE_PERFETTO_BASE_PLATFORM_HANDLE_H_
+#include <stdint.h>
+
#include "perfetto/base/build_config.h"
namespace perfetto {
@@ -30,10 +32,17 @@
// in Windows.h take an int, not a HANDLE.
// 2. Handles returned by old-school WINAPI like CreateFile, CreateEvent etc.
// These are proper HANDLE(s). PlatformHandle should be used here.
+//
+// On Windows, sockets have their own type (SOCKET) which is neither a HANDLE
+// nor an int. However Windows SOCKET(s) can have an event HANDLE attached
+// to them (which in Perfetto is a PlatformHandle), and that can be used in
+// WaitForMultipleObjects, hence in base::TaskRunner.AddFileDescriptorWatch().
+// On POSIX OSes, a SocketHandle is really just an int (a file descriptor).
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// Windows.h typedefs HANDLE to void*. We use void* here to avoid leaking
-// Windows.h through our headers.
+// Windows.h typedefs HANDLE to void*, and SOCKET to uintptr_t. We use their
+// types to avoid leaking Windows.h through our headers.
using PlatformHandle = void*;
+using SocketHandle = uintptr_t;
// On Windows both nullptr and 0xffff... (INVALID_HANDLE_VALUE) are invalid.
struct PlatformHandleChecker {
@@ -43,6 +52,7 @@
};
#else
using PlatformHandle = int;
+using SocketHandle = int;
struct PlatformHandleChecker {
static inline bool IsValid(PlatformHandle h) { return h >= 0; }
};
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index fd40946..3e5b101 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -49,6 +49,7 @@
}
void SleepMicroseconds(unsigned interval_us);
+void InitializeTime();
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
diff --git a/include/perfetto/ext/base/sys_types.h b/include/perfetto/ext/base/sys_types.h
index 999a152..49eeb11 100644
--- a/include/perfetto/ext/base/sys_types.h
+++ b/include/perfetto/ext/base/sys_types.h
@@ -21,6 +21,7 @@
// missing on Windows.
#include <sys/types.h>
+#include <cstdint>
#include "perfetto/base/build_config.h"
@@ -43,6 +44,11 @@
namespace perfetto {
namespace base {
+// The machine ID used in the tracing core.
+using MachineID = uint32_t;
+// The default value reserved for the host trace.
+constexpr MachineID kDefaultMachineID = 0;
+
constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);
diff --git a/include/perfetto/ext/base/thread_annotations.h b/include/perfetto/ext/base/thread_annotations.h
index beb8c45..6aee16a 100644
--- a/include/perfetto/ext/base/thread_annotations.h
+++ b/include/perfetto/ext/base/thread_annotations.h
@@ -24,15 +24,13 @@
extern "C" {
void AnnotateBenignRaceSized(const char* file,
int line,
- unsigned long address,
- unsigned long size,
+ const volatile void* address,
+ size_t size,
const char* description);
}
-#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
- AnnotateBenignRaceSized(__FILE__, __LINE__, \
- reinterpret_cast<unsigned long>(pointer), size, \
- description);
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
+ AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
#else // defined(ADDRESS_SANITIZER)
#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
#endif // defined(ADDRESS_SANITIZER)
diff --git a/include/perfetto/ext/base/unix_socket.h b/include/perfetto/ext/base/unix_socket.h
index 74ce2d9..e5cc536 100644
--- a/include/perfetto/ext/base/unix_socket.h
+++ b/include/perfetto/ext/base/unix_socket.h
@@ -27,6 +27,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
+#include "perfetto/base/platform_handle.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/base/weak_ptr.h"
@@ -36,23 +37,13 @@
namespace perfetto {
namespace base {
-// Define the SocketHandle and ScopedSocketHandle types.
-// On POSIX OSes, a SocketHandle is really just an int (a file descriptor).
-// On Windows, sockets are have their own type (SOCKET) which is neither a
-// HANDLE nor an int. However Windows SOCKET(s) can have a event HANDLE attached
-// to them (which in Perfetto is a PlatformHandle), and that can be used in
-// WaitForMultipleObjects, hence in base::TaskRunner.AddFileDescriptorWatch().
+// Define the ScopedSocketHandle type.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// uintptr_t really reads as SOCKET here (Windows headers typedef to that).
-// As usual we don't just use SOCKET here to avoid leaking Windows.h includes
-// in our headers.
-using SocketHandle = uintptr_t; // SOCKET
int CloseSocket(SocketHandle); // A wrapper around ::closesocket().
using ScopedSocketHandle =
ScopedResource<SocketHandle, CloseSocket, static_cast<SocketHandle>(-1)>;
#else
-using SocketHandle = int;
using ScopedSocketHandle = ScopedFile;
#endif
diff --git a/include/perfetto/ext/ipc/client_info.h b/include/perfetto/ext/ipc/client_info.h
index b7c997d..132ab69 100644
--- a/include/perfetto/ext/ipc/client_info.h
+++ b/include/perfetto/ext/ipc/client_info.h
@@ -18,6 +18,7 @@
#define INCLUDE_PERFETTO_EXT_IPC_CLIENT_INFO_H_
#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/sys_types.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/ipc/basic_types.h"
@@ -28,12 +29,16 @@
class ClientInfo {
public:
ClientInfo() = default;
- ClientInfo(ClientID client_id, uid_t uid, pid_t pid)
- : client_id_(client_id), uid_(uid), pid_(pid) {}
+ ClientInfo(ClientID client_id,
+ uid_t uid,
+ pid_t pid,
+ base::MachineID machine_id)
+ : client_id_(client_id), uid_(uid), pid_(pid), machine_id_(machine_id) {}
bool operator==(const ClientInfo& other) const {
- return std::tie(client_id_, uid_, pid_) ==
- std::tie(other.client_id_, other.uid_, other.pid_);
+ return std::tie(client_id_, uid_, pid_, machine_id_) ==
+ std::tie(other.client_id_, other.uid_, other.pid_,
+ other.machine_id_);
}
bool operator!=(const ClientInfo& other) const { return !(*this == other); }
@@ -54,12 +59,16 @@
// Posix process ID. Comes from the kernel and can be trusted.
int32_t pid() const { return pid_; }
+ // An integral ID that identifies the machine the client is on.
+ base::MachineID machine_id() const { return machine_id_; }
+
private:
ClientID client_id_ = 0;
// The following fields are emitted to trace packets and should be kept in
// sync with perfetto::ClientIdentity.
uid_t uid_ = kInvalidUid;
pid_t pid_ = base::kInvalidPid;
+ base::MachineID machine_id_ = base::kDefaultMachineID;
};
} // namespace ipc
diff --git a/include/perfetto/ext/tracing/core/basic_types.h b/include/perfetto/ext/tracing/core/basic_types.h
index b0090e9..c3484ac 100644
--- a/include/perfetto/ext/tracing/core/basic_types.h
+++ b/include/perfetto/ext/tracing/core/basic_types.h
@@ -98,6 +98,10 @@
constexpr TracingSessionID kBugreportSessionId =
static_cast<TracingSessionID>(-1);
+// The ID of a machine in a multi-machine tracing session.
+using MachineID = base::MachineID;
+constexpr MachineID kDefaultMachineID = base::kDefaultMachineID;
+
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_TRACING_CORE_BASIC_TYPES_H_
diff --git a/include/perfetto/ext/tracing/core/client_identity.h b/include/perfetto/ext/tracing/core/client_identity.h
index 1ab009a..3a18846 100644
--- a/include/perfetto/ext/tracing/core/client_identity.h
+++ b/include/perfetto/ext/tracing/core/client_identity.h
@@ -18,6 +18,7 @@
#define INCLUDE_PERFETTO_EXT_TRACING_CORE_CLIENT_IDENTITY_H_
#include "perfetto/ext/base/sys_types.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
namespace perfetto {
@@ -26,7 +27,8 @@
class ClientIdentity {
public:
ClientIdentity() = default;
- ClientIdentity(uid_t uid, pid_t pid) : uid_(uid), pid_(pid) {}
+ ClientIdentity(uid_t uid, pid_t pid, MachineID machine_id = kDefaultMachineID)
+ : uid_(uid), pid_(pid), machine_id_(machine_id) {}
bool has_uid() const { return uid_ != base::kInvalidUid; }
uid_t uid() const { return uid_; }
@@ -34,9 +36,15 @@
bool has_pid() const { return pid_ != base::kInvalidPid; }
pid_t pid() const { return pid_; }
+ bool has_non_default_machine_id() const {
+ return machine_id_ != kDefaultMachineID;
+ }
+ base::MachineID machine_id() const { return machine_id_; }
+
private:
uid_t uid_ = base::kInvalidUid;
pid_t pid_ = base::kInvalidPid;
+ MachineID machine_id_ = kDefaultMachineID;
};
} // namespace perfetto
diff --git a/include/perfetto/ext/tracing/ipc/default_socket.h b/include/perfetto/ext/tracing/ipc/default_socket.h
index 86d7b78..b7d667b 100644
--- a/include/perfetto/ext/tracing/ipc/default_socket.h
+++ b/include/perfetto/ext/tracing/ipc/default_socket.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,25 +17,9 @@
#ifndef INCLUDE_PERFETTO_EXT_TRACING_IPC_DEFAULT_SOCKET_H_
#define INCLUDE_PERFETTO_EXT_TRACING_IPC_DEFAULT_SOCKET_H_
-#include <string>
-#include <vector>
-
-#include "perfetto/base/export.h"
-
-namespace perfetto {
-
-PERFETTO_EXPORT_COMPONENT const char* GetConsumerSocket();
-// This function is used for tokenize the |producer_socket_names| string into
-// multiple producer socket names.
-PERFETTO_EXPORT_COMPONENT std::vector<std::string> TokenizeProducerSockets(
- const char* producer_socket_names);
-PERFETTO_EXPORT_COMPONENT const char* GetProducerSocket();
-
-// Optionally returns the relay socket name (nullable). The relay socket is used
-// for forwarding the IPC messages between the local producers and the remote
-// tracing service.
-PERFETTO_EXPORT_COMPONENT const char* GetRelaySocket();
-
-} // namespace perfetto
+// TODO(khokhlov): Migrate usages of "perfetto/ext/tracing/ipc/default_socket.h"
+// in Chromium to include "perfetto/tracing/internal/default_socket.h" instead,
+// then delete this file.
+#include "perfetto/tracing/default_socket.h"
#endif // INCLUDE_PERFETTO_EXT_TRACING_IPC_DEFAULT_SOCKET_H_
diff --git a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
index 1af399b..33e19b4 100644
--- a/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
+++ b/include/perfetto/ext/tracing/ipc/producer_ipc_client.h
@@ -25,6 +25,7 @@
#include "perfetto/ext/tracing/core/shared_memory.h"
#include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
#include "perfetto/ext/tracing/core/tracing_service.h"
+#include "perfetto/tracing/tracing_backend.h"
namespace perfetto {
@@ -89,7 +90,8 @@
size_t shared_memory_size_hint_bytes = 0,
size_t shared_memory_page_size_hint_bytes = 0,
std::unique_ptr<SharedMemory> shm = nullptr,
- std::unique_ptr<SharedMemoryArbiter> shm_arbiter = nullptr);
+ std::unique_ptr<SharedMemoryArbiter> shm_arbiter = nullptr,
+ CreateSocketAsync create_socket_async = {});
protected:
ProducerIPCClient() = delete;
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 159e155..3e0e9f2 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -33,6 +33,7 @@
"console_interceptor.h",
"data_source.h",
"debug_annotation.h",
+ "default_socket.h",
"event_context.h",
"interceptor.h",
"internal/basic_types.h",
diff --git a/include/perfetto/tracing/default_socket.h b/include/perfetto/tracing/default_socket.h
new file mode 100644
index 0000000..1989724
--- /dev/null
+++ b/include/perfetto/tracing/default_socket.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_DEFAULT_SOCKET_H_
+#define INCLUDE_PERFETTO_TRACING_DEFAULT_SOCKET_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+namespace perfetto {
+
+PERFETTO_EXPORT_COMPONENT const char* GetConsumerSocket();
+// This function is used for tokenize the |producer_socket_names| string into
+// multiple producer socket names.
+PERFETTO_EXPORT_COMPONENT std::vector<std::string> TokenizeProducerSockets(
+ const char* producer_socket_names);
+PERFETTO_EXPORT_COMPONENT const char* GetProducerSocket();
+
+// Optionally returns the relay socket name (nullable). The relay socket is used
+// for forwarding the IPC messages between the local producers and the remote
+// tracing service.
+PERFETTO_EXPORT_COMPONENT const char* GetRelaySocket();
+
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_DEFAULT_SOCKET_H_
diff --git a/include/perfetto/tracing/internal/system_tracing_backend.h b/include/perfetto/tracing/internal/system_tracing_backend.h
index c1ea664..fdcdc99 100644
--- a/include/perfetto/tracing/internal/system_tracing_backend.h
+++ b/include/perfetto/tracing/internal/system_tracing_backend.h
@@ -18,6 +18,7 @@
#define INCLUDE_PERFETTO_TRACING_INTERNAL_SYSTEM_TRACING_BACKEND_H_
#include "perfetto/base/export.h"
+#include "perfetto/tracing/default_socket.h"
#include "perfetto/tracing/tracing_backend.h"
namespace perfetto {
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 9fd18e3..1d924b2 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -1080,6 +1080,9 @@
// We haven't seen this category before. Let's figure out if it's enabled.
// This requires grabbing a lock to read the session's trace config.
auto ds = ctx->GetDataSourceLocked();
+ if (!ds) {
+ return false;
+ }
Category category{Category::FromDynamicCategory(dynamic_category)};
bool enabled = TrackEventInternal::IsCategoryEnabled(
*Registry, ds->config_, category);
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 44503ba..824f492 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -152,6 +152,16 @@
// event tracks for the same thread.
bool disallow_merging_with_system_tracks = false;
+ // If set, this function will be called by the producer client to create a
+ // socket for connection to the system service. The function takes two
+ // arguments: a name of the socket, and a callback that takes an open file
+ // descriptor. It should create a socket with the given name, connect to it,
+ // and return the corresponding descriptor via the callback.
+ // This is intended for the use-case where a process being traced is run
+ // inside a sandbox and can't create sockets directly.
+ // Not yet supported for consumer connections currently.
+ CreateSocketAsync create_socket_async;
+
protected:
friend class Tracing;
friend class internal::TracingMuxerImpl;
diff --git a/include/perfetto/tracing/tracing_backend.h b/include/perfetto/tracing/tracing_backend.h
index 965d726..b915776 100644
--- a/include/perfetto/tracing/tracing_backend.h
+++ b/include/perfetto/tracing/tracing_backend.h
@@ -17,10 +17,12 @@
#ifndef INCLUDE_PERFETTO_TRACING_TRACING_BACKEND_H_
#define INCLUDE_PERFETTO_TRACING_TRACING_BACKEND_H_
+#include <functional>
#include <memory>
#include <string>
#include "perfetto/base/export.h"
+#include "perfetto/base/platform_handle.h"
// The embedder can (but doesn't have to) extend the TracingBackend class and
// pass as an argument to Tracing::Initialize(kCustomBackend) to override the
@@ -43,6 +45,9 @@
class Producer;
class ProducerEndpoint;
+using CreateSocketCallback = std::function<void(base::SocketHandle)>;
+using CreateSocketAsync = std::function<void(CreateSocketCallback)>;
+
// Responsible for connecting to the producer.
class PERFETTO_EXPORT_COMPONENT TracingProducerBackend {
public:
@@ -74,6 +79,10 @@
// it to the service when connecting.
// It's used in startup tracing.
bool use_producer_provided_smb = false;
+
+ // If set, the producer will call this function to create and connect to a
+ // socket. See the corresponding field in TracingInitArgs for more info.
+ CreateSocketAsync create_socket_async;
};
virtual std::unique_ptr<ProducerEndpoint> ConnectProducer(
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 1366a44..c9cf032 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -22,6 +22,7 @@
deps = [
"../common:@TYPE@",
"android:@TYPE@",
+ "etw:@TYPE@",
"ftrace:@TYPE@",
"gpu:@TYPE@",
"inode_file:@TYPE@",
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index d0f8bbf..7af57dc 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -28,6 +28,7 @@
import "protos/perfetto/config/android/surfaceflinger_layers_config.proto";
import "protos/perfetto/config/android/surfaceflinger_transactions_config.proto";
import "protos/perfetto/config/chrome/chrome_config.proto";
+import "protos/perfetto/config/etw/etw_config.proto";
import "protos/perfetto/config/ftrace/ftrace_config.proto";
import "protos/perfetto/config/gpu/gpu_counter_config.proto";
import "protos/perfetto/config/gpu/vulkan_memory_config.proto";
@@ -45,7 +46,7 @@
import "protos/perfetto/config/system_info/system_info.proto";
// The configuration that is passed to each data source when starting tracing.
-// Next id: 125
+// Next id: 126
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -181,6 +182,9 @@
optional AndroidSdkSyspropGuardConfig android_sdk_sysprop_guard_config = 124
[lazy = true];
+ // Data source name: windows.etw
+ optional EtwConfig etw_config = 125 [lazy = true];
+
// This is a fallback mechanism to send a free-form text config to the
// producer. In theory this should never be needed. All the code that
// is part of the platform (i.e. traced service) is supposed to *not* truncate
diff --git a/protos/perfetto/config/etw/BUILD.gn b/protos/perfetto/config/etw/BUILD.gn
new file mode 100644
index 0000000..3e298f6
--- /dev/null
+++ b/protos/perfetto/config/etw/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("../../../../gn/perfetto.gni")
+import("../../../../gn/proto_library.gni")
+
+perfetto_proto_library("@TYPE@") {
+ sources = [ "etw_config.proto" ]
+}
diff --git a/protos/perfetto/config/etw/etw_config.proto b/protos/perfetto/config/etw/etw_config.proto
new file mode 100644
index 0000000..c8f7e83
--- /dev/null
+++ b/protos/perfetto/config/etw/etw_config.proto
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+// Proto definition based on the struct _EVENT_TRACE_PROPERTIES definition
+// See: https://learn.microsoft.com/en-us/windows/win32/api/evntrace/
+// ns-evntrace-event_trace_properties
+message EtwConfig {
+ // The KernelFlag represent list of kernel flags that we are intrested in.
+ // To get a more extensive list run 'xperf -providers k'.
+ enum KernelFlag {
+ C_SWITCH = 0;
+ IDLE_STATES = 1;
+ }
+
+ // The kernel_flags determines the flags that will be used by the etw tracing
+ // session. These kernel flags have been built to expose the useful events
+ // captured from the kernel mode only.
+ repeated KernelFlag kernel_flags = 1;
+}
\ No newline at end of file
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index c245505..96aa5c8 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -645,6 +645,26 @@
// End of protos/perfetto/config/chrome/chrome_config.proto
+// Begin of protos/perfetto/config/etw/etw_config.proto
+
+// Proto definition based on the struct _EVENT_TRACE_PROPERTIES definition
+// See: https://learn.microsoft.com/en-us/windows/win32/api/evntrace/
+// ns-evntrace-event_trace_properties
+message EtwConfig {
+ // The KernelFlag represent list of kernel flags that we are intrested in.
+ // To get a more extensive list run 'xperf -providers k'.
+ enum KernelFlag {
+ C_SWITCH = 0;
+ IDLE_STATES = 1;
+ }
+
+ // The kernel_flags determines the flags that will be used by the etw tracing
+ // session. These kernel flags have been built to expose the useful events
+ // captured from the kernel mode only.
+ repeated KernelFlag kernel_flags = 1;
+}
+// End of protos/perfetto/config/etw/etw_config.proto
+
// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
// Next id: 26.
@@ -1950,7 +1970,6 @@
ATOM_PERFETTO_TRIGGER = 329;
ATOM_TRANSCODING_DATA = 330;
ATOM_IMS_SERVICE_ENTITLEMENT_UPDATED = 331;
- ATOM_ART_DATUM_REPORTED = 332;
ATOM_DEVICE_ROTATED = 333;
ATOM_SIM_SPECIFIC_SETTINGS_RESTORED = 334;
ATOM_TEXT_CLASSIFIER_DOWNLOAD_REPORTED = 335;
@@ -1980,7 +1999,6 @@
ATOM_LOCATION_TIME_ZONE_PROVIDER_STATE_CHANGED = 359;
ATOM_FDTRACK_EVENT_OCCURRED = 364;
ATOM_TIMEOUT_AUTO_EXTENDED_REPORTED = 365;
- ATOM_ODREFRESH_REPORTED = 366;
ATOM_ALARM_BATCH_DELIVERED = 367;
ATOM_ALARM_SCHEDULED = 368;
ATOM_CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED = 369;
@@ -2048,6 +2066,8 @@
ATOM_HOTWORD_DETECTION_SERVICE_RESTARTED = 432;
ATOM_HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED = 433;
ATOM_HOTWORD_DETECTOR_EVENTS = 434;
+ ATOM_AD_SERVICES_API_CALLED = 435;
+ ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
ATOM_BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED = 437;
ATOM_CONTACTS_INDEXER_UPDATE_STATS_REPORTED = 440;
ATOM_APP_BACKGROUND_RESTRICTIONS_INFO = 441;
@@ -2069,24 +2089,47 @@
ATOM_ISOLATED_COMPILATION_SCHEDULED = 457;
ATOM_ISOLATED_COMPILATION_ENDED = 458;
ATOM_ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE = 459;
+ ATOM_SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED = 460;
ATOM_TELEPHONY_ANOMALY_DETECTED = 461;
ATOM_LETTERBOX_POSITION_CHANGED = 462;
ATOM_REMOTE_KEY_PROVISIONING_ATTEMPT = 463;
ATOM_REMOTE_KEY_PROVISIONING_NETWORK_INFO = 464;
ATOM_REMOTE_KEY_PROVISIONING_TIMING = 465;
ATOM_MEDIAOUTPUT_OP_INTERACTION_REPORT = 466;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
ATOM_SYNC_EXEMPTION_OCCURRED = 468;
ATOM_AUTOFILL_PRESENTATION_EVENT_REPORTED = 469;
ATOM_DOCK_STATE_CHANGED = 470;
+ ATOM_SAFETY_SOURCE_STATE_COLLECTED = 471;
+ ATOM_SAFETY_CENTER_SYSTEM_EVENT_REPORTED = 472;
+ ATOM_SAFETY_CENTER_INTERACTION_REPORTED = 473;
+ ATOM_SETTINGS_PROVIDER_SETTING_CHANGED = 474;
ATOM_BROADCAST_DELIVERY_EVENT_REPORTED = 475;
ATOM_SERVICE_REQUEST_EVENT_REPORTED = 476;
ATOM_PROVIDER_ACQUISITION_EVENT_REPORTED = 477;
ATOM_BLUETOOTH_DEVICE_NAME_REPORTED = 478;
+ ATOM_CB_CONFIG_UPDATED = 479;
+ ATOM_CB_MODULE_ERROR_REPORTED = 480;
+ ATOM_CB_SERVICE_FEATURE_CHANGED = 481;
+ ATOM_CB_RECEIVER_FEATURE_CHANGED = 482;
+ ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
+ ATOM_PRIVACY_SIGNAL_NOTIFICATION_INTERACTION = 484;
+ ATOM_PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION = 485;
+ ATOM_PRIVACY_SIGNALS_JOB_FAILURE = 486;
ATOM_VIBRATION_REPORTED = 487;
ATOM_UWB_RANGING_START = 489;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STATUS_REPORTED = 490;
+ ATOM_APP_COMPACTED_V2 = 491;
+ ATOM_AD_SERVICES_SETTINGS_USAGE_REPORTED = 493;
ATOM_DISPLAY_BRIGHTNESS_CHANGED = 494;
ATOM_ACTIVITY_ACTION_BLOCKED = 495;
+ ATOM_BACKGROUND_FETCH_PROCESS_REPORTED = 496;
+ ATOM_UPDATE_CUSTOM_AUDIENCE_PROCESS_REPORTED = 497;
+ ATOM_RUN_AD_BIDDING_PROCESS_REPORTED = 498;
+ ATOM_RUN_AD_SCORING_PROCESS_REPORTED = 499;
+ ATOM_RUN_AD_SELECTION_PROCESS_REPORTED = 500;
+ ATOM_RUN_AD_BIDDING_PER_CA_PROCESS_REPORTED = 501;
+ ATOM_MOBILE_DATA_DOWNLOAD_DOWNLOAD_RESULT_REPORTED = 502;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STORAGE_STATS_REPORTED = 503;
ATOM_NETWORK_DNS_SERVER_SUPPORT_REPORTED = 504;
ATOM_VM_BOOTED = 505;
ATOM_VM_EXITED = 506;
@@ -2095,13 +2138,19 @@
ATOM_MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED = 509;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED = 510;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED = 511;
+ ATOM_AD_SERVICES_MEASUREMENT_REGISTRATIONS = 512;
ATOM_HEARING_AID_INFO_REPORTED = 513;
ATOM_DEVICE_WIDE_JOB_CONSTRAINT_CHANGED = 514;
+ ATOM_AMBIENT_MODE_CHANGED = 515;
+ ATOM_ANR_LATENCY_REPORTED = 516;
+ ATOM_RESOURCE_API_INFO = 517;
+ ATOM_SYSTEM_DEFAULT_NETWORK_CHANGED = 518;
ATOM_IWLAN_SETUP_DATA_CALL_RESULT_REPORTED = 519;
ATOM_IWLAN_PDN_DISCONNECTED_REASON_REPORTED = 520;
ATOM_AIRPLANE_MODE_SESSION_REPORTED = 521;
ATOM_VM_CPU_STATUS_REPORTED = 522;
ATOM_VM_MEM_STATUS_REPORTED = 523;
+ ATOM_PACKAGE_INSTALLATION_SESSION_REPORTED = 524;
ATOM_DEFAULT_NETWORK_REMATCH_INFO = 525;
ATOM_NETWORK_SELECTION_PERFORMANCE = 526;
ATOM_NETWORK_NSD_REPORTED = 527;
@@ -2111,22 +2160,65 @@
ATOM_BLUETOOTH_LOCAL_SUPPORTED_FEATURES_REPORTED = 532;
ATOM_BLUETOOTH_GATT_APP_INFO = 533;
ATOM_BRIGHTNESS_CONFIGURATION_UPDATED = 534;
+ ATOM_AD_SERVICES_GET_TOPICS_REPORTED = 535;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_GET_TOP_TOPICS_REPORTED = 536;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 537;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_LAUNCHED = 538;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FINISHED = 539;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECTION_REPORTED = 540;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_TRIGGERED = 541;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FIRST_DEVICE_SCAN_LATENCY = 542;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECT_DEVICE_LATENCY = 543;
+ ATOM_PACKAGE_MANAGER_SNAPSHOT_REPORTED = 544;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED = 545;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED = 546;
ATOM_LAUNCHER_IMPRESSION_EVENT = 547;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_ALL_DEVICES_SCAN_LATENCY = 549;
+ ATOM_WS_WATCH_FACE_EDITED = 551;
+ ATOM_WS_WATCH_FACE_FAVORITE_ACTION_REPORTED = 552;
+ ATOM_WS_WATCH_FACE_SET_ACTION_REPORTED = 553;
+ ATOM_PACKAGE_UNINSTALLATION_REPORTED = 554;
+ ATOM_GAME_MODE_CHANGED = 555;
+ ATOM_GAME_MODE_CONFIGURATION_CHANGED = 556;
+ ATOM_BEDTIME_MODE_STATE_CHANGED = 557;
ATOM_NETWORK_SLICE_SESSION_ENDED = 558;
ATOM_NETWORK_SLICE_DAILY_DATA_USAGE_REPORTED = 559;
ATOM_NFC_TAG_TYPE_OCCURRED = 560;
ATOM_NFC_AID_CONFLICT_OCCURRED = 561;
ATOM_NFC_READER_CONFLICT_OCCURRED = 562;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_WS_TILE_LIST_CHANGED = 563;
+ ATOM_GET_TYPE_ACCESSED_WITHOUT_PERMISSION = 564;
+ ATOM_MOBILE_BUNDLED_APP_INFO_GATHERED = 566;
+ ATOM_WS_WATCH_FACE_COMPLICATION_SET_CHANGED = 567;
ATOM_MEDIA_DRM_CREATED = 568;
ATOM_MEDIA_DRM_ERRORED = 569;
ATOM_MEDIA_DRM_SESSION_OPENED = 570;
ATOM_MEDIA_DRM_SESSION_CLOSED = 571;
+ ATOM_USER_SELECTED_RESOLUTION = 572;
+ ATOM_UNSAFE_INTENT_EVENT_REPORTED = 573;
ATOM_PERFORMANCE_HINT_SESSION_REPORTED = 574;
+ ATOM_MEDIAMETRICS_MIDI_DEVICE_CLOSE_REPORTED = 576;
+ ATOM_BIOMETRIC_TOUCH_REPORTED = 577;
ATOM_HOTWORD_AUDIO_EGRESS_EVENT_REPORTED = 578;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_LOCATION_ENABLED_STATE_CHANGED = 580;
+ ATOM_IME_REQUEST_FINISHED = 581;
+ ATOM_USB_COMPLIANCE_WARNINGS_REPORTED = 582;
+ ATOM_APP_SUPPORTED_LOCALES_CHANGED = 583;
+ ATOM_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED = 586;
+ ATOM_BIOMETRIC_PROPERTIES_COLLECTED = 587;
+ ATOM_KERNEL_WAKEUP_ATTRIBUTED = 588;
+ ATOM_SCREEN_STATE_CHANGED_V2 = 589;
+ ATOM_WS_BACKUP_ACTION_REPORTED = 590;
+ ATOM_WS_RESTORE_ACTION_REPORTED = 591;
+ ATOM_DEVICE_LOG_ACCESS_EVENT_REPORTED = 592;
+ ATOM_MEDIA_SESSION_UPDATED = 594;
+ ATOM_WEAR_OOBE_STATE_CHANGED = 595;
+ ATOM_WS_NOTIFICATION_UPDATED = 596;
ATOM_NETWORK_VALIDATION_FAILURE_STATS_DAILY_REPORTED = 601;
+ ATOM_WS_COMPLICATION_TAPPED = 602;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
ATOM_WIFI_BYTES_TRANSFER = 10000;
ATOM_WIFI_BYTES_TRANSFER_BY_FG_BG = 10001;
ATOM_MOBILE_BYTES_TRANSFER = 10002;
@@ -2279,15 +2371,160 @@
ATOM_TELEPHONY_NETWORK_REQUESTS_V2 = 10153;
ATOM_DEVICE_TELEPHONY_PROPERTIES = 10154;
ATOM_REMOTE_KEY_PROVISIONING_ERROR_COUNTS = 10155;
+ ATOM_SAFETY_STATE = 10156;
ATOM_INCOMING_MMS = 10157;
ATOM_OUTGOING_MMS = 10158;
ATOM_MULTI_USER_INFO = 10160;
ATOM_NETWORK_BPF_MAP_INFO = 10161;
+ ATOM_OUTGOING_SHORT_CODE_SMS = 10162;
ATOM_CONNECTIVITY_STATE_SAMPLE = 10163;
ATOM_NETWORK_SELECTION_REMATCH_REASONS_INFO = 10164;
+ ATOM_GAME_MODE_INFO = 10165;
+ ATOM_GAME_MODE_CONFIGURATION = 10166;
+ ATOM_GAME_MODE_LISTENER = 10167;
ATOM_NETWORK_SLICE_REQUEST_COUNT = 10168;
+ ATOM_WS_TILE_SNAPSHOT = 10169;
+ ATOM_WS_ACTIVE_WATCH_FACE_COMPLICATION_SET_SNAPSHOT = 10170;
+ ATOM_PROCESS_STATE = 10171;
+ ATOM_PROCESS_ASSOCIATION = 10172;
ATOM_ADPF_SYSTEM_COMPONENT_INFO = 10173;
ATOM_NOTIFICATION_MEMORY_USE = 10174;
+ ATOM_HDR_CAPABILITIES = 10175;
+ ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_DND_STATE_CHANGED = 657;
+ ATOM_MTE_STATE = 10181;
+ ATOM_AD_SERVICES_BACK_COMPAT_GET_TOPICS_REPORTED = 598;
+ ATOM_AD_SERVICES_BACK_COMPAT_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 599;
+ ATOM_AD_SERVICES_MEASUREMENT_DEBUG_KEYS = 640;
+ ATOM_AD_SERVICES_ERROR_REPORTED = 662;
+ ATOM_AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED = 663;
+ ATOM_AD_SERVICES_MEASUREMENT_DELAYED_SOURCE_REGISTRATION = 673;
+ ATOM_AD_SERVICES_MEASUREMENT_ATTRIBUTION = 674;
+ ATOM_AD_SERVICES_MEASUREMENT_JOBS = 675;
+ ATOM_AD_SERVICES_MEASUREMENT_WIPEOUT = 676;
+ ATOM_AD_SERVICES_CONSENT_MIGRATED = 702;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
+ ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
+ ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
+ ATOM_CREDENTIAL_MANAGER_FINAL_PHASE_REPORTED = 653;
+ ATOM_CREDENTIAL_MANAGER_TOTAL_REPORTED = 667;
+ ATOM_CREDENTIAL_MANAGER_FINALNOUID_REPORTED = 668;
+ ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
+ ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
+ ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
}
// End of protos/perfetto/config/statsd/atom_ids.proto
@@ -2702,7 +2939,7 @@
// Begin of protos/perfetto/config/data_source_config.proto
// The configuration that is passed to each data source when starting tracing.
-// Next id: 125
+// Next id: 126
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2838,6 +3075,9 @@
optional AndroidSdkSyspropGuardConfig android_sdk_sysprop_guard_config = 124
[lazy = true];
+ // Data source name: windows.etw
+ optional EtwConfig etw_config = 125 [lazy = true];
+
// This is a fallback mechanism to send a free-form text config to the
// producer. In theory this should never be needed. All the code that
// is part of the platform (i.e. traced service) is supposed to *not* truncate
diff --git a/protos/perfetto/config/statsd/atom_ids.proto b/protos/perfetto/config/statsd/atom_ids.proto
index fbdd194..c436571 100644
--- a/protos/perfetto/config/statsd/atom_ids.proto
+++ b/protos/perfetto/config/statsd/atom_ids.proto
@@ -349,7 +349,6 @@
ATOM_PERFETTO_TRIGGER = 329;
ATOM_TRANSCODING_DATA = 330;
ATOM_IMS_SERVICE_ENTITLEMENT_UPDATED = 331;
- ATOM_ART_DATUM_REPORTED = 332;
ATOM_DEVICE_ROTATED = 333;
ATOM_SIM_SPECIFIC_SETTINGS_RESTORED = 334;
ATOM_TEXT_CLASSIFIER_DOWNLOAD_REPORTED = 335;
@@ -379,7 +378,6 @@
ATOM_LOCATION_TIME_ZONE_PROVIDER_STATE_CHANGED = 359;
ATOM_FDTRACK_EVENT_OCCURRED = 364;
ATOM_TIMEOUT_AUTO_EXTENDED_REPORTED = 365;
- ATOM_ODREFRESH_REPORTED = 366;
ATOM_ALARM_BATCH_DELIVERED = 367;
ATOM_ALARM_SCHEDULED = 368;
ATOM_CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED = 369;
@@ -447,6 +445,8 @@
ATOM_HOTWORD_DETECTION_SERVICE_RESTARTED = 432;
ATOM_HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED = 433;
ATOM_HOTWORD_DETECTOR_EVENTS = 434;
+ ATOM_AD_SERVICES_API_CALLED = 435;
+ ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
ATOM_BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED = 437;
ATOM_CONTACTS_INDEXER_UPDATE_STATS_REPORTED = 440;
ATOM_APP_BACKGROUND_RESTRICTIONS_INFO = 441;
@@ -468,24 +468,47 @@
ATOM_ISOLATED_COMPILATION_SCHEDULED = 457;
ATOM_ISOLATED_COMPILATION_ENDED = 458;
ATOM_ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE = 459;
+ ATOM_SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED = 460;
ATOM_TELEPHONY_ANOMALY_DETECTED = 461;
ATOM_LETTERBOX_POSITION_CHANGED = 462;
ATOM_REMOTE_KEY_PROVISIONING_ATTEMPT = 463;
ATOM_REMOTE_KEY_PROVISIONING_NETWORK_INFO = 464;
ATOM_REMOTE_KEY_PROVISIONING_TIMING = 465;
ATOM_MEDIAOUTPUT_OP_INTERACTION_REPORT = 466;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
ATOM_SYNC_EXEMPTION_OCCURRED = 468;
ATOM_AUTOFILL_PRESENTATION_EVENT_REPORTED = 469;
ATOM_DOCK_STATE_CHANGED = 470;
+ ATOM_SAFETY_SOURCE_STATE_COLLECTED = 471;
+ ATOM_SAFETY_CENTER_SYSTEM_EVENT_REPORTED = 472;
+ ATOM_SAFETY_CENTER_INTERACTION_REPORTED = 473;
+ ATOM_SETTINGS_PROVIDER_SETTING_CHANGED = 474;
ATOM_BROADCAST_DELIVERY_EVENT_REPORTED = 475;
ATOM_SERVICE_REQUEST_EVENT_REPORTED = 476;
ATOM_PROVIDER_ACQUISITION_EVENT_REPORTED = 477;
ATOM_BLUETOOTH_DEVICE_NAME_REPORTED = 478;
+ ATOM_CB_CONFIG_UPDATED = 479;
+ ATOM_CB_MODULE_ERROR_REPORTED = 480;
+ ATOM_CB_SERVICE_FEATURE_CHANGED = 481;
+ ATOM_CB_RECEIVER_FEATURE_CHANGED = 482;
+ ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
+ ATOM_PRIVACY_SIGNAL_NOTIFICATION_INTERACTION = 484;
+ ATOM_PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION = 485;
+ ATOM_PRIVACY_SIGNALS_JOB_FAILURE = 486;
ATOM_VIBRATION_REPORTED = 487;
ATOM_UWB_RANGING_START = 489;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STATUS_REPORTED = 490;
+ ATOM_APP_COMPACTED_V2 = 491;
+ ATOM_AD_SERVICES_SETTINGS_USAGE_REPORTED = 493;
ATOM_DISPLAY_BRIGHTNESS_CHANGED = 494;
ATOM_ACTIVITY_ACTION_BLOCKED = 495;
+ ATOM_BACKGROUND_FETCH_PROCESS_REPORTED = 496;
+ ATOM_UPDATE_CUSTOM_AUDIENCE_PROCESS_REPORTED = 497;
+ ATOM_RUN_AD_BIDDING_PROCESS_REPORTED = 498;
+ ATOM_RUN_AD_SCORING_PROCESS_REPORTED = 499;
+ ATOM_RUN_AD_SELECTION_PROCESS_REPORTED = 500;
+ ATOM_RUN_AD_BIDDING_PER_CA_PROCESS_REPORTED = 501;
+ ATOM_MOBILE_DATA_DOWNLOAD_DOWNLOAD_RESULT_REPORTED = 502;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STORAGE_STATS_REPORTED = 503;
ATOM_NETWORK_DNS_SERVER_SUPPORT_REPORTED = 504;
ATOM_VM_BOOTED = 505;
ATOM_VM_EXITED = 506;
@@ -494,13 +517,19 @@
ATOM_MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED = 509;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED = 510;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED = 511;
+ ATOM_AD_SERVICES_MEASUREMENT_REGISTRATIONS = 512;
ATOM_HEARING_AID_INFO_REPORTED = 513;
ATOM_DEVICE_WIDE_JOB_CONSTRAINT_CHANGED = 514;
+ ATOM_AMBIENT_MODE_CHANGED = 515;
+ ATOM_ANR_LATENCY_REPORTED = 516;
+ ATOM_RESOURCE_API_INFO = 517;
+ ATOM_SYSTEM_DEFAULT_NETWORK_CHANGED = 518;
ATOM_IWLAN_SETUP_DATA_CALL_RESULT_REPORTED = 519;
ATOM_IWLAN_PDN_DISCONNECTED_REASON_REPORTED = 520;
ATOM_AIRPLANE_MODE_SESSION_REPORTED = 521;
ATOM_VM_CPU_STATUS_REPORTED = 522;
ATOM_VM_MEM_STATUS_REPORTED = 523;
+ ATOM_PACKAGE_INSTALLATION_SESSION_REPORTED = 524;
ATOM_DEFAULT_NETWORK_REMATCH_INFO = 525;
ATOM_NETWORK_SELECTION_PERFORMANCE = 526;
ATOM_NETWORK_NSD_REPORTED = 527;
@@ -510,22 +539,65 @@
ATOM_BLUETOOTH_LOCAL_SUPPORTED_FEATURES_REPORTED = 532;
ATOM_BLUETOOTH_GATT_APP_INFO = 533;
ATOM_BRIGHTNESS_CONFIGURATION_UPDATED = 534;
+ ATOM_AD_SERVICES_GET_TOPICS_REPORTED = 535;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_GET_TOP_TOPICS_REPORTED = 536;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 537;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_LAUNCHED = 538;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FINISHED = 539;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECTION_REPORTED = 540;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_TRIGGERED = 541;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FIRST_DEVICE_SCAN_LATENCY = 542;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECT_DEVICE_LATENCY = 543;
+ ATOM_PACKAGE_MANAGER_SNAPSHOT_REPORTED = 544;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED = 545;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED = 546;
ATOM_LAUNCHER_IMPRESSION_EVENT = 547;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_ALL_DEVICES_SCAN_LATENCY = 549;
+ ATOM_WS_WATCH_FACE_EDITED = 551;
+ ATOM_WS_WATCH_FACE_FAVORITE_ACTION_REPORTED = 552;
+ ATOM_WS_WATCH_FACE_SET_ACTION_REPORTED = 553;
+ ATOM_PACKAGE_UNINSTALLATION_REPORTED = 554;
+ ATOM_GAME_MODE_CHANGED = 555;
+ ATOM_GAME_MODE_CONFIGURATION_CHANGED = 556;
+ ATOM_BEDTIME_MODE_STATE_CHANGED = 557;
ATOM_NETWORK_SLICE_SESSION_ENDED = 558;
ATOM_NETWORK_SLICE_DAILY_DATA_USAGE_REPORTED = 559;
ATOM_NFC_TAG_TYPE_OCCURRED = 560;
ATOM_NFC_AID_CONFLICT_OCCURRED = 561;
ATOM_NFC_READER_CONFLICT_OCCURRED = 562;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_WS_TILE_LIST_CHANGED = 563;
+ ATOM_GET_TYPE_ACCESSED_WITHOUT_PERMISSION = 564;
+ ATOM_MOBILE_BUNDLED_APP_INFO_GATHERED = 566;
+ ATOM_WS_WATCH_FACE_COMPLICATION_SET_CHANGED = 567;
ATOM_MEDIA_DRM_CREATED = 568;
ATOM_MEDIA_DRM_ERRORED = 569;
ATOM_MEDIA_DRM_SESSION_OPENED = 570;
ATOM_MEDIA_DRM_SESSION_CLOSED = 571;
+ ATOM_USER_SELECTED_RESOLUTION = 572;
+ ATOM_UNSAFE_INTENT_EVENT_REPORTED = 573;
ATOM_PERFORMANCE_HINT_SESSION_REPORTED = 574;
+ ATOM_MEDIAMETRICS_MIDI_DEVICE_CLOSE_REPORTED = 576;
+ ATOM_BIOMETRIC_TOUCH_REPORTED = 577;
ATOM_HOTWORD_AUDIO_EGRESS_EVENT_REPORTED = 578;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_LOCATION_ENABLED_STATE_CHANGED = 580;
+ ATOM_IME_REQUEST_FINISHED = 581;
+ ATOM_USB_COMPLIANCE_WARNINGS_REPORTED = 582;
+ ATOM_APP_SUPPORTED_LOCALES_CHANGED = 583;
+ ATOM_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED = 586;
+ ATOM_BIOMETRIC_PROPERTIES_COLLECTED = 587;
+ ATOM_KERNEL_WAKEUP_ATTRIBUTED = 588;
+ ATOM_SCREEN_STATE_CHANGED_V2 = 589;
+ ATOM_WS_BACKUP_ACTION_REPORTED = 590;
+ ATOM_WS_RESTORE_ACTION_REPORTED = 591;
+ ATOM_DEVICE_LOG_ACCESS_EVENT_REPORTED = 592;
+ ATOM_MEDIA_SESSION_UPDATED = 594;
+ ATOM_WEAR_OOBE_STATE_CHANGED = 595;
+ ATOM_WS_NOTIFICATION_UPDATED = 596;
ATOM_NETWORK_VALIDATION_FAILURE_STATS_DAILY_REPORTED = 601;
+ ATOM_WS_COMPLICATION_TAPPED = 602;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
ATOM_WIFI_BYTES_TRANSFER = 10000;
ATOM_WIFI_BYTES_TRANSFER_BY_FG_BG = 10001;
ATOM_MOBILE_BYTES_TRANSFER = 10002;
@@ -678,13 +750,158 @@
ATOM_TELEPHONY_NETWORK_REQUESTS_V2 = 10153;
ATOM_DEVICE_TELEPHONY_PROPERTIES = 10154;
ATOM_REMOTE_KEY_PROVISIONING_ERROR_COUNTS = 10155;
+ ATOM_SAFETY_STATE = 10156;
ATOM_INCOMING_MMS = 10157;
ATOM_OUTGOING_MMS = 10158;
ATOM_MULTI_USER_INFO = 10160;
ATOM_NETWORK_BPF_MAP_INFO = 10161;
+ ATOM_OUTGOING_SHORT_CODE_SMS = 10162;
ATOM_CONNECTIVITY_STATE_SAMPLE = 10163;
ATOM_NETWORK_SELECTION_REMATCH_REASONS_INFO = 10164;
+ ATOM_GAME_MODE_INFO = 10165;
+ ATOM_GAME_MODE_CONFIGURATION = 10166;
+ ATOM_GAME_MODE_LISTENER = 10167;
ATOM_NETWORK_SLICE_REQUEST_COUNT = 10168;
+ ATOM_WS_TILE_SNAPSHOT = 10169;
+ ATOM_WS_ACTIVE_WATCH_FACE_COMPLICATION_SET_SNAPSHOT = 10170;
+ ATOM_PROCESS_STATE = 10171;
+ ATOM_PROCESS_ASSOCIATION = 10172;
ATOM_ADPF_SYSTEM_COMPONENT_INFO = 10173;
ATOM_NOTIFICATION_MEMORY_USE = 10174;
+ ATOM_HDR_CAPABILITIES = 10175;
+ ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_DND_STATE_CHANGED = 657;
+ ATOM_MTE_STATE = 10181;
+ ATOM_AD_SERVICES_BACK_COMPAT_GET_TOPICS_REPORTED = 598;
+ ATOM_AD_SERVICES_BACK_COMPAT_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 599;
+ ATOM_AD_SERVICES_MEASUREMENT_DEBUG_KEYS = 640;
+ ATOM_AD_SERVICES_ERROR_REPORTED = 662;
+ ATOM_AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED = 663;
+ ATOM_AD_SERVICES_MEASUREMENT_DELAYED_SOURCE_REGISTRATION = 673;
+ ATOM_AD_SERVICES_MEASUREMENT_ATTRIBUTION = 674;
+ ATOM_AD_SERVICES_MEASUREMENT_JOBS = 675;
+ ATOM_AD_SERVICES_MEASUREMENT_WIPEOUT = 676;
+ ATOM_AD_SERVICES_CONSENT_MIGRATED = 702;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
+ ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
+ ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
+ ATOM_CREDENTIAL_MANAGER_FINAL_PHASE_REPORTED = 653;
+ ATOM_CREDENTIAL_MANAGER_TOTAL_REPORTED = 667;
+ ATOM_CREDENTIAL_MANAGER_FINALNOUID_REPORTED = 668;
+ ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
+ ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
+ ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
}
\ No newline at end of file
diff --git a/protos/perfetto/ipc/wire_protocol.proto b/protos/perfetto/ipc/wire_protocol.proto
index 8117316..e01a798 100644
--- a/protos/perfetto/ipc/wire_protocol.proto
+++ b/protos/perfetto/ipc/wire_protocol.proto
@@ -72,8 +72,16 @@
// endpoont of the connection. for AF_UNIX sockets, this is ignored and traced
// uses instead the SO_PEERCRED.
message SetPeerIdentity {
+ // The UID and PID of the producer process.
optional int32 pid = 1;
optional int32 uid = 2;
+
+ // The hint for the tracing service to infer the machine ID. This field
+ // should satisfy the requriement that different machines should have
+ // different values. In practice, this filed contains the Linux kernel
+ // boot_id, or a hash of kernel bootup timestamp and uname(2) if boot_id
+ // isn't available.
+ optional string machine_id_hint = 3;
}
// The client is expected to send requests with monotonically increasing
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index c5e842f..509c448 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -28,6 +28,7 @@
"initial_display_state.proto",
"network_trace.proto",
"packages_list.proto",
+ "shell_transition.proto",
"surfaceflinger_common.proto",
"surfaceflinger_layers.proto",
"surfaceflinger_transactions.proto",
@@ -37,6 +38,7 @@
perfetto_proto_library("winscope_deps") {
proto_generators = [ "source_set" ]
sources = [
+ "shell_transition.proto",
"surfaceflinger_common.proto",
"surfaceflinger_layers.proto",
"surfaceflinger_transactions.proto",
diff --git a/protos/perfetto/trace/android/frame_timeline_event.proto b/protos/perfetto/trace/android/frame_timeline_event.proto
index f1f0df8..db6ed90 100644
--- a/protos/perfetto/trace/android/frame_timeline_event.proto
+++ b/protos/perfetto/trace/android/frame_timeline_event.proto
@@ -45,6 +45,14 @@
JANK_DROPPED = 1024;
};
+ // Specifies the severity of a jank.
+ enum JankSeverityType {
+ SEVERITY_UNKNOWN = 0;
+ SEVERITY_NONE = 1;
+ SEVERITY_PARTIAL = 2;
+ SEVERITY_FULL = 3;
+ }
+
// Specifies how a frame was presented on screen w.r.t. timing.
// Can be different for SurfaceFrame and DisplayFrame.
enum PresentType {
@@ -121,6 +129,7 @@
optional int32 jank_type = 9;
optional PredictionType prediction_type = 10;
optional bool is_buffer = 11;
+ optional JankSeverityType jank_severity_type = 12;
};
// Indicates the start of expected timeline slice for DisplayFrames.
@@ -166,6 +175,7 @@
// frame.
optional int32 jank_type = 7;
optional PredictionType prediction_type = 8;
+ optional JankSeverityType jank_severity_type = 9;
};
// FrameEnd just sends the cookie to indicate that the corresponding
diff --git a/protos/perfetto/trace/android/shell_transition.proto b/protos/perfetto/trace/android/shell_transition.proto
new file mode 100644
index 0000000..9a34d3a
--- /dev/null
+++ b/protos/perfetto/trace/android/shell_transition.proto
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+// ShellTransition messages record information about the shell transitions in
+// the system. This is used to track the animations that are created and execute
+// through the shell transition system.
+message ShellTransition {
+ // The unique identifier of the transition.
+ optional int32 id = 1;
+
+ // The time the transition was created on the WM side
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 create_time_ns = 2;
+ // The time the transition was sent from the WM side to shell
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 send_time_ns = 3;
+ // The time the transition was dispatched by shell to execute
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 dispatch_time_ns = 4;
+ // If the transition merge was accepted by the transition handler, this
+ // contains the time the transition was merged into transition with id
+ // `merge_target`.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 merge_time_ns = 5;
+ // The time shell proposed the transition should be merged to the transition
+ // handler into transition with id `merge_target`.
+ // (using SystemClock.elapsedRealtimeNanos()).
+ optional int64 merge_request_time_ns = 6;
+ // If the transition was aborted on the shell side, this is the time that
+ // occured.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 shell_abort_time_ns = 7;
+ // If the transition was aborted on the wm side, this is the time that
+ // occured.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 wm_abort_time_ns = 8;
+ // The time WM considers the transition to be complete.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 finish_time_ns = 9;
+
+ // The id of the transaction that WM proposed to use as the starting
+ // transaction. It contains all the layer changes required to setup the
+ // transition and should be executed right at the start of the transition
+ // by the transition handler.
+ optional uint64 start_transaction_id = 10;
+ // The if of the transaction that WM proposed to use as the finish
+ // transaction. It contains all the layer changes required to set the final
+ // state of the transition.
+ optional uint64 finish_transaction_id = 11;
+
+ // The id of the handler that executed the transition. A HandlerMappings
+ // message in the trace will contain the mapping of id to a string
+ // representation of the handler.
+ optional int32 handler = 12;
+ // The transition type of this transition (e.g. TO_FRONT, OPEN, CLOSE).
+ optional int32 type = 13;
+
+ // The list of targets that are part of this transition.
+ repeated Target targets = 14;
+ // The id of the transition we have requested to merge or have merged this
+ // transition into.
+ optional int32 merge_target = 15;
+
+ // The flags set on this transition.
+ optional int32 flags = 16;
+ // The time the starting window was removed. Tracked because this can
+ // happen after the transition finishes, but the app may not yet be visible
+ // until the starting window is removed. So in a sense the transition is not
+ // finished until the starting window is removed. (b/284302118)
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 starting_window_remove_time_ns = 17;
+
+ // Contains the information about the windows targeted in a transition.
+ message Target {
+ // The transition mode of this target (e.g. TO_FRONT, CLOSE...)
+ optional int32 mode = 1;
+ // The layer id of this target.
+ optional int32 layer_id = 2;
+ // The window id of this target.
+ optional int32 window_id = 3;
+ // The flags set on this target.
+ optional int32 flags = 4;
+ }
+}
+
+// Contains mappings from handler ids to string representation of the handlers.
+message ShellHandlerMappings {
+ repeated ShellHandlerMapping mapping = 1;
+}
+
+message ShellHandlerMapping {
+ // The id of the handler used in the ShellTransition message.
+ optional int32 id = 1;
+ // A human readable and meaningful string representation of the handler.
+ optional string name = 2;
+}
diff --git a/protos/perfetto/trace/android/winscope.proto b/protos/perfetto/trace/android/winscope.proto
index 78fbdcc..296e3ee 100644
--- a/protos/perfetto/trace/android/winscope.proto
+++ b/protos/perfetto/trace/android/winscope.proto
@@ -18,6 +18,7 @@
package perfetto.protos;
+import "protos/perfetto/trace/android/shell_transition.proto";
import "protos/perfetto/trace/android/surfaceflinger_layers.proto";
import "protos/perfetto/trace/android/surfaceflinger_transactions.proto";
@@ -26,4 +27,5 @@
message WinscopeTraceData {
optional LayersSnapshotProto layers_snapshot = 1;
optional TransactionTraceEntry transactions = 2;
+ optional ShellTransition shell_transition = 3;
}
diff --git a/protos/perfetto/trace/etw/etw_event.proto b/protos/perfetto/trace/etw/etw_event.proto
index 9ef1b4e..c52ad6c 100644
--- a/protos/perfetto/trace/etw/etw_event.proto
+++ b/protos/perfetto/trace/etw/etw_event.proto
@@ -21,6 +21,7 @@
message EtwTraceEvent {
optional uint64 timestamp = 1;
+ optional uint32 cpu = 4;
oneof event {
CSwitchEtwEvent c_switch = 2;
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index bd055ab..2237aef 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -57,6 +57,7 @@
"net.proto",
"oom.proto",
"panel.proto",
+ "perf_trace_counters.proto",
"power.proto",
"printk.proto",
"raw_syscalls.proto",
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index a068c9d..f7e314b 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -57,6 +57,7 @@
import "protos/perfetto/trace/ftrace/net.proto";
import "protos/perfetto/trace/ftrace/oom.proto";
import "protos/perfetto/trace/ftrace/panel.proto";
+import "protos/perfetto/trace/ftrace/perf_trace_counters.proto";
import "protos/perfetto/trace/ftrace/power.proto";
import "protos/perfetto/trace/ftrace/printk.proto";
import "protos/perfetto/trace/ftrace/raw_syscalls.proto";
@@ -601,5 +602,6 @@
SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484;
BinderCommandFtraceEvent binder_command = 485;
BinderReturnFtraceEvent binder_return = 486;
+ SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
}
}
diff --git a/protos/perfetto/trace/ftrace/perf_trace_counters.proto b/protos/perfetto/trace/ftrace/perf_trace_counters.proto
new file mode 100644
index 0000000..0e3531c
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/perf_trace_counters.proto
@@ -0,0 +1,26 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message SchedSwitchWithCtrsFtraceEvent {
+ optional int32 old_pid = 1;
+ optional int32 new_pid = 2;
+ optional uint32 cctr = 3;
+ optional uint32 ctr0 = 4;
+ optional uint32 ctr1 = 5;
+ optional uint32 ctr2 = 6;
+ optional uint32 ctr3 = 7;
+ optional uint32 lctr0 = 8;
+ optional uint32 lctr1 = 9;
+ optional uint32 ctr4 = 10;
+ optional uint32 ctr5 = 11;
+ optional string prev_comm = 12;
+ optional int32 prev_pid = 13;
+ optional uint32 cyc = 14;
+ optional uint32 inst = 15;
+ optional uint32 stallbm = 16;
+ optional uint32 l3dm = 17;
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 2ae554d..8cfa67e 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -645,6 +645,26 @@
// End of protos/perfetto/config/chrome/chrome_config.proto
+// Begin of protos/perfetto/config/etw/etw_config.proto
+
+// Proto definition based on the struct _EVENT_TRACE_PROPERTIES definition
+// See: https://learn.microsoft.com/en-us/windows/win32/api/evntrace/
+// ns-evntrace-event_trace_properties
+message EtwConfig {
+ // The KernelFlag represent list of kernel flags that we are intrested in.
+ // To get a more extensive list run 'xperf -providers k'.
+ enum KernelFlag {
+ C_SWITCH = 0;
+ IDLE_STATES = 1;
+ }
+
+ // The kernel_flags determines the flags that will be used by the etw tracing
+ // session. These kernel flags have been built to expose the useful events
+ // captured from the kernel mode only.
+ repeated KernelFlag kernel_flags = 1;
+}
+// End of protos/perfetto/config/etw/etw_config.proto
+
// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
// Next id: 26.
@@ -1950,7 +1970,6 @@
ATOM_PERFETTO_TRIGGER = 329;
ATOM_TRANSCODING_DATA = 330;
ATOM_IMS_SERVICE_ENTITLEMENT_UPDATED = 331;
- ATOM_ART_DATUM_REPORTED = 332;
ATOM_DEVICE_ROTATED = 333;
ATOM_SIM_SPECIFIC_SETTINGS_RESTORED = 334;
ATOM_TEXT_CLASSIFIER_DOWNLOAD_REPORTED = 335;
@@ -1980,7 +1999,6 @@
ATOM_LOCATION_TIME_ZONE_PROVIDER_STATE_CHANGED = 359;
ATOM_FDTRACK_EVENT_OCCURRED = 364;
ATOM_TIMEOUT_AUTO_EXTENDED_REPORTED = 365;
- ATOM_ODREFRESH_REPORTED = 366;
ATOM_ALARM_BATCH_DELIVERED = 367;
ATOM_ALARM_SCHEDULED = 368;
ATOM_CAR_WATCHDOG_IO_OVERUSE_STATS_REPORTED = 369;
@@ -2048,6 +2066,8 @@
ATOM_HOTWORD_DETECTION_SERVICE_RESTARTED = 432;
ATOM_HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED = 433;
ATOM_HOTWORD_DETECTOR_EVENTS = 434;
+ ATOM_AD_SERVICES_API_CALLED = 435;
+ ATOM_AD_SERVICES_MESUREMENT_REPORTS_UPLOADED = 436;
ATOM_BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED = 437;
ATOM_CONTACTS_INDEXER_UPDATE_STATS_REPORTED = 440;
ATOM_APP_BACKGROUND_RESTRICTIONS_INFO = 441;
@@ -2069,24 +2089,47 @@
ATOM_ISOLATED_COMPILATION_SCHEDULED = 457;
ATOM_ISOLATED_COMPILATION_ENDED = 458;
ATOM_ONS_OPPORTUNISTIC_ESIM_PROVISIONING_COMPLETE = 459;
+ ATOM_SYSTEM_SERVER_PRE_WATCHDOG_OCCURRED = 460;
ATOM_TELEPHONY_ANOMALY_DETECTED = 461;
ATOM_LETTERBOX_POSITION_CHANGED = 462;
ATOM_REMOTE_KEY_PROVISIONING_ATTEMPT = 463;
ATOM_REMOTE_KEY_PROVISIONING_NETWORK_INFO = 464;
ATOM_REMOTE_KEY_PROVISIONING_TIMING = 465;
ATOM_MEDIAOUTPUT_OP_INTERACTION_REPORT = 466;
- ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
ATOM_SYNC_EXEMPTION_OCCURRED = 468;
ATOM_AUTOFILL_PRESENTATION_EVENT_REPORTED = 469;
ATOM_DOCK_STATE_CHANGED = 470;
+ ATOM_SAFETY_SOURCE_STATE_COLLECTED = 471;
+ ATOM_SAFETY_CENTER_SYSTEM_EVENT_REPORTED = 472;
+ ATOM_SAFETY_CENTER_INTERACTION_REPORTED = 473;
+ ATOM_SETTINGS_PROVIDER_SETTING_CHANGED = 474;
ATOM_BROADCAST_DELIVERY_EVENT_REPORTED = 475;
ATOM_SERVICE_REQUEST_EVENT_REPORTED = 476;
ATOM_PROVIDER_ACQUISITION_EVENT_REPORTED = 477;
ATOM_BLUETOOTH_DEVICE_NAME_REPORTED = 478;
+ ATOM_CB_CONFIG_UPDATED = 479;
+ ATOM_CB_MODULE_ERROR_REPORTED = 480;
+ ATOM_CB_SERVICE_FEATURE_CHANGED = 481;
+ ATOM_CB_RECEIVER_FEATURE_CHANGED = 482;
+ ATOM_JSSCRIPTENGINE_LATENCY_REPORTED = 483;
+ ATOM_PRIVACY_SIGNAL_NOTIFICATION_INTERACTION = 484;
+ ATOM_PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION = 485;
+ ATOM_PRIVACY_SIGNALS_JOB_FAILURE = 486;
ATOM_VIBRATION_REPORTED = 487;
ATOM_UWB_RANGING_START = 489;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STATUS_REPORTED = 490;
+ ATOM_APP_COMPACTED_V2 = 491;
+ ATOM_AD_SERVICES_SETTINGS_USAGE_REPORTED = 493;
ATOM_DISPLAY_BRIGHTNESS_CHANGED = 494;
ATOM_ACTIVITY_ACTION_BLOCKED = 495;
+ ATOM_BACKGROUND_FETCH_PROCESS_REPORTED = 496;
+ ATOM_UPDATE_CUSTOM_AUDIENCE_PROCESS_REPORTED = 497;
+ ATOM_RUN_AD_BIDDING_PROCESS_REPORTED = 498;
+ ATOM_RUN_AD_SCORING_PROCESS_REPORTED = 499;
+ ATOM_RUN_AD_SELECTION_PROCESS_REPORTED = 500;
+ ATOM_RUN_AD_BIDDING_PER_CA_PROCESS_REPORTED = 501;
+ ATOM_MOBILE_DATA_DOWNLOAD_DOWNLOAD_RESULT_REPORTED = 502;
+ ATOM_MOBILE_DATA_DOWNLOAD_FILE_GROUP_STORAGE_STATS_REPORTED = 503;
ATOM_NETWORK_DNS_SERVER_SUPPORT_REPORTED = 504;
ATOM_VM_BOOTED = 505;
ATOM_VM_EXITED = 506;
@@ -2095,13 +2138,19 @@
ATOM_MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED = 509;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED = 510;
ATOM_MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED = 511;
+ ATOM_AD_SERVICES_MEASUREMENT_REGISTRATIONS = 512;
ATOM_HEARING_AID_INFO_REPORTED = 513;
ATOM_DEVICE_WIDE_JOB_CONSTRAINT_CHANGED = 514;
+ ATOM_AMBIENT_MODE_CHANGED = 515;
+ ATOM_ANR_LATENCY_REPORTED = 516;
+ ATOM_RESOURCE_API_INFO = 517;
+ ATOM_SYSTEM_DEFAULT_NETWORK_CHANGED = 518;
ATOM_IWLAN_SETUP_DATA_CALL_RESULT_REPORTED = 519;
ATOM_IWLAN_PDN_DISCONNECTED_REASON_REPORTED = 520;
ATOM_AIRPLANE_MODE_SESSION_REPORTED = 521;
ATOM_VM_CPU_STATUS_REPORTED = 522;
ATOM_VM_MEM_STATUS_REPORTED = 523;
+ ATOM_PACKAGE_INSTALLATION_SESSION_REPORTED = 524;
ATOM_DEFAULT_NETWORK_REMATCH_INFO = 525;
ATOM_NETWORK_SELECTION_PERFORMANCE = 526;
ATOM_NETWORK_NSD_REPORTED = 527;
@@ -2111,22 +2160,65 @@
ATOM_BLUETOOTH_LOCAL_SUPPORTED_FEATURES_REPORTED = 532;
ATOM_BLUETOOTH_GATT_APP_INFO = 533;
ATOM_BRIGHTNESS_CONFIGURATION_UPDATED = 534;
+ ATOM_AD_SERVICES_GET_TOPICS_REPORTED = 535;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_GET_TOP_TOPICS_REPORTED = 536;
+ ATOM_AD_SERVICES_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 537;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_LAUNCHED = 538;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FINISHED = 539;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECTION_REPORTED = 540;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_DEVICE_SCAN_TRIGGERED = 541;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_FIRST_DEVICE_SCAN_LATENCY = 542;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_CONNECT_DEVICE_LATENCY = 543;
+ ATOM_PACKAGE_MANAGER_SNAPSHOT_REPORTED = 544;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED = 545;
+ ATOM_PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED = 546;
ATOM_LAUNCHER_IMPRESSION_EVENT = 547;
- ATOM_ODSIGN_REPORTED = 548;
- ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_WEAR_MEDIA_OUTPUT_SWITCHER_ALL_DEVICES_SCAN_LATENCY = 549;
+ ATOM_WS_WATCH_FACE_EDITED = 551;
+ ATOM_WS_WATCH_FACE_FAVORITE_ACTION_REPORTED = 552;
+ ATOM_WS_WATCH_FACE_SET_ACTION_REPORTED = 553;
+ ATOM_PACKAGE_UNINSTALLATION_REPORTED = 554;
+ ATOM_GAME_MODE_CHANGED = 555;
+ ATOM_GAME_MODE_CONFIGURATION_CHANGED = 556;
+ ATOM_BEDTIME_MODE_STATE_CHANGED = 557;
ATOM_NETWORK_SLICE_SESSION_ENDED = 558;
ATOM_NETWORK_SLICE_DAILY_DATA_USAGE_REPORTED = 559;
ATOM_NFC_TAG_TYPE_OCCURRED = 560;
ATOM_NFC_AID_CONFLICT_OCCURRED = 561;
ATOM_NFC_READER_CONFLICT_OCCURRED = 562;
- ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_WS_TILE_LIST_CHANGED = 563;
+ ATOM_GET_TYPE_ACCESSED_WITHOUT_PERMISSION = 564;
+ ATOM_MOBILE_BUNDLED_APP_INFO_GATHERED = 566;
+ ATOM_WS_WATCH_FACE_COMPLICATION_SET_CHANGED = 567;
ATOM_MEDIA_DRM_CREATED = 568;
ATOM_MEDIA_DRM_ERRORED = 569;
ATOM_MEDIA_DRM_SESSION_OPENED = 570;
ATOM_MEDIA_DRM_SESSION_CLOSED = 571;
+ ATOM_USER_SELECTED_RESOLUTION = 572;
+ ATOM_UNSAFE_INTENT_EVENT_REPORTED = 573;
ATOM_PERFORMANCE_HINT_SESSION_REPORTED = 574;
+ ATOM_MEDIAMETRICS_MIDI_DEVICE_CLOSE_REPORTED = 576;
+ ATOM_BIOMETRIC_TOUCH_REPORTED = 577;
ATOM_HOTWORD_AUDIO_EGRESS_EVENT_REPORTED = 578;
+ ATOM_APP_SEARCH_SCHEMA_MIGRATION_STATS_REPORTED = 579;
+ ATOM_LOCATION_ENABLED_STATE_CHANGED = 580;
+ ATOM_IME_REQUEST_FINISHED = 581;
+ ATOM_USB_COMPLIANCE_WARNINGS_REPORTED = 582;
+ ATOM_APP_SUPPORTED_LOCALES_CHANGED = 583;
+ ATOM_GRAMMATICAL_INFLECTION_CHANGED = 584;
+ ATOM_MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED = 586;
+ ATOM_BIOMETRIC_PROPERTIES_COLLECTED = 587;
+ ATOM_KERNEL_WAKEUP_ATTRIBUTED = 588;
+ ATOM_SCREEN_STATE_CHANGED_V2 = 589;
+ ATOM_WS_BACKUP_ACTION_REPORTED = 590;
+ ATOM_WS_RESTORE_ACTION_REPORTED = 591;
+ ATOM_DEVICE_LOG_ACCESS_EVENT_REPORTED = 592;
+ ATOM_MEDIA_SESSION_UPDATED = 594;
+ ATOM_WEAR_OOBE_STATE_CHANGED = 595;
+ ATOM_WS_NOTIFICATION_UPDATED = 596;
ATOM_NETWORK_VALIDATION_FAILURE_STATS_DAILY_REPORTED = 601;
+ ATOM_WS_COMPLICATION_TAPPED = 602;
+ ATOM_WS_WEAR_TIME_SESSION = 610;
ATOM_WIFI_BYTES_TRANSFER = 10000;
ATOM_WIFI_BYTES_TRANSFER_BY_FG_BG = 10001;
ATOM_MOBILE_BYTES_TRANSFER = 10002;
@@ -2279,15 +2371,160 @@
ATOM_TELEPHONY_NETWORK_REQUESTS_V2 = 10153;
ATOM_DEVICE_TELEPHONY_PROPERTIES = 10154;
ATOM_REMOTE_KEY_PROVISIONING_ERROR_COUNTS = 10155;
+ ATOM_SAFETY_STATE = 10156;
ATOM_INCOMING_MMS = 10157;
ATOM_OUTGOING_MMS = 10158;
ATOM_MULTI_USER_INFO = 10160;
ATOM_NETWORK_BPF_MAP_INFO = 10161;
+ ATOM_OUTGOING_SHORT_CODE_SMS = 10162;
ATOM_CONNECTIVITY_STATE_SAMPLE = 10163;
ATOM_NETWORK_SELECTION_REMATCH_REASONS_INFO = 10164;
+ ATOM_GAME_MODE_INFO = 10165;
+ ATOM_GAME_MODE_CONFIGURATION = 10166;
+ ATOM_GAME_MODE_LISTENER = 10167;
ATOM_NETWORK_SLICE_REQUEST_COUNT = 10168;
+ ATOM_WS_TILE_SNAPSHOT = 10169;
+ ATOM_WS_ACTIVE_WATCH_FACE_COMPLICATION_SET_SNAPSHOT = 10170;
+ ATOM_PROCESS_STATE = 10171;
+ ATOM_PROCESS_ASSOCIATION = 10172;
ATOM_ADPF_SYSTEM_COMPONENT_INFO = 10173;
ATOM_NOTIFICATION_MEMORY_USE = 10174;
+ ATOM_HDR_CAPABILITIES = 10175;
+ ATOM_WS_FAVOURITE_WATCH_FACE_LIST_SNAPSHOT = 10176;
+ ATOM_WIFI_AWARE_NDP_REPORTED = 638;
+ ATOM_WIFI_AWARE_ATTACH_REPORTED = 639;
+ ATOM_WIFI_SELF_RECOVERY_TRIGGERED = 661;
+ ATOM_SOFT_AP_STARTED = 680;
+ ATOM_SOFT_AP_STOPPED = 681;
+ ATOM_WIFI_LOCK_RELEASED = 687;
+ ATOM_WIFI_LOCK_DEACTIVATED = 688;
+ ATOM_WIFI_CONFIG_SAVED = 689;
+ ATOM_WIFI_AWARE_RESOURCE_USING_CHANGED = 690;
+ ATOM_WIFI_AWARE_HAL_API_CALLED = 691;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_RECEIVED = 692;
+ ATOM_WIFI_LOCAL_ONLY_REQUEST_SCAN_TRIGGERED = 693;
+ ATOM_WIFI_THREAD_TASK_EXECUTED = 694;
+ ATOM_WIFI_STATE_CHANGED = 700;
+ ATOM_WIFI_AWARE_CAPABILITIES = 10190;
+ ATOM_WIFI_MODULE_INFO = 10193;
+ ATOM_SETTINGS_SPA_REPORTED = 622;
+ ATOM_EXPRESS_EVENT_REPORTED = 528;
+ ATOM_EXPRESS_HISTOGRAM_SAMPLE_REPORTED = 593;
+ ATOM_EXPRESS_UID_EVENT_REPORTED = 644;
+ ATOM_EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED = 658;
+ ATOM_PERMISSION_RATIONALE_DIALOG_VIEWED = 645;
+ ATOM_PERMISSION_RATIONALE_DIALOG_ACTION_REPORTED = 646;
+ ATOM_APP_DATA_SHARING_UPDATES_NOTIFICATION_INTERACTION = 647;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_VIEWED = 648;
+ ATOM_APP_DATA_SHARING_UPDATES_FRAGMENT_ACTION_REPORTED = 649;
+ ATOM_WS_INCOMING_CALL_ACTION_REPORTED = 626;
+ ATOM_WS_CALL_DISCONNECTION_REPORTED = 627;
+ ATOM_WS_CALL_DURATION_REPORTED = 628;
+ ATOM_WS_CALL_USER_EXPERIENCE_LATENCY_REPORTED = 629;
+ ATOM_WS_CALL_INTERACTION_REPORTED = 630;
+ ATOM_FULL_SCREEN_INTENT_LAUNCHED = 631;
+ ATOM_BAL_ALLOWED = 632;
+ ATOM_IN_TASK_ACTIVITY_STARTED = 685;
+ ATOM_CACHED_APPS_HIGH_WATERMARK = 10189;
+ ATOM_ODREFRESH_REPORTED = 366;
+ ATOM_ODSIGN_REPORTED = 548;
+ ATOM_ART_DATUM_REPORTED = 332;
+ ATOM_ART_DEVICE_DATUM_REPORTED = 550;
+ ATOM_ART_DATUM_DELTA_REPORTED = 565;
+ ATOM_BACKGROUND_DEXOPT_JOB_ENDED = 467;
+ ATOM_WEAR_ADAPTIVE_SUSPEND_STATS_REPORTED = 619;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_OPERATIONAL_STATS_REPORTED = 620;
+ ATOM_WEAR_POWER_ANOMALY_SERVICE_EVENT_STATS_REPORTED = 621;
+ ATOM_EMERGENCY_STATE_CHANGED = 633;
+ ATOM_DND_STATE_CHANGED = 657;
+ ATOM_MTE_STATE = 10181;
+ ATOM_AD_SERVICES_BACK_COMPAT_GET_TOPICS_REPORTED = 598;
+ ATOM_AD_SERVICES_BACK_COMPAT_EPOCH_COMPUTATION_CLASSIFIER_REPORTED = 599;
+ ATOM_AD_SERVICES_MEASUREMENT_DEBUG_KEYS = 640;
+ ATOM_AD_SERVICES_ERROR_REPORTED = 662;
+ ATOM_AD_SERVICES_BACKGROUND_JOBS_EXECUTION_REPORTED = 663;
+ ATOM_AD_SERVICES_MEASUREMENT_DELAYED_SOURCE_REGISTRATION = 673;
+ ATOM_AD_SERVICES_MEASUREMENT_ATTRIBUTION = 674;
+ ATOM_AD_SERVICES_MEASUREMENT_JOBS = 675;
+ ATOM_AD_SERVICES_MEASUREMENT_WIPEOUT = 676;
+ ATOM_AD_SERVICES_CONSENT_MIGRATED = 702;
+ ATOM_RKPD_POOL_STATS = 664;
+ ATOM_RKPD_CLIENT_OPERATION = 665;
+ ATOM_AUTOFILL_UI_EVENT_REPORTED = 603;
+ ATOM_AUTOFILL_FILL_REQUEST_REPORTED = 604;
+ ATOM_AUTOFILL_FILL_RESPONSE_REPORTED = 605;
+ ATOM_AUTOFILL_SAVE_EVENT_REPORTED = 606;
+ ATOM_AUTOFILL_SESSION_COMMITTED = 607;
+ ATOM_AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED = 659;
+ ATOM_TEST_EXTENSION_ATOM_REPORTED = 660;
+ ATOM_TEST_RESTRICTED_ATOM_REPORTED = 672;
+ ATOM_STATS_SOCKET_LOSS_REPORTED = 752;
+ ATOM_PLUGIN_INITIALIZED = 655;
+ ATOM_TV_LOW_POWER_STANDBY_POLICY = 679;
+ ATOM_LOCKSCREEN_SHORTCUT_SELECTED = 611;
+ ATOM_LOCKSCREEN_SHORTCUT_TRIGGERED = 612;
+ ATOM_EMERGENCY_NUMBERS_INFO = 10180;
+ ATOM_QUALIFIED_RAT_LIST_CHANGED = 634;
+ ATOM_QNS_IMS_CALL_DROP_STATS = 635;
+ ATOM_QNS_FALLBACK_RESTRICTION_CHANGED = 636;
+ ATOM_QNS_RAT_PREFERENCE_MISMATCH_INFO = 10177;
+ ATOM_QNS_HANDOVER_TIME_MILLIS = 10178;
+ ATOM_QNS_HANDOVER_PINGPONG = 10179;
+ ATOM_SATELLITE_CONTROLLER = 10182;
+ ATOM_SATELLITE_SESSION = 10183;
+ ATOM_SATELLITE_INCOMING_DATAGRAM = 10184;
+ ATOM_SATELLITE_OUTGOING_DATAGRAM = 10185;
+ ATOM_SATELLITE_PROVISION = 10186;
+ ATOM_SATELLITE_SOS_MESSAGE_RECOMMENDER = 10187;
+ ATOM_IKE_SESSION_TERMINATED = 678;
+ ATOM_IKE_LIVENESS_CHECK_SESSION_VALIDATED = 760;
+ ATOM_BLUETOOTH_HASHED_DEVICE_NAME_REPORTED = 613;
+ ATOM_BLUETOOTH_L2CAP_COC_CLIENT_CONNECTION = 614;
+ ATOM_BLUETOOTH_L2CAP_COC_SERVER_CONNECTION = 615;
+ ATOM_BLUETOOTH_LE_SESSION_CONNECTED = 656;
+ ATOM_RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED = 666;
+ ATOM_BLUETOOTH_PROFILE_CONNECTION_ATTEMPTED = 696;
+ ATOM_HEALTH_CONNECT_UI_IMPRESSION = 623;
+ ATOM_HEALTH_CONNECT_UI_INTERACTION = 624;
+ ATOM_HEALTH_CONNECT_APP_OPENED_REPORTED = 625;
+ ATOM_HEALTH_CONNECT_API_CALLED = 616;
+ ATOM_HEALTH_CONNECT_USAGE_STATS = 617;
+ ATOM_HEALTH_CONNECT_STORAGE_STATS = 618;
+ ATOM_HEALTH_CONNECT_API_INVOKED = 643;
+ ATOM_EXERCISE_ROUTE_API_CALLED = 654;
+ ATOM_ATOM_9999 = 9999;
+ ATOM_ATOM_99999 = 99999;
+ ATOM_THREADNETWORK_TELEMETRY_DATA_REPORTED = 738;
+ ATOM_THREADNETWORK_TOPO_ENTRY_REPEATED = 739;
+ ATOM_THREADNETWORK_DEVICE_INFO_REPORTED = 740;
+ ATOM_EMERGENCY_NUMBER_DIALED = 637;
+ ATOM_SANDBOX_API_CALLED = 488;
+ ATOM_SANDBOX_ACTIVITY_EVENT_OCCURRED = 735;
+ ATOM_SANDBOX_SDK_STORAGE = 10159;
+ ATOM_CRONET_ENGINE_CREATED = 703;
+ ATOM_CRONET_TRAFFIC_REPORTED = 704;
+ ATOM_CRONET_ENGINE_BUILDER_INITIALIZED = 762;
+ ATOM_CRONET_HTTP_FLAGS_INITIALIZED = 763;
+ ATOM_CRONET_INITIALIZED = 764;
+ ATOM_DAILY_KEEPALIVE_INFO_REPORTED = 650;
+ ATOM_IP_CLIENT_RA_INFO_REPORTED = 778;
+ ATOM_APF_SESSION_INFO_REPORTED = 777;
+ ATOM_CREDENTIAL_MANAGER_API_CALLED = 585;
+ ATOM_CREDENTIAL_MANAGER_INIT_PHASE_REPORTED = 651;
+ ATOM_CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED = 652;
+ ATOM_CREDENTIAL_MANAGER_FINAL_PHASE_REPORTED = 653;
+ ATOM_CREDENTIAL_MANAGER_TOTAL_REPORTED = 667;
+ ATOM_CREDENTIAL_MANAGER_FINALNOUID_REPORTED = 668;
+ ATOM_CREDENTIAL_MANAGER_GET_REPORTED = 669;
+ ATOM_CREDENTIAL_MANAGER_AUTH_CLICK_REPORTED = 670;
+ ATOM_CREDENTIAL_MANAGER_APIV2_CALLED = 671;
+ ATOM_UWB_ACTIVITY_INFO = 10188;
+ ATOM_MEDIA_ACTION_REPORTED = 608;
+ ATOM_MEDIA_CONTROLS_LAUNCHED = 609;
+ ATOM_MEDIA_CODEC_RECLAIM_REQUEST_COMPLETED = 600;
+ ATOM_MEDIA_CODEC_STARTED = 641;
+ ATOM_MEDIA_CODEC_STOPPED = 642;
+ ATOM_MEDIA_CODEC_RENDERED = 684;
}
// End of protos/perfetto/config/statsd/atom_ids.proto
@@ -2702,7 +2939,7 @@
// Begin of protos/perfetto/config/data_source_config.proto
// The configuration that is passed to each data source when starting tracing.
-// Next id: 125
+// Next id: 126
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2838,6 +3075,9 @@
optional AndroidSdkSyspropGuardConfig android_sdk_sysprop_guard_config = 124
[lazy = true];
+ // Data source name: windows.etw
+ optional EtwConfig etw_config = 125 [lazy = true];
+
// This is a fallback mechanism to send a free-form text config to the
// producer. In theory this should never be needed. All the code that
// is part of the platform (i.e. traced service) is supposed to *not* truncate
@@ -3981,6 +4221,14 @@
JANK_DROPPED = 1024;
};
+ // Specifies the severity of a jank.
+ enum JankSeverityType {
+ SEVERITY_UNKNOWN = 0;
+ SEVERITY_NONE = 1;
+ SEVERITY_PARTIAL = 2;
+ SEVERITY_FULL = 3;
+ }
+
// Specifies how a frame was presented on screen w.r.t. timing.
// Can be different for SurfaceFrame and DisplayFrame.
enum PresentType {
@@ -4057,6 +4305,7 @@
optional int32 jank_type = 9;
optional PredictionType prediction_type = 10;
optional bool is_buffer = 11;
+ optional JankSeverityType jank_severity_type = 12;
};
// Indicates the start of expected timeline slice for DisplayFrames.
@@ -4102,6 +4351,7 @@
// frame.
optional int32 jank_type = 7;
optional PredictionType prediction_type = 8;
+ optional JankSeverityType jank_severity_type = 9;
};
// FrameEnd just sends the cookie to indicate that the corresponding
@@ -4284,6 +4534,104 @@
// End of protos/perfetto/trace/android/packages_list.proto
+// Begin of protos/perfetto/trace/android/shell_transition.proto
+
+// ShellTransition messages record information about the shell transitions in
+// the system. This is used to track the animations that are created and execute
+// through the shell transition system.
+message ShellTransition {
+ // The unique identifier of the transition.
+ optional int32 id = 1;
+
+ // The time the transition was created on the WM side
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 create_time_ns = 2;
+ // The time the transition was sent from the WM side to shell
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 send_time_ns = 3;
+ // The time the transition was dispatched by shell to execute
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 dispatch_time_ns = 4;
+ // If the transition merge was accepted by the transition handler, this
+ // contains the time the transition was merged into transition with id
+ // `merge_target`.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 merge_time_ns = 5;
+ // The time shell proposed the transition should be merged to the transition
+ // handler into transition with id `merge_target`.
+ // (using SystemClock.elapsedRealtimeNanos()).
+ optional int64 merge_request_time_ns = 6;
+ // If the transition was aborted on the shell side, this is the time that
+ // occured.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 shell_abort_time_ns = 7;
+ // If the transition was aborted on the wm side, this is the time that
+ // occured.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 wm_abort_time_ns = 8;
+ // The time WM considers the transition to be complete.
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 finish_time_ns = 9;
+
+ // The id of the transaction that WM proposed to use as the starting
+ // transaction. It contains all the layer changes required to setup the
+ // transition and should be executed right at the start of the transition
+ // by the transition handler.
+ optional uint64 start_transaction_id = 10;
+ // The if of the transaction that WM proposed to use as the finish
+ // transaction. It contains all the layer changes required to set the final
+ // state of the transition.
+ optional uint64 finish_transaction_id = 11;
+
+ // The id of the handler that executed the transition. A HandlerMappings
+ // message in the trace will contain the mapping of id to a string
+ // representation of the handler.
+ optional int32 handler = 12;
+ // The transition type of this transition (e.g. TO_FRONT, OPEN, CLOSE).
+ optional int32 type = 13;
+
+ // The list of targets that are part of this transition.
+ repeated Target targets = 14;
+ // The id of the transition we have requested to merge or have merged this
+ // transition into.
+ optional int32 merge_target = 15;
+
+ // The flags set on this transition.
+ optional int32 flags = 16;
+ // The time the starting window was removed. Tracked because this can
+ // happen after the transition finishes, but the app may not yet be visible
+ // until the starting window is removed. So in a sense the transition is not
+ // finished until the starting window is removed. (b/284302118)
+ // (using SystemClock.elapsedRealtimeNanos())
+ optional int64 starting_window_remove_time_ns = 17;
+
+ // Contains the information about the windows targeted in a transition.
+ message Target {
+ // The transition mode of this target (e.g. TO_FRONT, CLOSE...)
+ optional int32 mode = 1;
+ // The layer id of this target.
+ optional int32 layer_id = 2;
+ // The window id of this target.
+ optional int32 window_id = 3;
+ // The flags set on this target.
+ optional int32 flags = 4;
+ }
+}
+
+// Contains mappings from handler ids to string representation of the handlers.
+message ShellHandlerMappings {
+ repeated ShellHandlerMapping mapping = 1;
+}
+
+message ShellHandlerMapping {
+ // The id of the handler used in the ShellTransition message.
+ optional int32 id = 1;
+ // A human readable and meaningful string representation of the handler.
+ optional string name = 2;
+}
+
+// End of protos/perfetto/trace/android/shell_transition.proto
+
// Begin of protos/perfetto/trace/android/surfaceflinger_common.proto
message RegionProto {
@@ -5320,6 +5668,7 @@
message EtwTraceEvent {
optional uint64 timestamp = 1;
+ optional uint32 cpu = 4;
oneof event {
CSwitchEtwEvent c_switch = 2;
@@ -8137,6 +8486,30 @@
// End of protos/perfetto/trace/ftrace/panel.proto
+// Begin of protos/perfetto/trace/ftrace/perf_trace_counters.proto
+
+message SchedSwitchWithCtrsFtraceEvent {
+ optional int32 old_pid = 1;
+ optional int32 new_pid = 2;
+ optional uint32 cctr = 3;
+ optional uint32 ctr0 = 4;
+ optional uint32 ctr1 = 5;
+ optional uint32 ctr2 = 6;
+ optional uint32 ctr3 = 7;
+ optional uint32 lctr0 = 8;
+ optional uint32 lctr1 = 9;
+ optional uint32 ctr4 = 10;
+ optional uint32 ctr5 = 11;
+ optional string prev_comm = 12;
+ optional int32 prev_pid = 13;
+ optional uint32 cyc = 14;
+ optional uint32 inst = 15;
+ optional uint32 stallbm = 16;
+ optional uint32 l3dm = 17;
+}
+
+// End of protos/perfetto/trace/ftrace/perf_trace_counters.proto
+
// Begin of protos/perfetto/trace/ftrace/power.proto
message CpuFrequencyFtraceEvent {
@@ -9419,6 +9792,7 @@
SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484;
BinderCommandFtraceEvent binder_command = 485;
BinderReturnFtraceEvent binder_return = 486;
+ SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
}
}
@@ -13257,7 +13631,7 @@
// See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
//
// Next reserved id: 14 (up to 15).
-// Next id: 96.
+// Next id: 99.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -13376,6 +13750,8 @@
// Winscope traces
LayersSnapshotProto surfaceflinger_layers_snapshot = 93;
TransactionTraceEntry surfaceflinger_transactions = 94;
+ ShellTransition shell_transition = 96;
+ ShellHandlerMappings shell_handler_mappings = 97;
// Events from the Windows etw infrastructure.
EtwTraceEventBundle etw_events = 95;
@@ -13483,6 +13859,11 @@
// being present for a particular sequence does not necessarily imply data
// loss.
optional bool first_packet_on_sequence = 87;
+
+ // The machine ID for identifying trace packets in a multi-machine tracing
+ // session. Is emitted by the tracing service for producers running on a
+ // remote host (e.g. a VM guest). For more context: go/crosetto-vm-tracing.
+ optional uint32 machine_id = 98;
}
// End of protos/perfetto/trace/trace_packet.proto
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 9966894..e8306b7 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -29,6 +29,7 @@
import "protos/perfetto/trace/android/initial_display_state.proto";
import "protos/perfetto/trace/android/network_trace.proto";
import "protos/perfetto/trace/android/packages_list.proto";
+import "protos/perfetto/trace/android/shell_transition.proto";
import "protos/perfetto/trace/android/surfaceflinger_layers.proto";
import "protos/perfetto/trace/android/surfaceflinger_transactions.proto";
import "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto";
@@ -97,7 +98,7 @@
// See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
//
// Next reserved id: 14 (up to 15).
-// Next id: 96.
+// Next id: 99.
message TracePacket {
// The timestamp of the TracePacket.
// By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -216,6 +217,8 @@
// Winscope traces
LayersSnapshotProto surfaceflinger_layers_snapshot = 93;
TransactionTraceEntry surfaceflinger_transactions = 94;
+ ShellTransition shell_transition = 96;
+ ShellHandlerMappings shell_handler_mappings = 97;
// Events from the Windows etw infrastructure.
EtwTraceEventBundle etw_events = 95;
@@ -323,4 +326,9 @@
// being present for a particular sequence does not necessarily imply data
// loss.
optional bool first_packet_on_sequence = 87;
+
+ // The machine ID for identifying trace packets in a multi-machine tracing
+ // session. Is emitted by the tracing service for producers running on a
+ // remote host (e.g. a VM guest). For more context: go/crosetto-vm-tracing.
+ optional uint32 machine_id = 98;
}
diff --git a/protos/perfetto/trace_processor/serialization.proto b/protos/perfetto/trace_processor/serialization.proto
index 35b7cf8..f559bd9 100644
--- a/protos/perfetto/trace_processor/serialization.proto
+++ b/protos/perfetto/trace_processor/serialization.proto
@@ -39,43 +39,12 @@
// Schema for serializing the column of Trace Processor table.
message SerializedColumn {
- // Schema used to store a serialized vector of data.
- message Vector {
- optional uint64 size = 1;
- optional bytes data = 2;
- }
-
// Schema used to store a serialized |BitVector|.
message BitVector {
optional bytes words = 1;
optional bytes counts = 2;
optional uint32 size = 3;
}
- // A schema for serialization of any of the descendants of
- // |overlays::StorageOverlay|.
- message Overlay {
- // A schema for serialization of |overlays::ArrangementOverlay|.
- message ArrangementOverlay {
- optional Vector vector = 1;
- }
-
- // A schema for serialization of |overlays::NullOverlay|.
- message NullOverlay {
- optional BitVector bit_vector = 1;
- }
-
- // A schema for serialization of |overlays::NullOverlay|.
- message SelectorOverlay {
- optional BitVector bit_vector = 1;
- }
-
- // Overlays are saved as either vectors or BitVectors.
- oneof data {
- ArrangementOverlay arrangement_overlay = 1;
- NullOverlay null_overlay = 2;
- SelectorOverlay selector_overlay = 3;
- }
- }
// A schema for serialization of any of the descendants of |storage::Storage|.
message Storage {
// Dummy storage should not contain any data. It's used to signify that
@@ -89,35 +58,61 @@
// A schema for serialization of |storage::Numeric|.
message NumericStorage {
- optional Vector values = 1;
+ optional bytes values = 1;
optional bool is_sorted = 2;
optional uint32 column_type = 3;
}
// A schema for serialization of |storage::SetIdStorage|.
message SetIdStorage {
- optional Vector values = 1;
+ optional bytes values = 1;
}
// A schema for serialization of |storage::StringStorage|.
message StringStorage {
- optional Vector values = 1;
+ optional bytes values = 1;
optional bool is_sorted = 2;
}
+ // A schema for serialization of |storage::NullStorage|.
+ message NullStorage {
+ optional BitVector bit_vector = 1;
+ optional Storage storage = 2;
+ }
+
+ // A schema for serialization of |storage::ArrangementStorage|.
+ message ArrangementStorage {
+ optional bytes values = 1;
+ optional Storage storage = 2;
+ }
+
+ // A schema for serialization of |storage::SelectorStorage|.
+ message SelectorStorage {
+ optional BitVector bit_vector = 1;
+ optional Storage storage = 2;
+ }
+
+ // A schema for serialization of |storage::DenseNullStorage|.
+ message DenseNullStorage {
+ optional BitVector bit_vector = 1;
+ optional Storage storage = 2;
+ }
+
oneof data {
DummyStorage dummy_storage = 1;
IdStorage id_storage = 2;
NumericStorage numeric_storage = 3;
SetIdStorage set_id_storage = 4;
StringStorage string_storage = 5;
+ NullStorage null_storage = 6;
+ ArrangementStorage arrangement_storage = 7;
+ SelectorStorage selector_storage = 8;
+ DenseNullStorage dense_null_storage = 9;
}
}
// Name of the table this column is part of.
optional string table_name = 1;
optional string column_name = 2;
-
optional Storage storage = 3;
- repeated Overlay overlay = 4;
}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 90cde7d..a68feef 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -1460,7 +1460,7 @@
message StartUp {
// This enum must be kept up to date with LaunchCauseMetrics.LaunchCause.
- enum LauchCauseType {
+ enum LaunchCauseType {
OTHER = 0;
CUSTOM_TAB = 1;
TWA = 2;
@@ -1483,7 +1483,8 @@
}
optional int64 activity_id = 1;
- optional LauchCauseType launch_cause = 2;
+ // deprecated field 2.
+ optional LaunchCauseType launch_cause = 3;
}
message WebContentInteraction {
diff --git a/python/perfetto/prebuilts/manifests/trace_processor_shell.py b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
index 2f881f5..0196c4b 100755
--- a/python/perfetto/prebuilts/manifests/trace_processor_shell.py
+++ b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
@@ -1,15 +1,15 @@
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACE_PROCESSOR_SHELL_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'trace_processor_shell',
'file_size':
- 9830664,
+ 9978200,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/trace_processor_shell',
'sha256':
- 'd617f9237f77477a6e80065cbcfc84d6ce80505ee34851a0b435f0815aba2645',
+ 'f3e21eb29fb51cb2ea9b81b69132c5ae93ce3276c57ccd27fcf7c675306b4e41',
'platform':
'darwin',
'machine': ['x86_64']
@@ -19,11 +19,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 8328808,
+ 8493976,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/trace_processor_shell',
'sha256':
- 'cc5b81618ddfa0a6eb63e0d418c0a52e302ca2617906fae6743d71f82c454611',
+ '84f35765141374b8d883813ac533e0c004cf72d1c6f05aef0c973364ff541eb9',
'platform':
'darwin',
'machine': ['arm64']
@@ -33,11 +33,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 9682088,
+ 9830856,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/trace_processor_shell',
'sha256':
- '0beb26c1d744cb982630b23ce031a767d6ed7113b81dca0710f792111592ee9e',
+ 'b3dc0a9c641b84a57fa5d59637921ae2237e4f05b1778341a691df220faf0cd7',
'platform':
'linux',
'machine': ['x86_64']
@@ -47,11 +47,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 7087544,
+ 7231096,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/trace_processor_shell',
'sha256':
- '4df289643b09261bd5718d6894e301023b4101e94e2dd81ead63037fb454054d',
+ 'a21252830fb1bbb7b3fd9665ce6e70920cffa6b1e72c16589c90896c002c3348',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 9090808,
+ 9238056,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/trace_processor_shell',
'sha256':
- '9cb0ce22fba1929bef599d89a197a2ea718061840006c4122032cf0fc5fc0c90',
+ 'f77519ec19743ec2c22ed78fe3a20106a482a28d77c4154378af108c5f7bdd4a',
'platform':
'linux',
'machine': ['aarch64']
@@ -75,55 +75,55 @@
'file_name':
'trace_processor_shell',
'file_size':
- 6723512,
+ 6870968,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/trace_processor_shell',
'sha256':
- 'c5949a736b1b5edb285f56c22e9af83e1d0ec6d331adaac77330a277d8bee07c'
+ '2c7055fb44085ec60ad8bb970d495c9c88070fce08902f11fcd44e0ae3369876'
}, {
'arch':
'android-arm64',
'file_name':
'trace_processor_shell',
'file_size':
- 8267112,
+ 8414568,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/trace_processor_shell',
'sha256':
- '32475b5fe7e1af557eab0d898accb37c657bc4b2dd221135b55114e61bd8be39'
+ 'd8ca0dc2bab7ea604a6721f0ac0e2b433b43261f247c6c98c510dc17aafe5a72'
}, {
'arch':
'android-x86',
'file_name':
'trace_processor_shell',
'file_size':
- 9164668,
+ 9328508,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/trace_processor_shell',
'sha256':
- '02aeff08c0c3e553c95c851401ade9aac6784ad2f19e312dabbb24801d02c0e5'
+ 'de6a6ea45769888e59a1678d37b6e355b27b834d34a0b9e4980a942d333b88cc'
}, {
'arch':
'android-x64',
'file_name':
'trace_processor_shell',
'file_size':
- 9430440,
+ 9577896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/trace_processor_shell',
'sha256':
- 'de75622f96f2551a7bbc28a0f063e0dfcc39241cd4fe09c39d0b2541d860a30a'
+ 'cd4b16c5f78a060934204737ba8b312e824ff7cc28f3732daf7d64e733a727f9'
}, {
'arch':
'windows-amd64',
'file_name':
'trace_processor_shell.exe',
'file_size':
- 9100288,
+ 9248256,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/trace_processor_shell.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/trace_processor_shell.exe',
'sha256':
- 'f0065f1dfa3f8001959bf18f5cd30138aa58409e39a13ccdd52a1860384e34fb',
+ '26584b4bbab40f8b0ad991a869e7483f92d7223e1473b879a6ceafa49b76390a',
'platform':
'win32',
'machine': ['amd64']
diff --git a/python/perfetto/prebuilts/manifests/tracebox.py b/python/perfetto/prebuilts/manifests/tracebox.py
index a1fd44e..21698c1 100755
--- a/python/perfetto/prebuilts/manifests/tracebox.py
+++ b/python/perfetto/prebuilts/manifests/tracebox.py
@@ -1,15 +1,15 @@
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACEBOX_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'tracebox',
'file_size':
- 1498680,
+ 1498816,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/tracebox',
'sha256':
- '57252aaf73b6a82cfb3bbc6cd68a9ec953f61296cf2c2d651125bb437dc50144',
+ '185014447d35357edbd20e7ce9924842a0d5c6576bd2257abae2ed48b65fd3b8',
'platform':
'darwin',
'machine': ['x86_64']
@@ -19,11 +19,11 @@
'file_name':
'tracebox',
'file_size':
- 1376136,
+ 1392776,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/tracebox',
'sha256':
- '5f37217af7d47b39624c62625847ec6b98db2ebd1a512e85921eb16bcb402b35',
+ '082bb50e64df5e232673eebb1cd8b0dd752a394105f600cb0262730833f6b7f3',
'platform':
'darwin',
'machine': ['arm64']
@@ -33,11 +33,11 @@
'file_name':
'tracebox',
'file_size':
- 2218840,
+ 2229096,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/tracebox',
'sha256':
- '89b1412e66b0a227cd8d16e6bf3d7416609c153d4b5c64d6e1a7c600870cd27b',
+ 'c99120caedb845e1c3fad4428263a683b44c357c76d65848dd8e437250066e38',
'platform':
'linux',
'machine': ['x86_64']
@@ -47,11 +47,11 @@
'file_name':
'tracebox',
'file_size':
- 1332292,
+ 1339796,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/tracebox',
'sha256':
- 'b5a3a8e0869b35bc9ceb06f3e4ca5bb6c1e1b1c5e6ea23cfed541e40a45ad96a',
+ '6732165916b74f0b820991d1aaed2086a6b56e91f6c604291efe6636f0bdda71',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
'file_name':
'tracebox',
'file_size':
- 2147464,
+ 2157312,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/tracebox',
'sha256':
- '00fdbdfc5877352b9323b6ea1a119132b66e696941f380da117d0d47ae1baaf9',
+ '7d09865a6d7118e67d2acd0c56b2a94ce8bd5f614869d29a72fe633515ab1fbd',
'platform':
'linux',
'machine': ['aarch64']
@@ -75,11 +75,11 @@
'file_name':
'tracebox',
'file_size':
- 1230804,
+ 1247188,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/tracebox',
'sha256':
- '02ee157f995708e78f57d6e5e110054d2e2b407dbf2db120cc967c7c553081e7'
+ '4ecc192172ac2bca49557cbdbb1f7d660718d4fb4a7314fd19b2b2e52be8bc0c'
}, {
'arch':
'android-arm64',
@@ -88,9 +88,9 @@
'file_size':
1854120,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/tracebox',
'sha256':
- 'e0031963f623da25c387b78e20747e07829043732f5afe16cfdea9bad3c0f27c'
+ '1ca89113279d5c6a9ae273bde03b4d84373efe6923dc637cb840908f13b9639e'
}, {
'arch':
'android-x86',
@@ -99,9 +99,9 @@
'file_size':
1853356,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/tracebox',
'sha256':
- '6e9e4b7b0d3773ab5011e476eca883eeb0ab7538fc7d2cd4642b6abc01711ba9'
+ 'cf689a191c1252734ebbfda3106600da324610f761515cfbffbeac2ebdfee715'
}, {
'arch':
'android-x64',
@@ -110,7 +110,7 @@
'file_size':
2149032,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/tracebox',
'sha256':
- '4b494907a43e898047ce98271796464deb3010b6755cee8d9c8a6c341502979b'
+ '99e9ebdb5b5308d95551a4ad060d615d7defb6877c4061d21c783c45a71d372f'
}]
diff --git a/python/perfetto/prebuilts/manifests/traceconv.py b/python/perfetto/prebuilts/manifests/traceconv.py
index 7ada0fa..f01ef0a 100755
--- a/python/perfetto/prebuilts/manifests/traceconv.py
+++ b/python/perfetto/prebuilts/manifests/traceconv.py
@@ -1,15 +1,15 @@
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACECONV_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'traceconv',
'file_size':
- 9020880,
+ 9184800,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/traceconv',
'sha256':
- 'ae7903f9bb8a98b32fdf96c0124797cf9b4b58a9bcb45a05574d74d33b11ada9',
+ 'b651d0a5b5606c1c3e24723e94d8ecb233a01f0dfccc95a2c6a4e773cb8f52d7',
'platform':
'darwin',
'machine': ['x86_64']
@@ -19,11 +19,11 @@
'file_name':
'traceconv',
'file_size':
- 7580200,
+ 7761896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/traceconv',
'sha256':
- '619f31cd84a70fcb75e2a1cbe911f77feca0273ea0b1e706ae4be5ac020cdf7d',
+ '3b019f5ddd5293d3181f7c30f91dc7b08f3a2e83ebb3b52b8f3905dc5161747d',
'platform':
'darwin',
'machine': ['arm64']
@@ -33,11 +33,11 @@
'file_name':
'traceconv',
'file_size':
- 8773576,
+ 8928296,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/traceconv',
'sha256':
- 'abc0b4191abe5106e1d4382bc0d238e53c39a83e1ba91237fbed5f789ef1c82b',
+ '830d20ffec266218d49f6b6c8efed4538bc59b51d8d2f735cbbb6a1435131b50',
'platform':
'linux',
'machine': ['x86_64']
@@ -47,11 +47,11 @@
'file_name':
'traceconv',
'file_size':
- 6620748,
+ 6770204,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/traceconv',
'sha256':
- 'f2dd9f1bfabea567ff20aedd0798e90093eff2bfdc15aacbfafe0abe52aa1fd1',
+ '93a9e5ccb94559b871af8f6da45f858aee01801b31776703892dcf3d7ea769b7',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
'file_name':
'traceconv',
'file_size':
- 8240792,
+ 8393944,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/traceconv',
'sha256':
- '76428eca01ced4b769241915fb5233b43b9b59e9062917e45d523db10f33e546',
+ '88a92ccbcd8e851673e018b7f599514daf05dde9b7e4de9641fa5629124abf12',
'platform':
'linux',
'machine': ['aarch64']
@@ -75,55 +75,55 @@
'file_name':
'traceconv',
'file_size':
- 6247672,
+ 6378744,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/traceconv',
'sha256':
- '3eb80bbd78c6210b73ef20654b9d80994747e552b3d342a8d2ec4aa58dabcc33'
+ '6cb7d30d656aa4f172e6724f105a56e249e7043ecf637c65e1e3868885535cff'
}, {
'arch':
'android-arm64',
'file_name':
'traceconv',
'file_size':
- 7545032,
+ 7692488,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/traceconv',
'sha256':
- '30037aba74f7718f71273a83141373e623ddfe561294d01b41433dc9b572fee5'
+ '1668808efbdf8d5b116d4716d61d2bd002f71ce465206d3b83af4fcc7a4c19cd'
}, {
'arch':
'android-x86',
'file_name':
'traceconv',
'file_size':
- 8410300,
+ 8557756,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/traceconv',
'sha256':
- '9ad420368453699da12ce6359db514283e92b2caa2d415d9db61977cc79649a9'
+ '653733582cae0021eae0e1b5d8db387c1bae772d77b307f1e2111b78ec4ea67c'
}, {
'arch':
'android-x64',
'file_name':
'traceconv',
'file_size':
- 8544512,
+ 8708352,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/traceconv',
'sha256':
- '60a62f8712186c5f984ff2fbc4ff8c47b6226b62a83b31f878e0597c8bb90cd8'
+ '7fc564ac581b81d79573f57dae027c47bd7a857ff0f89df984380c3c657d5876'
}, {
'arch':
'windows-amd64',
'file_name':
'traceconv.exe',
'file_size':
- 8049664,
+ 8204288,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/traceconv.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/traceconv.exe',
'sha256':
- '9e89ccd0cdb466ea5fe960b71538b0ff8a2858b9e7ecdbcafa9ffe60839c9afc',
+ 'e33bad8061f08f9c3cfe6e91ef6f1696b6ac90d0799edcb57052f24888b436e2',
'platform':
'win32',
'machine': ['amd64']
diff --git a/python/tools/record_android_trace.py b/python/tools/record_android_trace.py
index 06a4fa7..7dbca53 100755
--- a/python/tools/record_android_trace.py
+++ b/python/tools/record_android_trace.py
@@ -431,7 +431,7 @@
fname = os.path.basename(path)
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
- address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}'
+ address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}&referrer=record_android_trace'
if open_browser:
webbrowser.open_new_tab(address)
else:
diff --git a/src/base/time.cc b/src/base/time.cc
index 1507916..e799542 100644
--- a/src/base/time.cc
+++ b/src/base/time.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <atomic>
+
#include "perfetto/base/time.h"
#include "perfetto/base/build_config.h"
@@ -30,6 +32,86 @@
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+namespace {
+
+// Returns the current value of the performance counter.
+int64_t QPCNowRaw() {
+ LARGE_INTEGER perf_counter_now = {};
+ // According to the MSDN documentation for QueryPerformanceCounter(), this
+ // will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
+ ::QueryPerformanceCounter(&perf_counter_now);
+ return perf_counter_now.QuadPart;
+}
+
+double TSCTicksPerSecond() {
+ // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
+ // frequency, because there is no guarantee that the TSC frequency is equal to
+ // the performance counter frequency.
+ // The TSC frequency is cached in a static variable because it takes some time
+ // to compute it.
+ static std::atomic<double> tsc_ticks_per_second = 0;
+ double value = tsc_ticks_per_second.load(std::memory_order_relaxed);
+ if (value != 0)
+ return value;
+
+ // Increase the thread priority to reduces the chances of having a context
+ // switch during a reading of the TSC and the performance counter.
+ const int previous_priority = ::GetThreadPriority(::GetCurrentThread());
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+ // The first time that this function is called, make an initial reading of the
+ // TSC and the performance counter. Initialization of static variable is
+ // thread-safe. Threads can race initializing tsc_initial vs
+ // perf_counter_initial, although they should be storing very similar values.
+
+ static const uint64_t tsc_initial = __rdtsc();
+ static const int64_t perf_counter_initial = QPCNowRaw();
+
+ // Make a another reading of the TSC and the performance counter every time
+ // that this function is called.
+ const uint64_t tsc_now = __rdtsc();
+ const int64_t perf_counter_now = QPCNowRaw();
+
+ // Reset the thread priority.
+ ::SetThreadPriority(::GetCurrentThread(), previous_priority);
+
+ // Make sure that at least 50 ms elapsed between the 2 readings. The first
+ // time that this function is called, we don't expect this to be the case.
+ // Note: The longer the elapsed time between the 2 readings is, the more
+ // accurate the computed TSC frequency will be. The 50 ms value was
+ // chosen because local benchmarks show that it allows us to get a
+ // stddev of less than 1 tick/us between multiple runs.
+ // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
+ // this will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
+ LARGE_INTEGER perf_counter_frequency = {};
+ ::QueryPerformanceFrequency(&perf_counter_frequency);
+ PERFETTO_CHECK(perf_counter_now >= perf_counter_initial);
+ const int64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
+ const double elapsed_time_seconds =
+ static_cast<double>(perf_counter_ticks) /
+ static_cast<double>(perf_counter_frequency.QuadPart);
+
+ constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
+ if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
+ return 0;
+
+ // Compute the frequency of the TSC.
+ PERFETTO_CHECK(tsc_now >= tsc_initial);
+ const uint64_t tsc_ticks = tsc_now - tsc_initial;
+ // Racing with another thread to write |tsc_ticks_per_second| is benign
+ // because both threads will write a valid result.
+ tsc_ticks_per_second.store(
+ static_cast<double>(tsc_ticks) / elapsed_time_seconds,
+ std::memory_order_relaxed);
+
+ return tsc_ticks_per_second.load(std::memory_order_relaxed);
+}
+
+} // namespace
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
TimeNanos GetWallTimeNs() {
LARGE_INTEGER freq;
@@ -42,6 +124,13 @@
}
TimeNanos GetThreadCPUTimeNs() {
+#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
+ // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
+ // backed by the actual number of CPU cycles executed, rather than a
+ // constant-rate timer like Intel. To work around this, use GetThreadTimes
+ // (which isn't as accurate but is meaningful as a measure of elapsed
+ // per-thread time).
FILETIME dummy, kernel_ftime, user_ftime;
::GetThreadTimes(GetCurrentThread(), &dummy, &dummy, &kernel_ftime,
&user_ftime);
@@ -51,6 +140,23 @@
user_ftime.dwHighDateTime * 0x100000000 + user_ftime.dwLowDateTime;
return TimeNanos((kernel_time + user_time) * 100);
+#else // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // Get the number of TSC ticks used by the current thread.
+ ULONG64 thread_cycle_time = 0;
+ ::QueryThreadCycleTime(GetCurrentThread(), &thread_cycle_time);
+
+ // Get the frequency of the TSC.
+ const double tsc_ticks_per_second = TSCTicksPerSecond();
+ if (tsc_ticks_per_second == 0)
+ return TimeNanos();
+
+ // Return the CPU time of the current thread.
+ const double thread_time_seconds =
+ static_cast<double>(thread_cycle_time) / tsc_ticks_per_second;
+ constexpr int64_t kNanosecondsPerSecond = 1000 * 1000 * 1000;
+ return TimeNanos(
+ static_cast<int64_t>(thread_time_seconds * kNanosecondsPerSecond));
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
}
void SleepMicroseconds(unsigned interval_us) {
@@ -61,12 +167,22 @@
::Sleep(static_cast<DWORD>((interval_us + 999) / 1000));
}
+void InitializeTime() {
+#if !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+ // Make an early first call to TSCTicksPerSecond() to start 50 ms elapsed time
+ // (see comment in TSCTicksPerSecond()).
+ TSCTicksPerSecond();
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_ARM64)
+}
+
#else // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
void SleepMicroseconds(unsigned interval_us) {
::usleep(static_cast<useconds_t>(interval_us));
}
+void InitializeTime() {}
+
#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
std::string GetTimeFmt(const std::string& fmt) {
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 029b450..0c862ee 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -21,6 +21,7 @@
#include <utility>
#include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/time.h"
@@ -44,6 +45,47 @@
kUseTCPSocket ? base::SockFamily::kInet : base::SockFamily::kUnix;
base::CrashKey g_crash_key_uid("ipc_uid");
+
+base::MachineID GenerateMachineID(base::UnixSocket* sock,
+ const std::string& machine_id_hint) {
+ // The special value of base::kDefaultMachineID is reserved for local
+ // producers.
+ if (!sock->is_connected() || sock->family() == base::SockFamily::kUnix)
+ return base::kDefaultMachineID;
+
+ base::Hasher hasher;
+ // Use the hint from the client, or fallback to hostname if the client
+ // doesn't provide a hint.
+ if (!machine_id_hint.empty()) {
+ hasher.Update(machine_id_hint);
+ } else {
+ // Use the socket address without the port number part as the hint.
+ auto host_id = sock->GetSockAddr();
+ auto pos = std::string::npos;
+ switch (sock->family()) {
+ case base::SockFamily::kInet:
+ PERFETTO_FALLTHROUGH;
+ case base::SockFamily::kInet6:
+ PERFETTO_FALLTHROUGH;
+ case base::SockFamily::kVsock:
+ pos = host_id.rfind(":");
+ if (pos != std::string::npos)
+ host_id.resize(pos);
+ break;
+ case base::SockFamily::kUnspec:
+ PERFETTO_FALLTHROUGH;
+ case base::SockFamily::kUnix:
+ PERFETTO_DFATAL("Should be unreachable.");
+ return base::kDefaultMachineID;
+ }
+ hasher.Update(host_id);
+ }
+
+ // Take the lower 32-bit from the hash.
+ uint32_t digest = static_cast<uint32_t>(hasher.digest());
+ // Avoid the extremely unlikely case that the hasher digest happens to be 0.
+ return digest == base::kDefaultMachineID ? 1 : digest;
+}
} // namespace
uid_t HostImpl::ClientConnection::GetPosixPeerUid() const {
@@ -285,8 +327,8 @@
auto peer_uid = client->GetPosixPeerUid();
auto scoped_key = g_crash_key_uid.SetScoped(static_cast<int64_t>(peer_uid));
- service->client_info_ =
- ClientInfo(client->id, peer_uid, client->GetLinuxPeerPid());
+ service->client_info_ = ClientInfo(
+ client->id, peer_uid, client->GetLinuxPeerPid(), client->GetMachineID());
service->received_fd_ = &client->received_fd;
method.invoker(service, *decoded_req_args, std::move(deferred_reply));
service->received_fd_ = nullptr;
@@ -307,9 +349,12 @@
return;
}
- client->pid_override = req_frame.set_peer_identity().pid();
- client->uid_override =
- static_cast<uid_t>(req_frame.set_peer_identity().uid());
+ const auto& set_peer_identity = req_frame.set_peer_identity();
+ client->pid_override = set_peer_identity.pid();
+ client->uid_override = static_cast<uid_t>(set_peer_identity.uid());
+
+ client->machine_id = GenerateMachineID(client->sock.get(),
+ set_peer_identity.machine_id_hint());
}
void HostImpl::ReplyToMethodInvocation(ClientID client_id,
@@ -375,7 +420,8 @@
ClientID client_id = client->id;
ClientInfo client_info(client_id, client->GetPosixPeerUid(),
- client->GetLinuxPeerPid());
+ client->GetLinuxPeerPid(), client->GetMachineID());
+
clients_by_socket_.erase(it);
PERFETTO_DCHECK(clients_.count(client_id));
clients_.erase(client_id);
diff --git a/src/ipc/host_impl.h b/src/ipc/host_impl.h
index 8738459..c54a9e7 100644
--- a/src/ipc/host_impl.h
+++ b/src/ipc/host_impl.h
@@ -67,14 +67,19 @@
BufferedFrameDeserializer frame_deserializer;
base::ScopedFile received_fd;
std::function<bool(int)> send_fd_cb_fuchsia;
- // Peer identity set using IPCFrame sent by the client. These 2 fields
+ // Peer identity set using IPCFrame sent by the client. These 3 fields
// should be used only for non-AF_UNIX connections AF_UNIX connections
// should only rely on the peer identity obtained from the socket.
uid_t uid_override = base::kInvalidUid;
pid_t pid_override = base::kInvalidPid;
+ // |machine_id| is mapped from machine_id_hint (or socket hostname if
+ // |the client doesn't support machine_id_hint).
+ base::MachineID machine_id = base::kDefaultMachineID;
+
pid_t GetLinuxPeerPid() const;
uid_t GetPosixPeerUid() const;
+ base::MachineID GetMachineID() const { return machine_id; }
};
struct ExposedService {
ExposedService(ServiceID, const std::string&, std::unique_ptr<Service>);
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index c6e0404..927a5a6 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -15,11 +15,12 @@
*/
#include "src/ipc/host_impl.h"
-
+#include <sys/socket.h>
#include <memory>
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/sys_types.h"
#include "perfetto/ext/base/temp_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/utils.h"
@@ -37,6 +38,7 @@
namespace ipc {
namespace {
+using ::perfetto::ipc::Frame;
using ::perfetto::ipc::gen::ReplyProto;
using ::perfetto::ipc::gen::RequestProto;
using ::testing::_;
@@ -95,6 +97,12 @@
base::SockType::kStream);
}
+ FakeClient(const char* sock_name, base::TaskRunner* task_runner) {
+ auto sock_family = base::GetSockFamily(sock_name);
+ sock_ = base::UnixSocket::Connect(sock_name, this, task_runner, sock_family,
+ base::SockType::kStream);
+ }
+
FakeClient(base::ScopedSocketHandle connected_socket,
base::TaskRunner* task_runner) {
sock_ = base::UnixSocket::AdoptConnected(std::move(connected_socket), this,
@@ -114,6 +122,21 @@
SendFrame(frame);
}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ void SetPeerIdentity(uid_t uid,
+ pid_t pid,
+ const std::string& machine_id_hint) {
+ Frame ipc_frame;
+ ipc_frame.set_request_id(0);
+ auto* set_peer_identity = ipc_frame.mutable_set_peer_identity();
+ set_peer_identity->set_pid(pid);
+ set_peer_identity->set_uid(static_cast<int32_t>(uid));
+ set_peer_identity->set_machine_id_hint(machine_id_hint);
+ SendFrame(ipc_frame);
+ }
+#endif
+
void InvokeMethod(ServiceID service_id,
MethodID method_id,
const ProtoMessage& args,
@@ -530,6 +553,97 @@
EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).WillOnce(Return());
task_runner_->RunUntilIdle();
}
+
+TEST_F(HostImplTest, SetPeerIdentityUnixSocket) {
+ FakeService* fake_service = new FakeService("FakeService");
+ ASSERT_TRUE(host_->ExposeService(std::unique_ptr<Service>(fake_service)));
+ // SetPeerIdentity must be the first message. Use getpid()+1/geteuid+1 to
+ // check that this message doesn't take effect for Unix socket.
+ cli_->SetPeerIdentity(geteuid() + 1, getpid() + 1, "test_machine_id_hint");
+
+ auto on_bind = task_runner_->CreateCheckpoint("on_bind");
+ cli_->BindService("FakeService");
+ EXPECT_CALL(*cli_, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
+ task_runner_->RunUntilCheckpoint("on_bind");
+
+ RequestProto req_args;
+ req_args.set_data("foo");
+ cli_->InvokeMethod(cli_->last_bound_service_id_, 1, req_args);
+ EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
+ .WillOnce(
+ Invoke([fake_service](const RequestProto& req, DeferredBase* reply) {
+ ASSERT_EQ("foo", req.data());
+ std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
+ reply_args->set_data("bar");
+ reply->Resolve(AsyncResult<ProtoMessage>(
+ std::unique_ptr<ProtoMessage>(reply_args.release())));
+ // Verifies the pid() and uid() values in ClientInfo.
+ const auto& client_info = fake_service->client_info();
+ ASSERT_EQ(client_info.uid(), getuid());
+ ASSERT_EQ(client_info.pid(), getpid());
+ ASSERT_EQ(client_info.machine_id(), base::kDefaultMachineID);
+ }));
+
+ EXPECT_CALL(*cli_, OnInvokeMethodReply(_)).WillOnce(Return());
+ task_runner_->RunUntilIdle();
+}
+
+TEST(HostImpl, SetPeerIdentityTcpSocket) {
+ std::unique_ptr<base::TestTaskRunner> task_runner(new base::TestTaskRunner());
+ std::unique_ptr<HostImpl> host_impl;
+ std::unique_ptr<FakeClient> cli;
+
+ auto tear_down = base::OnScopeExit([&]() {
+ task_runner->RunUntilIdle();
+ cli.reset();
+ host_impl.reset();
+ task_runner->RunUntilIdle();
+ task_runner.reset();
+ });
+
+ Host* host = Host::CreateInstance("127.0.0.1:0", task_runner.get()).release();
+ ASSERT_NE(nullptr, host);
+ host_impl.reset(static_cast<HostImpl*>(host));
+
+ auto sock_name = host_impl->sock()->GetSockAddr();
+ cli.reset(new FakeClient(sock_name.c_str(), task_runner.get()));
+
+ auto on_connect = task_runner->CreateCheckpoint("on_connect");
+ EXPECT_CALL(*cli, OnConnect()).WillOnce(Invoke(on_connect));
+ task_runner->RunUntilCheckpoint("on_connect");
+
+ FakeService* fake_service = new FakeService("FakeService");
+ ASSERT_TRUE(host->ExposeService(std::unique_ptr<Service>(fake_service)));
+ // Set peer identity with fake values.
+ cli->SetPeerIdentity(123, 456, "test_machine_id_hint");
+
+ auto on_bind = task_runner->CreateCheckpoint("on_bind");
+ cli->BindService("FakeService");
+ EXPECT_CALL(*cli, OnServiceBound(_)).WillOnce(InvokeWithoutArgs(on_bind));
+ task_runner->RunUntilCheckpoint("on_bind");
+
+ RequestProto req_args;
+ req_args.set_data("foo");
+ cli->InvokeMethod(cli->last_bound_service_id_, 1, req_args);
+ EXPECT_CALL(*fake_service, OnFakeMethod1(_, _))
+ .WillOnce(
+ Invoke([fake_service](const RequestProto& req, DeferredBase* reply) {
+ ASSERT_EQ("foo", req.data());
+ std::unique_ptr<ReplyProto> reply_args(new ReplyProto());
+ reply_args->set_data("bar");
+ reply->Resolve(AsyncResult<ProtoMessage>(
+ std::unique_ptr<ProtoMessage>(reply_args.release())));
+ // Verify peer identity.
+ const auto& client_info = fake_service->client_info();
+ ASSERT_EQ(client_info.uid(), 123u);
+ ASSERT_EQ(client_info.pid(), 456);
+ // ClientInfo contains non-default raw machine ID.
+ ASSERT_NE(client_info.machine_id(), base::kDefaultMachineID);
+ }));
+
+ EXPECT_CALL(*cli, OnInvokeMethodReply(_)).WillOnce(Return());
+ task_runner->RunUntilIdle();
+}
#endif // OS_WIN
// TODO(primiano): add the tests below in next CLs.
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index f2dcbb3..68bb2ba 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -481,3 +481,4 @@
samsung/tracing_mark_write
binder/binder_command
binder/binder_return
+perf_trace_counters/sched_switch_with_ctrs
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index b09ed17..b89512e 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -269,7 +269,6 @@
":top_level_unittests",
"containers:unittests",
"db:unittests",
- "db/overlays:unittests",
"db/storage:unittests",
"importers/android_bugreport:unittests",
"importers/common:unittests",
diff --git a/src/trace_processor/containers/BUILD.gn b/src/trace_processor/containers/BUILD.gn
index e4750d2..4c4cb8d 100644
--- a/src/trace_processor/containers/BUILD.gn
+++ b/src/trace_processor/containers/BUILD.gn
@@ -38,6 +38,7 @@
deps = [
"../../../gn:default_deps",
"../../../include/perfetto/protozero",
+ "../../../protos/perfetto/trace_processor:zero",
"../../base",
]
}
@@ -55,6 +56,8 @@
":containers",
"../../../gn:default_deps",
"../../../gn:gtest_and_gmock",
+ "../../../include/perfetto/protozero",
+ "../../../protos/perfetto/trace_processor:zero",
]
}
diff --git a/src/trace_processor/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc
index dc5de84..b566558 100644
--- a/src/trace_processor/containers/bit_vector.cc
+++ b/src/trace_processor/containers/bit_vector.cc
@@ -18,6 +18,7 @@
#include <limits>
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector_iterators.h"
#if PERFETTO_BUILDFLAG(PERFETTO_X64_CPU_OPT)
@@ -329,5 +330,38 @@
return std::move(builder).Build();
}
+void BitVector::Serialize(
+ protos::pbzero::SerializedColumn::BitVector* msg) const {
+ msg->set_size(size_);
+ if (!counts_.empty()) {
+ msg->set_counts(reinterpret_cast<const uint8_t*>(counts_.data()),
+ sizeof(uint32_t) * counts_.size());
+ }
+ if (!words_.empty()) {
+ msg->set_words(reinterpret_cast<const uint8_t*>(words_.data()),
+ sizeof(uint64_t) * words_.size());
+ }
+}
+
+// Deserialize BitVector from proto.
+void BitVector::Deserialize(
+ const protos::pbzero::SerializedColumn::BitVector::Decoder& bv_msg) {
+ size_ = bv_msg.size();
+ if (bv_msg.has_counts()) {
+ counts_.resize(
+ static_cast<size_t>(bv_msg.counts().size / sizeof(uint32_t)));
+ memcpy(counts_.data(), bv_msg.counts().data, bv_msg.counts().size);
+ } else {
+ counts_.clear();
+ }
+
+ if (bv_msg.has_words()) {
+ words_.resize(static_cast<size_t>(bv_msg.words().size / sizeof(uint64_t)));
+ memcpy(words_.data(), bv_msg.words().data, bv_msg.words().size);
+ } else {
+ words_.clear();
+ }
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index b7bbec1..0e5e72b 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -23,12 +23,21 @@
#include <algorithm>
#include <array>
+#include <cstring>
#include <optional>
#include <vector>
#include "perfetto/base/logging.h"
namespace perfetto {
+
+namespace protos {
+namespace pbzero {
+class SerializedColumn_BitVector;
+class SerializedColumn_BitVector_Decoder;
+} // namespace pbzero
+} // namespace protos
+
namespace trace_processor {
namespace internal {
@@ -395,6 +404,13 @@
return BlockCount(n) * Block::kBits + BlockCount(n) * sizeof(uint32_t);
}
+ // Serialize internals of BitVector to proto.
+ void Serialize(protos::pbzero::SerializedColumn_BitVector* msg) const;
+
+ // Deserialize BitVector from proto.
+ void Deserialize(
+ const protos::pbzero::SerializedColumn_BitVector_Decoder& bv_msg);
+
private:
friend class internal::BaseIterator;
friend class internal::AllBitsIterator;
diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc
index a324c33..ad282dc 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -19,6 +19,8 @@
#include <bitset>
#include <random>
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector_iterators.h"
#include "test/gtest_and_gmock.h"
@@ -720,6 +722,38 @@
ASSERT_FALSE(set_it);
}
+TEST(BitVectorUnittest, SerializeSimple) {
+ BitVector bv{1, 0, 1, 0, 1, 0, 1};
+ protozero::HeapBuffered<protos::pbzero::SerializedColumn::BitVector> msg;
+ bv.Serialize(msg.get());
+ auto buffer = msg.SerializeAsArray();
+
+ protos::pbzero::SerializedColumn::BitVector::Decoder decoder(buffer.data(),
+ buffer.size());
+ ASSERT_EQ(decoder.size(), 7u);
+}
+
+TEST(BitVectorUnittest, SerializeDeserializeSimple) {
+ BitVector bv{1, 0, 1, 0, 1, 0, 1};
+ protozero::HeapBuffered<protos::pbzero::SerializedColumn::BitVector> msg;
+ bv.Serialize(msg.get());
+ auto buffer = msg.SerializeAsArray();
+
+ protos::pbzero::SerializedColumn::BitVector::Decoder decoder(buffer.data(),
+ buffer.size());
+
+ BitVector des;
+ des.Deserialize(decoder);
+
+ ASSERT_EQ(des.size(), 7u);
+ ASSERT_EQ(des.CountSetBits(), 4u);
+
+ ASSERT_TRUE(des.IsSet(0));
+ ASSERT_TRUE(des.IsSet(2));
+ ASSERT_TRUE(des.IsSet(4));
+ ASSERT_TRUE(des.IsSet(6));
+}
+
} // namespace
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index 067d768..6b02d28 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -44,7 +44,6 @@
"../util:glob",
"../util:regex",
"../util:util",
- "overlays",
"storage",
]
}
@@ -67,11 +66,12 @@
":view_unittest",
"../../../gn:default_deps",
"../../../gn:gtest_and_gmock",
+ "../../../include/perfetto/trace_processor:basic_types",
"../../base",
"../tables",
"../views",
- "overlays",
"storage",
+ "storage:fake_storage",
]
}
@@ -84,6 +84,7 @@
"../../../gn:default_deps",
"../../../include/perfetto/base",
"../../../include/perfetto/ext/base",
+ "../../../include/perfetto/trace_processor:basic_types",
"../../base:test_support",
"../tables:tables_python",
]
diff --git a/src/trace_processor/db/overlays/BUILD.gn b/src/trace_processor/db/overlays/BUILD.gn
deleted file mode 100644
index aad8e15..0000000
--- a/src/trace_processor/db/overlays/BUILD.gn
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import("../../../../gn/perfetto_tp_tables.gni")
-import("../../../../gn/test.gni")
-
-source_set("overlays") {
- sources = [
- "arrangement_overlay.cc",
- "arrangement_overlay.h",
- "null_overlay.cc",
- "null_overlay.h",
- "selector_overlay.cc",
- "selector_overlay.h",
- "storage_overlay.cc",
- "storage_overlay.h",
- "types.h",
- ]
- deps = [
- "../../../../gn:default_deps",
- "../../../base",
- "../../containers",
- "../storage",
- ]
-}
-
-perfetto_unittest_source_set("unittests") {
- testonly = true
- sources = [
- "arrangement_overlay_unittest.cc",
- "null_overlay_unittest.cc",
- "selector_overlay_unittest.cc",
- ]
- deps = [
- ":overlays",
- "../../../../gn:default_deps",
- "../../../../gn:gtest_and_gmock",
- ]
-}
diff --git a/src/trace_processor/db/overlays/arrangement_overlay.cc b/src/trace_processor/db/overlays/arrangement_overlay.cc
deleted file mode 100644
index 0ee1952..0000000
--- a/src/trace_processor/db/overlays/arrangement_overlay.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/arrangement_overlay.h"
-#include <iterator>
-#include "perfetto/ext/base/flat_hash_map.h"
-#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-using Range = RowMap::Range;
-
-StorageRange ArrangementOverlay::MapToStorageRange(TableRange t_range) const {
- PERFETTO_CHECK(t_range.range.end <= arrangement_->size());
- const auto [min, max] =
- std::minmax_element(arrangement_->data() + t_range.range.start,
- arrangement_->data() + t_range.range.end);
-
- return StorageRange(*min, *max + 1);
-}
-
-TableRangeOrBitVector ArrangementOverlay::MapToTableRangeOrBitVector(
- StorageRange s_range,
- OverlayOp) const {
- BitVector ret(static_cast<uint32_t>(arrangement_->size()), false);
- for (uint32_t i = 0; i < arrangement_->size(); ++i) {
- if (s_range.range.Contains((*arrangement_)[i]))
- ret.Set(i);
- }
- return TableRangeOrBitVector(std::move(ret));
-}
-
-TableBitVector ArrangementOverlay::MapToTableBitVector(StorageBitVector s_bv,
- OverlayOp) const {
- BitVector::Builder builder(static_cast<uint32_t>(arrangement_->size()));
- uint32_t cur_idx = 0;
-
- // Fast path: we compare as many groups of 64 elements as we can.
- // This should be very easy for the compiler to auto-vectorize.
- uint32_t fast_path_elements = builder.BitsInCompleteWordsUntilFull();
- for (uint32_t i = 0; i < fast_path_elements; i += BitVector::kBitsInWord) {
- uint64_t word = 0;
- // This part should be optimised by SIMD and is expected to be fast.
- for (uint32_t k = 0; k < BitVector::kBitsInWord; ++k, ++cur_idx) {
- bool comp_result = s_bv.bv.IsSet((*arrangement_)[cur_idx]);
- word |= static_cast<uint64_t>(comp_result) << k;
- }
- builder.AppendWord(word);
- }
-
- // Slow path: we compare <64 elements and append to fill the Builder.
- uint32_t back_elements = builder.BitsUntilFull();
- for (uint32_t i = 0; i < back_elements; ++i, ++cur_idx) {
- builder.Append(s_bv.bv.IsSet((*arrangement_)[cur_idx]));
- }
- return TableBitVector{std::move(builder).Build()};
-}
-
-BitVector ArrangementOverlay::IsStorageLookupRequired(
- OverlayOp,
- const TableIndexVector& t_iv) const {
- return BitVector(t_iv.size(), true);
-}
-
-StorageIndexVector ArrangementOverlay::MapToStorageIndexVector(
- TableIndexVector t_iv) const {
- std::vector<uint32_t> ret;
- for (const auto& i : t_iv.indices) {
- ret.push_back((*arrangement_)[i]);
- }
- return StorageIndexVector{ret};
-}
-
-BitVector ArrangementOverlay::IndexSearch(OverlayOp,
- const TableIndexVector&) const {
- PERFETTO_FATAL("IndexSearch should not be called inside ArrangementOverlay");
-}
-
-CostEstimatePerRow ArrangementOverlay::EstimateCostPerRow(OverlayOp) const {
- CostEstimatePerRow estimate;
- // Cost of std::min and std::max
- estimate.to_storage_range = 20;
- // Free
- estimate.to_table_bit_vector = 0;
- // Cost of creating trivial vector of 1s
- estimate.is_storage_search_required = 0;
- // Cost of a lookup inside |arrangement_|
- estimate.map_to_storage_index_vector = 10;
- // Shouldn't be called
- estimate.index_search = 0;
-
- return estimate;
-}
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/arrangement_overlay.h b/src/trace_processor/db/overlays/arrangement_overlay.h
deleted file mode 100644
index 1f590cb..0000000
--- a/src/trace_processor/db/overlays/arrangement_overlay.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_ARRANGEMENT_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_ARRANGEMENT_OVERLAY_H_
-
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-// Overlay responsible for arranging the elements of Storage. It deals with
-// duplicates, permutations and selection. For selection only it's more
-// efficient to use `SelectorOverlay`.
-class ArrangementOverlay : public StorageOverlay {
- public:
- explicit ArrangementOverlay(const std::vector<uint32_t>* arrangement)
- : arrangement_(std::move(arrangement)) {}
-
- StorageRange MapToStorageRange(TableRange) const override;
-
- TableRangeOrBitVector MapToTableRangeOrBitVector(StorageRange,
- OverlayOp) const override;
-
- TableBitVector MapToTableBitVector(StorageBitVector,
- OverlayOp) const override;
-
- BitVector IsStorageLookupRequired(OverlayOp,
- const TableIndexVector&) const override;
-
- StorageIndexVector MapToStorageIndexVector(TableIndexVector) const override;
-
- BitVector IndexSearch(OverlayOp, const TableIndexVector&) const override;
-
- CostEstimatePerRow EstimateCostPerRow(OverlayOp) const override;
-
- private:
- const std::vector<uint32_t>* arrangement_;
-};
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_ARRANGEMENT_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/arrangement_overlay_unittest.cc b/src/trace_processor/db/overlays/arrangement_overlay_unittest.cc
deleted file mode 100644
index 1418bab..0000000
--- a/src/trace_processor/db/overlays/arrangement_overlay_unittest.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/arrangement_overlay.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-namespace {
-
-TEST(ArrangementOverlay, MapToStorageRangeFirst) {
- std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
- ArrangementOverlay overlay(&arrangement);
- StorageRange r = overlay.MapToStorageRange(TableRange(2, 4));
-
- ASSERT_EQ(r.range.start, 2u);
- ASSERT_EQ(r.range.end, 3u);
-}
-
-TEST(ArrangementOverlay, MapToStorageRangeSecond) {
- std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
- ArrangementOverlay overlay(&arrangement);
- StorageRange r = overlay.MapToStorageRange(TableRange(5, 10));
-
- ASSERT_EQ(r.range.start, 1u);
- ASSERT_EQ(r.range.end, 5u);
-}
-
-TEST(ArrangementOverlay, MapToTableBitVector) {
- std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
- ArrangementOverlay overlay(&arrangement);
-
- BitVector storage_bv{0, 1, 0, 1, 0};
-
- // Table bv:
- // 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
- TableBitVector table_bv =
- overlay.MapToTableBitVector({std::move(storage_bv)}, OverlayOp::kOther);
-
- ASSERT_EQ(table_bv.bv.size(), 10u);
- ASSERT_EQ(table_bv.bv.CountSetBits(), 6u);
-
- ASSERT_TRUE(table_bv.bv.IsSet(0));
- ASSERT_TRUE(table_bv.bv.IsSet(1));
- ASSERT_TRUE(table_bv.bv.IsSet(4));
- ASSERT_TRUE(table_bv.bv.IsSet(5));
- ASSERT_TRUE(table_bv.bv.IsSet(8));
- ASSERT_TRUE(table_bv.bv.IsSet(9));
-}
-
-TEST(ArrangementOverlay, IsStorageLookupRequired) {
- std::vector<uint32_t> arrangement{0, 1, 1, 0, 0, 1, 1, 0};
- ArrangementOverlay overlay(&arrangement);
-
- std::vector<uint32_t> table_idx{0, 1, 2};
- BitVector lookup_bv =
- overlay.IsStorageLookupRequired(OverlayOp::kIsNull, {table_idx});
-
- ASSERT_EQ(lookup_bv.size(), 3u);
-}
-
-TEST(ArrangementOverlay, MapToStorageIndexVector) {
- std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
- ArrangementOverlay overlay(&arrangement);
-
- std::vector<uint32_t> table_idx{1, 3, 7};
- StorageIndexVector storage_iv = overlay.MapToStorageIndexVector({table_idx});
-
- std::vector<uint32_t> res{1, 2, 4};
- ASSERT_EQ(storage_iv.indices, res);
-}
-
-} // namespace
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/null_overlay.cc b/src/trace_processor/db/overlays/null_overlay.cc
deleted file mode 100644
index 7bc1b43..0000000
--- a/src/trace_processor/db/overlays/null_overlay.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/null_overlay.h"
-#include "perfetto/ext/base/flat_hash_map.h"
-#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-using Range = RowMap::Range;
-
-StorageRange NullOverlay::MapToStorageRange(TableRange t_range) const {
- uint32_t start = non_null_->CountSetBits(t_range.range.start);
- uint32_t end = non_null_->CountSetBits(t_range.range.end);
-
- return StorageRange(start, end);
-}
-
-TableRangeOrBitVector NullOverlay::MapToTableRangeOrBitVector(
- StorageRange s_range,
- OverlayOp op) const {
- PERFETTO_DCHECK(s_range.range.end <= non_null_->CountSetBits());
-
- BitVector range_to_bv(s_range.range.start, false);
- range_to_bv.Resize(s_range.range.end, true);
-
- return TableRangeOrBitVector(
- MapToTableBitVector(StorageBitVector{std::move(range_to_bv)}, op).bv);
-}
-
-TableBitVector NullOverlay::MapToTableBitVector(StorageBitVector s_bv,
- OverlayOp op) const {
- BitVector res = non_null_->Copy();
- res.UpdateSetBits(s_bv.bv);
-
- if (op != OverlayOp::kIsNull)
- return {std::move(res)};
-
- BitVector not_non_null = non_null_->Copy();
- not_non_null.Not();
-
- if (res.CountSetBits() == 0)
- return {std::move(not_non_null)};
-
- res.Or(not_non_null);
- return {std::move(res)};
-}
-
-BitVector NullOverlay::IsStorageLookupRequired(
- OverlayOp op,
- const TableIndexVector& t_iv) const {
- PERFETTO_DCHECK(t_iv.indices.size() <= non_null_->size());
-
- if (op != OverlayOp::kOther)
- return BitVector(t_iv.size(), false);
-
- BitVector in_storage(static_cast<uint32_t>(t_iv.indices.size()), false);
-
- // For each index in TableIndexVector check whether this index is in storage.
- for (uint32_t i = 0; i < t_iv.indices.size(); ++i) {
- if (non_null_->IsSet(t_iv.indices[i]))
- in_storage.Set(i);
- }
-
- return in_storage;
-}
-
-StorageIndexVector NullOverlay::MapToStorageIndexVector(
- TableIndexVector t_iv_with_idx_in_storage) const {
- PERFETTO_DCHECK(t_iv_with_idx_in_storage.indices.size() <=
- non_null_->CountSetBits());
-
- std::vector<uint32_t> storage_index_vector;
- storage_index_vector.reserve(t_iv_with_idx_in_storage.indices.size());
- for (auto t_idx : t_iv_with_idx_in_storage.indices) {
- storage_index_vector.push_back(non_null_->CountSetBits(t_idx));
- }
-
- return StorageIndexVector({std::move(storage_index_vector)});
-}
-
-BitVector NullOverlay::IndexSearch(
- OverlayOp op,
- const TableIndexVector& t_iv_overlay_idx) const {
- if (op == OverlayOp::kOther)
- return BitVector(t_iv_overlay_idx.size(), false);
-
- BitVector res(static_cast<uint32_t>(t_iv_overlay_idx.indices.size()), false);
- if (op == OverlayOp::kIsNull) {
- for (uint32_t i = 0; i < res.size(); ++i) {
- if (!non_null_->IsSet(t_iv_overlay_idx.indices[i]))
- res.Set(i);
- }
- return res;
- }
-
- PERFETTO_DCHECK(op == OverlayOp::kIsNotNull);
- for (uint32_t i = 0; i < res.size(); ++i) {
- if (non_null_->IsSet(t_iv_overlay_idx.indices[i]))
- res.Set(i);
- }
- return res;
-}
-
-CostEstimatePerRow NullOverlay::EstimateCostPerRow(OverlayOp op) const {
- // TODO(b/283763282): Replace with benchmarked data.
- CostEstimatePerRow res;
-
- // Two |BitVector::CountSetBits| calls.
- res.to_storage_range = 100;
-
- // Cost of |BitVector::UpdateSetBits|
- res.to_table_bit_vector = 100;
-
- if (op == OverlayOp::kOther) {
- // Cost of |BitVector::IsSet| and |BitVector::Set|
- res.is_storage_search_required = 10;
-
- // Cost of iterating all set bits and looping the index vector divided by
- // number of indices.
- res.map_to_storage_index_vector = 100;
-
- // Won't be called.
- res.index_search = 0;
- } else {
- // Cost of creating trivial BitVector.
- res.is_storage_search_required = 0;
-
- // Won't be called
- res.map_to_storage_index_vector = 0;
-
- // Cost of calling |BitVector::IsSet|
- res.index_search = 10;
- }
-
- return res;
-}
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/null_overlay.h b/src/trace_processor/db/overlays/null_overlay.h
deleted file mode 100644
index 5753fa3..0000000
--- a/src/trace_processor/db/overlays/null_overlay.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
-
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-// Introduces the layer of nullability - spreads out the storage with nulls
-// using BitVector.
-class NullOverlay : public StorageOverlay {
- public:
- explicit NullOverlay(const BitVector* null) : non_null_(std::move(null)) {}
-
- StorageRange MapToStorageRange(TableRange) const override;
-
- TableRangeOrBitVector MapToTableRangeOrBitVector(StorageRange,
- OverlayOp) const override;
-
- TableBitVector MapToTableBitVector(StorageBitVector,
- OverlayOp) const override;
-
- BitVector IsStorageLookupRequired(OverlayOp,
- const TableIndexVector&) const override;
-
- StorageIndexVector MapToStorageIndexVector(TableIndexVector) const override;
-
- BitVector IndexSearch(OverlayOp, const TableIndexVector&) const override;
-
- CostEstimatePerRow EstimateCostPerRow(OverlayOp) const override;
-
- private:
- // Non null data in the overlay.
- const BitVector* non_null_;
-};
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/null_overlay_unittest.cc b/src/trace_processor/db/overlays/null_overlay_unittest.cc
deleted file mode 100644
index aff9474..0000000
--- a/src/trace_processor/db/overlays/null_overlay_unittest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/null_overlay.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-namespace {
-
-TEST(NullOverlay, MapToStorageRangeOutsideBoundary) {
- BitVector bv{0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0};
- NullOverlay overlay(&bv);
- StorageRange r = overlay.MapToStorageRange(TableRange(1, 6));
-
- ASSERT_EQ(r.range.start, 0u);
- ASSERT_EQ(r.range.end, 2u);
-}
-
-TEST(NullOverlay, MapToStorageRangeOnBoundary) {
- BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
- NullOverlay overlay(&bv);
- StorageRange r = overlay.MapToStorageRange(TableRange(3, 8));
-
- ASSERT_EQ(r.range.start, 1u);
- ASSERT_EQ(r.range.end, 4u);
-}
-
-TEST(NullOverlay, MapToTableRangeOutsideBoundary) {
- BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
- NullOverlay overlay(&bv);
- auto r =
- overlay.MapToTableRangeOrBitVector(StorageRange(1, 3), OverlayOp::kOther);
-
- // All set bits between |bv| index 3 and 6.
- ASSERT_EQ(std::move(r).TakeIfBitVector().CountSetBits(), 2u);
-}
-
-TEST(NullOverlay, MapToTableRangeOnBoundary) {
- BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
- NullOverlay overlay(&bv);
- auto r =
- overlay.MapToTableRangeOrBitVector(StorageRange(0, 5), OverlayOp::kOther);
-
- ASSERT_EQ(std::move(r).TakeIfBitVector().CountSetBits(), 5u);
-}
-
-TEST(NullOverlay, MapToTableBitVector) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- BitVector storage_bv{0, 1, 0, 1};
- TableBitVector table_bv =
- overlay.MapToTableBitVector({std::move(storage_bv)}, OverlayOp::kOther);
-
- ASSERT_EQ(table_bv.bv.CountSetBits(), 2u);
- ASSERT_TRUE(table_bv.bv.IsSet(2));
- ASSERT_TRUE(table_bv.bv.IsSet(6));
-}
-
-TEST(NullOverlay, MapToTableBitVectorIsNull) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- BitVector storage_bv{0, 1, 0, 1};
- TableBitVector table_bv =
- overlay.MapToTableBitVector({std::move(storage_bv)}, OverlayOp::kIsNull);
-
- // Result is all of the zeroes from |bv| and set bits from |storage_bv|
- // 1, 0, 1, 1, 1, 0, 1, 1
-
- ASSERT_EQ(table_bv.bv.CountSetBits(), 6u);
- ASSERT_FALSE(table_bv.bv.IsSet(1));
- ASSERT_FALSE(table_bv.bv.IsSet(5));
-}
-
-TEST(NullOverlay, IsStorageLookupRequiredNullOp) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{0, 2, 4, 6};
- BitVector lookup_bv =
- overlay.IsStorageLookupRequired(OverlayOp::kIsNull, {table_idx});
-
- ASSERT_EQ(lookup_bv.CountSetBits(), 0u);
-}
-
-TEST(NullOverlay, IsStorageLookupRequiredOtherOp) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{0, 2, 4, 6};
- BitVector lookup_bv =
- overlay.IsStorageLookupRequired(OverlayOp::kOther, {table_idx});
-
- ASSERT_EQ(lookup_bv.size(), 4u);
- ASSERT_EQ(lookup_bv.CountSetBits(), 2u);
- ASSERT_TRUE(lookup_bv.IsSet(1));
- ASSERT_TRUE(lookup_bv.IsSet(3));
-}
-
-TEST(NullOverlay, MapToStorageIndexVector) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{1, 5, 2};
- StorageIndexVector storage_iv = overlay.MapToStorageIndexVector({table_idx});
-
- std::vector<uint32_t> res{0, 2, 1};
- ASSERT_EQ(storage_iv.indices, res);
-}
-
-TEST(NullOverlay, IndexSearchOtherOp) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{0, 3, 4};
- BitVector idx_search_bv = overlay.IndexSearch(OverlayOp::kOther, {table_idx});
-
- ASSERT_EQ(idx_search_bv.CountSetBits(), 0u);
-}
-
-TEST(NullOverlay, IndexSearchIsNullOp) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{0, 3, 4};
- BitVector idx_search_bv =
- overlay.IndexSearch(OverlayOp::kIsNull, {table_idx});
-
- ASSERT_EQ(idx_search_bv.size(), 3u);
- ASSERT_EQ(idx_search_bv.CountSetBits(), 3u);
-}
-
-TEST(NullOverlay, IndexSearchIsNotNullOp) {
- BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
- NullOverlay overlay(&bv);
-
- std::vector<uint32_t> table_idx{0, 3, 4};
- BitVector idx_search_bv =
- overlay.IndexSearch(OverlayOp::kIsNotNull, {table_idx});
-
- ASSERT_EQ(idx_search_bv.size(), 3u);
- ASSERT_EQ(idx_search_bv.CountSetBits(), 0u);
-}
-
-} // namespace
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/selector_overlay.cc b/src/trace_processor/db/overlays/selector_overlay.cc
deleted file mode 100644
index bf31071..0000000
--- a/src/trace_processor/db/overlays/selector_overlay.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/selector_overlay.h"
-#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-using Range = RowMap::Range;
-
-StorageRange SelectorOverlay::MapToStorageRange(TableRange t_range) const {
- // Table data is smaller than Storage, so we need to expand the data.
- return StorageRange{
- Range(selected_->IndexOfNthSet(t_range.range.start),
- selected_->IndexOfNthSet(t_range.range.end - 1) + 1)};
-}
-
-TableRangeOrBitVector SelectorOverlay::MapToTableRangeOrBitVector(
- StorageRange s_range,
- OverlayOp) const {
- if (s_range.range.size() == 0)
- return TableRangeOrBitVector(Range());
-
- uint32_t start = selected_->CountSetBits(s_range.range.start);
- uint32_t end = selected_->CountSetBits(s_range.range.end);
-
- return TableRangeOrBitVector(Range(start, end));
-}
-
-TableBitVector SelectorOverlay::MapToTableBitVector(StorageBitVector s_bv,
- OverlayOp) const {
- PERFETTO_DCHECK(s_bv.bv.size() <= selected_->size());
- BitVector res(selected_->CountSetBits());
- // TODO(b/283763282): Implement this variation of |UpdateSetBits| in
- // BitVector.
- for (auto it = selected_->IterateSetBits(); it && it.index() < s_bv.bv.size();
- it.Next()) {
- if (s_bv.bv.IsSet(it.index()))
- res.Set(it.ordinal());
- }
- return TableBitVector({std::move(res)});
-}
-
-BitVector SelectorOverlay::IsStorageLookupRequired(
- OverlayOp,
- const TableIndexVector& t_iv) const {
- return BitVector(static_cast<uint32_t>(t_iv.indices.size()), true);
-}
-
-StorageIndexVector SelectorOverlay::MapToStorageIndexVector(
- TableIndexVector t_iv) const {
- PERFETTO_DCHECK(t_iv.indices.empty() ||
- *std::max_element(t_iv.indices.begin(), t_iv.indices.end()) <=
- selected_->size());
- // To go from TableIndexVector to StorageIndexVector we need to find index in
- // |selector_| by looking only into set bits.
- std::vector<uint32_t> s_iv;
- s_iv.reserve(t_iv.indices.size());
- for (auto t_idx : t_iv.indices) {
- s_iv.push_back(selected_->IndexOfNthSet(t_idx));
- }
-
- return StorageIndexVector({std::move(s_iv)});
-}
-
-BitVector SelectorOverlay::IndexSearch(OverlayOp,
- const TableIndexVector&) const {
- // |t_iv| doesn't contain any values that are null in |selected_| as other
- // overlays are not able to access them. This function should not be called.
- PERFETTO_FATAL("Should not be called in SelectorOverlay.");
-}
-
-CostEstimatePerRow SelectorOverlay::EstimateCostPerRow(OverlayOp) const {
- CostEstimatePerRow estimate;
- // Cost of two |IndexOfNthSet|
- estimate.to_storage_range = 20;
- // Cost of iterating over all selected bits and calling |IsSet| each time (and
- // |Set| if true)
- estimate.to_table_bit_vector = 100;
- // Cost of creating trivial vector of 1s
- estimate.is_storage_search_required = 0;
- // Cost of |IndexOfNthSet| for each row
- estimate.map_to_storage_index_vector = 10;
- // Shouldn't be called
- estimate.index_search = 0;
-
- return estimate;
-}
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/selector_overlay.h b/src/trace_processor/db/overlays/selector_overlay.h
deleted file mode 100644
index ac591d3..0000000
--- a/src/trace_processor/db/overlays/selector_overlay.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_SELECTOR_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_SELECTOR_OVERLAY_H_
-
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-// Overlay responsible for selecting specific rows from Storage.
-class SelectorOverlay : public StorageOverlay {
- public:
- explicit SelectorOverlay(const BitVector* selected) : selected_(selected) {}
-
- StorageRange MapToStorageRange(TableRange) const override;
-
- TableRangeOrBitVector MapToTableRangeOrBitVector(StorageRange,
- OverlayOp) const override;
-
- TableBitVector MapToTableBitVector(StorageBitVector,
- OverlayOp) const override;
-
- BitVector IsStorageLookupRequired(OverlayOp,
- const TableIndexVector&) const override;
-
- StorageIndexVector MapToStorageIndexVector(TableIndexVector) const override;
-
- BitVector IndexSearch(OverlayOp, const TableIndexVector&) const override;
-
- CostEstimatePerRow EstimateCostPerRow(OverlayOp) const override;
-
- private:
- const BitVector* selected_;
-};
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_SELECTOR_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/selector_overlay_unittest.cc b/src/trace_processor/db/overlays/selector_overlay_unittest.cc
deleted file mode 100644
index 28b4f02..0000000
--- a/src/trace_processor/db/overlays/selector_overlay_unittest.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/selector_overlay.h"
-#include "src/trace_processor/db/overlays/types.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-namespace {
-
-TEST(SelectorOverlay, MapToStorageRangeFirst) {
- BitVector selector{0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1};
- SelectorOverlay overlay(&selector);
- StorageRange r = overlay.MapToStorageRange(TableRange(1, 4));
-
- ASSERT_EQ(r.range.start, 4u);
- ASSERT_EQ(r.range.end, 8u);
-}
-
-TEST(SelectorOverlay, MapToStorageRangeSecond) {
- BitVector selector{0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0};
- SelectorOverlay overlay(&selector);
- StorageRange r = overlay.MapToStorageRange(TableRange(1, 3));
-
- ASSERT_EQ(r.range.start, 4u);
- ASSERT_EQ(r.range.end, 7u);
-}
-
-TEST(SelectorOverlay, MapToTableRangeFirst) {
- BitVector selector{0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1};
- SelectorOverlay overlay(&selector);
- auto r =
- overlay.MapToTableRangeOrBitVector(StorageRange(2, 5), OverlayOp::kOther);
-
- Range range = std::move(r).TakeIfRange();
- ASSERT_EQ(range.start, 1u);
- ASSERT_EQ(range.end, 3u);
-}
-
-TEST(SelectorOverlay, MapToTableRangeSecond) {
- BitVector selector{0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0};
- SelectorOverlay overlay(&selector);
- auto r = overlay.MapToTableRangeOrBitVector(StorageRange(0, 10),
- OverlayOp::kOther);
-
- Range range = std::move(r).TakeIfRange();
- ASSERT_EQ(range.start, 0u);
- ASSERT_EQ(range.end, 6u);
-}
-
-TEST(SelectorOverlay, MapToTableBitVector) {
- BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
- SelectorOverlay overlay(&selector);
-
- BitVector storage_bv{1, 0, 1, 0, 1, 0, 1, 0};
- TableBitVector table_bv =
- overlay.MapToTableBitVector({std::move(storage_bv)}, OverlayOp::kOther);
-
- ASSERT_EQ(table_bv.bv.size(), 4u);
- ASSERT_EQ(table_bv.bv.CountSetBits(), 2u);
- ASSERT_TRUE(table_bv.bv.IsSet(1));
- ASSERT_TRUE(table_bv.bv.IsSet(3));
-}
-
-TEST(SelectorOverlay, IsStorageLookupRequired) {
- BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
- SelectorOverlay overlay(&selector);
-
- std::vector<uint32_t> table_idx{0, 1, 2};
- BitVector lookup_bv =
- overlay.IsStorageLookupRequired(OverlayOp::kIsNull, {table_idx});
-
- ASSERT_EQ(lookup_bv.size(), 3u);
-}
-
-TEST(SelectorOverlay, MapToStorageIndexVector) {
- BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
- SelectorOverlay overlay(&selector);
-
- std::vector<uint32_t> table_idx{1, 3, 2};
- StorageIndexVector storage_iv = overlay.MapToStorageIndexVector({table_idx});
-
- std::vector<uint32_t> res{2, 6, 5};
- ASSERT_EQ(storage_iv.indices, res);
-}
-
-} // namespace
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/storage_overlay.cc b/src/trace_processor/db/overlays/storage_overlay.cc
deleted file mode 100644
index 56897f2..0000000
--- a/src/trace_processor/db/overlays/storage_overlay.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-StorageOverlay::~StorageOverlay() = default;
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/overlays/storage_overlay.h b/src/trace_processor/db/overlays/storage_overlay.h
deleted file mode 100644
index d6ad8df..0000000
--- a/src/trace_processor/db/overlays/storage_overlay.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_STORAGE_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_STORAGE_OVERLAY_H_
-
-#include "src/trace_processor/db/overlays/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-// Abstract class which is layered on top of Storage transforming how the
-// storage should be interpreted. The main purpose of this class is to be
-// responsible for for mapping between table indices and storage indices (i.e.
-// in both directions).
-//
-// Overlays are designed to be "layered" on top of each other (i.e. the mapping
-// algorithms compose). To make it easier to reason about this class, we
-// ignore any other overlays and assume we are mapping directly between table
-// indices and storage indices. i.e. even if "table indices" we are working with
-// come from another overlay, we still consider them as having come from the
-// table and vice versa for "storage indices".
-//
-// The core functions in this class work with input and output arguments which
-// use the same data structure but have different semantics (i.e. input might
-// be in terms of storage indices and output might be in terms of table
-// indices).
-//
-// For this reason, we use the defined wrapper structs which "tag" the data
-// structure with the semantics.
-class StorageOverlay {
- public:
- virtual ~StorageOverlay();
-
- // Maps a range of indices in table space to an equivalent range of
- // indices in the storage space.
- virtual StorageRange MapToStorageRange(TableRange) const = 0;
-
- // Returns the smallest Range or BitVector containing all of the elements
- // matching the OverlayOp.
- virtual TableRangeOrBitVector MapToTableRangeOrBitVector(StorageRange,
- OverlayOp) const = 0;
-
- // Maps a BitVector of indices in storage space to an equivalent range of
- // indices in the table space.
- virtual TableBitVector MapToTableBitVector(StorageBitVector,
- OverlayOp) const = 0;
-
- // Returns a BitVector where each boolean indicates if the corresponding index
- // in |indices| needs to be mapped and searched in the storage or if the
- // overlay can provide the answer without storage lookup.
- virtual BitVector IsStorageLookupRequired(OverlayOp,
- const TableIndexVector&) const = 0;
-
- // Maps a vector of indices in the table space with an equivalent range
- // of indices in the storage space.
- //
- // Note: callers must call |IsStorageSearchRequired| first and only call
- // this method with indices where |IsStorageSearchRequired| returned true.
- // Passing indices here which are not mappable is undefined behaviour.
- virtual StorageIndexVector MapToStorageIndexVector(
- TableIndexVector) const = 0;
-
- // Given a vector of indices given in table space, returns whether the index
- // matches the operation given by |op|.
- //
- // Note: callers must call |IsStorageSearchRequired| first and only call
- // this method with indices where |IsStorageSearchRequired| returned false.
- // Passing indices here which are not searchable is undefined behaviour.
- virtual BitVector IndexSearch(OverlayOp, const TableIndexVector&) const = 0;
-
- // Estimates the per-row costs of the methods of this class. Allows for
- // deciding which algorithm to use to search/sort the storage.
- virtual CostEstimatePerRow EstimateCostPerRow(OverlayOp) const = 0;
-};
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_STORAGE_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/types.h b/src/trace_processor/db/overlays/types.h
deleted file mode 100644
index 8975084..0000000
--- a/src/trace_processor/db/overlays/types.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_TYPES_H_
-#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_TYPES_H_
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/db/storage/types.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace overlays {
-
-using Range = RowMap::Range;
-
-// A range of indices in the table space.
-struct TableRange {
- TableRange(uint32_t start, uint32_t end) : range(start, end) {}
- explicit TableRange(Range r) : range(r) {}
-
- Range range;
-};
-
-// A range of indices in the storage space.
-struct StorageRange {
- StorageRange(uint32_t start, uint32_t end) : range(start, end) {}
- explicit StorageRange(Range r) : range(r) {}
-
- Range range;
-};
-
-// A BitVector with set bits corresponding to indices in the table space.
-struct TableBitVector {
- BitVector bv;
-};
-
-// A BitVector with set bits corresponding to indices in the table space.
-struct StorageBitVector {
- BitVector bv;
-};
-
-// RangeOrBitVector of indices in the table space.
-struct TableRangeOrBitVector {
- explicit TableRangeOrBitVector(Range range) : val(range) {}
- explicit TableRangeOrBitVector(BitVector bv) : val(std::move(bv)) {}
- explicit TableRangeOrBitVector(RangeOrBitVector r_or_bv)
- : val(std::move(r_or_bv)) {}
-
- bool IsRange() const { return val.IsRange(); }
- bool IsBitVector() const { return val.IsBitVector(); }
-
- BitVector TakeIfBitVector() && { return std::move(val).TakeIfBitVector(); }
- Range TakeIfRange() && { return std::move(val).TakeIfRange(); }
-
- RangeOrBitVector val = RangeOrBitVector(Range());
-};
-
-// Represents a vector of indices in the table space.
-struct TableIndexVector {
- std::vector<uint32_t> indices;
-
- uint32_t size() const { return static_cast<uint32_t>(indices.size()); }
-};
-
-// Represents a vector of indices in the storage space.
-struct StorageIndexVector {
- std::vector<uint32_t> indices;
-
- uint32_t size() const { return static_cast<uint32_t>(indices.size()); }
-};
-
-// A subset of FilterOp containing operations which can be handled by
-// overlays.
-enum class OverlayOp {
- kIsNull,
- kIsNotNull,
- kOther,
-};
-
-inline OverlayOp FilterOpToOverlayOp(FilterOp op) {
- if (op == FilterOp::kIsNull) {
- return OverlayOp::kIsNull;
- }
- if (op == FilterOp::kIsNotNull) {
- return OverlayOp::kIsNotNull;
- }
- return OverlayOp::kOther;
-}
-
-// Contains estimates of the cost for each of method in this class per row.
-struct CostEstimatePerRow {
- uint32_t to_storage_range;
- uint32_t to_table_bit_vector;
- uint32_t is_storage_search_required;
- uint32_t map_to_storage_index_vector;
- uint32_t index_search;
-};
-
-} // namespace overlays
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_OVERLAYS_TYPES_H_
diff --git a/src/trace_processor/db/query_executor.cc b/src/trace_processor/db/query_executor.cc
index 21e4c7c..046aee4 100644
--- a/src/trace_processor/db/query_executor.cc
+++ b/src/trace_processor/db/query_executor.cc
@@ -24,16 +24,16 @@
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/string_pool.h"
-#include "src/trace_processor/db/overlays/arrangement_overlay.h"
-#include "src/trace_processor/db/overlays/null_overlay.h"
-#include "src/trace_processor/db/overlays/selector_overlay.h"
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-#include "src/trace_processor/db/overlays/types.h"
#include "src/trace_processor/db/query_executor.h"
+#include "src/trace_processor/db/storage/arrangement_storage.h"
+#include "src/trace_processor/db/storage/dense_null_storage.h"
#include "src/trace_processor/db/storage/dummy_storage.h"
#include "src/trace_processor/db/storage/id_storage.h"
+#include "src/trace_processor/db/storage/null_storage.h"
#include "src/trace_processor/db/storage/numeric_storage.h"
+#include "src/trace_processor/db/storage/selector_storage.h"
#include "src/trace_processor/db/storage/set_id_storage.h"
#include "src/trace_processor/db/storage/string_storage.h"
#include "src/trace_processor/db/storage/types.h"
@@ -45,96 +45,25 @@
namespace {
using Range = RowMap::Range;
-using OverlayOp = overlays::OverlayOp;
-using StorageRange = overlays::StorageRange;
-using TableRange = overlays::TableRange;
using Storage = storage::Storage;
-using StorageOverlay = overlays::StorageOverlay;
-using TableIndexVector = overlays::TableIndexVector;
-using StorageIndexVector = overlays::StorageIndexVector;
-using TableBitVector = overlays::TableBitVector;
-using StorageBitVector = overlays::StorageBitVector;
-using OverlaysVec = base::SmallVector<const overlays::StorageOverlay*,
- QueryExecutor::kMaxOverlayCount>;
-// Helper struct to simplify operations on |global| and |current| sets of
-// indices. Having this coupling enables efficient implementation of
-// IndexedColumnFilter.
-struct IndexFilterHelper {
- explicit IndexFilterHelper(std::vector<uint32_t> indices) {
- current_ = indices;
- global_ = std::move(indices);
- }
-
- // Removes pairs of elements that are not set in the |bv| and returns
- // Indices made of them.
- static std::pair<IndexFilterHelper, IndexFilterHelper> Partition(
- IndexFilterHelper indices,
- const BitVector& bv) {
- if (bv.CountSetBits() == 0) {
- return {IndexFilterHelper(), indices};
- }
-
- IndexFilterHelper set_partition;
- IndexFilterHelper non_set_partition;
- for (auto it = bv.IterateAllBits(); it; it.Next()) {
- uint32_t idx = it.index();
- if (it.IsSet()) {
- set_partition.PushBack({indices.current_[idx], indices.global_[idx]});
- } else {
- non_set_partition.PushBack(
- {indices.current_[idx], indices.global_[idx]});
- }
- }
- return {set_partition, non_set_partition};
- }
-
- // Removes pairs of elements that are not set in the |bv|. Returns count of
- // removed elements.
- uint32_t KeepAtSet(BitVector filter_nulls) {
- PERFETTO_DCHECK(filter_nulls.size() == current_.size() ||
- filter_nulls.CountSetBits() == 0);
- uint32_t count_removed =
- static_cast<uint32_t>(current_.size()) - filter_nulls.CountSetBits();
-
- uint32_t i = 0;
- auto filter = [&i, &filter_nulls](uint32_t) {
- return !filter_nulls.IsSet(i++);
- };
-
- auto current_it = std::remove_if(current_.begin(), current_.end(), filter);
- current_.erase(current_it, current_.end());
-
- i = 0;
- auto global_it = std::remove_if(global_.begin(), global_.end(), filter);
- global_.erase(global_it, global_.end());
-
- return count_removed;
- }
-
- std::vector<uint32_t>& current() { return current_; }
-
- std::vector<uint32_t>& global() { return global_; }
-
- private:
- IndexFilterHelper() = default;
-
- void PushBack(std::pair<uint32_t, uint32_t> cur_and_global_idx) {
- current_.push_back(cur_and_global_idx.first);
- global_.push_back(cur_and_global_idx.second);
- }
-
- std::vector<uint32_t> current_;
- std::vector<uint32_t> global_;
-};
} // namespace
void QueryExecutor::FilterColumn(const Constraint& c,
- const SimpleColumn& col,
+ const storage::Storage& storage,
RowMap* rm) {
+ // Shortcut of empty row map.
if (rm->empty())
return;
+ // Comparison of NULL with any operation apart from |IS_NULL| and
+ // |IS_NOT_NULL| should return no rows.
+ if (c.value.is_null() && c.op != FilterOp::kIsNull &&
+ c.op != FilterOp::kIsNotNull) {
+ rm->Clear();
+ return;
+ }
+
uint32_t rm_size = rm->size();
uint32_t rm_first = rm->Get(0);
uint32_t rm_last = rm->Get(rm_size - 1);
@@ -146,43 +75,22 @@
bool disallows_index_search = rm->IsRange();
bool prefers_index_search =
rm->IsIndexVector() || rm_size < 1024 || rm_size * 10 < range_size;
+
if (!disallows_index_search && prefers_index_search) {
- *rm = IndexSearch(c, col, rm);
+ IndexSearch(c, storage, rm);
return;
}
- LinearSearch(c, col, rm);
+ LinearSearch(c, storage, rm);
}
void QueryExecutor::LinearSearch(const Constraint& c,
- const SimpleColumn& col,
+ const storage::Storage& storage,
RowMap* rm) {
// TODO(b/283763282): Align these to word boundaries.
- TableRange bounds{Range(rm->Get(0), rm->Get(rm->size() - 1) + 1)};
-
- // Translate the bounds to the storage level.
- for (const auto& overlay : col.overlays) {
- bounds = TableRange({overlay->MapToStorageRange(bounds).range});
- }
+ Range bounds(rm->Get(0), rm->Get(rm->size() - 1) + 1);
// Search the storage.
- overlays::TableRangeOrBitVector res(
- col.storage->Search(c.op, c.value, bounds.range));
-
- // Translate the result to global level.
- OverlayOp op = overlays::FilterOpToOverlayOp(c.op);
- for (uint32_t i = 0; i < col.overlays.size(); ++i) {
- uint32_t rev_i = static_cast<uint32_t>(col.overlays.size()) - 1 - i;
-
- if (res.IsBitVector()) {
- TableBitVector t_bv = col.overlays[rev_i]->MapToTableBitVector(
- StorageBitVector{std::move(res).TakeIfBitVector()}, op);
- res.val = RangeOrBitVector(std::move(t_bv.bv));
- } else {
- res = col.overlays[rev_i]->MapToTableRangeOrBitVector(
- StorageRange(std::move(res).TakeIfRange()), op);
- }
- }
-
+ RangeOrBitVector res = storage.Search(c.op, c.value, bounds);
if (rm->IsRange()) {
if (res.IsRange()) {
Range range = std::move(res).TakeIfRange();
@@ -203,75 +111,44 @@
rm->Intersect(RowMap(std::move(res).TakeIfBitVector()));
}
-RowMap QueryExecutor::IndexSearch(const Constraint& c,
- const SimpleColumn& col,
- RowMap* rm) {
+void QueryExecutor::IndexSearch(const Constraint& c,
+ const storage::Storage& storage,
+ RowMap* rm) {
// Create outmost TableIndexVector.
std::vector<uint32_t> table_indices = std::move(*rm).TakeAsIndexVector();
- // Datastructures for storing data across overlays.
- IndexFilterHelper to_filter(std::move(table_indices));
- std::vector<uint32_t> matched;
- uint32_t count_removed = 0;
- uint32_t count_starting_indices =
- static_cast<uint32_t>(to_filter.current().size());
+ RangeOrBitVector matched = storage.IndexSearch(
+ c.op, c.value, table_indices.data(),
+ static_cast<uint32_t>(table_indices.size()), false /* sorted */);
- // Fetch the list of indices that require storage lookup and deal with all
- // of the indices that can be compared before it.
- OverlayOp op = overlays::FilterOpToOverlayOp(c.op);
- for (const auto& overlay : col.overlays) {
- BitVector partition =
- overlay->IsStorageLookupRequired(op, {to_filter.current()});
-
- // Most overlays don't require partitioning.
- if (partition.CountSetBits() == partition.size()) {
- to_filter.current() =
- overlay->MapToStorageIndexVector({to_filter.current()}).indices;
- continue;
- }
-
- // Separate indices that don't require storage lookup. Those can be dealt
- // with in each pass.
- auto [storage_lookup, no_storage_lookup] =
- IndexFilterHelper::Partition(to_filter, partition);
- to_filter = storage_lookup;
-
- // Erase the values which don't match the constraint and add the
- // remaining ones to the result.
- BitVector valid_bv =
- overlay->IndexSearch(op, {no_storage_lookup.current()});
- count_removed += no_storage_lookup.KeepAtSet(std::move(valid_bv));
- matched.insert(matched.end(), no_storage_lookup.global().begin(),
- no_storage_lookup.global().end());
-
- // Update the current indices to the next storage overlay.
- to_filter.current() =
- overlay->MapToStorageIndexVector({to_filter.current()}).indices;
+ if (matched.IsBitVector()) {
+ BitVector res = std::move(matched).TakeIfBitVector();
+ uint32_t i = 0;
+ table_indices.erase(
+ std::remove_if(table_indices.begin(), table_indices.end(),
+ [&i, &res](uint32_t) { return !res.IsSet(i++); }),
+ table_indices.end());
+ *rm = RowMap(std::move(table_indices));
+ return;
}
- RangeOrBitVector matched_in_storage = col.storage->IndexSearch(
- c.op, c.value, to_filter.current().data(),
- static_cast<uint32_t>(to_filter.current().size()));
-
+ Range res = std::move(matched).TakeIfRange();
+ if (res.size() == 0) {
+ rm->Clear();
+ return;
+ }
+ if (res.size() == table_indices.size()) {
+ return;
+ }
// TODO(b/283763282): Remove after implementing extrinsic binary search.
- PERFETTO_DCHECK(matched_in_storage.IsBitVector());
-
- count_removed +=
- to_filter.KeepAtSet(std::move(matched_in_storage).TakeIfBitVector());
- matched.insert(matched.end(), to_filter.global().begin(),
- to_filter.global().end());
-
- PERFETTO_CHECK(count_starting_indices == matched.size() + count_removed);
-
- std::sort(matched.begin(), matched.end());
- return RowMap(std::move(matched));
+ PERFETTO_FATAL("Extrinsic binary search is not implemented.");
}
RowMap QueryExecutor::FilterLegacy(const Table* table,
const std::vector<Constraint>& c_vec) {
RowMap rm(0, table->row_count());
for (const auto& c : c_vec) {
- if (rm.size() == 0) {
+ if (rm.empty()) {
return rm;
}
const Column& col = table->columns()[c.col_idx];
@@ -285,13 +162,14 @@
use_legacy = use_legacy || (col.overlay().size() != column_size &&
col.overlay().row_map().IsRange());
- // Mismatched types.
- use_legacy = use_legacy || (overlays::FilterOpToOverlayOp(c.op) ==
- overlays::OverlayOp::kOther &&
- col.type() != c.value.type);
-
- // Dense columns.
- use_legacy = use_legacy || col.IsDense();
+ // Comparing ints with doubles and doubles with ints.
+ bool int_with_double =
+ col.type() == SqlValue::kLong && c.value.type == SqlValue::kDouble;
+ bool double_with_int =
+ col.type() == SqlValue::kDouble && c.value.type == SqlValue::kLong;
+ use_legacy = use_legacy ||
+ (c.op != FilterOp::kIsNull && c.op != FilterOp::kIsNotNull &&
+ (int_with_double || double_with_int));
// Extrinsically sorted columns.
use_legacy = use_legacy ||
@@ -302,108 +180,97 @@
continue;
}
- // String columns are inherently nullable: null values are signified with
- // Id::Null().
- PERFETTO_CHECK(
- !(col.col_type() == ColumnType::kString && col.IsNullable()));
-
- SimpleColumn s_col{OverlaysVec(), nullptr};
-
// Create storage
std::unique_ptr<Storage> storage;
if (col.IsSetId()) {
if (col.IsNullable()) {
- storage.reset(new storage::SetIdStorage(
- &col.storage<std::optional<uint32_t>>().non_null_vector()));
+ storage = std::make_unique<storage::SetIdStorage>(
+ &col.storage<std::optional<uint32_t>>().non_null_vector());
} else {
- storage.reset(
- new storage::SetIdStorage(&col.storage<uint32_t>().vector()));
+ storage = std::make_unique<storage::SetIdStorage>(
+ &col.storage<uint32_t>().vector());
}
} else {
switch (col.col_type()) {
case ColumnType::kDummy:
- storage.reset(new storage::DummyStorage());
+ storage = std::make_unique<storage::DummyStorage>();
break;
case ColumnType::kId:
- storage.reset(new storage::IdStorage(column_size));
+ storage = std::make_unique<storage::IdStorage>(column_size);
break;
case ColumnType::kString:
- storage.reset(new storage::StringStorage(
+ storage = std::make_unique<storage::StringStorage>(
table->string_pool(), &col.storage<StringPool::Id>().vector(),
- col.IsSorted()));
+ col.IsSorted());
break;
case ColumnType::kInt64:
if (col.IsNullable()) {
- storage.reset(new storage::NumericStorage<int64_t>(
+ storage = std::make_unique<storage::NumericStorage<int64_t>>(
&col.storage<std::optional<int64_t>>().non_null_vector(),
- col.col_type(), col.IsSorted()));
+ col.col_type(), col.IsSorted());
} else {
- storage.reset(new storage::NumericStorage<int64_t>(
+ storage = std::make_unique<storage::NumericStorage<int64_t>>(
&col.storage<int64_t>().vector(), col.col_type(),
- col.IsSorted()));
+ col.IsSorted());
}
break;
case ColumnType::kUint32:
if (col.IsNullable()) {
- storage.reset(new storage::NumericStorage<uint32_t>(
+ storage = std::make_unique<storage::NumericStorage<uint32_t>>(
&col.storage<std::optional<uint32_t>>().non_null_vector(),
- col.col_type(), col.IsSorted()));
-
+ col.col_type(), col.IsSorted());
} else {
- storage.reset(new storage::NumericStorage<uint32_t>(
+ storage = std::make_unique<storage::NumericStorage<uint32_t>>(
&col.storage<uint32_t>().vector(), col.col_type(),
- col.IsSorted()));
+ col.IsSorted());
}
break;
case ColumnType::kInt32:
if (col.IsNullable()) {
- storage.reset(new storage::NumericStorage<int32_t>(
+ storage = std::make_unique<storage::NumericStorage<int32_t>>(
&col.storage<std::optional<int32_t>>().non_null_vector(),
- col.col_type(), col.IsSorted()));
-
+ col.col_type(), col.IsSorted());
} else {
- storage.reset(new storage::NumericStorage<int32_t>(
+ storage = std::make_unique<storage::NumericStorage<int32_t>>(
&col.storage<int32_t>().vector(), col.col_type(),
- col.IsSorted()));
+ col.IsSorted());
}
break;
case ColumnType::kDouble:
if (col.IsNullable()) {
- storage.reset(new storage::NumericStorage<double_t>(
- &col.storage<std::optional<double_t>>().non_null_vector(),
- col.col_type(), col.IsSorted()));
-
+ storage = std::make_unique<storage::NumericStorage<double>>(
+ &col.storage<std::optional<double>>().non_null_vector(),
+ col.col_type(), col.IsSorted());
} else {
- storage.reset(new storage::NumericStorage<double_t>(
- &col.storage<double_t>().vector(), col.col_type(),
- col.IsSorted()));
+ storage = std::make_unique<storage::NumericStorage<double>>(
+ &col.storage<double>().vector(), col.col_type(),
+ col.IsSorted());
}
}
}
- s_col.storage = storage.get();
-
- // Create cEngine overlays based on col.overlay()
- overlays::SelectorOverlay selector_overlay(
- col.overlay().row_map().GetIfBitVector());
- if (col.overlay().size() != column_size &&
- col.overlay().row_map().IsBitVector())
- s_col.overlays.emplace_back(&selector_overlay);
-
- overlays::ArrangementOverlay arrangement_overlay(
- col.overlay().row_map().GetIfIndexVector());
- if (col.overlay().row_map().IsIndexVector())
- s_col.overlays.emplace_back(&arrangement_overlay);
-
- // Add nullability
- BitVector null_bv;
- overlays::NullOverlay null_overlay(
- col.IsNullable() ? col.storage_base().bv() : &null_bv);
- if (col.IsNullable())
- s_col.overlays.emplace_back(&null_overlay);
-
+ if (col.IsNullable()) {
+ // String columns are inherently nullable: null values are signified
+ // with Id::Null().
+ PERFETTO_CHECK(col.col_type() != ColumnType::kString);
+ if (col.IsDense()) {
+ storage = std::make_unique<storage::DenseNullStorage>(
+ std::move(storage), col.storage_base().bv());
+ } else {
+ storage = std::make_unique<storage::NullStorage>(
+ std::move(storage), col.storage_base().bv());
+ }
+ }
+ if (col.overlay().row_map().IsIndexVector()) {
+ storage = std::make_unique<storage::ArrangementStorage>(
+ std::move(storage), col.overlay().row_map().GetIfIndexVector());
+ }
+ if (col.overlay().row_map().IsBitVector()) {
+ storage = std::make_unique<storage::SelectorStorage>(
+ std::move(storage), col.overlay().row_map().GetIfBitVector());
+ }
uint32_t pre_count = rm.size();
- FilterColumn(c, s_col, &rm);
+ FilterColumn(c, *storage.get(), &rm);
PERFETTO_DCHECK(rm.size() <= pre_count);
}
return rm;
diff --git a/src/trace_processor/db/query_executor.h b/src/trace_processor/db/query_executor.h
index fd566a6..013dbf2 100644
--- a/src/trace_processor/db/query_executor.h
+++ b/src/trace_processor/db/query_executor.h
@@ -24,8 +24,6 @@
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/overlays/storage_overlay.h"
-#include "src/trace_processor/db/overlays/types.h"
#include "src/trace_processor/db/storage/storage.h"
namespace perfetto {
@@ -37,22 +35,16 @@
public:
static constexpr uint32_t kMaxOverlayCount = 8;
- // Overlay-based definition of the column.
- struct SimpleColumn {
- base::SmallVector<const overlays::StorageOverlay*, kMaxOverlayCount>
- overlays;
- const storage::Storage* storage;
- };
-
// |row_count| is the size of the last overlay.
- QueryExecutor(const std::vector<SimpleColumn>& columns, uint32_t row_count)
+ QueryExecutor(const std::vector<storage::Storage*>& columns,
+ uint32_t row_count)
: columns_(columns), row_count_(row_count) {}
// Apply all the constraints on the data and return the filtered RowMap.
RowMap Filter(const std::vector<Constraint>& cs) {
RowMap rm(0, row_count_);
for (const auto& c : cs) {
- FilterColumn(c, columns_[c.col_idx], &rm);
+ FilterColumn(c, *columns_[c.col_idx], &rm);
}
return rm;
}
@@ -72,31 +64,31 @@
// Used only in unittests. Exposes private function.
static void BoundedColumnFilterForTesting(const Constraint& c,
- const SimpleColumn& col,
+ const storage::Storage& col,
RowMap* rm) {
LinearSearch(c, col, rm);
}
// Used only in unittests. Exposes private function.
- static RowMap IndexedColumnFilterForTesting(const Constraint& c,
- const SimpleColumn& col,
- RowMap* rm) {
- return IndexSearch(c, col, rm);
+ static void IndexedColumnFilterForTesting(const Constraint& c,
+ const storage::Storage& col,
+ RowMap* rm) {
+ IndexSearch(c, col, rm);
}
private:
// Updates RowMap with result of filtering single column using the Constraint.
- static void FilterColumn(const Constraint&, const SimpleColumn&, RowMap*);
+ static void FilterColumn(const Constraint&, const storage::Storage&, RowMap*);
// Filters the column using Range algorithm - tries to find the smallest Range
// to filter the storage with.
- static void LinearSearch(const Constraint&, const SimpleColumn&, RowMap*);
+ static void LinearSearch(const Constraint&, const storage::Storage&, RowMap*);
// Filters the column using Index algorithm - finds the indices to filter the
// storage with.
- static RowMap IndexSearch(const Constraint&, const SimpleColumn&, RowMap*);
+ static void IndexSearch(const Constraint&, const storage::Storage&, RowMap*);
- std::vector<SimpleColumn> columns_;
+ std::vector<storage::Storage*> columns_;
// Number of rows in the outmost overlay.
uint32_t row_count_ = 0;
diff --git a/src/trace_processor/db/query_executor_benchmark.cc b/src/trace_processor/db/query_executor_benchmark.cc
index 5bbc99f..eb23903 100644
--- a/src/trace_processor/db/query_executor_benchmark.cc
+++ b/src/trace_processor/db/query_executor_benchmark.cc
@@ -20,9 +20,11 @@
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/trace_processor/basic_types.h"
#include "src/base/test/utils.h"
#include "src/trace_processor/db/table.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
+#include "src/trace_processor/tables/profiler_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
@@ -35,6 +37,7 @@
using ExpectedFrameTimelineSliceTable = tables::ExpectedFrameTimelineSliceTable;
using RawTable = tables::RawTable;
using FtraceEventTable = tables::FtraceEventTable;
+using HeapGraphObjectTable = tables::HeapGraphObjectTable;
// `SELECT * FROM SLICE` on android_monitor_contention_trace.at
static char kSliceTable[] = "test/data/slice_table_for_benchmarks.csv";
@@ -50,6 +53,10 @@
static char kFtraceEventTable[] =
"test/data/ftrace_event_cpu_for_benchmarks.csv";
+// `SELECT id, upid, reference_set_id FROM heap_graph_object` on
+static char kHeapGraphObjectTable[] =
+ "test/data/heap_pgraph_object_for_benchmarks_query.csv";
+
enum DB { V1, V2 };
std::vector<std::string> SplitCSVLine(const std::string& line) {
@@ -193,6 +200,24 @@
tables::FtraceEventTable table_{&pool_, &raw_};
};
+struct HeapGraphObjectTableForBenchmark {
+ explicit HeapGraphObjectTableForBenchmark(benchmark::State& state) {
+ std::vector<std::string> table_rows_as_string =
+ ReadCSV(state, kHeapGraphObjectTable);
+
+ for (size_t i = 1; i < table_rows_as_string.size(); ++i) {
+ std::vector<std::string> row_vec = SplitCSVLine(table_rows_as_string[i]);
+
+ HeapGraphObjectTable::Row row;
+ row.upid = *base::StringToUInt32(row_vec[1]);
+ row.reference_set_id = base::StringToUInt32(row_vec[2]);
+ table_.Insert(row);
+ }
+ }
+ StringPool pool_;
+ HeapGraphObjectTable table_{&pool_};
+};
+
void BenchmarkSliceTable(benchmark::State& state,
SliceTableForBenchmark& table,
std::initializer_list<Constraint> c) {
@@ -346,6 +371,38 @@
BENCHMARK(BM_QEFilterWithArrangement)->ArgsProduct({{DB::V1, DB::V2}});
+static void BM_QEDenseNullFilter(benchmark::State& state) {
+ Table::kUseFilterV2 = state.range(0) == 1;
+
+ HeapGraphObjectTableForBenchmark table(state);
+ Constraint c{table.table_.reference_set_id().index_in_table(), FilterOp::kGt,
+ SqlValue::Long(1000)};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(table.table_.FilterToRowMap({c}));
+ }
+ state.counters["s/row"] =
+ benchmark::Counter(static_cast<double>(table.table_.row_count()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
+}
+BENCHMARK(BM_QEDenseNullFilter)->ArgsProduct({{DB::V1, DB::V2}});
+
+static void BM_QEDenseNullFilterIsNull(benchmark::State& state) {
+ Table::kUseFilterV2 = state.range(0) == 1;
+
+ HeapGraphObjectTableForBenchmark table(state);
+ Constraint c{table.table_.reference_set_id().index_in_table(),
+ FilterOp::kIsNull, SqlValue()};
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(table.table_.FilterToRowMap({c}));
+ }
+ state.counters["s/row"] =
+ benchmark::Counter(static_cast<double>(table.table_.row_count()),
+ benchmark::Counter::kIsIterationInvariantRate |
+ benchmark::Counter::kInvert);
+}
+BENCHMARK(BM_QEDenseNullFilterIsNull)->ArgsProduct({{DB::V1, DB::V2}});
+
} // namespace
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/db/query_executor_unittest.cc b/src/trace_processor/db/query_executor_unittest.cc
index 73f88d2..a2c05d8 100644
--- a/src/trace_processor/db/query_executor_unittest.cc
+++ b/src/trace_processor/db/query_executor_unittest.cc
@@ -15,12 +15,16 @@
*/
#include "src/trace_processor/db/query_executor.h"
-#include "src/trace_processor/db/overlays/arrangement_overlay.h"
-#include "src/trace_processor/db/overlays/null_overlay.h"
-#include "src/trace_processor/db/overlays/selector_overlay.h"
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/db/storage/arrangement_storage.h"
+#include "src/trace_processor/db/storage/fake_storage.h"
#include "src/trace_processor/db/storage/id_storage.h"
+#include "src/trace_processor/db/storage/null_storage.h"
#include "src/trace_processor/db/storage/numeric_storage.h"
+#include "src/trace_processor/db/storage/selector_storage.h"
#include "src/trace_processor/db/storage/set_id_storage.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/string_storage.h"
#include "test/gtest_and_gmock.h"
@@ -28,26 +32,22 @@
namespace trace_processor {
namespace {
-using OverlaysVec = base::SmallVector<const overlays::StorageOverlay*,
- QueryExecutor::kMaxOverlayCount>;
-using SimpleColumn = QueryExecutor::SimpleColumn;
+using testing::ElementsAre;
using IdStorage = storage::IdStorage;
using SetIdStorage = storage::SetIdStorage;
using StringStorage = storage::StringStorage;
-
-using ArrangementOverlay = overlays::ArrangementOverlay;
-using NullOverlay = overlays::NullOverlay;
-using SelectorOverlay = overlays::SelectorOverlay;
+using NullStorage = storage::NullStorage;
+using ArrangementStorage = storage::ArrangementStorage;
+using SelectorStorage = storage::SelectorStorage;
TEST(QueryExecutor, OnlyStorageRange) {
std::vector<int64_t> storage_data{1, 2, 3, 4, 5};
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- SimpleColumn col{OverlaysVec(), &storage};
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
RowMap rm(0, 5);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
ASSERT_EQ(rm.size(), 3u);
ASSERT_EQ(rm.Get(0), 2u);
@@ -56,11 +56,10 @@
TEST(QueryExecutor, OnlyStorageRangeIsNull) {
std::vector<int64_t> storage_data{1, 2, 3, 4, 5};
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- SimpleColumn col{OverlaysVec(), &storage};
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 5);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
ASSERT_EQ(rm.size(), 0u);
}
@@ -73,64 +72,56 @@
[](int64_t n) { return n % 5; });
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- SimpleColumn col{OverlaysVec(), &storage};
Constraint c{0, FilterOp::kLt, SqlValue::Long(2)};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 4u);
- ASSERT_EQ(res.Get(0), 0u);
- ASSERT_EQ(res.Get(1), 1u);
- ASSERT_EQ(res.Get(2), 5u);
- ASSERT_EQ(res.Get(3), 6u);
+ ASSERT_EQ(rm.size(), 4u);
+ ASSERT_EQ(rm.Get(0), 0u);
+ ASSERT_EQ(rm.Get(1), 1u);
+ ASSERT_EQ(rm.Get(2), 5u);
+ ASSERT_EQ(rm.Get(3), 6u);
}
TEST(QueryExecutor, OnlyStorageIndexIsNull) {
std::vector<int64_t> storage_data{1, 2, 3, 4, 5};
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- SimpleColumn col{OverlaysVec(), &storage};
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 5);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 0u);
+ ASSERT_EQ(rm.size(), 0u);
}
-TEST(QueryExecutor, NullOverlayBounds) {
+TEST(QueryExecutor, NullBounds) {
std::vector<int64_t> storage_data(5);
std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
BitVector bv{1, 1, 0, 1, 1, 0, 0, 0, 1, 0};
- overlays::NullOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
-
- SimpleColumn col{overlays_vec, &storage};
+ storage::NullStorage storage(std::move(numeric), &bv);
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
RowMap rm(0, 10);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
ASSERT_EQ(rm.size(), 2u);
ASSERT_EQ(rm.Get(0), 4u);
ASSERT_EQ(rm.Get(1), 8u);
}
-TEST(QueryExecutor, NullOverlayRangeIsNull) {
+TEST(QueryExecutor, NullRangeIsNull) {
std::vector<int64_t> storage_data(5);
std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
BitVector bv{1, 1, 0, 1, 1, 0, 0, 0, 1, 0};
- overlays::NullOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
+ storage::NullStorage storage(std::move(numeric), &bv);
- SimpleColumn col{overlays_vec, &storage};
-
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 10);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
ASSERT_EQ(rm.size(), 5u);
ASSERT_EQ(rm.Get(0), 2u);
@@ -140,163 +131,174 @@
ASSERT_EQ(rm.Get(4), 9u);
}
-TEST(QueryExecutor, NullOverlayIndex) {
+TEST(QueryExecutor, NullIndex) {
std::vector<int64_t> storage_data(6);
std::iota(storage_data.begin(), storage_data.end(), 0);
std::transform(storage_data.begin(), storage_data.end(), storage_data.begin(),
[](int64_t n) { return n % 3; });
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
BitVector bv{1, 1, 0, 1, 1, 0, 1, 0, 0, 1};
- NullOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
-
- SimpleColumn col{overlays_vec, &storage};
+ storage::NullStorage storage(std::move(numeric), &bv);
Constraint c{0, FilterOp::kGe, SqlValue::Long(1)};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 4u);
- ASSERT_EQ(res.Get(0), 1u);
- ASSERT_EQ(res.Get(1), 3u);
- ASSERT_EQ(res.Get(2), 6u);
- ASSERT_EQ(res.Get(3), 9u);
+ ASSERT_EQ(rm.size(), 4u);
+ ASSERT_EQ(rm.Get(0), 1u);
+ ASSERT_EQ(rm.Get(1), 3u);
+ ASSERT_EQ(rm.Get(2), 6u);
+ ASSERT_EQ(rm.Get(3), 9u);
}
-TEST(QueryExecutor, NullOverlayIndexIsNull) {
+TEST(QueryExecutor, NullIndexIsNull) {
std::vector<int64_t> storage_data(5);
std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
+
BitVector bv{1, 1, 0, 1, 1, 0, 0, 0, 1, 0};
- overlays::NullOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
+ storage::NullStorage storage(std::move(numeric), &bv);
- SimpleColumn col{overlays_vec, &storage};
-
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 5u);
- ASSERT_EQ(res.Get(0), 2u);
- ASSERT_EQ(res.Get(1), 5u);
- ASSERT_EQ(res.Get(2), 6u);
- ASSERT_EQ(res.Get(3), 7u);
- ASSERT_EQ(res.Get(4), 9u);
+ ASSERT_EQ(rm.size(), 5u);
+ ASSERT_EQ(rm.Get(0), 2u);
+ ASSERT_EQ(rm.Get(1), 5u);
+ ASSERT_EQ(rm.Get(2), 6u);
+ ASSERT_EQ(rm.Get(3), 7u);
+ ASSERT_EQ(rm.Get(4), 9u);
}
-TEST(QueryExecutor, SelectorOverlayBounds) {
+TEST(QueryExecutor, SelectorStorageBounds) {
std::vector<int64_t> storage_data(5);
std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
BitVector bv{1, 1, 0, 0, 1};
- SelectorOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
-
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(numeric), &bv);
Constraint c{0, FilterOp::kGt, SqlValue::Long(1)};
RowMap rm(0, 3);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(rm.size(), 1u);
- ASSERT_EQ(rm.Get(0), 2u);
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u));
}
-TEST(QueryExecutor, SelectorOverlayIndex) {
+TEST(QueryExecutor, SelectorStorageIndex) {
std::vector<int64_t> storage_data(10);
std::iota(storage_data.begin(), storage_data.end(), 0);
std::transform(storage_data.begin(), storage_data.end(), storage_data.begin(),
[](int64_t n) { return n % 5; });
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
BitVector bv{1, 1, 0, 1, 1, 0, 1, 0, 0, 1};
- SelectorOverlay overlay(&bv);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
-
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(numeric), &bv);
Constraint c{0, FilterOp::kGe, SqlValue::Long(2)};
RowMap rm(0, 6);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 3u);
- ASSERT_EQ(res.Get(0), 2u);
- ASSERT_EQ(res.Get(1), 3u);
- ASSERT_EQ(res.Get(2), 5u);
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u, 3u, 5u));
}
-TEST(QueryExecutor, ArrangementOverlayBounds) {
+TEST(QueryExecutor, ArrangementStorageBounds) {
std::vector<int64_t> storage_data(5);
std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
std::vector<uint32_t> arrangement{4, 1, 2, 2, 3};
- overlays::ArrangementOverlay overlay(&arrangement);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
-
- SimpleColumn col{overlays_vec, &storage};
+ storage::ArrangementStorage storage(std::move(numeric), &arrangement);
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
RowMap rm(0, 5);
- QueryExecutor::BoundedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(rm.size(), 2u);
- ASSERT_EQ(rm.Get(0), 0u);
- ASSERT_EQ(rm.Get(1), 4u);
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(0u, 4u));
}
-TEST(QueryExecutor, ArrangmentOverlayIndex) {
- std::vector<int64_t> storage_data(5);
- std::iota(storage_data.begin(), storage_data.end(), 0);
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+TEST(QueryExecutor, ArrangementStorageSubsetInputRange) {
+ std::unique_ptr<storage::Storage> fake =
+ storage::FakeStorage::SearchSubset(5u, RowMap::Range(2u, 4u));
std::vector<uint32_t> arrangement{4, 1, 2, 2, 3};
- overlays::ArrangementOverlay overlay(&arrangement);
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&overlay);
+ storage::ArrangementStorage storage(std::move(fake), &arrangement);
- SimpleColumn col{overlays_vec, &storage};
+ Constraint c{0, FilterOp::kGe, SqlValue::Long(0u)};
+ RowMap rm(1, 3);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
+
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u));
+}
+
+TEST(QueryExecutor, ArrangementStorageSubsetInputBitvector) {
+ std::unique_ptr<storage::Storage> fake =
+ storage::FakeStorage::SearchSubset(5u, BitVector({0, 0, 1, 1, 0}));
+
+ std::vector<uint32_t> arrangement{4, 1, 2, 2, 3};
+ storage::ArrangementStorage storage(std::move(fake), &arrangement);
+
+ Constraint c{0, FilterOp::kGe, SqlValue::Long(0u)};
+ RowMap rm(1, 3);
+ QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
+
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u));
+}
+
+TEST(QueryExecutor, ArrangementStorageIndex) {
+ std::vector<int64_t> storage_data(5);
+ std::iota(storage_data.begin(), storage_data.end(), 0);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
+
+ std::vector<uint32_t> arrangement{4, 1, 2, 2, 3};
+ storage::ArrangementStorage storage(std::move(numeric), &arrangement);
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
RowMap rm(0, 5);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, col, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 2u);
- ASSERT_EQ(res.Get(0), 0u);
- ASSERT_EQ(res.Get(1), 4u);
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(0u, 4u));
+}
+
+TEST(QueryExecutor, MismatchedTypeNullWithOtherOperations) {
+ std::vector<int64_t> storage_data{0, 1, 2, 3, 0, 1, 2, 3};
+ storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+
+ // Filter.
+ Constraint c{0, FilterOp::kGe, SqlValue()};
+ QueryExecutor exec({&storage}, 6);
+ RowMap res = exec.Filter({c});
+
+ ASSERT_TRUE(res.empty());
}
TEST(QueryExecutor, SingleConstraintWithNullAndSelector) {
std::vector<int64_t> storage_data{0, 1, 2, 3, 0, 1, 2, 3};
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
// Current vector
// 0, 1, NULL, 2, 3, 0, NULL, NULL, 1, 2, 3, NULL
BitVector null_bv{1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0};
- NullOverlay null_overlay(&null_bv);
+ auto null =
+ std::make_unique<storage::NullStorage>(std::move(numeric), &null_bv);
// Final vector
// 0, NULL, 3, NULL, 1, 3
BitVector selector_bv{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- overlays_vec.emplace_back(&null_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(null), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kGe, SqlValue::Long(2)};
- QueryExecutor exec({col}, 6);
+ QueryExecutor exec({&storage}, 6);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -306,27 +308,23 @@
TEST(QueryExecutor, SingleConstraintWithNullAndArrangement) {
std::vector<int64_t> storage_data{0, 1, 2, 3, 0, 1, 2, 3};
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
// Current vector
// 0, 1, NULL, 2, 3, 0, NULL, NULL, 1, 2, 3, NULL
BitVector null_bv{1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0};
- NullOverlay null_overlay(&null_bv);
+ auto null =
+ std::make_unique<storage::NullStorage>(std::move(numeric), &null_bv);
// Final vector
// NULL, 3, NULL, NULL, 3, NULL
std::vector<uint32_t> arrangement{2, 4, 6, 2, 4, 6};
- ArrangementOverlay arrangement_overlay(&arrangement);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&arrangement_overlay);
- overlays_vec.emplace_back(&null_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ ArrangementStorage storage(std::move(null), &arrangement);
// Filter.
Constraint c{0, FilterOp::kGe, SqlValue::Long(1)};
- QueryExecutor exec({col}, 6);
+ QueryExecutor exec({&storage}, 6);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -336,27 +334,23 @@
TEST(QueryExecutor, IsNullWithSelector) {
std::vector<int64_t> storage_data{0, 1, 2, 3, 0, 1, 2, 3};
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64);
// Current vector
// 0, 1, NULL, 2, 3, 0, NULL, NULL, 1, 2, 3, NULL
BitVector null_bv{1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0};
- NullOverlay null_overlay(&null_bv);
+ auto null =
+ std::make_unique<storage::NullStorage>(std::move(numeric), &null_bv);
// Final vector
// 0, NULL, 3, NULL, 1, 3
BitVector selector_bv{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- overlays_vec.emplace_back(&null_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(null), &selector_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 6);
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
+ QueryExecutor exec({&storage}, 6);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -366,26 +360,21 @@
TEST(QueryExecutor, BinarySearch) {
std::vector<int64_t> storage_data{0, 1, 2, 3, 4, 5, 6};
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64,
- true);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64, true);
// Add nulls - {0, 1, NULL, NULL, 2, 3, NULL, NULL, 4, 5, 6, NULL}
BitVector null_bv{1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0};
- NullOverlay null_overlay(&null_bv);
+ auto null =
+ std::make_unique<storage::NullStorage>(std::move(numeric), &null_bv);
// Final vector {1, NULL, 3, NULL, 5, NULL}.
BitVector selector_bv{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- overlays_vec.emplace_back(&null_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(null), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
- QueryExecutor exec({col}, 6);
+ QueryExecutor exec({&storage}, 6);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -395,26 +384,21 @@
TEST(QueryExecutor, BinarySearchIsNull) {
std::vector<int64_t> storage_data{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64,
- true);
+ auto numeric = std::make_unique<storage::NumericStorage<int64_t>>(
+ &storage_data, ColumnType::kInt64, true);
// Select 6 elements from storage, resulting in a vector {0, 1, 3, 4, 6, 7}.
BitVector selector_bv{1, 1, 0, 1, 1, 0, 1, 1, 0, 0};
- SelectorOverlay selector_overlay(&selector_bv);
+ auto selector =
+ std::make_unique<SelectorStorage>(std::move(numeric), &selector_bv);
// Add nulls, final vector {NULL, NULL, NULL 0, 1, 3, 4, 6, 7}.
BitVector null_bv{0, 0, 0, 1, 1, 1, 1, 1, 1};
- NullOverlay null_overlay(&null_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&null_overlay);
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ storage::NullStorage storage(std::move(selector), &null_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 9);
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
+ QueryExecutor exec({&storage}, 9);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 3u);
@@ -425,25 +409,20 @@
TEST(QueryExecutor, SetIdStorage) {
std::vector<uint32_t> storage_data{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9};
- SetIdStorage storage(&storage_data);
+ auto numeric = std::make_unique<storage::SetIdStorage>(&storage_data);
// Select 6 elements from storage, resulting in a vector {0, 3, 3, 6, 9, 9}.
BitVector selector_bv{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
- SelectorOverlay selector_overlay(&selector_bv);
+ auto selector =
+ std::make_unique<SelectorStorage>(std::move(numeric), &selector_bv);
// Add nulls - vector (size 10) {NULL, 0, 3, NULL, 3, 6, NULL, 9, 9, NULL}.
BitVector null_bv{0, 1, 1, 0, 1, 1, 0, 1, 1, 0};
- NullOverlay null_overlay(&null_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&null_overlay);
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ storage::NullStorage storage(std::move(selector), &null_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 10);
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
+ QueryExecutor exec({&storage}, 10);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 4u);
@@ -458,12 +437,9 @@
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64,
true);
- OverlaysVec overlays_vec;
- SimpleColumn col{overlays_vec, &storage};
-
// Filter.
Constraint c{0, FilterOp::kNe, SqlValue::Long(5)};
- QueryExecutor exec({col}, 10);
+ QueryExecutor exec({&storage}, 10);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 9u);
@@ -471,12 +447,10 @@
TEST(QueryExecutor, IdSearchIsNull) {
IdStorage storage(5);
- OverlaysVec overlays_vec;
- SimpleColumn col{overlays_vec, &storage};
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 5);
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 0u);
@@ -484,12 +458,10 @@
TEST(QueryExecutor, IdSearchIsNotNull) {
IdStorage storage(5);
- OverlaysVec overlays_vec;
- SimpleColumn col{overlays_vec, &storage};
// Filter.
- Constraint c{0, FilterOp::kIsNotNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 5);
+ Constraint c{0, FilterOp::kIsNotNull, SqlValue()};
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 5u);
@@ -497,12 +469,10 @@
TEST(QueryExecutor, IdSearchNotEq) {
IdStorage storage(5);
- OverlaysVec overlays_vec;
- SimpleColumn col{overlays_vec, &storage};
// Filter.
Constraint c{0, FilterOp::kNe, SqlValue::Long(3)};
- QueryExecutor exec({col}, 5);
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 4u);
@@ -517,20 +487,15 @@
ids.push_back(pool.InternString(base::StringView(string)));
}
ids.insert(ids.begin() + 3, StringPool::Id::Null());
- StringStorage storage(&pool, &ids);
+ auto string = std::make_unique<StringStorage>(&pool, &ids);
// Final vec {"cheese", "pasta", "NULL", "pierogi", "fries"}.
BitVector selector_bv{1, 1, 0, 1, 1, 0, 1};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
- QueryExecutor exec({col}, 5);
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 1u);
@@ -545,20 +510,15 @@
for (const auto& string : strings) {
ids.push_back(pool.InternString(base::StringView(string)));
}
- StringStorage storage(&pool, &ids, true);
+ auto string = std::make_unique<StringStorage>(&pool, &ids, true);
// Final vec {"apple", "burger", "doughnut", "eggplant"}.
BitVector selector_bv{1, 1, 0, 1, 1, 0};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kGe, SqlValue::String("camembert")};
- QueryExecutor exec({col}, 4);
+ QueryExecutor exec({&storage}, 4);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -573,26 +533,32 @@
for (const auto& string : strings) {
ids.push_back(pool.InternString(base::StringView(string)));
}
- StringStorage storage(&pool, &ids, true);
+ auto string = std::make_unique<StringStorage>(&pool, &ids, true);
// Final vec {"apple", "burger", "doughnut", "eggplant"}.
BitVector selector_bv{1, 1, 0, 1, 1, 0};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kNe, SqlValue::String("doughnut")};
- QueryExecutor exec({col}, 4);
+ QueryExecutor exec({&storage}, 4);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 3u);
ASSERT_EQ(res.Get(0), 0u);
}
+TEST(QueryExecutor, MismatchedTypeIdWithString) {
+ IdStorage storage(5);
+
+ // Filter.
+ Constraint c{0, FilterOp::kGe, SqlValue::String("cheese")};
+ QueryExecutor exec({&storage}, 5);
+ RowMap res = exec.Filter({c});
+
+ ASSERT_EQ(res.size(), 0u);
+}
+
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
TEST(QueryExecutor, StringBinarySearchRegex) {
StringPool pool;
@@ -603,20 +569,15 @@
ids.push_back(pool.InternString(base::StringView(string)));
}
ids.insert(ids.begin() + 3, StringPool::Id::Null());
- StringStorage storage(&pool, &ids);
+ auto string = std::make_unique<StringStorage>(&pool, &ids);
// Final vec {"cheese", "pasta", "NULL", "pierogi", "fries"}.
BitVector selector_bv{1, 1, 0, 1, 1, 0, 1};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kRegex, SqlValue::String("p.*")};
- QueryExecutor exec({col}, 5);
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 2u);
@@ -633,20 +594,15 @@
ids.push_back(pool.InternString(base::StringView(string)));
}
ids.insert(ids.begin() + 3, StringPool::Id::Null());
- StringStorage storage(&pool, &ids);
+ auto string = std::make_unique<StringStorage>(&pool, &ids);
// Final vec {"cheese", "pasta", "NULL", "pierogi", "fries"}.
BitVector selector_bv{1, 1, 0, 1, 1, 0, 1};
- SelectorOverlay selector_overlay(&selector_bv);
-
- // Create the column.
- OverlaysVec overlays_vec;
- overlays_vec.emplace_back(&selector_overlay);
- SimpleColumn col{overlays_vec, &storage};
+ SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
Constraint c{0, FilterOp::kRegex, SqlValue::Long(4)};
- QueryExecutor exec({col}, 5);
+ QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
ASSERT_EQ(res.size(), 0u);
diff --git a/src/trace_processor/db/storage/BUILD.gn b/src/trace_processor/db/storage/BUILD.gn
index 3b90470..bd80ec7 100644
--- a/src/trace_processor/db/storage/BUILD.gn
+++ b/src/trace_processor/db/storage/BUILD.gn
@@ -16,12 +16,20 @@
source_set("storage") {
sources = [
+ "arrangement_storage.cc",
+ "arrangement_storage.h",
+ "dense_null_storage.cc",
+ "dense_null_storage.h",
"dummy_storage.cc",
"dummy_storage.h",
"id_storage.cc",
"id_storage.h",
+ "null_storage.cc",
+ "null_storage.h",
"numeric_storage.cc",
"numeric_storage.h",
+ "selector_storage.cc",
+ "selector_storage.h",
"set_id_storage.cc",
"set_id_storage.h",
"storage.cc",
@@ -43,17 +51,37 @@
]
}
-perfetto_unittest_source_set("unittests") {
+perfetto_unittest_source_set("fake_storage") {
testonly = true
sources = [
- "id_storage_unittest.cc",
- "numeric_storage_unittest.cc",
- "set_id_storage_unittest.cc",
- "string_storage_unittest.cc",
+ "fake_storage.cc",
+ "fake_storage.h",
]
deps = [
":storage",
"../../../../gn:default_deps",
"../../../../gn:gtest_and_gmock",
+ "../../containers",
+ ]
+}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ sources = [
+ "arrangement_storage_unittest.cc",
+ "dense_null_storage_unittest.cc",
+ "id_storage_unittest.cc",
+ "null_storage_unittest.cc",
+ "numeric_storage_unittest.cc",
+ "selector_storage_unittest.cc",
+ "set_id_storage_unittest.cc",
+ "string_storage_unittest.cc",
+ ]
+ deps = [
+ ":fake_storage",
+ ":storage",
+ "../../../../gn:default_deps",
+ "../../../../gn:gtest_and_gmock",
+ "../../containers",
]
}
diff --git a/src/trace_processor/db/storage/arrangement_storage.cc b/src/trace_processor/db/storage/arrangement_storage.cc
new file mode 100644
index 0000000..fac7f68
--- /dev/null
+++ b/src/trace_processor/db/storage/arrangement_storage.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/arrangement_storage.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <vector>
+
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/tp_metatrace.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using Range = RowMap::Range;
+
+} // namespace
+
+ArrangementStorage::ArrangementStorage(std::unique_ptr<Storage> inner,
+ const std::vector<uint32_t>* arrangement)
+ : inner_(std::move(inner)), arrangement_(arrangement) {
+ PERFETTO_DCHECK(*std::max_element(arrangement->begin(), arrangement->end()) <=
+ inner_->size());
+}
+
+Storage::SearchValidationResult ArrangementStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return inner_->ValidateSearchConstraints(sql_val, op);
+}
+
+RangeOrBitVector ArrangementStorage::Search(FilterOp op,
+ SqlValue sql_val,
+ Range in) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "ArrangementStorage::Search");
+
+ const auto& arrangement = *arrangement_;
+ PERFETTO_DCHECK(in.end <= arrangement.size());
+ const auto [min_i, max_i] =
+ std::minmax_element(arrangement.begin() + static_cast<int32_t>(in.start),
+ arrangement.begin() + static_cast<int32_t>(in.end));
+
+ auto storage_result = inner_->Search(op, sql_val, Range(*min_i, *max_i + 1));
+ BitVector::Builder builder(in.end, in.start);
+ if (storage_result.IsRange()) {
+ Range storage_range = std::move(storage_result).TakeIfRange();
+ for (uint32_t i = in.start; i < in.end; ++i) {
+ builder.Append(storage_range.Contains(arrangement[i]));
+ }
+ } else {
+ BitVector storage_bitvector = std::move(storage_result).TakeIfBitVector();
+ PERFETTO_DCHECK(storage_bitvector.size() == *max_i + 1);
+
+ // After benchmarking, it turns out this complexity *is* actually worthwhile
+ // and has a noticable impact on the performance of this function in real
+ // world tables.
+
+ // Fast path: we compare as many groups of 64 elements as we can.
+ // This should be very easy for the compiler to auto-vectorize.
+ const uint32_t* arrangement_idx = arrangement.data() + in.start;
+ uint32_t fast_path_elements = builder.BitsInCompleteWordsUntilFull();
+ for (uint32_t i = 0; i < fast_path_elements; i += BitVector::kBitsInWord) {
+ uint64_t word = 0;
+ // This part should be optimised by SIMD and is expected to be fast.
+ for (uint32_t k = 0; k < BitVector::kBitsInWord; ++k, ++arrangement_idx) {
+ bool comp_result = storage_bitvector.IsSet(*arrangement_idx);
+ word |= static_cast<uint64_t>(comp_result) << k;
+ }
+ builder.AppendWord(word);
+ }
+
+ // Slow path: we compare <64 elements and append to fill the Builder.
+ uint32_t back_elements = builder.BitsUntilFull();
+ for (uint32_t i = 0; i < back_elements; ++i, ++arrangement_idx) {
+ builder.Append(storage_bitvector.IsSet(*arrangement_idx));
+ }
+ }
+ return RangeOrBitVector(std::move(builder).Build());
+}
+
+RangeOrBitVector ArrangementStorage::IndexSearch(FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size,
+ bool sorted) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "ArrangementStorage::IndexSearch");
+
+ std::vector<uint32_t> storage_iv;
+ for (uint32_t* it = indices; it != indices + indices_size; ++it) {
+ storage_iv.push_back((*arrangement_)[*it]);
+ }
+ return inner_->IndexSearch(op, sql_val, storage_iv.data(),
+ static_cast<uint32_t>(storage_iv.size()), sorted);
+}
+
+void ArrangementStorage::StableSort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void ArrangementStorage::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void ArrangementStorage::Serialize(StorageProto* storage) const {
+ auto* arrangement_storage = storage->set_arrangement_storage();
+ arrangement_storage->set_values(
+ reinterpret_cast<const uint8_t*>(arrangement_->data()),
+ sizeof(uint32_t) * arrangement_->size());
+ inner_->Serialize(arrangement_storage->set_storage());
+}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/arrangement_storage.h b/src/trace_processor/db/storage/arrangement_storage.h
new file mode 100644
index 0000000..97a944e
--- /dev/null
+++ b/src/trace_processor/db/storage/arrangement_storage.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_ARRANGEMENT_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_ARRANGEMENT_STORAGE_H_
+
+#include <memory>
+#include "src/trace_processor/db/storage/storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Storage responsible for rearranging the elements of another Storage. It deals
+// with duplicates, permutations and selection; for selection only, it's more
+// efficient to use `SelectorStorage`.
+class ArrangementStorage : public Storage {
+ public:
+ explicit ArrangementStorage(std::unique_ptr<Storage> inner,
+ const std::vector<uint32_t>* arrangement);
+
+ Storage::SearchValidationResult ValidateSearchConstraints(SqlValue, FilterOp)
+ const override;
+
+ RangeOrBitVector Search(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const override;
+
+ RangeOrBitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count,
+ bool sorted) const override;
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Serialize(StorageProto*) const override;
+
+ uint32_t size() const override {
+ return static_cast<uint32_t>(arrangement_->size());
+ }
+
+ private:
+ std::unique_ptr<Storage> inner_;
+ const std::vector<uint32_t>* arrangement_;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_ARRANGEMENT_STORAGE_H_
diff --git a/src/trace_processor/db/storage/arrangement_storage_unittest.cc b/src/trace_processor/db/storage/arrangement_storage_unittest.cc
new file mode 100644
index 0000000..f7b30f1
--- /dev/null
+++ b/src/trace_processor/db/storage/arrangement_storage_unittest.cc
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/arrangement_storage.h"
+
+#include "src/trace_processor/db/storage/fake_storage.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+using Range = RowMap::Range;
+
+std::vector<uint32_t> ToIndexVector(RangeOrBitVector& r_or_bv) {
+ RowMap rm;
+ if (r_or_bv.IsBitVector()) {
+ rm = RowMap(std::move(r_or_bv).TakeIfBitVector());
+ } else {
+ Range range = std::move(r_or_bv).TakeIfRange();
+ rm = RowMap(range.start, range.end);
+ }
+ return rm.GetAllIndices();
+}
+
+TEST(ArrangementStorage, SearchAll) {
+ std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
+ ArrangementStorage storage(FakeStorage::SearchAll(5), &arrangement);
+
+ auto res =
+ storage.Search(FilterOp::kGe, SqlValue::Long(0u), RowMap::Range(2, 4));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(2u, 3u));
+}
+
+TEST(ArrangementStorage, SearchNone) {
+ std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
+ ArrangementStorage storage(FakeStorage::SearchNone(5), &arrangement);
+
+ auto res =
+ storage.Search(FilterOp::kGe, SqlValue::Long(0u), RowMap::Range(2, 4));
+ ASSERT_THAT(ToIndexVector(res), IsEmpty());
+}
+
+TEST(ArrangementStorage, DISABLED_SearchLimited) {
+ std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
+ ArrangementStorage storage(FakeStorage::SearchSubset(5, Range(4, 5)),
+ &arrangement);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0u), Range(2, 7));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(6u));
+}
+
+TEST(ArrangementStorage, SearchBitVector) {
+ std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
+ ArrangementStorage storage(
+ FakeStorage::SearchSubset(5, BitVector({0, 1, 0, 1, 0})), &arrangement);
+
+ // Table bv:
+ // 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0u), Range(0, 10));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 4, 5, 8, 9));
+}
+
+TEST(ArrangementStorage, IndexSearch) {
+ std::vector<uint32_t> arrangement{1, 1, 2, 2, 3, 3, 4, 4, 1, 1};
+ ArrangementStorage storage(
+ FakeStorage::SearchSubset(5, BitVector({0, 1, 0, 1, 0})), &arrangement);
+
+ std::vector<uint32_t> table_idx{7u, 1u, 3u};
+ RangeOrBitVector res =
+ storage.IndexSearch(FilterOp::kGe, SqlValue::Long(0u), table_idx.data(),
+ static_cast<uint32_t>(table_idx.size()), false);
+
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1u));
+}
+
+} // namespace
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/dense_null_storage.cc b/src/trace_processor/db/storage/dense_null_storage.cc
new file mode 100644
index 0000000..9f764a4
--- /dev/null
+++ b/src/trace_processor/db/storage/dense_null_storage.cc
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/dense_null_storage.h"
+
+#include <cstdint>
+#include <variant>
+
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "src/trace_processor/tp_metatrace.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+DenseNullStorage::DenseNullStorage(std::unique_ptr<Storage> inner,
+ const BitVector* non_null)
+ : inner_(std::move(inner)), non_null_(non_null) {}
+
+Storage::SearchValidationResult DenseNullStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return inner_->ValidateSearchConstraints(sql_val, op);
+}
+
+RangeOrBitVector DenseNullStorage::Search(FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range in) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "DenseNullStorage::Search");
+
+ RangeOrBitVector inner_res = inner_->Search(op, sql_val, in);
+ BitVector res;
+ if (inner_res.IsRange()) {
+ // If the inner storage returns a range, mask out the appropriate values in
+ // |non_null_| which matches the range. Then, resize to |in.end| as this
+ // is mandated by the API contract of |Storage::Search|.
+ RowMap::Range inner_range = std::move(inner_res).TakeIfRange();
+ PERFETTO_DCHECK(inner_range.end <= in.end);
+ PERFETTO_DCHECK(inner_range.start >= in.start);
+ res = non_null_->IntersectRange(inner_range.start, inner_range.end);
+ res.Resize(in.end, false);
+ } else {
+ res = std::move(inner_res).TakeIfBitVector();
+ }
+ PERFETTO_DCHECK(res.size() == in.end);
+
+ if (op == FilterOp::kIsNull) {
+ // For IS NULL, we need to add any rows in |non_null_| which are zeros: we
+ // do this by taking the appropriate number of rows, inverting it and then
+ // bitwise or-ing the result with it.
+ BitVector non_null_copy = non_null_->Copy();
+ non_null_copy.Resize(in.end);
+ non_null_copy.Not();
+ res.Or(non_null_copy);
+ } else {
+ // For anything else, we just need to ensure that any rows which are null
+ // are removed as they would not match.
+ res.And(*non_null_);
+ }
+ return RangeOrBitVector(std::move(res));
+}
+
+RangeOrBitVector DenseNullStorage::IndexSearch(FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size,
+ bool sorted) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "DenseNullStorage::IndexSearch");
+
+ RangeOrBitVector inner_res =
+ inner_->IndexSearch(op, sql_val, indices, indices_size, sorted);
+ if (inner_res.IsRange()) {
+ RowMap::Range inner_range = std::move(inner_res).TakeIfRange();
+ BitVector::Builder builder(indices_size, inner_range.start);
+ for (uint32_t i = inner_range.start; i < inner_range.end; ++i) {
+ builder.Append(non_null_->IsSet(indices[i]));
+ }
+ return RangeOrBitVector(std::move(builder).Build());
+ }
+
+ BitVector::Builder builder(indices_size);
+ for (uint32_t i = 0; i < indices_size; ++i) {
+ builder.Append(non_null_->IsSet(indices[i]));
+ }
+ BitVector non_null = std::move(builder).Build();
+ PERFETTO_DCHECK(non_null.size() == indices_size);
+
+ BitVector res = std::move(inner_res).TakeIfBitVector();
+ PERFETTO_DCHECK(res.size() == indices_size);
+
+ if (op == FilterOp::kIsNull) {
+ BitVector null = std::move(non_null);
+ null.Not();
+ res.Or(null);
+ } else {
+ res.And(non_null);
+ }
+ return RangeOrBitVector(std::move(res));
+}
+
+void DenseNullStorage::StableSort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void DenseNullStorage::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void DenseNullStorage::Serialize(StorageProto* storage) const {
+ auto* null_storage = storage->set_dense_null_storage();
+ non_null_->Serialize(null_storage->set_bit_vector());
+ inner_->Serialize(null_storage->set_storage());
+}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/dense_null_storage.h b/src/trace_processor/db/storage/dense_null_storage.h
new file mode 100644
index 0000000..ec7b6e9
--- /dev/null
+++ b/src/trace_processor/db/storage/dense_null_storage.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_DENSE_NULL_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_DENSE_NULL_STORAGE_H_
+
+#include <memory>
+#include <variant>
+
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Storage which introduces the layer of nullability but without changing the
+// "spacing" of the underlying storage i.e. this storage simply "masks" out
+// rows in the underlying storage with nulls.
+class DenseNullStorage : public Storage {
+ public:
+ DenseNullStorage(std::unique_ptr<Storage> inner, const BitVector* non_null);
+
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
+ RangeOrBitVector Search(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const override;
+
+ RangeOrBitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count,
+ bool sorted) const override;
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Serialize(StorageProto*) const override;
+
+ uint32_t size() const override { return non_null_->size(); }
+
+ private:
+ std::unique_ptr<Storage> inner_;
+ const BitVector* non_null_ = nullptr;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_DENSE_NULL_STORAGE_H_
diff --git a/src/trace_processor/db/storage/dense_null_storage_unittest.cc b/src/trace_processor/db/storage/dense_null_storage_unittest.cc
new file mode 100644
index 0000000..d8ec93c
--- /dev/null
+++ b/src/trace_processor/db/storage/dense_null_storage_unittest.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/dense_null_storage.h"
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/storage/fake_storage.h"
+#include "src/trace_processor/db/storage/numeric_storage.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+using Range = RowMap::Range;
+
+std::vector<uint32_t> ToIndexVector(RangeOrBitVector& r_or_bv) {
+ RowMap rm;
+ if (r_or_bv.IsBitVector()) {
+ rm = RowMap(std::move(r_or_bv).TakeIfBitVector());
+ } else {
+ Range range = std::move(r_or_bv).TakeIfRange();
+ rm = RowMap(range.start, range.end);
+ }
+ return rm.GetAllIndices();
+}
+
+TEST(DenseNullStorage, NoFilteringSearch) {
+ std::vector<uint32_t> data{0, 1, 0, 1, 0};
+ auto numeric =
+ std::make_unique<NumericStorage<uint32_t>>(&data, ColumnType::kUint32);
+
+ BitVector bv{0, 1, 0, 1, 0};
+ DenseNullStorage storage(std::move(numeric), &bv);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0), Range(0, 5));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1, 3));
+}
+
+TEST(DenseNullStorage, RestrictInputSearch) {
+ std::vector<uint32_t> data{0, 1, 0, 1, 0};
+ auto numeric =
+ std::make_unique<NumericStorage<uint32_t>>(&data, ColumnType::kUint32);
+
+ BitVector bv{0, 1, 0, 1, 0};
+ DenseNullStorage storage(std::move(numeric), &bv);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0), Range(1, 3));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1));
+}
+
+TEST(DenseNullStorage, RangeFilterSearch) {
+ auto fake = FakeStorage::SearchSubset(5, Range(1, 3));
+
+ BitVector bv{0, 1, 0, 1, 0};
+ DenseNullStorage storage(std::move(fake), &bv);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0), Range(0, 5));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1));
+}
+
+TEST(DenseNullStorage, BitvectorFilterSearch) {
+ auto fake = FakeStorage::SearchSubset(5, BitVector({0, 1, 1, 0, 0}));
+
+ BitVector bv{0, 1, 0, 1, 0};
+ DenseNullStorage storage(std::move(fake), &bv);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0), Range(0, 5));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1));
+}
+
+TEST(DenseNullStorage, IsNullSearch) {
+ auto fake = FakeStorage::SearchSubset(5, BitVector({1, 1, 0, 0, 1}));
+
+ BitVector bv{1, 0, 0, 1, 1};
+ DenseNullStorage storage(std::move(fake), &bv);
+
+ auto res = storage.Search(FilterOp::kIsNull, SqlValue(), Range(0, 5));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 2, 4));
+}
+
+TEST(DenseNullStorage, IndexSearch) {
+ std::vector<uint32_t> data{1, 0, 0, 1, 1, 1};
+ auto numeric =
+ std::make_unique<NumericStorage<uint32_t>>(&data, ColumnType::kUint32);
+
+ BitVector bv{1, 0, 0, 1, 1, 1};
+ DenseNullStorage storage(std::move(numeric), &bv);
+
+ std::vector<uint32_t> index({5, 2, 3, 4, 1});
+ auto res = storage.IndexSearch(FilterOp::kGe, SqlValue::Long(0), index.data(),
+ static_cast<uint32_t>(index.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 2, 3));
+}
+
+TEST(DenseNullStorage, IsNullIndexSearch) {
+ auto fake = FakeStorage::SearchSubset(6, BitVector({0, 0, 0, 1, 1, 1}));
+
+ BitVector bv{0, 1, 0, 1, 1, 1};
+ DenseNullStorage storage(std::move(fake), &bv);
+
+ std::vector<uint32_t> index({5, 2, 3, 4, 1});
+ auto res = storage.IndexSearch(FilterOp::kIsNull, SqlValue(), index.data(),
+ static_cast<uint32_t>(index.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 2, 3));
+}
+
+} // namespace
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/dummy_storage.cc b/src/trace_processor/db/storage/dummy_storage.cc
index 286a028..b4f0be5 100644
--- a/src/trace_processor/db/storage/dummy_storage.cc
+++ b/src/trace_processor/db/storage/dummy_storage.cc
@@ -21,6 +21,12 @@
namespace trace_processor {
namespace storage {
+DummyStorage::SearchValidationResult DummyStorage::ValidateSearchConstraints(
+ SqlValue,
+ FilterOp) const {
+ PERFETTO_FATAL("Shouldn't be called");
+}
+
RangeOrBitVector DummyStorage::Search(FilterOp, SqlValue, RowMap::Range) const {
PERFETTO_FATAL("Shouldn't be called");
}
@@ -45,7 +51,7 @@
return 0;
}
-void DummyStorage::Serialize(protos::pbzero::SerializedColumn::Storage*) const {
+void DummyStorage::Serialize(StorageProto*) const {
PERFETTO_FATAL("Shouldn't be called");
}
diff --git a/src/trace_processor/db/storage/dummy_storage.h b/src/trace_processor/db/storage/dummy_storage.h
index 62dd0ef..7fd3a40 100644
--- a/src/trace_processor/db/storage/dummy_storage.h
+++ b/src/trace_processor/db/storage/dummy_storage.h
@@ -36,6 +36,9 @@
RangeOrBitVector Search(FilterOp, SqlValue, RowMap::Range) const override;
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector IndexSearch(FilterOp,
SqlValue,
uint32_t*,
@@ -46,7 +49,7 @@
void Sort(uint32_t*, uint32_t) const override;
- void Serialize(protos::pbzero::SerializedColumn_Storage*) const override;
+ void Serialize(StorageProto*) const override;
uint32_t size() const override;
};
diff --git a/src/trace_processor/db/storage/fake_storage.cc b/src/trace_processor/db/storage/fake_storage.cc
new file mode 100644
index 0000000..fd9ac2c
--- /dev/null
+++ b/src/trace_processor/db/storage/fake_storage.cc
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/fake_storage.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+FakeStorage::FakeStorage(uint32_t size, SearchStrategy strategy)
+ : size_(size), strategy_(strategy) {}
+
+FakeStorage::SearchValidationResult FakeStorage::ValidateSearchConstraints(
+ SqlValue,
+ FilterOp) const {
+ return SearchValidationResult::kOk;
+}
+
+RangeOrBitVector FakeStorage::Search(FilterOp,
+ SqlValue,
+ RowMap::Range in) const {
+ switch (strategy_) {
+ case kAll:
+ return RangeOrBitVector(in);
+ case kNone:
+ return RangeOrBitVector(RowMap::Range());
+ case kRange:
+ return RangeOrBitVector(RowMap::Range(std::max(in.start, range_.start),
+ std::min(in.end, range_.end)));
+ case kBitVector:
+ return RangeOrBitVector{bit_vector_.IntersectRange(in.start, in.end)};
+ }
+ PERFETTO_FATAL("For GCC");
+}
+
+RangeOrBitVector FakeStorage::IndexSearch(FilterOp,
+ SqlValue,
+ uint32_t* indices,
+ uint32_t indices_size,
+ bool) const {
+ switch (strategy_) {
+ case kAll:
+ return RangeOrBitVector(RowMap::Range(0, indices_size));
+ case kNone:
+ return RangeOrBitVector(RowMap::Range());
+ case kRange:
+ case kBitVector: {
+ BitVector::Builder builder(indices_size);
+ for (uint32_t* it = indices; it != indices + indices_size; ++it) {
+ bool in_range = strategy_ == kRange && range_.Contains(*it);
+ bool in_bv = strategy_ == kBitVector && bit_vector_.IsSet(*it);
+ builder.Append(in_range || in_bv);
+ }
+ return RangeOrBitVector(std::move(builder).Build());
+ }
+ }
+ PERFETTO_FATAL("For GCC");
+}
+
+void FakeStorage::StableSort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void FakeStorage::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void FakeStorage::Serialize(StorageProto*) const {
+ // FakeStorage doesn't really make sense to serialize.
+ PERFETTO_FATAL("Not implemented");
+}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/fake_storage.h b/src/trace_processor/db/storage/fake_storage.h
new file mode 100644
index 0000000..2320269
--- /dev/null
+++ b/src/trace_processor/db/storage/fake_storage.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_FAKE_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_FAKE_STORAGE_H_
+
+#include <memory>
+#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Fake implementation of Storage for use in tests.
+class FakeStorage final : public Storage {
+ public:
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
+ RangeOrBitVector Search(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const override;
+
+ RangeOrBitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count,
+ bool sorted) const override;
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Serialize(StorageProto*) const override;
+
+ static std::unique_ptr<Storage> SearchAll(uint32_t size) {
+ return std::unique_ptr<Storage>(
+ new FakeStorage(size, SearchStrategy::kAll));
+ }
+
+ static std::unique_ptr<Storage> SearchNone(uint32_t size) {
+ return std::unique_ptr<Storage>(
+ new FakeStorage(size, SearchStrategy::kNone));
+ }
+
+ static std::unique_ptr<Storage> SearchSubset(uint32_t size, RowMap::Range r) {
+ std::unique_ptr<FakeStorage> storage(
+ new FakeStorage(size, SearchStrategy::kRange));
+ storage->range_ = r;
+ return std::move(storage);
+ }
+
+ static std::unique_ptr<Storage> SearchSubset(uint32_t size, BitVector bv) {
+ std::unique_ptr<FakeStorage> storage(
+ new FakeStorage(size, SearchStrategy::kBitVector));
+ storage->bit_vector_ = std::move(bv);
+ return std::move(storage);
+ }
+
+ uint32_t size() const override { return size_; }
+
+ private:
+ enum SearchStrategy { kNone, kAll, kRange, kBitVector };
+ FakeStorage(uint32_t size, SearchStrategy strategy);
+
+ uint32_t size_ = 0;
+ SearchStrategy strategy_ = SearchStrategy::kNone;
+ RowMap::Range range_;
+ BitVector bit_vector_;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_FAKE_STORAGE_H_
diff --git a/src/trace_processor/db/storage/id_storage.cc b/src/trace_processor/db/storage/id_storage.cc
index 528ca3c..bec9b041 100644
--- a/src/trace_processor/db/storage/id_storage.cc
+++ b/src/trace_processor/db/storage/id_storage.cc
@@ -16,12 +16,15 @@
#include "src/trace_processor/db/storage/id_storage.h"
+#include <optional>
+
#include "perfetto/base/logging.h"
-#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/public/compiler.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/types.h"
+#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/tp_metatrace.h"
namespace perfetto {
@@ -68,33 +71,112 @@
}
return RangeOrBitVector(std::move(builder).Build());
}
+
} // namespace
+IdStorage::SearchValidationResult IdStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_DFATAL(
+ "Invalid filter operation. NULL should only be compared with 'IS NULL' "
+ "and 'IS NOT NULL'");
+ return SearchValidationResult::kNoData;
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraint");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): Remove after adding support for double
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+
+ // Bounds of the value.
+ if (PERFETTO_UNLIKELY(val.AsLong() > std::numeric_limits<uint32_t>::max())) {
+ if (op == FilterOp::kLe || op == FilterOp::kLt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+ if (PERFETTO_UNLIKELY(val.AsLong() < std::numeric_limits<uint32_t>::min())) {
+ if (op == FilterOp::kGe || op == FilterOp::kGt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector IdStorage::Search(FilterOp op,
SqlValue sql_val,
- RowMap::Range range) const {
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "IdStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ PERFETTO_DCHECK(search_range.end <= size_);
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(search_range);
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
if (op == FilterOp::kNe) {
- if (sql_val.AsLong() > std::numeric_limits<uint32_t>::max() ||
- sql_val.AsLong() < std::numeric_limits<uint32_t>::min())
- return RangeOrBitVector(Range(0, size_));
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
- BitVector ret(range.start, false);
- ret.Resize(range.end, true);
+ BitVector ret(search_range.start, false);
+ ret.Resize(search_range.end, true);
ret.Resize(size_, false);
-
ret.Clear(val);
return RangeOrBitVector(std::move(ret));
}
- return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
RangeOrBitVector IdStorage::IndexSearch(FilterOp op,
@@ -108,29 +190,17 @@
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- // Validate sql_val
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
}
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
- }
uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
switch (op) {
@@ -153,46 +223,15 @@
return IndexSearchWithComparator(val, indices, indices_size,
std::greater_equal<uint32_t>());
case FilterOp::kIsNotNull:
- return RangeOrBitVector(Range(indices_size, true));
case FilterOp::kIsNull:
case FilterOp::kGlob:
case FilterOp::kRegex:
- return RangeOrBitVector(Range());
+ PERFETTO_FATAL("Invalid filter operation");
}
PERFETTO_FATAL("FilterOp not matched");
}
-Range IdStorage::BinarySearchIntrinsic(FilterOp op,
- SqlValue sql_val,
- Range range) const {
- PERFETTO_DCHECK(range.end <= size_);
-
- // Validate sql_value
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return range;
- }
- return Range();
- }
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
-
+Range IdStorage::BinarySearchIntrinsic(FilterOp op, Id val, Range range) const {
switch (op) {
case FilterOp::kEq:
return Range(val, val + (range.start <= val && val < range.end));
@@ -205,13 +244,11 @@
case FilterOp::kGt:
return RowMap::Range(std::max(val + 1, range.start), range.end);
case FilterOp::kIsNotNull:
- return range;
case FilterOp::kNe:
- PERFETTO_FATAL("Shouldn't be called");
case FilterOp::kIsNull:
case FilterOp::kGlob:
case FilterOp::kRegex:
- return RowMap::Range();
+ PERFETTO_FATAL("Invalid filter operation");
}
PERFETTO_FATAL("FilterOp not matched");
}
@@ -225,8 +262,7 @@
std::sort(indices, indices + indices_size);
}
-void IdStorage::Serialize(
- protos::pbzero::SerializedColumn::Storage* storage) const {
+void IdStorage::Serialize(StorageProto* storage) const {
auto* id_storage = storage->set_id_storage();
id_storage->set_size(size_);
}
diff --git a/src/trace_processor/db/storage/id_storage.h b/src/trace_processor/db/storage/id_storage.h
index 6f60d9c..475c29b 100644
--- a/src/trace_processor/db/storage/id_storage.h
+++ b/src/trace_processor/db/storage/id_storage.h
@@ -16,6 +16,10 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_ID_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_ID_STORAGE_H_
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
@@ -34,6 +38,9 @@
public:
explicit IdStorage(uint32_t size) : size_(size) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -48,14 +55,16 @@
void Sort(uint32_t* rows, uint32_t rows_size) const override;
- void Serialize(protos::pbzero::SerializedColumn_Storage*) const override;
+ void Serialize(StorageProto*) const override;
uint32_t size() const override { return size_; }
private:
- BitVector IndexSearch(FilterOp, SqlValue, uint32_t*, uint32_t) const;
+ using Id = uint32_t;
+
+ BitVector IndexSearch(FilterOp, Id, uint32_t*, uint32_t) const;
RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ Id,
RowMap::Range search_range) const;
const uint32_t size_ = 0;
diff --git a/src/trace_processor/db/storage/id_storage_unittest.cc b/src/trace_processor/db/storage/id_storage_unittest.cc
index 13229e3..0db3159 100644
--- a/src/trace_processor/db/storage/id_storage_unittest.cc
+++ b/src/trace_processor/db/storage/id_storage_unittest.cc
@@ -14,18 +14,101 @@
* limitations under the License.
*/
#include "src/trace_processor/db/storage/id_storage.h"
+#include <limits>
#include "src/trace_processor/db/storage/types.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
-TEST(IdStorageUnittest, BinarySearchIntrinsicEqSimple) {
+TEST(IdStorageUnittest, InvalidSearchConstraints) {
+ IdStorage storage(100);
+ Range test_range(10, 20);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Value bounds
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
+TEST(IdStorageUnittest, SearchEqSimple) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kEq, SqlValue::Long(15), Range(10, 20))
.TakeIfRange();
@@ -34,21 +117,21 @@
ASSERT_EQ(range.end, 16u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicEqOnRangeBoundary) {
+TEST(IdStorageUnittest, SearchEqOnRangeBoundary) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kEq, SqlValue::Long(20), Range(10, 20))
.TakeIfRange();
ASSERT_EQ(range.size(), 0u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicEqOutsideRange) {
+TEST(IdStorageUnittest, SearchEqOutsideRange) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kEq, SqlValue::Long(25), Range(10, 20))
.TakeIfRange();
ASSERT_EQ(range.size(), 0u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicEqTooBig) {
+TEST(IdStorageUnittest, SearchEqTooBig) {
IdStorage storage(100);
Range range =
storage.Search(FilterOp::kEq, SqlValue::Long(125), Range(10, 20))
@@ -56,7 +139,7 @@
ASSERT_EQ(range.size(), 0u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicLe) {
+TEST(IdStorageUnittest, SearchLe) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kLe, SqlValue::Long(50), Range(30, 70))
.TakeIfRange();
@@ -64,7 +147,7 @@
ASSERT_EQ(range.end, 51u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicLt) {
+TEST(IdStorageUnittest, SearchLt) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kLt, SqlValue::Long(50), Range(30, 70))
.TakeIfRange();
@@ -72,7 +155,7 @@
ASSERT_EQ(range.end, 50u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicGe) {
+TEST(IdStorageUnittest, SearchGe) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kGe, SqlValue::Long(40), Range(30, 70))
.TakeIfRange();
@@ -80,7 +163,7 @@
ASSERT_EQ(range.end, 70u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicGt) {
+TEST(IdStorageUnittest, SearchGt) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kGt, SqlValue::Long(40), Range(30, 70))
.TakeIfRange();
@@ -88,7 +171,7 @@
ASSERT_EQ(range.end, 70u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicNe) {
+TEST(IdStorageUnittest, SearchNe) {
IdStorage storage(100);
BitVector bv =
storage.Search(FilterOp::kNe, SqlValue::Long(40), Range(30, 70))
@@ -96,11 +179,110 @@
ASSERT_EQ(bv.CountSetBits(), 39u);
}
-TEST(IdStorageUnittest, BinarySearchIntrinsicNeInvalidNum) {
+TEST(IdStorageUnittest, SearchNeInvalidNum) {
IdStorage storage(100);
Range r = storage.Search(FilterOp::kNe, SqlValue::Long(-1), Range(30, 70))
.TakeIfRange();
- ASSERT_EQ(r.size(), 100u);
+ ASSERT_EQ(r.size(), 40u);
+}
+
+TEST(IdStorageUnittest, IndexSearchEqSimple) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kEq, SqlValue::Long(3), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 1u);
+ ASSERT_TRUE(bv.IsSet(1));
+}
+
+TEST(IdStorageUnittest, IndexSearchEqTooBig) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kEq, SqlValue::Long(20), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 0u);
+}
+
+TEST(IdStorageUnittest, IndexSearchNe) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kNe, SqlValue::Long(3), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 7u);
+ ASSERT_FALSE(bv.IsSet(1));
+}
+
+TEST(IdStorageUnittest, IndexSearchLe) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kLe, SqlValue::Long(3), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 3u);
+ ASSERT_TRUE(bv.IsSet(0));
+ ASSERT_TRUE(bv.IsSet(1));
+ ASSERT_TRUE(bv.IsSet(6));
+}
+
+TEST(IdStorageUnittest, IndexSearchLt) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kLt, SqlValue::Long(3), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 2u);
+}
+
+TEST(IdStorageUnittest, IndexSearchGe) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kGe, SqlValue::Long(6), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 3u);
+}
+
+TEST(IdStorageUnittest, IndexSearchGt) {
+ IdStorage storage(12);
+ std::vector<uint32_t> indices{1, 3, 5, 7, 9, 11, 2, 4};
+
+ BitVector bv =
+ storage
+ .IndexSearch(FilterOp::kGt, SqlValue::Long(6), indices.data(),
+ static_cast<uint32_t>(indices.size()), false)
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 3u);
+ ASSERT_TRUE(bv.IsSet(3));
+ ASSERT_TRUE(bv.IsSet(4));
+ ASSERT_TRUE(bv.IsSet(5));
}
TEST(IdStorageUnittest, Sort) {
diff --git a/src/trace_processor/db/storage/null_storage.cc b/src/trace_processor/db/storage/null_storage.cc
new file mode 100644
index 0000000..6de2759
--- /dev/null
+++ b/src/trace_processor/db/storage/null_storage.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/null_storage.h"
+
+#include <cstdint>
+#include <variant>
+
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "src/trace_processor/tp_metatrace.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using Range = RowMap::Range;
+
+RangeOrBitVector ReconcileStorageResult(FilterOp op,
+ const BitVector& non_null,
+ RangeOrBitVector storage_result,
+ Range in_range) {
+ PERFETTO_CHECK(in_range.end <= non_null.size());
+
+ // Reconcile the results of the Search operation with the non-null indices
+ // to ensure only those positions are set.
+ BitVector res;
+ if (storage_result.IsRange()) {
+ Range range = std::move(storage_result).TakeIfRange();
+ if (range.size() > 0) {
+ res = non_null.IntersectRange(non_null.IndexOfNthSet(range.start),
+ non_null.IndexOfNthSet(range.end - 1) + 1);
+
+ // We should always have at least as many elements as the input range
+ // itself.
+ PERFETTO_CHECK(res.size() <= in_range.end);
+ }
+ } else {
+ res = non_null.Copy();
+ res.UpdateSetBits(std::move(storage_result).TakeIfBitVector());
+ }
+
+ // Ensure that |res| exactly matches the size which we need to return,
+ // padding with zeros or truncating if necessary.
+ res.Resize(in_range.end, false);
+
+ // For the IS NULL constraint, we also need to include all the null indices
+ // themselves.
+ if (PERFETTO_UNLIKELY(op == FilterOp::kIsNull)) {
+ BitVector null = non_null.IntersectRange(in_range.start, in_range.end);
+ null.Resize(in_range.end, false);
+ null.Not();
+ res.Or(null);
+ }
+ return RangeOrBitVector(std::move(res));
+}
+
+} // namespace
+
+Storage::SearchValidationResult NullStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return storage_->ValidateSearchConstraints(sql_val, op);
+}
+
+NullStorage::NullStorage(std::unique_ptr<Storage> storage,
+ const BitVector* non_null)
+ : storage_(std::move(storage)), non_null_(non_null) {
+ PERFETTO_DCHECK(non_null_->CountSetBits() <= storage_->size());
+}
+
+RangeOrBitVector NullStorage::Search(FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range in) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "NullStorage::Search");
+
+ // Figure out the bounds of the indices in the underlying storage and search
+ // it.
+ uint32_t start = non_null_->CountSetBits(in.start);
+ uint32_t end = non_null_->CountSetBits(in.end);
+ return ReconcileStorageResult(
+ op, *non_null_, storage_->Search(op, sql_val, RowMap::Range(start, end)),
+ in);
+}
+
+RangeOrBitVector NullStorage::IndexSearch(FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size,
+ bool sorted) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "NullStorage::IndexSearch");
+
+ BitVector::Builder storage_non_null(indices_size);
+ std::vector<uint32_t> storage_iv;
+ storage_iv.reserve(indices_size);
+ for (uint32_t* it = indices; it != indices + indices_size; it++) {
+ bool is_non_null = non_null_->IsSet(*it);
+ if (is_non_null) {
+ storage_iv.push_back(non_null_->CountSetBits(*it));
+ }
+ storage_non_null.Append(is_non_null);
+ }
+ RangeOrBitVector range_or_bv =
+ storage_->IndexSearch(op, sql_val, storage_iv.data(),
+ static_cast<uint32_t>(storage_iv.size()), sorted);
+ return ReconcileStorageResult(op, std::move(storage_non_null).Build(),
+ std::move(range_or_bv), Range(0, indices_size));
+}
+
+void NullStorage::StableSort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void NullStorage::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void NullStorage::Serialize(StorageProto* storage) const {
+ auto* null_storage = storage->set_null_storage();
+ non_null_->Serialize(null_storage->set_bit_vector());
+ storage_->Serialize(null_storage->set_storage());
+}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/null_storage.h b/src/trace_processor/db/storage/null_storage.h
new file mode 100644
index 0000000..3087749
--- /dev/null
+++ b/src/trace_processor/db/storage/null_storage.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_NULL_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_NULL_STORAGE_H_
+
+#include <memory>
+#include <variant>
+
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Storage which introduces the layer of nullability. Specifically, spreads out
+// the storage with nulls using a BitVector.
+class NullStorage : public Storage {
+ public:
+ NullStorage(std::unique_ptr<Storage> storage, const BitVector* non_null);
+
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
+ RangeOrBitVector Search(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const override;
+
+ RangeOrBitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count,
+ bool sorted) const override;
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Serialize(StorageProto*) const override;
+
+ uint32_t size() const override { return non_null_->size(); }
+
+ private:
+ std::unique_ptr<Storage> storage_ = nullptr;
+ const BitVector* non_null_ = nullptr;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_NULL_STORAGE_H_
diff --git a/src/trace_processor/db/storage/null_storage_unittest.cc b/src/trace_processor/db/storage/null_storage_unittest.cc
new file mode 100644
index 0000000..daa235f
--- /dev/null
+++ b/src/trace_processor/db/storage/null_storage_unittest.cc
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/null_storage.h"
+
+#include <memory>
+#include <vector>
+
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/fake_storage.h"
+#include "src/trace_processor/db/storage/numeric_storage.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+using Range = RowMap::Range;
+
+std::vector<uint32_t> ToIndexVector(RangeOrBitVector& r_or_bv) {
+ RowMap rm;
+ if (r_or_bv.IsBitVector()) {
+ rm = RowMap(std::move(r_or_bv).TakeIfBitVector());
+ } else {
+ Range range = std::move(r_or_bv).TakeIfRange();
+ rm = RowMap(range.start, range.end);
+ }
+ return rm.GetAllIndices();
+}
+
+TEST(NullStorage, SearchInputInsideBoundary) {
+ BitVector bv{0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullStorage storage(FakeStorage::SearchAll(4u), &bv);
+
+ auto res = storage.Search(FilterOp::kGt, SqlValue::Long(0), Range(1, 6));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(3, 4));
+}
+
+TEST(NullStorage, SearchInputOutsideBoundary) {
+ BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullStorage storage(FakeStorage::SearchAll(5u), &bv);
+
+ auto res = storage.Search(FilterOp::kGt, SqlValue::Long(0), Range(3, 8));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(3, 4, 7));
+}
+
+TEST(NullStorage, SubsetResultOutsideBoundary) {
+ BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullStorage storage(FakeStorage::SearchSubset(5u, RowMap::Range(1, 3)), &bv);
+
+ auto res = storage.Search(FilterOp::kGt, SqlValue::Long(0), Range(0, 11));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(3, 4));
+}
+
+TEST(NullStorage, SubsetResultOnBoundary) {
+ BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+ NullStorage storage(FakeStorage::SearchAll(5u), &bv);
+
+ auto res = storage.Search(FilterOp::kGt, SqlValue::Long(0), Range(0, 11));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1, 3, 4, 7, 8));
+}
+
+TEST(NullStorage, BitVectorSubset) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchSubset(4u, BitVector{0, 1, 0, 1}),
+ &bv);
+
+ auto res = storage.Search(FilterOp::kGt, SqlValue::Long(0), Range(0, 8));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(2, 6));
+}
+
+TEST(NullStorage, BitVectorSubsetIsNull) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchSubset(4u, BitVector{0, 1, 0, 1}),
+ &bv);
+
+ auto res = storage.Search(FilterOp::kIsNull, SqlValue(), Range(0, 8));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 2, 3, 4, 6, 7));
+}
+
+TEST(NullStorage, IndexSearchAllElements) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchAll(4u), &bv);
+
+ std::vector<uint32_t> table_idx{1, 5, 2};
+ auto res =
+ storage.IndexSearch(FilterOp::kGt, SqlValue::Long(0), table_idx.data(),
+ uint32_t(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 2));
+}
+
+TEST(NullStorage, IndexSearchPartialElements) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchAll(4u), &bv);
+
+ std::vector<uint32_t> table_idx{1, 4, 2};
+ auto res =
+ storage.IndexSearch(FilterOp::kGt, SqlValue::Long(0), table_idx.data(),
+ uint32_t(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 2));
+}
+
+TEST(NullStorage, IndexSearchIsNullOpEmptyRes) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchNone(4u), &bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 5, 4, 2};
+ auto res =
+ storage.IndexSearch(FilterOp::kIsNull, SqlValue(), table_idx.data(),
+ uint32_t(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 3));
+}
+
+TEST(NullStorage, IndexSearchIsNullOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchSubset(4u, Range(2, 3)), &bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 2, 4, 5};
+ auto res =
+ storage.IndexSearch(FilterOp::kIsNull, SqlValue(), table_idx.data(),
+ uint32_t(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 1, 3, 4));
+}
+
+TEST(NullStorage, IndexSearchIsNotNullOp) {
+ BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+ NullStorage storage(FakeStorage::SearchAll(4u), &bv);
+
+ std::vector<uint32_t> table_idx{0, 3, 4};
+ auto res =
+ storage.IndexSearch(FilterOp::kIsNotNull, SqlValue(), table_idx.data(),
+ uint32_t(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), IsEmpty());
+}
+
+} // namespace
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/numeric_storage.cc b/src/trace_processor/db/storage/numeric_storage.cc
index abf9a14..0b792de 100644
--- a/src/trace_processor/db/storage/numeric_storage.cc
+++ b/src/trace_processor/db/storage/numeric_storage.cc
@@ -17,12 +17,17 @@
#include "src/trace_processor/db/storage/numeric_storage.h"
+#include <cmath>
#include <cstddef>
#include <string>
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/public/compiler.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/tp_metatrace.h"
@@ -32,7 +37,7 @@
namespace storage {
namespace {
-// All viable numeric values for ColumnTypes.
+using Range = RowMap::Range;
using NumericValue = std::variant<uint32_t, int32_t, int64_t, double_t>;
// Using the fact that binary operators in std are operators() of classes, we
@@ -46,33 +51,22 @@
std::equal_to<T>,
std::not_equal_to<T>>;
-// Based on SqlValue and ColumnType, casts SqlValue to proper type, returns
-// std::nullopt if SqlValue can't be cast and should be considered invalid for
-// comparison.
-inline std::optional<NumericValue> GetNumericTypeVariant(ColumnType type,
- SqlValue val) {
- if (val.is_null())
- return std::nullopt;
-
+// Based on SqlValue and ColumnType, casts SqlValue to proper type. Assumes the
+// |val| and |type| are correct.
+inline NumericValue GetNumericTypeVariant(ColumnType type, SqlValue val) {
switch (type) {
case ColumnType::kDouble:
return val.AsDouble();
case ColumnType::kInt64:
return val.AsLong();
case ColumnType::kInt32:
- if (val.AsLong() > std::numeric_limits<int32_t>::max() ||
- val.AsLong() < std::numeric_limits<int32_t>::min())
- return std::nullopt;
return static_cast<int32_t>(val.AsLong());
case ColumnType::kUint32:
- if (val.AsLong() > std::numeric_limits<uint32_t>::max() ||
- val.AsLong() < std::numeric_limits<uint32_t>::min())
- return std::nullopt;
return static_cast<uint32_t>(val.AsLong());
case ColumnType::kString:
case ColumnType::kDummy:
case ColumnType::kId:
- return std::nullopt;
+ PERFETTO_FATAL("Invalid type");
}
PERFETTO_FATAL("For GCC");
}
@@ -201,73 +195,198 @@
} // namespace
+NumericStorageBase::SearchValidationResult
+NumericStorageBase::ValidateSearchConstraints(SqlValue val, FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_FATAL(
+ "Invalid path. NULL should only be compared with 'IS NULL' and 'IS NOT "
+ "NULL'");
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraint");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): There is currently no support for comparison with double
+ // and it is prevented on QueryExecutor level.
+ if (type_ != ColumnType::kDouble) {
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+ }
+
+ // Bounds of the value.
+ enum ExtremeVal { kTooBig, kTooSmall, kOk };
+ ExtremeVal extreme_validator = kOk;
+
+ switch (type_) {
+ case ColumnType::kDouble:
+ // Any value would make a sensible comparison with a double.
+ case ColumnType::kInt64:
+ // TODO(b/307482437): As long as the type is not double there is nothing
+ // to verify here, as all values are going to be in the int64_t limits.
+ break;
+ case ColumnType::kInt32:
+ if (val.AsLong() > std::numeric_limits<int32_t>::max()) {
+ extreme_validator = kTooBig;
+ break;
+ }
+ if (val.AsLong() < std::numeric_limits<int32_t>::min()) {
+ extreme_validator = kTooSmall;
+ break;
+ }
+ break;
+ case ColumnType::kUint32:
+ if (val.AsLong() > std::numeric_limits<uint32_t>::max()) {
+ extreme_validator = kTooBig;
+ break;
+ }
+ if (val.AsLong() < std::numeric_limits<uint32_t>::min()) {
+ extreme_validator = kTooSmall;
+ break;
+ }
+ break;
+ case ColumnType::kString:
+ case ColumnType::kDummy:
+ case ColumnType::kId:
+ break;
+ }
+
+ switch (extreme_validator) {
+ case kOk:
+ return Storage::SearchValidationResult::kOk;
+ case kTooBig:
+ if (op == FilterOp::kLt || op == FilterOp::kLe || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ case kTooSmall:
+ if (op == FilterOp::kGt || op == FilterOp::kGe || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ PERFETTO_FATAL("For GCC");
+}
+
RangeOrBitVector NumericStorageBase::Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const {
+ SqlValue sql_val,
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "NumericStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ NumericValue val = GetNumericTypeVariant(type_, sql_val);
+
if (is_sorted_) {
if (op != FilterOp::kNe) {
- return RangeOrBitVector(BinarySearchIntrinsic(op, value, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
// Not equal is a special operation on binary search, as it doesn't define a
// range, and rather just `not` range returned with `equal` operation.
- RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, value, range);
+ RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, val, search_range);
BitVector bv(r.start, true);
- bv.Resize(r.end);
- bv.Resize(range.end, true);
+ bv.Resize(r.end, false);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(LinearSearchInternal(op, value, range));
+ return RangeOrBitVector(LinearSearchInternal(op, val, search_range));
}
RangeOrBitVector NumericStorageBase::IndexSearch(FilterOp op,
- SqlValue value,
+ SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
+ uint32_t indices_size,
bool sorted) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "NumericStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+ NumericValue val = GetNumericTypeVariant(type_, sql_val);
if (sorted) {
return RangeOrBitVector(
- BinarySearchExtrinsic(op, value, indices, indices_count));
+ BinarySearchExtrinsic(op, val, indices, indices_size));
}
- return RangeOrBitVector(
- IndexSearchInternal(op, value, indices, indices_count));
+ return RangeOrBitVector(IndexSearchInternal(op, val, indices, indices_size));
}
BitVector NumericStorageBase::LinearSearchInternal(FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
RowMap::Range range) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return BitVector(size(), true);
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return BitVector(size(), false);
-
BitVector::Builder builder(range.end, range.start);
- if (const auto* u32 = std::get_if<uint32_t>(&*val)) {
+ if (const auto* u32 = std::get_if<uint32_t>(&val)) {
auto* start = static_cast<const uint32_t*>(data_) + range.start;
TypedLinearSearch(*u32, start, op, builder);
- } else if (const auto* i64 = std::get_if<int64_t>(&*val)) {
+ } else if (const auto* i64 = std::get_if<int64_t>(&val)) {
auto* start = static_cast<const int64_t*>(data_) + range.start;
TypedLinearSearch(*i64, start, op, builder);
- } else if (const auto* i32 = std::get_if<int32_t>(&*val)) {
+ } else if (const auto* i32 = std::get_if<int32_t>(&val)) {
auto* start = static_cast<const int32_t*>(data_) + range.start;
TypedLinearSearch(*i32, start, op, builder);
- } else if (const auto* db = std::get_if<double>(&*val)) {
+ } else if (const auto* db = std::get_if<double>(&val)) {
auto* start = static_cast<const double*>(data_) + range.start;
TypedLinearSearch(*db, start, op, builder);
} else {
@@ -278,16 +397,9 @@
BitVector NumericStorageBase::IndexSearchInternal(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return BitVector(indices_count, true);
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return BitVector(indices_count, false);
-
BitVector::Builder builder(indices_count);
std::visit(
[this, indices, op, &builder](auto val) {
@@ -300,37 +412,30 @@
},
GetFilterOpVariant<T>(op));
},
- *val);
+ val);
return std::move(builder).Build();
}
RowMap::Range NumericStorageBase::BinarySearchIntrinsic(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
RowMap::Range search_range) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return search_range;
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return RowMap::Range();
-
switch (op) {
case FilterOp::kEq:
- return RowMap::Range(LowerBoundIntrinsic(data_, *val, search_range),
- UpperBoundIntrinsic(data_, *val, search_range));
+ return RowMap::Range(LowerBoundIntrinsic(data_, val, search_range),
+ UpperBoundIntrinsic(data_, val, search_range));
case FilterOp::kLe: {
return RowMap::Range(search_range.start,
- UpperBoundIntrinsic(data_, *val, search_range));
+ UpperBoundIntrinsic(data_, val, search_range));
}
case FilterOp::kLt:
return RowMap::Range(search_range.start,
- LowerBoundIntrinsic(data_, *val, search_range));
+ LowerBoundIntrinsic(data_, val, search_range));
case FilterOp::kGe:
- return RowMap::Range(LowerBoundIntrinsic(data_, *val, search_range),
+ return RowMap::Range(LowerBoundIntrinsic(data_, val, search_range),
search_range.end);
case FilterOp::kGt:
- return RowMap::Range(UpperBoundIntrinsic(data_, *val, search_range),
+ return RowMap::Range(UpperBoundIntrinsic(data_, val, search_range),
search_range.end);
case FilterOp::kNe:
case FilterOp::kIsNull:
@@ -344,34 +449,26 @@
RowMap::Range NumericStorageBase::BinarySearchExtrinsic(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-
- if (op == FilterOp::kIsNotNull)
- return RowMap::Range(0, size());
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return RowMap::Range();
-
switch (op) {
case FilterOp::kEq:
return RowMap::Range(
- LowerBoundExtrinsic(data_, *val, indices, indices_count),
- UpperBoundExtrinsic(data_, *val, indices, indices_count));
+ LowerBoundExtrinsic(data_, val, indices, indices_count),
+ UpperBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kLe:
return RowMap::Range(
- 0, UpperBoundExtrinsic(data_, *val, indices, indices_count));
+ 0, UpperBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kLt:
return RowMap::Range(
- 0, LowerBoundExtrinsic(data_, *val, indices, indices_count));
+ 0, LowerBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kGe:
return RowMap::Range(
- LowerBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ LowerBoundExtrinsic(data_, val, indices, indices_count), size_);
case FilterOp::kGt:
return RowMap::Range(
- UpperBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ UpperBoundExtrinsic(data_, val, indices, indices_count), size_);
case FilterOp::kNe:
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
@@ -383,7 +480,6 @@
}
void NumericStorageBase::StableSort(uint32_t* rows, uint32_t rows_size) const {
- NumericValue val = *GetNumericTypeVariant(type_, SqlValue::Long(0));
std::visit(
[this, &rows, rows_size](auto val_data) {
using T = decltype(val_data);
@@ -395,20 +491,19 @@
return first_val < second_val;
});
},
- val);
+ GetNumericTypeVariant(type_, SqlValue::Long(0)));
}
-void NumericStorageBase::Sort(uint32_t*, uint32_t) const {}
+void NumericStorageBase::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_ELOG("Not implemented");
+}
-void NumericStorageBase::Serialize(
- protos::pbzero::SerializedColumn::Storage* msg) const {
+void NumericStorageBase::Serialize(StorageProto* msg) const {
auto* numeric_storage_msg = msg->set_numeric_storage();
numeric_storage_msg->set_is_sorted(is_sorted_);
numeric_storage_msg->set_column_type(static_cast<uint32_t>(type_));
- auto* values_msg = numeric_storage_msg->set_values();
- values_msg->set_size(size_);
-
uint32_t type_size;
switch (type_) {
case ColumnType::kInt64:
@@ -428,8 +523,8 @@
case ColumnType::kString:
PERFETTO_FATAL("Invalid column type for NumericStorage");
}
- values_msg->set_data(static_cast<const uint8_t*>(data_),
- static_cast<size_t>(type_size * size_));
+ numeric_storage_msg->set_values(static_cast<const uint8_t*>(data_),
+ static_cast<size_t>(type_size) * size_);
}
} // namespace storage
diff --git a/src/trace_processor/db/storage/numeric_storage.h b/src/trace_processor/db/storage/numeric_storage.h
index 3618361..741a8e0 100644
--- a/src/trace_processor/db/storage/numeric_storage.h
+++ b/src/trace_processor/db/storage/numeric_storage.h
@@ -18,6 +18,7 @@
#include <variant>
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
@@ -33,6 +34,9 @@
// Storage for all numeric type data (i.e. doubles, int32, int64, uint32).
class NumericStorageBase : public Storage {
public:
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -47,7 +51,7 @@
void Sort(uint32_t* rows, uint32_t rows_size) const override;
- void Serialize(protos::pbzero::SerializedColumn_Storage*) const override;
+ void Serialize(StorageProto*) const override;
inline uint32_t size() const override { return size_; }
@@ -59,21 +63,24 @@
: size_(size), data_(data), type_(type), is_sorted_(is_sorted) {}
private:
+ // All viable numeric values for ColumnTypes.
+ using NumericValue = std::variant<uint32_t, int32_t, int64_t, double>;
+
BitVector LinearSearchInternal(FilterOp op,
- SqlValue val,
+ NumericValue val,
RowMap::Range) const;
BitVector IndexSearchInternal(FilterOp op,
- SqlValue value,
+ NumericValue value,
uint32_t* indices,
uint32_t indices_count) const;
RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ NumericValue val,
RowMap::Range search_range) const;
RowMap::Range BinarySearchExtrinsic(FilterOp op,
- SqlValue val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const;
diff --git a/src/trace_processor/db/storage/numeric_storage_unittest.cc b/src/trace_processor/db/storage/numeric_storage_unittest.cc
index b6ffb59..36fa51a 100644
--- a/src/trace_processor/db/storage/numeric_storage_unittest.cc
+++ b/src/trace_processor/db/storage/numeric_storage_unittest.cc
@@ -20,11 +20,160 @@
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
+TEST(NumericStorageUnittest, InvalidSearchConstraintsGeneralChecks) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<uint32_t> storage(&data_vec, ColumnType::kUint32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
+TEST(NumericStorageUnittest, InvalidValueBoundsUint32) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<uint32_t> storage(&data_vec, ColumnType::kUint32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ Range search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
+TEST(NumericStorageUnittest, InvalidValueBoundsInt32) {
+ std::vector<int32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<int32_t> storage(&data_vec, ColumnType::kInt32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 10);
+ Range search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
TEST(NumericStorageUnittest, StableSortTrivial) {
std::vector<uint32_t> data_vec{0, 1, 2, 0, 1, 2, 0, 1, 2};
std::vector<uint32_t> out = {0, 1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/src/trace_processor/db/storage/selector_storage.cc b/src/trace_processor/db/storage/selector_storage.cc
new file mode 100644
index 0000000..934b900
--- /dev/null
+++ b/src/trace_processor/db/storage/selector_storage.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/selector_storage.h"
+
+#include "protos/perfetto/trace_processor/serialization.pbzero.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/storage/types.h"
+#include "src/trace_processor/tp_metatrace.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+using Range = RowMap::Range;
+
+SelectorStorage::SelectorStorage(std::unique_ptr<Storage> inner,
+ const BitVector* selector)
+ : inner_(std::move(inner)), selector_(selector) {}
+
+Storage::SearchValidationResult SelectorStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return inner_->ValidateSearchConstraints(sql_val, op);
+}
+
+RangeOrBitVector SelectorStorage::Search(FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range in) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "SelectorStorage::Search");
+
+ // Figure out the bounds of the indices in the underlying storage and search
+ // it.
+ uint32_t start_idx = selector_->IndexOfNthSet(in.start);
+ uint32_t end_idx = selector_->IndexOfNthSet(in.end - 1) + 1;
+
+ auto storage_result = inner_->Search(op, sql_val, Range(start_idx, end_idx));
+ if (storage_result.IsRange()) {
+ Range storage_range = std::move(storage_result).TakeIfRange();
+ uint32_t out_start = selector_->CountSetBits(storage_range.start);
+ uint32_t out_end = selector_->CountSetBits(storage_range.end);
+ return RangeOrBitVector(Range(out_start, out_end));
+ }
+
+ BitVector storage_bitvector = std::move(storage_result).TakeIfBitVector();
+ PERFETTO_DCHECK(storage_bitvector.size() <= selector_->size());
+
+ // TODO(b/283763282): implement ParallelExtractBits to optimize this
+ // operation.
+ BitVector::Builder res(in.end);
+ for (auto it = selector_->IterateSetBits();
+ it && it.index() < storage_bitvector.size(); it.Next()) {
+ res.Append(storage_bitvector.IsSet(it.index()));
+ }
+ return RangeOrBitVector(std::move(res).Build());
+}
+
+RangeOrBitVector SelectorStorage::IndexSearch(FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size,
+ bool sorted) const {
+ PERFETTO_DCHECK(indices_size == 0 ||
+ *std::max_element(indices, indices + indices_size) <=
+ selector_->size());
+
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "SelectorStorage::IndexSearch");
+
+ // To go from TableIndexVector to StorageIndexVector we need to find index in
+ // |selector_| by looking only into set bits.
+ std::vector<uint32_t> storage_iv;
+ storage_iv.reserve(indices_size);
+ for (const uint32_t* it = indices; it != indices + indices_size; ++it) {
+ storage_iv.push_back(selector_->IndexOfNthSet(*it));
+ }
+ return inner_->IndexSearch(op, sql_val, storage_iv.data(),
+ static_cast<uint32_t>(storage_iv.size()), sorted);
+}
+
+void SelectorStorage::StableSort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void SelectorStorage::Sort(uint32_t*, uint32_t) const {
+ // TODO(b/307482437): Implement.
+ PERFETTO_FATAL("Not implemented");
+}
+
+void SelectorStorage::Serialize(StorageProto* storage) const {
+ auto* selector_storage = storage->set_selector_storage();
+ inner_->Serialize(selector_storage->set_storage());
+ selector_->Serialize(selector_storage->set_bit_vector());
+}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/selector_storage.h b/src/trace_processor/db/storage/selector_storage.h
new file mode 100644
index 0000000..9d368e2
--- /dev/null
+++ b/src/trace_processor/db/storage/selector_storage.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_SELECTOR_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_SELECTOR_STORAGE_H_
+
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Storage which "selects" specific rows from an underlying storage using a
+// BitVector. See ArrangementStorage for a more generic class which allows
+// duplication and rearragement but is less performant.
+class SelectorStorage : public Storage {
+ public:
+ SelectorStorage(std::unique_ptr<Storage> storage, const BitVector* non_null);
+
+ Storage::SearchValidationResult ValidateSearchConstraints(SqlValue, FilterOp)
+ const override;
+
+ RangeOrBitVector Search(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const override;
+
+ RangeOrBitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count,
+ bool sorted) const override;
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Serialize(StorageProto*) const override;
+
+ uint32_t size() const override { return selector_->size(); }
+
+ private:
+ std::unique_ptr<Storage> inner_ = nullptr;
+ const BitVector* selector_ = nullptr;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_SELECTOR_STORAGE_H_
diff --git a/src/trace_processor/db/storage/selector_storage_unittest.cc b/src/trace_processor/db/storage/selector_storage_unittest.cc
new file mode 100644
index 0000000..04c3de2
--- /dev/null
+++ b/src/trace_processor/db/storage/selector_storage_unittest.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/storage/selector_storage.h"
+
+#include "src/trace_processor/db/storage/fake_storage.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+using Range = RowMap::Range;
+
+std::vector<uint32_t> ToIndexVector(RangeOrBitVector& r_or_bv) {
+ RowMap rm;
+ if (r_or_bv.IsBitVector()) {
+ rm = RowMap(std::move(r_or_bv).TakeIfBitVector());
+ } else {
+ Range range = std::move(r_or_bv).TakeIfRange();
+ rm = RowMap(range.start, range.end);
+ }
+ return rm.GetAllIndices();
+}
+
+TEST(SelectorStorage, SearchAll) {
+ BitVector selector{0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1};
+ SelectorStorage storage(FakeStorage::SearchAll(10), &selector);
+
+ auto res =
+ storage.Search(FilterOp::kGe, SqlValue::Long(0u), RowMap::Range(1, 4));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1u, 2u, 3u));
+}
+
+TEST(SelectorStorage, SearchNone) {
+ BitVector selector{0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1};
+ SelectorStorage storage(FakeStorage::SearchNone(10), &selector);
+
+ auto res =
+ storage.Search(FilterOp::kGe, SqlValue::Long(0u), RowMap::Range(1, 4));
+ ASSERT_THAT(ToIndexVector(res), IsEmpty());
+}
+
+TEST(SelectorStorage, SearchLimited) {
+ BitVector selector{0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1};
+ SelectorStorage storage(FakeStorage::SearchSubset(10, Range(4, 5)),
+ &selector);
+
+ auto res =
+ storage.Search(FilterOp::kGe, SqlValue::Long(0u), RowMap::Range(1, 5));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(2u));
+}
+
+TEST(SelectorStorage, SearchBitVector) {
+ BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
+ SelectorStorage storage(
+ FakeStorage::SearchSubset(8, BitVector({0, 1, 0, 1, 0, 1, 0, 0})),
+ &selector);
+
+ auto res = storage.Search(FilterOp::kGe, SqlValue::Long(0u), Range(0, 4));
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(0, 2));
+}
+
+TEST(SelectorStorage, IndexSearch) {
+ BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
+ SelectorStorage storage(
+ FakeStorage::SearchSubset(8, BitVector({0, 1, 0, 1, 0, 1, 0, 0})),
+ &selector);
+
+ std::vector<uint32_t> table_idx{1u, 0u, 3u};
+ RangeOrBitVector res =
+ storage.IndexSearch(FilterOp::kGe, SqlValue::Long(0u), table_idx.data(),
+ static_cast<uint32_t>(table_idx.size()), false);
+ ASSERT_THAT(ToIndexVector(res), ElementsAre(1u));
+}
+
+} // namespace
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/set_id_storage.cc b/src/trace_processor/db/storage/set_id_storage.cc
index 61bc2aa..94b94dc 100644
--- a/src/trace_processor/db/storage/set_id_storage.cc
+++ b/src/trace_processor/db/storage/set_id_storage.cc
@@ -58,73 +58,140 @@
} // namespace
+SetIdStorage::SearchValidationResult SetIdStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_FATAL(
+ "Invalid filter operation. NULL should only be compared with 'IS NULL' "
+ "and 'IS NOT NULL'");
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraints.");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): Remove after adding support for double
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+
+ // Bounds of the value.
+ if (PERFETTO_UNLIKELY(val.AsLong() > std::numeric_limits<uint32_t>::max())) {
+ if (op == FilterOp::kLe || op == FilterOp::kLt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+ if (PERFETTO_UNLIKELY(val.AsLong() < std::numeric_limits<uint32_t>::min())) {
+ if (op == FilterOp::kGe || op == FilterOp::kGt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector SetIdStorage::Search(FilterOp op,
SqlValue sql_val,
- RowMap::Range range) const {
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "SetIdStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- PERFETTO_DCHECK(range.end <= size());
+ PERFETTO_DCHECK(search_range.end <= size());
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
if (op == FilterOp::kNe) {
- if (sql_val.is_null()) {
- return RangeOrBitVector(Range());
- }
// Not equal is a special operation on binary search, as it doesn't define a
// range, and rather just `not` range returned with `equal` operation.
RowMap::Range eq_range =
- BinarySearchIntrinsic(FilterOp::kEq, sql_val, range);
- BitVector bv(eq_range.start, true);
- bv.Resize(eq_range.end);
- bv.Resize(std::min(range.end - 1, eq_range.end), true);
+ BinarySearchIntrinsic(FilterOp::kEq, val, search_range);
+ BitVector bv(search_range.start, false);
+ bv.Resize(eq_range.start, true);
+ bv.Resize(eq_range.end, false);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
RangeOrBitVector SetIdStorage::IndexSearch(FilterOp op,
SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
+ uint32_t indices_size,
bool) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "SetIdStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- // Validate sql_val
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
}
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
- }
uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
- BitVector::Builder builder(indices_count);
+ BitVector::Builder builder(indices_size);
// TODO(mayzner): Instead of utils::IndexSearchWithComparator, use the
// property of SetId data - that for each index i, data[i] <= i.
@@ -154,7 +221,7 @@
std::greater_equal<uint32_t>(), builder);
break;
case FilterOp::kIsNotNull:
- return RangeOrBitVector(Range(0, indices_count));
+ return RangeOrBitVector(Range(0, indices_size));
case FilterOp::kIsNull:
return RangeOrBitVector(Range());
case FilterOp::kGlob:
@@ -165,34 +232,8 @@
}
Range SetIdStorage::BinarySearchIntrinsic(FilterOp op,
- SqlValue sql_val,
+ SetId val,
Range range) const {
- // Validate sql_value
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return range;
- }
- return Range();
- }
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
-
switch (op) {
case FilterOp::kEq:
return Range(LowerBoundIntrinsic(values_->data(), val, range),
@@ -232,10 +273,9 @@
void SetIdStorage::Serialize(
protos::pbzero::SerializedColumn::Storage* msg) const {
- auto* vec_msg = msg->set_set_id_storage()->set_values();
- vec_msg->set_size(size());
- vec_msg->set_data(reinterpret_cast<const uint8_t*>(values_->data()),
- sizeof(SetId) * size());
+ auto* vec_msg = msg->set_set_id_storage();
+ vec_msg->set_values(reinterpret_cast<const uint8_t*>(values_->data()),
+ sizeof(SetId) * size());
}
} // namespace storage
diff --git a/src/trace_processor/db/storage/set_id_storage.h b/src/trace_processor/db/storage/set_id_storage.h
index 91dab18..6886bef 100644
--- a/src/trace_processor/db/storage/set_id_storage.h
+++ b/src/trace_processor/db/storage/set_id_storage.h
@@ -36,6 +36,9 @@
explicit SetIdStorage(const std::vector<uint32_t>* data) : values_(data) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -50,16 +53,16 @@
void Sort(uint32_t* rows, uint32_t rows_size) const override;
- void Serialize(protos::pbzero::SerializedColumn_Storage*) const override;
+ void Serialize(StorageProto*) const override;
uint32_t size() const override {
return static_cast<uint32_t>(values_->size());
}
private:
- BitVector IndexSearch(FilterOp, SqlValue, uint32_t*, uint32_t) const;
- RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ BitVector IndexSearch(FilterOp, SetId, uint32_t*, uint32_t) const;
+ RowMap::Range BinarySearchIntrinsic(FilterOp,
+ SetId,
RowMap::Range search_range) const;
// TODO(b/307482437): After the migration vectors should be owned by storage,
diff --git a/src/trace_processor/db/storage/set_id_storage_unittest.cc b/src/trace_processor/db/storage/set_id_storage_unittest.cc
index f487dfa..5023345 100644
--- a/src/trace_processor/db/storage/set_id_storage_unittest.cc
+++ b/src/trace_processor/db/storage/set_id_storage_unittest.cc
@@ -19,11 +19,96 @@
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
+TEST(SetIdStorageUnittest, InvalidSearchConstraints) {
+ std::vector<uint32_t> storage_data{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9};
+ SetIdStorage storage(&storage_data);
+
+ Range test_range(3, 9);
+ Range full_range(0, 9);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Value bounds
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
TEST(SetIdStorageUnittest, SearchEqSimple) {
std::vector<uint32_t> storage_data{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9};
diff --git a/src/trace_processor/db/storage/storage.h b/src/trace_processor/db/storage/storage.h
index 32edc4d..2a9c9fb 100644
--- a/src/trace_processor/db/storage/storage.h
+++ b/src/trace_processor/db/storage/storage.h
@@ -16,13 +16,11 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_STORAGE_H_
-#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/types.h"
namespace perfetto {
-
namespace protos::pbzero {
class SerializedColumn_Storage;
}
@@ -33,23 +31,62 @@
// Backing storage for columnar tables.
class Storage {
public:
+ using StorageProto = protos::pbzero::SerializedColumn_Storage;
+
+ enum class SearchValidationResult { kOk = 0, kAllData = 1, kNoData = 2 };
+
virtual ~Storage();
+ // Verifies whether any further filtering is needed and if not, whether the
+ // search would return all values or none of them. This allows for skipping
+ // the |Search| and |IndexSearch| in special cases.
+ //
+ // Notes for callers:
+ // * This function is being called by Search and IndexSearch and there is no
+ // need to call it before searches.
+ // * The SqlValue and FilterOp have to be valid in Sqlite: it will crash if
+ // either: value is NULL and operation is different than "IS NULL" and "IS
+ // NOT NULL" or the operation is "IS NULL" and "IS NOT NULL" and value is
+ // different than NULL.
+ virtual SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const = 0;
+
// Searches for elements which match |op| and |value| between |range.start|
// and |range.end|.
- virtual RangeOrBitVector Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const = 0;
+ //
+ // Returns either a range or BitVector which indicate the positions in |range|
+ // which match the constraint. If a BitVector is returned, it will be
+ // *precisely* as large as |range.end|.
+ //
+ // Notes for implementors:
+ // * Implementations should ensure that the return value *only* includes
+ // positions in |range| as callers will expect this to be true and can
+ // optimize based on this.
+ // * Implementations should ensure that, if they return a BitVector, it is
+ // precisely of size |range.end|.
+ virtual RangeOrBitVector Search(FilterOp, SqlValue, RowMap::Range) const = 0;
// Searches for elements which match |op| and |value| at the positions given
- // by |indices| array.
- // If the order defined by |indices| makes storage sorted, |sorted| flag
- // should be set to true.
- virtual RangeOrBitVector IndexSearch(FilterOp op,
- SqlValue value,
+ // by |indices| array. The |sorted| flag allows the caller to specify if the
+ // order defined by |indices| makes storage sorted; implementations can use
+ // this to optimize how they search the storage.
+ //
+ // Returns either a range of BitVector which indicate the positions in
+ // |indices| which match the constraint. If a BitVector is returned, it will
+ // be *precisely* as large as |indices_count|.
+ //
+ // Notes for callers:
+ // * Callers should note that the return value of this function corresponds
+ // to positions in |indices| *not* positions in the storage.
+ //
+ // Notes for implementors:
+ // * Implementations should ensure that, if they return a BitVector, it is
+ // precisely of size |indices_count|.
+ virtual RangeOrBitVector IndexSearch(FilterOp,
+ SqlValue,
uint32_t* indices,
uint32_t indices_count,
- bool sorted = false) const = 0;
+ bool sorted) const = 0;
// Sorts |rows| in ascending order with the comparator:
// data[rows[a]] < data[rows[b]].
@@ -60,7 +97,7 @@
virtual void StableSort(uint32_t* rows, uint32_t rows_size) const = 0;
// Serializes storage data to proto format.
- virtual void Serialize(protos::pbzero::SerializedColumn_Storage*) const = 0;
+ virtual void Serialize(StorageProto*) const = 0;
// Number of elements in stored data.
virtual uint32_t size() const = 0;
diff --git a/src/trace_processor/db/storage/string_storage.cc b/src/trace_processor/db/storage/string_storage.cc
index 2179e86..d9c9b98 100644
--- a/src/trace_processor/db/storage/string_storage.cc
+++ b/src/trace_processor/db/storage/string_storage.cc
@@ -19,7 +19,6 @@
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
-#include "perfetto/trace_processor/basic_types.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "perfetto/base/logging.h"
@@ -27,6 +26,7 @@
#include "src/trace_processor/containers/null_term_string_view.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
#include "src/trace_processor/db/storage/utils.h"
@@ -163,63 +163,105 @@
} // namespace
+StringStorage::SearchValidationResult StringStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ break;
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kGt || op == FilterOp::kGe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector StringStorage::Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const {
- PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::LinearSearch",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ SqlValue sql_val,
+ Range search_range) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::Search",
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
if (is_sorted_) {
if (op != FilterOp::kNe) {
- return RangeOrBitVector(BinarySearchIntrinsic(op, value, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, search_range));
}
// Not equal is a special operation on binary search, as it doesn't define
// a range, and rather just `not` range returned with `equal` operation.
- RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, value, range);
+ Range r = BinarySearchIntrinsic(FilterOp::kEq, sql_val, search_range);
BitVector bv(r.start, true);
bv.Resize(r.end);
- bv.Resize(range.end, true);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(LinearSearchInternal(op, value, range));
+ return RangeOrBitVector(LinearSearch(op, sql_val, search_range));
}
RangeOrBitVector StringStorage::IndexSearch(FilterOp op,
- SqlValue value,
+ SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
- bool sorted) const {
+ uint32_t indices_size,
+ bool indices_sorted) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- if (sorted) {
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ if (indices_sorted) {
return RangeOrBitVector(
- BinarySearchExtrinsic(op, value, indices, indices_count));
+ BinarySearchExtrinsic(op, sql_val, indices, indices_size));
}
return RangeOrBitVector(
- IndexSearchInternal(op, value, indices, indices_count, sorted));
+ IndexSearchInternal(op, sql_val, indices, indices_size));
}
-BitVector StringStorage::LinearSearchInternal(FilterOp op,
- SqlValue sql_val,
- RowMap::Range range) const {
+BitVector StringStorage::LinearSearch(FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range range) const {
if (sql_val.is_null() &&
(op != FilterOp::kIsNotNull && op != FilterOp::kIsNull)) {
- return BitVector();
+ return BitVector(range.end, false);
}
if (sql_val.type != SqlValue::kString &&
(op == FilterOp::kGlob || op == FilterOp::kRegex)) {
- return BitVector();
+ return BitVector(range.end, false);
}
StringPool::Id val =
@@ -227,16 +269,6 @@
? StringPool::Id::Null()
: string_pool_->InternString(base::StringView(sql_val.AsString()));
const StringPool::Id* start = values_->data() + range.start;
- PERFETTO_TP_TRACE(
- metatrace::Category::DB, "StringStorage::Search",
- [range, op, &sql_val](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
- r->AddArg("Op", std::to_string(static_cast<uint32_t>(op)));
- r->AddArg("String", sql_val.type == SqlValue::Type::kString
- ? sql_val.AsString()
- : "NULL");
- });
BitVector::Builder builder(range.end, range.start);
switch (op) {
@@ -318,11 +350,11 @@
return std::move(builder).Build();
}
-RangeOrBitVector StringStorage::IndexSearchInternal(FilterOp op,
- SqlValue sql_val,
- uint32_t* indices,
- uint32_t indices_size,
- bool) const {
+RangeOrBitVector StringStorage::IndexSearchInternal(
+ FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size) const {
if (sql_val.is_null() &&
(op != FilterOp::kIsNotNull && op != FilterOp::kIsNull)) {
return RangeOrBitVector(Range());
@@ -482,13 +514,12 @@
void StringStorage::Serialize(
protos::pbzero::SerializedColumn::Storage* msg) const {
- auto* string_storage = msg->set_string_storage();
- string_storage->set_is_sorted(is_sorted_);
+ auto* string_storage_msg = msg->set_string_storage();
+ string_storage_msg->set_is_sorted(is_sorted_);
- auto* vec_msg = string_storage->set_values();
- vec_msg->set_size(size());
- vec_msg->set_data(reinterpret_cast<const uint8_t*>(values_->data()),
- sizeof(StringPool::Id) * size());
+ string_storage_msg->set_values(
+ reinterpret_cast<const uint8_t*>(values_->data()),
+ sizeof(StringPool::Id) * size());
}
} // namespace storage
diff --git a/src/trace_processor/db/storage/string_storage.h b/src/trace_processor/db/storage/string_storage.h
index 48ea5e8..1e89f24 100644
--- a/src/trace_processor/db/storage/string_storage.h
+++ b/src/trace_processor/db/storage/string_storage.h
@@ -16,6 +16,7 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_STRING_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_STRING_STORAGE_H_
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/storage/storage.h"
@@ -38,6 +39,9 @@
bool is_sorted = false)
: values_(data), string_pool_(string_pool), is_sorted_(is_sorted) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -51,20 +55,19 @@
void Sort(uint32_t* rows, uint32_t rows_size) const override;
- void Serialize(protos::pbzero::SerializedColumn_Storage*) const override;
+ void Serialize(StorageProto*) const override;
uint32_t size() const override {
return static_cast<uint32_t>(values_->size());
}
private:
- BitVector LinearSearchInternal(FilterOp, SqlValue, RowMap::Range) const;
+ BitVector LinearSearch(FilterOp, SqlValue, RowMap::Range) const;
RangeOrBitVector IndexSearchInternal(FilterOp op,
SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_size,
- bool) const;
+ uint32_t indices_size) const;
RowMap::Range BinarySearchExtrinsic(FilterOp,
SqlValue,
diff --git a/src/trace_processor/db/storage/types.h b/src/trace_processor/db/storage/types.h
index ff3fc57..6884988 100644
--- a/src/trace_processor/db/storage/types.h
+++ b/src/trace_processor/db/storage/types.h
@@ -26,7 +26,8 @@
// Used for result of filtering, which is sometimes (for more optimised
// operations) a Range and BitVector otherwise. Stores a variant of Range and
// BitVector.
-struct RangeOrBitVector {
+class RangeOrBitVector {
+ public:
using Range = RowMap::Range;
explicit RangeOrBitVector(Range range) : val(range) {}
explicit RangeOrBitVector(BitVector bv) : val(std::move(bv)) {}
@@ -43,6 +44,7 @@
return std::move(*std::get_if<Range>(&val));
}
+ private:
std::variant<RowMap::Range, BitVector> val = Range();
};
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index 4751fef..2a3a8b5 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -139,6 +139,12 @@
context_->storage->mutable_surfaceflinger_transactions_table(), id);
}
+ BoundInserter AddArgsTo(tables::WindowManagerShellTransitionsTable::Id id) {
+ return AddArgsTo(
+ context_->storage->mutable_window_manager_shell_transitions_table(),
+ id);
+ }
+
BoundInserter AddArgsTo(MetadataId id) {
auto* table = context_->storage->mutable_metadata_table();
uint32_t row = *table->id().IndexOf(id);
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 557512c..225f951 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
namespace trace_processor {
namespace {
-std::array<FtraceMessageDescriptor, 487> descriptors{{
+std::array<FtraceMessageDescriptor, 488> descriptors{{
{nullptr, 0, {}},
{nullptr, 0, {}},
{nullptr, 0, {}},
@@ -5351,6 +5351,30 @@
{"cmd", ProtoSchemaType::kUint32},
},
},
+ {
+ "sched_switch_with_ctrs",
+ 17,
+ {
+ {},
+ {"old_pid", ProtoSchemaType::kInt32},
+ {"new_pid", ProtoSchemaType::kInt32},
+ {"cctr", ProtoSchemaType::kUint32},
+ {"ctr0", ProtoSchemaType::kUint32},
+ {"ctr1", ProtoSchemaType::kUint32},
+ {"ctr2", ProtoSchemaType::kUint32},
+ {"ctr3", ProtoSchemaType::kUint32},
+ {"lctr0", ProtoSchemaType::kUint32},
+ {"lctr1", ProtoSchemaType::kUint32},
+ {"ctr4", ProtoSchemaType::kUint32},
+ {"ctr5", ProtoSchemaType::kUint32},
+ {"prev_comm", ProtoSchemaType::kString},
+ {"prev_pid", ProtoSchemaType::kInt32},
+ {"cyc", ProtoSchemaType::kUint32},
+ {"inst", ProtoSchemaType::kUint32},
+ {"stallbm", ProtoSchemaType::kUint32},
+ {"l3dm", ProtoSchemaType::kUint32},
+ },
+ },
}};
} // namespace
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index c699ab9..711415f 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -53,6 +53,47 @@
return context->clock_tracker->ToTraceTime(clock_id, ts);
}
+// Fast path for parsing the event id of an ftrace event.
+// Speculate on the fact that, if the timestamp was found, the common pid
+// will appear immediately after and the event id immediately after that.
+uint64_t TryFastParseFtraceEventId(const uint8_t* start, const uint8_t* end) {
+ constexpr auto kPidFieldNumber = protos::pbzero::FtraceEvent::kPidFieldNumber;
+ constexpr auto kPidFieldTag = MakeTagVarInt(kPidFieldNumber);
+
+ // If the next byte is not the common pid's tag, just skip the field.
+ constexpr uint32_t kMaxPidLength = 5;
+ if (PERFETTO_UNLIKELY(static_cast<uint32_t>(end - start) <= kMaxPidLength ||
+ start[0] != kPidFieldTag)) {
+ return 0;
+ }
+
+ // Skip the common pid.
+ uint64_t common_pid = 0;
+ const uint8_t* common_pid_end = ParseVarInt(start + 1, end, &common_pid);
+ if (PERFETTO_UNLIKELY(common_pid_end == start + 1)) {
+ return 0;
+ }
+
+ // Read the next varint: this should be the event id tag.
+ uint64_t event_tag = 0;
+ const uint8_t* event_id_end = ParseVarInt(common_pid_end, end, &event_tag);
+ if (event_id_end == common_pid_end) {
+ return 0;
+ }
+
+ constexpr uint8_t kFieldTypeNumBits = 3;
+ constexpr uint64_t kFieldTypeMask =
+ (1 << kFieldTypeNumBits) - 1; // 0000 0111;
+
+ // The event wire type should be length delimited.
+ auto wire_type = static_cast<protozero::proto_utils::ProtoWireType>(
+ event_tag & kFieldTypeMask);
+ if (wire_type != protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+ return 0;
+ }
+ return event_tag >> kFieldTypeNumBits;
+}
+
} // namespace
PERFETTO_ALWAYS_INLINE
@@ -125,29 +166,53 @@
const uint8_t* data = event.data();
const size_t length = event.length();
- ProtoDecoder decoder(data, length);
- // Speculate on the fact that the timestamp is often the 1st field of the
- // event.
+ // Speculate on the following sequence of varints
+ // - timestamp tag
+ // - timestamp (64 bit)
+ // - common pid tag
+ // - common pid (32 bit)
+ // - event tag
uint64_t raw_timestamp = 0;
bool timestamp_found = false;
+ uint64_t event_id = 0;
if (PERFETTO_LIKELY(length > 10 && data[0] == kTimestampFieldTag)) {
// Fastpath.
- const uint8_t* next = ParseVarInt(data + 1, data + 11, &raw_timestamp);
- timestamp_found = next != data + 1;
- decoder.Reset(next);
- } else {
- // Slowpath.
+ const uint8_t* ts_end = ParseVarInt(data + 1, data + 11, &raw_timestamp);
+ timestamp_found = ts_end != data + 1;
+ if (PERFETTO_LIKELY(timestamp_found)) {
+ event_id = TryFastParseFtraceEventId(ts_end, data + length);
+ }
+ }
+
+ // Slowpath for finding the timestamp.
+ if (PERFETTO_UNLIKELY(!timestamp_found)) {
+ ProtoDecoder decoder(data, length);
if (auto ts_field = decoder.FindField(kTimestampFieldNumber)) {
timestamp_found = true;
raw_timestamp = ts_field.as_uint64();
}
+ if (PERFETTO_UNLIKELY(!timestamp_found)) {
+ context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+ return;
+ }
}
- if (PERFETTO_UNLIKELY(!timestamp_found)) {
- PERFETTO_ELOG("Timestamp field not found in FtraceEvent");
- context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
- return;
+ // Slowpath for finding the event id.
+ if (PERFETTO_UNLIKELY(event_id == 0)) {
+ ProtoDecoder decoder(data, length);
+ for (auto f = decoder.ReadField(); f.valid(); f = decoder.ReadField()) {
+ // Find the first length-delimited tag as this corresponds to the ftrace
+ // event.
+ if (f.type() == protozero::proto_utils::ProtoWireType::kLengthDelimited) {
+ event_id = f.id();
+ break;
+ }
+ }
+ if (PERFETTO_UNLIKELY(!event_id)) {
+ context_->storage->IncrementStats(stats::ftrace_bundle_tokenizer_errors);
+ return;
+ }
}
// ClockTracker will increment some error stats if it failed to convert the
diff --git a/src/trace_processor/importers/json/json_trace_parser.cc b/src/trace_processor/importers/json/json_trace_parser.cc
index 4b4330b..70b8e81 100644
--- a/src/trace_processor/importers/json/json_trace_parser.cc
+++ b/src/trace_processor/importers/json/json_trace_parser.cc
@@ -109,8 +109,7 @@
base::StringView name = value.isMember("name")
? base::StringView(value["name"].asCString())
: base::StringView();
- StringId name_id = name.empty() ? storage->InternString("[No name]")
- : storage->InternString(name);
+ StringId name_id = name.empty() ? kNullStringId : storage->InternString(name);
auto args_inserter = [this, &value](ArgsTracker::BoundInserter* inserter) {
if (value.isMember("args")) {
@@ -128,7 +127,8 @@
row.ts = timestamp;
row.track_id = track_id;
row.category = cat_id;
- row.name = name_id;
+ row.name =
+ name_id == kNullStringId ? storage->InternString("[No name]") : name_id;
row.thread_ts = json::CoerceToTs(value["tts"]);
// tdur will only exist on 'X' events.
row.thread_dur = json::CoerceToTs(value["tdur"]);
@@ -166,15 +166,31 @@
case 'b':
case 'e':
case 'n': {
- if (!opt_pid || id.empty()) {
+ Json::Value id2 = value.isMember("id2") ? value["id2"] : Json::Value();
+ std::string local = id2.isMember("local") ? id2["local"].asString() : "";
+ std::string global =
+ id2.isMember("global") ? id2["global"].asString() : "";
+ if (!opt_pid || (id.empty() && global.empty() && local.empty())) {
context_->storage->IncrementStats(stats::json_parser_failure);
break;
}
UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
- int64_t cookie = static_cast<int64_t>(base::Hasher::Combine(id.c_str()));
- StringId scope = kNullStringId;
- TrackId track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
- name_id, upid, cookie, true /* source_id_is_process_scoped */, scope);
+ TrackId track_id;
+ if (!id.empty() || !global.empty()) {
+ const std::string& real_id = id.empty() ? global : id;
+ int64_t cookie = static_cast<int64_t>(
+ base::Hasher::Combine(cat_id.raw_id(), real_id));
+ track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
+ name_id, upid, cookie, false /* source_id_is_process_scoped */,
+ kNullStringId /* source_scope */);
+ } else {
+ PERFETTO_DCHECK(!local.empty());
+ int64_t cookie =
+ static_cast<int64_t>(base::Hasher::Combine(cat_id.raw_id(), local));
+ track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
+ name_id, upid, cookie, true /* source_id_is_process_scoped */,
+ kNullStringId /* source_scope */);
+ }
if (phase == 'b') {
slice_tracker->BeginTyped(storage->mutable_slice_table(),
diff --git a/src/trace_processor/importers/proto/atoms.descriptor b/src/trace_processor/importers/proto/atoms.descriptor
index 2d4c191..923f583 100644
--- a/src/trace_processor/importers/proto/atoms.descriptor
+++ b/src/trace_processor/importers/proto/atoms.descriptor
Binary files differ
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
index 04e10bd..c631a3d 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
@@ -163,6 +163,10 @@
"Expired Prediction") /* PREDICTION_EXPIRED */,
context->storage->InternString(
"Unknown Prediction") /* PREDICTION_UNKNOWN */}},
+ jank_severity_type_ids_{{context->storage->InternString("Unknown"),
+ context->storage->InternString("None"),
+ context->storage->InternString("Partial"),
+ context->storage->InternString("Full")}},
expected_timeline_track_name_(
context->storage->InternString("Expected Timeline")),
actual_timeline_track_name_(
@@ -175,6 +179,8 @@
on_time_finish_id_(context->storage->InternString("On time finish")),
gpu_composition_id_(context->storage->InternString("GPU composition")),
jank_type_id_(context->storage->InternString("Jank type")),
+ jank_severity_type_id_(
+ context->storage->InternString("Jank severity type")),
layer_name_id_(context->storage->InternString("Layer name")),
prediction_type_id_(context->storage->InternString("Prediction type")),
is_buffer_id_(context->storage->InternString("Is Buffer?")),
@@ -277,16 +283,36 @@
actual_row.name = name_id;
actual_row.display_frame_token = token;
actual_row.upid = upid;
+ actual_row.on_time_finish = event.on_time_finish();
+ actual_row.gpu_composition = event.gpu_composition();
+
+ // parse present type
StringId present_type = present_type_ids_[0];
if (event.has_present_type() &&
ValidatePresentType(context_, event.present_type())) {
present_type = present_type_ids_[static_cast<size_t>(event.present_type())];
}
actual_row.present_type = present_type;
- actual_row.on_time_finish = event.on_time_finish();
- actual_row.gpu_composition = event.gpu_composition();
+
+ // parse jank type
StringId jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
actual_row.jank_type = jank_type;
+
+ // parse jank severity type
+ if (event.has_jank_severity_type()) {
+ actual_row.jank_severity_type = jank_severity_type_ids_[static_cast<size_t>(
+ event.jank_severity_type())];
+ } else {
+ // NOTE: Older traces don't have this field. If JANK_NONE use
+ // |severity_type| "None", and is not present, use "Unknown".
+ actual_row.jank_severity_type =
+ (event.jank_type() == FrameTimelineEvent::JANK_NONE)
+ ? jank_severity_type_ids_[1] /* None */
+ : jank_severity_type_ids_[0]; /* Unknown */
+ }
+ StringId jank_severity_type = actual_row.jank_severity_type;
+
+ // parse prediction type
StringId prediction_type = prediction_type_ids_[0];
if (event.has_prediction_type() &&
ValidatePredictionType(context_, event.prediction_type())) {
@@ -294,6 +320,7 @@
prediction_type_ids_[static_cast<size_t>(event.prediction_type())];
}
actual_row.prediction_type = prediction_type;
+
if (DisplayFrameJanky(event.jank_type())) {
actual_row.jank_tag = jank_tag_self_id_;
} else if (event.jank_type() == FrameTimelineEvent::JANK_SF_STUFFING) {
@@ -307,8 +334,8 @@
std::optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
context_->storage->mutable_actual_frame_timeline_slice_table(),
actual_row,
- [this, token, jank_type, present_type, prediction_type,
- &event](ArgsTracker::BoundInserter* inserter) {
+ [this, token, jank_type, jank_severity_type, present_type,
+ prediction_type, &event](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(display_frame_token_id_, Variadic::Integer(token));
inserter->AddArg(present_type_id_, Variadic::String(present_type));
inserter->AddArg(on_time_finish_id_,
@@ -316,6 +343,8 @@
inserter->AddArg(gpu_composition_id_,
Variadic::Integer(event.gpu_composition()));
inserter->AddArg(jank_type_id_, Variadic::String(jank_type));
+ inserter->AddArg(jank_severity_type_id_,
+ Variadic::String(jank_severity_type));
inserter->AddArg(prediction_type_id_,
Variadic::String(prediction_type));
});
@@ -471,6 +500,10 @@
actual_row.display_frame_token = display_frame_token;
actual_row.upid = upid;
actual_row.layer_name = layer_name_id;
+ actual_row.on_time_finish = event.on_time_finish();
+ actual_row.gpu_composition = event.gpu_composition();
+
+ // parse present type
StringId present_type = present_type_ids_[0];
bool present_type_validated = false;
if (event.has_present_type() &&
@@ -479,10 +512,26 @@
present_type = present_type_ids_[static_cast<size_t>(event.present_type())];
}
actual_row.present_type = present_type;
- actual_row.on_time_finish = event.on_time_finish();
- actual_row.gpu_composition = event.gpu_composition();
+
+ // parse jank type
StringId jank_type = JankTypeBitmaskToStringId(context_, event.jank_type());
actual_row.jank_type = jank_type;
+
+ // parse jank severity type
+ if (event.has_jank_severity_type()) {
+ actual_row.jank_severity_type = jank_severity_type_ids_[static_cast<size_t>(
+ event.jank_severity_type())];
+ } else {
+ // NOTE: Older traces don't have this field. If JANK_NONE use
+ // |severity_type| "None", and is not present, use "Unknown".
+ actual_row.jank_severity_type =
+ (event.jank_type() == FrameTimelineEvent::JANK_NONE)
+ ? jank_severity_type_ids_[1] /* None */
+ : jank_severity_type_ids_[0]; /* Unknown */
+ }
+ StringId jank_severity_type = actual_row.jank_severity_type;
+
+ // parse prediction type
StringId prediction_type = prediction_type_ids_[0];
if (event.has_prediction_type() &&
ValidatePredictionType(context_, event.prediction_type())) {
@@ -490,6 +539,7 @@
prediction_type_ids_[static_cast<size_t>(event.prediction_type())];
}
actual_row.prediction_type = prediction_type;
+
if (SurfaceFrameJanky(event.jank_type())) {
actual_row.jank_tag = jank_tag_self_id_;
} else if (DisplayFrameJanky(event.jank_type())) {
@@ -513,8 +563,8 @@
std::optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
context_->storage->mutable_actual_frame_timeline_slice_table(),
actual_row,
- [this, jank_type, present_type, token, layer_name_id, display_frame_token,
- prediction_type, is_buffer,
+ [this, jank_type, jank_severity_type, present_type, token, layer_name_id,
+ display_frame_token, prediction_type, is_buffer,
&event](ArgsTracker::BoundInserter* inserter) {
inserter->AddArg(surface_frame_token_id_, Variadic::Integer(token));
inserter->AddArg(display_frame_token_id_,
@@ -526,6 +576,8 @@
inserter->AddArg(gpu_composition_id_,
Variadic::Integer(event.gpu_composition()));
inserter->AddArg(jank_type_id_, Variadic::String(jank_type));
+ inserter->AddArg(jank_severity_type_id_,
+ Variadic::String(jank_severity_type));
inserter->AddArg(prediction_type_id_,
Variadic::String(prediction_type));
inserter->AddArg(is_buffer_id_, Variadic::String(is_buffer));
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.h b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
index 9c4e75e..596f415 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.h
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
@@ -66,6 +66,7 @@
std::map<int64_t, TrackSetId> cookie_track_set_id_map_;
std::array<StringId, 6> present_type_ids_;
std::array<StringId, 4> prediction_type_ids_;
+ std::array<StringId, 4> jank_severity_type_ids_;
StringId expected_timeline_track_name_;
StringId actual_timeline_track_name_;
@@ -75,6 +76,7 @@
StringId on_time_finish_id_;
StringId gpu_composition_id_;
StringId jank_type_id_;
+ StringId jank_severity_type_id_;
StringId layer_name_id_;
StringId prediction_type_id_;
StringId is_buffer_id_;
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 8add6f0..6d68464 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -403,7 +403,8 @@
tables::ClockSnapshotTable::Row row;
row.ts = *opt_trace_ts;
row.clock_id = static_cast<int64_t>(clock_timestamp.clock.id);
- row.clock_value = clock_timestamp.timestamp;
+ row.clock_value =
+ clock_timestamp.timestamp * clock_timestamp.clock.unit_multiplier_ns;
row.clock_name = GetBuiltinClockNameOrNull(clock_timestamp.clock.id);
row.snapshot_id = *snapshot_id;
diff --git a/src/trace_processor/importers/proto/statsd_module.cc b/src/trace_processor/importers/proto/statsd_module.cc
index e536e11..49a7300 100644
--- a/src/trace_processor/importers/proto/statsd_module.cc
+++ b/src/trace_processor/importers/proto/statsd_module.cc
@@ -141,6 +141,54 @@
TraceStorage& storage_;
};
+// If we don't know about the atom format put whatever details we
+// can. This has the following restrictions:
+// - We can't tell the difference between double, fixed64, sfixed64
+// so those all show up as double
+// - We can't tell the difference between float, fixed32, sfixed32
+// so those all show up as float
+// - We can't tell the difference between int32, int64 and sint32
+// and sint64. We assume int32/int64.
+// - We only show the length of strings, nested messages, packed ints
+// and any other length delimited fields.
+base::Status ParseGenericEvent(const protozero::ConstBytes& cb,
+ util::ProtoToArgsParser::Delegate& delegate) {
+ protozero::ProtoDecoder decoder(cb);
+ for (auto f = decoder.ReadField(); f.valid(); f = decoder.ReadField()) {
+ switch (f.type()) {
+ case protozero::proto_utils::ProtoWireType::kLengthDelimited: {
+ base::StackString<64> name("field_%u", f.id());
+ std::string name_str = name.ToStdString();
+ util::ProtoToArgsParser::Key key{name_str, name_str};
+ delegate.AddBytes(key, f.as_bytes());
+ break;
+ }
+ case protozero::proto_utils::ProtoWireType::kVarInt: {
+ base::StackString<64> name("field_%u", f.id());
+ std::string name_str = name.ToStdString();
+ util::ProtoToArgsParser::Key key{name_str, name_str};
+ delegate.AddInteger(key, f.as_int64());
+ break;
+ }
+ case protozero::proto_utils::ProtoWireType::kFixed32: {
+ base::StackString<64> name("field_%u_assuming_float", f.id());
+ std::string name_str = name.ToStdString();
+ util::ProtoToArgsParser::Key key{name_str, name_str};
+ delegate.AddDouble(key, static_cast<double>(f.as_float()));
+ break;
+ }
+ case protozero::proto_utils::ProtoWireType::kFixed64: {
+ base::StackString<64> name("field_%u_assuming_double", f.id());
+ std::string name_str = name.ToStdString();
+ util::ProtoToArgsParser::Key key{name_str, name_str};
+ delegate.AddDouble(key, f.as_double());
+ break;
+ }
+ }
+ }
+ return base::OkStatus();
+}
+
} // namespace
using perfetto::protos::pbzero::StatsdAtom;
@@ -247,10 +295,26 @@
SliceId slice = opt_slice.value();
auto inserter = context_->args_tracker->AddArgsTo(slice);
InserterDelegate delegate(inserter, *context_->storage.get());
- base::Status result = args_parser_.ParseMessage(
- nested_bytes, kAtomProtoName, nullptr /* parse all fields */, delegate);
- if (!result.ok()) {
- PERFETTO_ELOG("%s", result.c_message());
+
+ const auto& fields = pool_.descriptor()->fields();
+ const auto& field_it = fields.find(nested_field_id);
+ base::Status status;
+
+ if (field_it == fields.end()) {
+ /// Field ids 100000 and over are OEM atoms - we can't have the
+ // descriptor for them so don't report errors. See:
+ // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/proto_logging/stats/atoms.proto;l=1290;drc=a34b11bfebe897259a0340a59f1793ae2dffd762
+ if (nested_field_id < 100000) {
+ context_->storage->IncrementStats(stats::atom_unknown);
+ }
+
+ status = ParseGenericEvent(field.as_bytes(), delegate);
+ } else {
+ status = args_parser_.ParseMessage(
+ nested_bytes, kAtomProtoName, nullptr /* parse all fields */, delegate);
+ }
+
+ if (!status.ok()) {
context_->storage->IncrementStats(stats::atom_unknown);
}
}
@@ -263,18 +327,18 @@
return context_->storage->InternString("Could not load atom descriptor");
}
+ StringId name_id;
const auto& fields = pool_.descriptor()->fields();
const auto& field_it = fields.find(atom_field_id);
if (field_it == fields.end()) {
- context_->storage->IncrementStats(stats::atom_unknown);
- return context_->storage->InternString("Unknown atom");
+ base::StackString<255> name("atom_%u", atom_field_id);
+ name_id = context_->storage->InternString(name.string_view());
+ } else {
+ const FieldDescriptor& field = field_it->second;
+ name_id = context_->storage->InternString(base::StringView(field.name()));
}
-
- const FieldDescriptor& field = field_it->second;
- StringId name =
- context_->storage->InternString(base::StringView(field.name()));
- atom_names_[atom_field_id] = name;
- return name;
+ atom_names_[atom_field_id] = name_id;
+ return name_id;
}
return *cached_name;
}
diff --git a/src/trace_processor/importers/proto/winscope/BUILD.gn b/src/trace_processor/importers/proto/winscope/BUILD.gn
index 2d48f0c..7b04e85 100644
--- a/src/trace_processor/importers/proto/winscope/BUILD.gn
+++ b/src/trace_processor/importers/proto/winscope/BUILD.gn
@@ -16,30 +16,35 @@
source_set("full") {
sources = [
+ "shell_transitions_parser.cc",
+ "shell_transitions_parser.h",
+ "shell_transitions_tracker.cc",
+ "shell_transitions_tracker.h",
"surfaceflinger_layers_parser.cc",
"surfaceflinger_layers_parser.h",
"surfaceflinger_transactions_parser.cc",
"surfaceflinger_transactions_parser.h",
- "winscope_args_parser.h",
"winscope_args_parser.cc",
+ "winscope_args_parser.h",
"winscope_module.cc",
"winscope_module.h",
]
deps = [
":gen_cc_winscope_descriptor",
+ "../:proto_importer_module",
"../../../../../gn:default_deps",
- "../../../../../protos/perfetto/trace/android:zero",
"../../../../../protos/perfetto/trace:zero",
+ "../../../../../protos/perfetto/trace/android:zero",
"../../../storage",
"../../../tables",
"../../../types",
"../../common",
"../../common:parser_types",
- "../:proto_importer_module",
]
}
perfetto_cc_proto_descriptor("gen_cc_winscope_descriptor") {
descriptor_name = "winscope.descriptor"
- descriptor_target = "../../../../../protos/perfetto/trace/android:winscope_descriptor"
+ descriptor_target =
+ "../../../../../protos/perfetto/trace/android:winscope_descriptor"
}
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
new file mode 100644
index 0000000..68d2a82
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h"
+#include "src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h"
+
+#include "protos/perfetto/trace/android/shell_transition.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/proto/winscope/winscope.descriptor.h"
+#include "src/trace_processor/importers/proto/winscope/winscope_args_parser.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ShellTransitionsParser::ShellTransitionsParser(TraceProcessorContext* context)
+ : context_(context), args_parser_{pool_} {
+ pool_.AddFromFileDescriptorSet(kWinscopeDescriptor.data(),
+ kWinscopeDescriptor.size());
+}
+
+void ShellTransitionsParser::ParseTransition(protozero::ConstBytes blob) {
+ protos::pbzero::ShellTransition::Decoder transition(blob);
+
+ auto row_id =
+ ShellTransitionsTracker::GetOrCreate(context_)->InternTransition(
+ transition.id());
+
+ auto* window_manager_shell_transitions_table =
+ context_->storage->mutable_window_manager_shell_transitions_table();
+ auto row = window_manager_shell_transitions_table->FindById(row_id).value();
+
+ if (transition.has_dispatch_time_ns()) {
+ row.set_ts(transition.dispatch_time_ns());
+ }
+
+ auto inserter = context_->args_tracker->AddArgsTo(row_id);
+ WinscopeArgsParser writer(inserter, *context_->storage.get());
+ base::Status status = args_parser_.ParseMessage(
+ blob, kShellTransitionsProtoName, nullptr /* parse all fields */, writer);
+
+ if (!status.ok()) {
+ context_->storage->IncrementStats(
+ stats::winscope_shell_transitions_parse_errors);
+ }
+}
+
+void ShellTransitionsParser::ParseHandlerMappings(protozero::ConstBytes blob) {
+ auto* shell_handlers_table =
+ context_->storage
+ ->mutable_window_manager_shell_transition_handlers_table();
+
+ protos::pbzero::ShellHandlerMappings::Decoder handler_mappings(blob);
+ for (auto it = handler_mappings.mapping(); it; ++it) {
+ protos::pbzero::ShellHandlerMapping::Decoder mapping(it.field().as_bytes());
+
+ tables::WindowManagerShellTransitionHandlersTable::Row row;
+ row.handler_id = mapping.id();
+ row.handler_name = context_->storage->InternString(
+ base::StringView(mapping.name().ToStdString()));
+ shell_handlers_table->Insert(row);
+ }
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
new file mode 100644
index 0000000..44b86d1
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_parser.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
+
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+
+namespace perfetto {
+
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class ShellTransitionsParser {
+ public:
+ explicit ShellTransitionsParser(TraceProcessorContext*);
+ void ParseTransition(protozero::ConstBytes);
+ void ParseHandlerMappings(protozero::ConstBytes);
+
+ private:
+ static constexpr auto* kShellTransitionsProtoName =
+ ".perfetto.protos.ShellTransition";
+
+ TraceProcessorContext* const context_;
+ DescriptorPool pool_;
+ util::ProtoToArgsParser args_parser_;
+};
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_PARSER_H_
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
new file mode 100644
index 0000000..6025d91
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_transitions_tracker.h"
+#include "perfetto/ext/base/crash_keys.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+ShellTransitionsTracker::ShellTransitionsTracker(TraceProcessorContext* context)
+ : context_(context) {}
+
+ShellTransitionsTracker::~ShellTransitionsTracker() = default;
+
+tables::WindowManagerShellTransitionsTable::Id
+ShellTransitionsTracker::InternTransition(int32_t transition_id) {
+ auto pos = transition_id_to_row_mapping_.find(transition_id);
+ if (pos != transition_id_to_row_mapping_.end()) {
+ return pos->second;
+ }
+
+ auto* window_manager_shell_transitions_table =
+ context_->storage->mutable_window_manager_shell_transitions_table();
+
+ tables::WindowManagerShellTransitionsTable::Row row;
+ row.transition_id = transition_id;
+ auto row_id = window_manager_shell_transitions_table->Insert(row).id;
+
+ transition_id_to_row_mapping_.insert({transition_id, row_id});
+
+ return row_id;
+}
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
new file mode 100644
index 0000000..07ef736
--- /dev/null
+++ b/src/trace_processor/importers/proto/winscope/shell_transitions_tracker.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Tracks information in the transition table.
+class ShellTransitionsTracker : public Destructible {
+ public:
+ explicit ShellTransitionsTracker(TraceProcessorContext*);
+ virtual ~ShellTransitionsTracker() override;
+
+ static ShellTransitionsTracker* GetOrCreate(TraceProcessorContext* context) {
+ if (!context->shell_transitions_tracker) {
+ context->shell_transitions_tracker.reset(
+ new ShellTransitionsTracker(context));
+ }
+ return static_cast<ShellTransitionsTracker*>(
+ context->shell_transitions_tracker.get());
+ }
+
+ tables::WindowManagerShellTransitionsTable::Id InternTransition(
+ int32_t transition_id);
+
+ private:
+ TraceProcessorContext* context_;
+ std::unordered_map<int32_t, tables::WindowManagerShellTransitionsTable::Id>
+ transition_id_to_row_mapping_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SHELL_TRANSITIONS_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.cc b/src/trace_processor/importers/proto/winscope/winscope_module.cc
index de9e224..7f6c154 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.cc
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.cc
@@ -23,11 +23,14 @@
WinscopeModule::WinscopeModule(TraceProcessorContext* context)
: surfaceflinger_layers_parser_(context),
- surfaceflinger_transactions_parser_(context) {
+ surfaceflinger_transactions_parser_(context),
+ shell_transitions_parser_(context) {
RegisterForField(TracePacket::kSurfaceflingerLayersSnapshotFieldNumber,
context);
RegisterForField(TracePacket::kSurfaceflingerTransactionsFieldNumber,
context);
+ RegisterForField(TracePacket::kShellTransitionFieldNumber, context);
+ RegisterForField(TracePacket::kShellHandlerMappingsFieldNumber, context);
}
void WinscopeModule::ParseTracePacketData(const TracePacket::Decoder& decoder,
@@ -43,6 +46,13 @@
surfaceflinger_transactions_parser_.Parse(
timestamp, decoder.surfaceflinger_transactions());
return;
+ case TracePacket::kShellTransitionFieldNumber:
+ shell_transitions_parser_.ParseTransition(decoder.shell_transition());
+ return;
+ case TracePacket::kShellHandlerMappingsFieldNumber:
+ shell_transitions_parser_.ParseHandlerMappings(
+ decoder.shell_handler_mappings());
+ return;
}
}
diff --git a/src/trace_processor/importers/proto/winscope/winscope_module.h b/src/trace_processor/importers/proto/winscope/winscope_module.h
index d9428d0..fffe42c 100644
--- a/src/trace_processor/importers/proto/winscope/winscope_module.h
+++ b/src/trace_processor/importers/proto/winscope/winscope_module.h
@@ -21,6 +21,7 @@
#include "perfetto/base/build_config.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/importers/proto/winscope/shell_transitions_parser.h"
#include "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_parser.h"
#include "src/trace_processor/importers/proto/winscope/surfaceflinger_transactions_parser.h"
@@ -41,6 +42,7 @@
private:
SurfaceFlingerLayersParser surfaceflinger_layers_parser_;
SurfaceFlingerTransactionsParser surfaceflinger_transactions_parser_;
+ ShellTransitionsParser shell_transitions_parser_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/perfetto_sql/engine/created_function.cc b/src/trace_processor/perfetto_sql/engine/created_function.cc
index 162dbc6..9c32a97 100644
--- a/src/trace_processor/perfetto_sql/engine/created_function.cc
+++ b/src/trace_processor/perfetto_sql/engine/created_function.cc
@@ -587,6 +587,10 @@
SqlValue& out,
Destructors&) {
State* state = static_cast<State*>(ctx);
+
+ // Enter the function and ensure that we have a statement allocated.
+ RETURN_IF_ERROR(state->PushStackEntry());
+
if (argc != state->prototype().arguments.size()) {
return base::ErrStatus(
"%s: invalid number of args; expected %zu, received %zu",
@@ -608,9 +612,6 @@
}
}
- // Enter the function and ensure that we have a statement allocated.
- RETURN_IF_ERROR(state->PushStackEntry());
-
std::optional<Memoizer::MemoizedArgs> memoized_args =
Memoizer::AsMemoizedArgs(argc, argv);
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_map.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_map.sql
index f049075..0a21d27 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_map.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_map.sql
@@ -2,6 +2,8 @@
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
+INCLUDE PERFETTO MODULE chrome.event_latency_description;
+
-- Source of truth of the descriptions of EventLatency-based scroll jank causes.
CREATE PERFETTO TABLE chrome_scroll_jank_cause_descriptions (
-- The name of the EventLatency stage.
@@ -92,3 +94,27 @@
cause_thread,
cause_description
FROM cause_descriptions;
+
+-- Combined description of scroll jank cause and associated event latency stage.
+CREATE PERFETTO VIEW chrome_scroll_jank_causes_with_event_latencies(
+ -- The name of the EventLatency stage.
+ name STRING,
+ -- Description of the EventLatency stage.
+ description STRING,
+ -- The process name that may cause scroll jank.
+ cause_process STRING,
+ -- The thread name that may cause scroll jank. The thread will be on the
+ -- cause_process.
+ cause_thread STRING,
+ -- Description of the cause of scroll jank on this process and thread.
+ cause_description STRING
+) AS
+SELECT
+ stages.name,
+ stages.description,
+ causes.cause_process,
+ causes.cause_thread,
+ causes.cause_description
+FROM chrome_event_latency_stage_descriptions stages
+LEFT JOIN chrome_scroll_jank_cause_descriptions causes
+ ON causes.event_latency_stage = stages.name;
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_utils.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_utils.sql
index 2c6d0d0..35780fb 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_utils.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/scroll_jank_cause_utils.sql
@@ -3,46 +3,181 @@
-- found in the LICENSE file.
--- Retrieve the thread id of the thread on a particular process, if the name of
--- that process is known. Returns an error if there are multiple threads in
--- the given process with the same name.
-CREATE PERFETTO FUNCTION internal_find_utid_by_upid_and_name(
- -- Unique process id
- upid INT,
- -- The name of the thread
- thread_name STRING)
+-- Function to retrieve the upid for a surfaceflinger, as these are attributed
+-- to the GPU but are recorded on a different data source (and track group).
+CREATE PERFETTO FUNCTION internal_get_process_id_for_surfaceflinger()
+-- The process id for surfaceflinger.
+RETURNS INT AS
+SELECT
+ upid
+FROM process
+WHERE name GLOB '*surfaceflinger*'
+LIMIT 1;
+
+-- Map a generic process type to a specific name or substring of a name that
+-- can be found in the trace process table.
+CREATE PERFETTO TABLE internal_process_type_to_name (
+ -- The process type: one of 'Browser' or 'GPU'.
+ process_type STRING,
+ -- The process name for Chrome traces.
+ process_name STRING,
+ -- Substring identifying the process for system traces.
+ process_glob STRING
+) AS
+WITH process_names (
+ process_type,
+ process_name,
+ process_glob
+ )
+AS (
+VALUES
+ ('Browser', 'Browser', '*.chrome'),
+ ('GPU', 'Gpu', '*.chrome:privileged_process*'))
+SELECT
+ process_type,
+ process_name,
+ process_glob
+FROM process_names;
+
+CREATE PERFETTO FUNCTION internal_get_process_name(
+ -- The process type: one of 'Browser' or 'GPU'.
+ type STRING
+)
+-- The process name
+RETURNS STRING AS
+SELECT
+ process_name
+FROM internal_process_type_to_name
+WHERE process_type = $type
+LIMIT 1;
+
+CREATE PERFETTO FUNCTION internal_get_process_glob(
+ -- The process type: one of 'Browser' or 'GPU'.
+ type STRING
+)
+-- A substring of the process name that can be used in GLOB calculations.
+RETURNS STRING AS
+SELECT
+ process_glob
+FROM internal_process_type_to_name
+WHERE process_type = $type
+LIMIT 1;
+
+-- TODO(b/309937901): Add chrome instance id for multiple chromes/webviews in a
+-- trace, as this may result in multiple browser and GPU processes.
+-- Function to retrieve the chrome process ID for a specific process type. Does
+-- not retrieve the Renderer process, as this is determined when the
+-- EventLatency is known. See function
+-- internal_get_renderer_upid_for_event_latency below.
+CREATE PERFETTO FUNCTION internal_get_process_id_by_type(
+ -- The process type: one of 'Browser' or 'GPU'.
+ type STRING
+)
RETURNS TABLE (
- -- Unique thread id.
- utid INT
+ -- The process id for the process type.
+ upid INT
) AS
SELECT
- DISTINCT utid
-FROM thread
-WHERE upid = $upid
- AND name = $thread_name;
+ upid
+FROM process
+WHERE name = internal_get_process_name($type)
+ OR name GLOB internal_get_process_glob($type);
--- Function to retrieve the track id of the thread on a particular process if
+-- Function to retrieve the chrome process ID that a given EventLatency slice
+-- occurred on. This is the Renderer process.
+CREATE PERFETTO FUNCTION internal_get_renderer_upid_for_event_latency(
+ -- The slice id for an EventLatency slice.
+ id INT
+)
+-- The process id for an EventLatency slice. This is the Renderer process.
+RETURNS INT AS
+SELECT
+ upid
+FROM process_slice
+WHERE id = $id;
+
+-- Helper function to retrieve all of the upids for a given process, thread,
+-- or EventLatency.
+CREATE PERFETTO FUNCTION internal_processes_by_type_for_event_latency(
+ -- The process type that the thread is on: one of 'Browser', 'Renderer' or
+ -- 'GPU'.
+ type STRING,
+ -- The name of the thread.
+ thread STRING,
+ -- The slice id of an EventLatency slice.
+ event_latency_id INT)
+RETURNS TABLE (
+ upid INT
+) AS
+WITH all_upids AS (
+ -- Renderer process upids
+ SELECT
+ $type AS process,
+ $thread AS thread,
+ $event_latency_id AS event_latency_id,
+ internal_get_renderer_upid_for_event_latency($event_latency_id) AS upid
+ WHERE $type = 'Renderer'
+ UNION ALL
+ -- surfaceflinger upids
+ SELECT
+ $type AS process,
+ $thread AS thread,
+ $event_latency_id AS event_latency_id,
+ internal_get_process_id_for_surfaceflinger() AS upid
+ WHERE $type = 'GPU' AND $thread = 'surfaceflinger'
+ UNION ALL
+ -- Generic Browser and GPU process upids
+ SELECT
+ $type AS process,
+ $thread AS thread,
+ $event_latency_id AS event_latency_id,
+ upid
+ FROM internal_get_process_id_by_type($type)
+ WHERE $type = 'Browser'
+ OR ($type = 'GPU' AND $thread != 'surfaceflinger')
+)
+SELECT
+ upid
+FROM all_upids;
+
+-- Function to retrieve the thread id of the thread on a particular process if
-- there are any slices during a particular EventLatency slice duration; this
-- upid/thread combination refers to a cause of Scroll Jank.
-CREATE PERFETTO FUNCTION chrome_select_scroll_jank_cause_track(
+CREATE PERFETTO FUNCTION chrome_select_scroll_jank_cause_thread(
-- The slice id of an EventLatency slice.
event_latency_id INT,
- -- The process id that the thread is on.
- upid INT,
+ -- The process type that the thread is on: one of 'Browser', 'Renderer' or
+ -- 'GPU'.
+ process_type STRING,
-- The name of the thread.
thread_name STRING)
RETURNS TABLE (
- -- The track id associated with |thread| on the process with |upid|.
- track_id INT
+ -- The utid associated with |thread| on the process with |upid|.
+ utid INT
) AS
+WITH threads AS (
+ SELECT
+ utid
+ FROM thread
+ WHERE upid IN
+ (
+ SELECT DISTINCT
+ upid
+ FROM internal_processes_by_type_for_event_latency(
+ $process_type,
+ $thread_name,
+ $event_latency_id)
+ )
+ AND name = $thread_name
+)
SELECT
- DISTINCT track_id
+ DISTINCT utid
FROM thread_slice
WHERE utid IN
(
SELECT
utid
- FROM internal_find_utid_by_upid_and_name($upid, $thread_name)
+ FROM threads
)
AND ts >= (SELECT ts FROM slice WHERE id = $event_latency_id LIMIT 1)
AND ts <= (SELECT ts + dur FROM slice WHERE id = $event_latency_id LIMIT 1);
diff --git a/src/trace_processor/sqlite/sqlite_engine.cc b/src/trace_processor/sqlite/sqlite_engine.cc
index b1d1be4..9f845b1 100644
--- a/src/trace_processor/sqlite/sqlite_engine.cc
+++ b/src/trace_processor/sqlite/sqlite_engine.cc
@@ -18,6 +18,7 @@
#include <memory>
#include <optional>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -105,11 +106,17 @@
// them) because |OnSqliteTableDestroyed| will be called as each DROP is
// executed.
std::vector<std::string> drop_stmts;
- for (auto it = sqlite_tables_.GetIterator(); it; ++it) {
- if (it.value() != SqliteTable::TableType::kExplicitCreate) {
+ std::unordered_set<std::string> dropped_tables;
+ for (auto it = all_created_sqlite_tables_.rbegin();
+ it != all_created_sqlite_tables_.rend(); it++) {
+ if (auto* type = sqlite_tables_.Find(*it);
+ !type || *type != SqliteTable::TableType::kExplicitCreate) {
continue;
}
- base::StackString<1024> drop("DROP TABLE %s", it.key().c_str());
+ if (auto it_and_ins = dropped_tables.insert(*it); !it_and_ins.second) {
+ continue;
+ }
+ base::StackString<1024> drop("DROP TABLE %s", it->c_str());
drop_stmts.emplace_back(drop.ToStdString());
}
for (const auto& drop : drop_stmts) {
@@ -248,6 +255,7 @@
SqliteTable::TableType type) {
auto it_and_inserted = sqlite_tables_.Insert(name, type);
PERFETTO_CHECK(it_and_inserted.second);
+ all_created_sqlite_tables_.push_back(name);
}
void SqliteEngine::OnSqliteTableDestroyed(const std::string& name) {
diff --git a/src/trace_processor/sqlite/sqlite_engine.h b/src/trace_processor/sqlite/sqlite_engine.h
index 42ef5ae..9af23fe 100644
--- a/src/trace_processor/sqlite/sqlite_engine.h
+++ b/src/trace_processor/sqlite/sqlite_engine.h
@@ -24,6 +24,7 @@
#include <optional>
#include <string>
#include <type_traits>
+#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/flat_hash_map.h"
@@ -137,6 +138,7 @@
SqliteEngine& operator=(SqliteEngine&&) = delete;
base::FlatHashMap<std::string, SqliteTable::TableType> sqlite_tables_;
+ std::vector<std::string> all_created_sqlite_tables_;
base::FlatHashMap<std::string, std::unique_ptr<SqliteTable>> saved_tables_;
base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_;
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 3f7c3bd..d6d40b3 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -324,7 +324,7 @@
module.xDisconnect = &xDestroy;
break;
case TableType::kExplicitCreate:
- // xConnect and xDestroy will be called when the table is CREATE-ed and
+ // xCreate and xDestroy will be called when the table is CREATE-ed and
// DROP-ed respectively.
module.xCreate = &xCreate;
module.xDestroy = &xDestroy;
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index d583632..1e40489 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -262,6 +262,11 @@
kSingle, kInfo, kAnalysis, \
"SurfaceFlinger transactions packet has unknown fields, which results " \
"in some arguments missing. You may need a newer version of trace " \
+ "processor to parse them."), \
+ F(winscope_shell_transitions_parse_errors, \
+ kSingle, kInfo, kAnalysis, \
+ "Shell transition packet has unknown fields, which results " \
+ "in some arguments missing. You may need a newer version of trace " \
"processor to parse them.")
// clang-format on
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index e75233e..79463f0 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -738,6 +738,24 @@
return &surfaceflinger_transactions_table_;
}
+ const tables::WindowManagerShellTransitionsTable&
+ window_manager_shell_transitions_table() const {
+ return window_manager_shell_transitions_table_;
+ }
+ tables::WindowManagerShellTransitionsTable*
+ mutable_window_manager_shell_transitions_table() {
+ return &window_manager_shell_transitions_table_;
+ }
+
+ const tables::WindowManagerShellTransitionHandlersTable&
+ window_manager_shell_transition_handlers_table() const {
+ return window_manager_shell_transition_handlers_table_;
+ }
+ tables::WindowManagerShellTransitionHandlersTable*
+ mutable_window_manager_shell_transition_handlers_table() {
+ return &window_manager_shell_transition_handlers_table_;
+ }
+
const tables::ExperimentalProtoPathTable& experimental_proto_path_table()
const {
return experimental_proto_path_table_;
@@ -995,6 +1013,10 @@
tables::SurfaceFlingerLayerTable surfaceflinger_layer_table_{&string_pool_};
tables::SurfaceFlingerTransactionsTable surfaceflinger_transactions_table_{
&string_pool_};
+ tables::WindowManagerShellTransitionsTable
+ window_manager_shell_transitions_table_{&string_pool_};
+ tables::WindowManagerShellTransitionHandlersTable
+ window_manager_shell_transition_handlers_table_{&string_pool_};
tables::ExperimentalProtoPathTable experimental_proto_path_table_{
&string_pool_};
diff --git a/src/trace_processor/tables/slice_tables.py b/src/trace_processor/tables/slice_tables.py
index 7548637..241fa10 100644
--- a/src/trace_processor/tables/slice_tables.py
+++ b/src/trace_processor/tables/slice_tables.py
@@ -190,13 +190,24 @@
],
parent=SLICE_TABLE,
tabledoc=TableDoc(
- doc='''''',
+ doc='''
+ This table contains information on the expected timeline of either
+ a display frame or a surface frame.
+ ''',
group='Slice',
columns={
- 'display_frame_token': '''''',
- 'surface_frame_token': '''''',
- 'upid': '''''',
- 'layer_name': ''''''
+ 'display_frame_token':
+ 'Display frame token (vsync id).',
+ 'surface_frame_token':
+ '''
+ Surface frame token (vsync id), null if this is a display frame.
+ ''',
+ 'upid':
+ '''
+ Unique process id of the app that generates the surface frame.
+ ''',
+ 'layer_name':
+ 'Layer name if this is a surface frame.',
}))
ACTUAL_FRAME_TIMELINE_SLICE_TABLE = Table(
@@ -212,24 +223,48 @@
C('on_time_finish', CppInt32()),
C('gpu_composition', CppInt32()),
C('jank_type', CppString()),
+ C('jank_severity_type', CppString()),
C('prediction_type', CppString()),
C('jank_tag', CppString()),
],
parent=SLICE_TABLE,
tabledoc=TableDoc(
- doc='''''',
+ doc='''
+ This table contains information on the actual timeline and additional
+ analysis related to the performance of either a display frame or a
+ surface frame.
+ ''',
group='Slice',
columns={
- 'display_frame_token': '''''',
- 'surface_frame_token': '''''',
- 'upid': '''''',
- 'layer_name': '''''',
- 'present_type': '''''',
- 'on_time_finish': '''''',
- 'gpu_composition': '''''',
- 'jank_type': '''''',
- 'prediction_type': '''''',
- 'jank_tag': ''''''
+ 'display_frame_token':
+ 'Display frame token (vsync id).',
+ 'surface_frame_token':
+ '''
+ Surface frame token (vsync id), null if this is a display frame.
+ ''',
+ 'upid':
+ '''
+ Unique process id of the app that generates the surface frame.
+ ''',
+ 'layer_name':
+ 'Layer name if this is a surface frame.',
+ 'present_type':
+ 'Frame\'s present type (eg. on time / early / late).',
+ 'on_time_finish':
+ 'Whether the frame finishes on time.',
+ 'gpu_composition':
+ 'Whether the frame used gpu composition.',
+ 'jank_type':
+ '''
+ Specify the jank types for this frame if there's jank, or
+ none if no jank occured.
+ ''',
+ 'jank_severity_type':
+ 'Severity of the jank: none if no jank.',
+ 'prediction_type':
+ 'Frame\'s prediction type (eg. valid / expired).',
+ 'jank_tag':
+ 'Jank tag based on jank type, used for slice visualization.'
}))
EXPERIMENTAL_FLAT_SLICE_TABLE = Table(
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 16981fa..989d5e2 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -120,6 +120,10 @@
default;
SurfaceFlingerLayerTable::~SurfaceFlingerLayerTable() = default;
SurfaceFlingerTransactionsTable::~SurfaceFlingerTransactionsTable() = default;
+WindowManagerShellTransitionsTable::~WindowManagerShellTransitionsTable() =
+ default;
+WindowManagerShellTransitionHandlersTable::
+ ~WindowManagerShellTransitionHandlersTable() = default;
} // namespace tables
diff --git a/src/trace_processor/tables/winscope_tables.py b/src/trace_processor/tables/winscope_tables.py
index 22f2080..12e7cef 100644
--- a/src/trace_processor/tables/winscope_tables.py
+++ b/src/trace_processor/tables/winscope_tables.py
@@ -18,6 +18,7 @@
from python.generators.trace_processor_table.public import CppTableId
from python.generators.trace_processor_table.public import TableDoc
from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import CppString
SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE = Table(
python_module=__file__,
@@ -60,16 +61,53 @@
C('arg_set_id', CppUint32()),
],
tabledoc=TableDoc(
- doc='SurfaceFlinger transactions. Each row contains a set of transactions that SurfaceFlinger committed together.',
+ doc='SurfaceFlinger transactions. Each row contains a set of ' +
+ 'transactions that SurfaceFlinger committed together.',
group='Winscope',
columns={
'ts': 'Timestamp of the transactions commit',
'arg_set_id': 'Extra args parsed from the proto message',
}))
+WINDOW_MANAGER_SHELL_TRANSITIONS_TABLE = Table(
+ python_module=__file__,
+ class_name='WindowManagerShellTransitionsTable',
+ sql_name='window_manager_shell_transitions',
+ columns=[
+ C('ts', CppInt64()),
+ C('transition_id', CppInt64()),
+ C('arg_set_id', CppUint32()),
+ ],
+ tabledoc=TableDoc(
+ doc='Window Manager Shell Transitions',
+ group='Winscope',
+ columns={
+ 'ts': 'The timestamp the transition started playing',
+ 'transition_id': 'The id of the transition',
+ 'arg_set_id': 'Extra args parsed from the proto message',
+ }))
+
+WINDOW_MANAGER_SHELL_TRANSITION_HANDLERS_TABLE = Table(
+ python_module=__file__,
+ class_name='WindowManagerShellTransitionHandlersTable',
+ sql_name='window_manager_shell_transition_handlers',
+ columns=[
+ C('handler_id', CppInt64()),
+ C('handler_name', CppString()),
+ ],
+ tabledoc=TableDoc(
+ doc='Window Manager Shell Transition Handlers',
+ group='Winscope',
+ columns={
+ 'handler_id': 'The id of the handler',
+ 'handler_name': 'The name of the handler',
+ }))
+
# Keep this list sorted.
ALL_TABLES = [
SURFACE_FLINGER_LAYERS_SNAPSHOT_TABLE,
SURFACE_FLINGER_LAYER_TABLE,
SURFACE_FLINGER_TRANSACTIONS_TABLE,
+ WINDOW_MANAGER_SHELL_TRANSITIONS_TABLE,
+ WINDOW_MANAGER_SHELL_TRANSITION_HANDLERS_TABLE,
]
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 976357c..766df13 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -479,12 +479,13 @@
PERFETTO_CHECK(tables_views_in_sqlite_count_ >=
engine_->RuntimeTablesAndViewsCount());
- // Tables and views in sqlite with all objects from Perfeto Sql Engine without
- // tables and views.
+ // Add the number of tables/views registered with SQLite to the number of
+ // "objects" (tables, views, functions etc) that we've registered and take
+ // away the number of "runtime" tables/views which are registered (which will
+ // be double counted).
uint64_t registered_count_before = tables_views_in_sqlite_count_ +
engine_->AllRegisteredObjectsCount() -
engine_->RuntimeTablesAndViewsCount();
-
InitPerfettoSqlEngine();
return static_cast<size_t>(registered_count_before -
engine_->AllRegisteredObjectsCount());
@@ -854,6 +855,10 @@
RegisterStaticTable(storage->surfaceflinger_layer_table());
RegisterStaticTable(storage->surfaceflinger_transactions_table());
+ RegisterStaticTable(storage->window_manager_shell_transitions_table());
+ RegisterStaticTable(
+ storage->window_manager_shell_transition_handlers_table());
+
RegisterStaticTable(storage->metadata_table());
RegisterStaticTable(storage->cpu_table());
RegisterStaticTable(storage->cpu_freq_table());
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index c2644ee..1eb16e4 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -123,6 +123,7 @@
std::unique_ptr<Destructible> i2c_tracker; // I2CTracker
std::unique_ptr<Destructible> perf_data_tracker; // PerfDataTracker
std::unique_ptr<Destructible> content_analyzer;
+ std::unique_ptr<Destructible> shell_transitions_tracker;
// These fields are trace readers which will be called by |forwarding_parser|
// once the format of the trace is discovered. They are placed here as they
diff --git a/src/traceconv/pprof_builder.cc b/src/traceconv/pprof_builder.cc
index 1ca8654..d8de2dc 100644
--- a/src/traceconv/pprof_builder.cc
+++ b/src/traceconv/pprof_builder.cc
@@ -559,7 +559,7 @@
bool WriteMappings(trace_processor::TraceProcessor* tp,
const std::set<int64_t>& seen_mappings) {
Iterator mapping_it = tp->ExecuteQuery(
- "SELECT id, exact_offset, start, end, name "
+ "SELECT id, exact_offset, start, end, name, build_id "
"FROM stack_profile_mapping;");
size_t mappings_no = 0;
while (mapping_it.Next()) {
@@ -569,10 +569,10 @@
++mappings_no;
auto interned_filename = ToStringTableId(
interner_->InternString(mapping_it.Get(4).AsString()));
+ auto interned_build_id = ToStringTableId(
+ interner_->InternString(mapping_it.Get(5).AsString()));
auto* gmapping = result_->add_mapping();
gmapping->set_id(ToPprofId(id));
- // Do not set the build_id here to avoid downstream services
- // trying to symbolize (e.g. b/141735056)
gmapping->set_file_offset(
static_cast<uint64_t>(mapping_it.Get(1).AsLong()));
gmapping->set_memory_start(
@@ -580,6 +580,7 @@
gmapping->set_memory_limit(
static_cast<uint64_t>(mapping_it.Get(3).AsLong()));
gmapping->set_filename(interned_filename);
+ gmapping->set_build_id(interned_build_id);
}
if (!mapping_it.Status().ok()) {
PERFETTO_DFATAL_OR_ELOG("Invalid mapping iterator: %s",
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 7ad51cf..62bd4b9 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -7361,6 +7361,64 @@
kUnsetFtraceId,
430,
kUnsetSize},
+ {"sched_switch_with_ctrs",
+ "perf_trace_counters",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "old_pid", 1, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "new_pid", 2, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "cctr", 3, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr0", 4, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr1", 5, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr2", 6, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr3", 7, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "lctr0", 8, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "lctr1", 9, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr4", 10, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr5", 11, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "prev_comm", 12, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "prev_pid", 13, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "cyc", 14, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "inst", 15, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "stallbm", 16, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "l3dm", 17, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 487,
+ kUnsetSize},
{"cpu_frequency",
"power",
{
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format b/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format
new file mode 100644
index 0000000..e357f11
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format
@@ -0,0 +1,16 @@
+name: sched_switch_with_ctrs
+ID: 1237
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:char prev_comm[16]; offset:8; size:16; signed:0;
+ field:pid_t prev_pid; offset:24; size:4; signed:1;
+ field:u32 cyc; offset:28; size:4; signed:0;
+ field:u32 inst; offset:32; size:4; signed:0;
+ field:u32 stallbm; offset:36; size:4; signed:0;
+ field:u32 l3dm; offset:40; size:4; signed:0;
+
+print fmt: "prev_comm=%s, prev_pid=%d, CYC=%u, INST=%u, STALLBM=%u, L3DM=%u", REC->prev_comm, REC->prev_pid, REC->cyc, REC->inst, REC->stallbm, REC->l3dm
diff --git a/src/traced_relay/relay_service.cc b/src/traced_relay/relay_service.cc
index 1ef5855..1a67439 100644
--- a/src/traced_relay/relay_service.cc
+++ b/src/traced_relay/relay_service.cc
@@ -15,22 +15,37 @@
*/
#include "src/traced_relay/relay_service.h"
+
#include <memory>
+#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/utils.h"
#include "protos/perfetto/ipc/wire_protocol.gen.h"
#include "src/ipc/buffered_frame_deserializer.h"
#include "src/traced_relay/socket_relay_handler.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#endif
+
using ::perfetto::protos::gen::IPCFrame;
namespace perfetto {
RelayService::RelayService(base::TaskRunner* task_runner)
- : task_runner_(task_runner) {}
+ : task_runner_(task_runner), machine_id_hint_(GetMachineIdHint()) {}
void RelayService::Start(const char* listening_socket_name,
const char* client_socket_name) {
@@ -77,6 +92,8 @@
set_peer_identity->set_uid(
static_cast<int32_t>(server_conn->peer_uid_posix()));
+ set_peer_identity->set_machine_id_hint(machine_id_hint_);
+
// Buffer the SetPeerIdentity request.
auto req = ipc::BufferedFrameDeserializer::Serialize(ipc_frame);
SocketWithBuffer server, client;
@@ -123,4 +140,61 @@
PERFETTO_DFATAL("Should be unreachable.");
}
+std::string RelayService::GetMachineIdHint(
+ bool use_pseudo_boot_id_for_testing) {
+ // Gets kernel boot ID if possible.
+ std::string boot_id;
+ if (!use_pseudo_boot_id_for_testing &&
+ base::ReadFile("/proc/sys/kernel/random/boot_id", &boot_id)) {
+ return base::StripSuffix(boot_id, "\n");
+ }
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ auto get_pseudo_boot_id = []() -> std::string {
+ base::Hasher hasher;
+ const char* dev_path = "/dev";
+ // Generate a pseudo-unique identifier for the current machine.
+ // Source 1: system boot timestamp from the creation time of /dev inode.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+ // Mac or iOS, just use stat(2).
+ struct stat stat_buf {};
+ int rc = PERFETTO_EINTR(stat(dev_path, &stat_buf));
+ if (rc == -1)
+ return std::string();
+ hasher.Update(reinterpret_cast<const char*>(&stat_buf.st_birthtimespec),
+ sizeof(stat_buf.st_birthtimespec));
+#else
+ // Android or Linux, use statx(2)
+ struct statx stat_buf {};
+ auto rc = PERFETTO_EINTR(syscall(__NR_statx, /*dirfd=*/-1, dev_path,
+ /*flags=*/0, STATX_BTIME, &stat_buf));
+ if (rc == -1)
+ return std::string();
+ hasher.Update(reinterpret_cast<const char*>(&stat_buf.stx_btime),
+ sizeof(stat_buf.stx_btime));
+#endif
+
+ // Source 2: uname(2).
+ utsname kernel_info{};
+ if (uname(&kernel_info) == -1)
+ return std::string();
+
+ // Create a non-cryptographic digest of bootup timestamp and everything in
+ // utsname.
+ hasher.Update(reinterpret_cast<const char*>(&kernel_info),
+ sizeof(kernel_info));
+ return base::Uint64ToHexStringNoPrefix(hasher.digest());
+ };
+
+ auto pseudo_boot_id = get_pseudo_boot_id();
+ if (!pseudo_boot_id.empty())
+ return pseudo_boot_id;
+#endif
+
+ // If all above failed, return nothing.
+ return std::string();
+}
+
} // namespace perfetto
diff --git a/src/traced_relay/relay_service.h b/src/traced_relay/relay_service.h
index 8e5bf6d..1760df3 100644
--- a/src/traced_relay/relay_service.h
+++ b/src/traced_relay/relay_service.h
@@ -40,6 +40,13 @@
// |server_socket_name| and |client_socket_name| ports.
void Start(const char* server_socket_name, const char* client_socket_name);
+ static std::string GetMachineIdHint(
+ bool use_pseudo_boot_id_for_testing = false);
+
+ void SetMachineIdHintForTesting(std::string machine_id_hint) {
+ machine_id_hint_ = machine_id_hint;
+ }
+
private:
struct PendingConnection {
// This keeps a connected UnixSocketRaw server socket in its first element.
@@ -60,6 +67,9 @@
base::TaskRunner* const task_runner_ = nullptr;
+ // A hint to the host traced for inferring the identifier of this machine.
+ std::string machine_id_hint_;
+
std::unique_ptr<base::UnixSocket> listening_socket_;
std::string client_socket_name_;
diff --git a/src/traced_relay/relay_service_integrationtest.cc b/src/traced_relay/relay_service_integrationtest.cc
index 7e29041..bb83ecf 100644
--- a/src/traced_relay/relay_service_integrationtest.cc
+++ b/src/traced_relay/relay_service_integrationtest.cc
@@ -15,6 +15,9 @@
*/
#include <memory>
+#include <string>
+#include <vector>
+#include "perfetto/ext/base/unix_socket.h"
#include "src/traced_relay/relay_service.h"
#include "src/base/test/test_task_runner.h"
@@ -28,6 +31,17 @@
namespace perfetto {
namespace {
+struct TestParams {
+ std::string id;
+ std::string tcp_sock_name;
+ std::string unix_sock_name;
+ std::string producer_name;
+
+ std::unique_ptr<RelayService> relay_service;
+ std::unique_ptr<base::UnixSocket> server_socket;
+ std::unique_ptr<FakeProducerThread> producer_thread;
+};
+
TEST(TracedRelayIntegrationTest, BasicCase) {
base::TestTaskRunner task_runner;
@@ -102,6 +116,125 @@
ASSERT_EQ(packet.trusted_pid(), pid);
ASSERT_EQ(packet.trusted_uid(), uid);
ASSERT_EQ(packet.for_testing().seq_value(), rnd_engine());
+ // The tracing service should emit non-default machine ID in trace packets.
+ ASSERT_NE(packet.machine_id(), 0u);
+ }
+}
+
+TEST(TracedRelayIntegrationTest, MachineID_MultiRelayService) {
+ base::TestTaskRunner task_runner;
+ std::vector<TestParams> test_params(2);
+
+ base::UnixSocket::EventListener event_listener;
+ for (size_t i = 0; i < test_params.size(); i++) {
+ auto& param = test_params[i];
+ param.id = std::to_string(i + 1);
+ param.server_socket = base::UnixSocket::Listen(
+ "127.0.0.1:0", &event_listener, &task_runner, base::SockFamily::kInet,
+ base::SockType::kStream);
+ ASSERT_TRUE(param.server_socket->is_listening());
+ param.tcp_sock_name = param.server_socket->GetSockAddr();
+ param.relay_service = std::make_unique<RelayService>(&task_runner);
+ param.relay_service->SetMachineIdHintForTesting("test-machine-id-" +
+ param.id);
+ param.unix_sock_name = std::string("@traced_relay_") + param.id;
+ param.producer_name = std::string("perfetto.FakeProducer.") + param.id;
+ }
+ for (auto& param : test_params) {
+ // Shut down listening sockets to free the port. It's unlikely that the port
+ // will be taken by another process so quickly before we reach the code
+ // below.
+ param.server_socket = nullptr;
+ }
+ auto relay_sock_name =
+ test_params[0].tcp_sock_name + "," + test_params[1].tcp_sock_name;
+
+ for (auto& param : test_params) {
+ param.relay_service->Start(param.unix_sock_name.c_str(),
+ param.tcp_sock_name.c_str());
+ }
+
+ TestHelper helper(&task_runner, TestHelper::Mode::kStartDaemons,
+ relay_sock_name.c_str());
+ ASSERT_EQ(helper.num_producers(), 2u);
+ helper.StartServiceIfRequired();
+
+ for (auto& param : test_params) {
+ auto checkpoint_name = "perfetto.FakeProducer.connected." + param.id;
+ auto producer_connected = task_runner.CreateCheckpoint(checkpoint_name);
+ auto noop = []() {};
+ auto connected = std::bind(
+ [&](std::function<void()> checkpoint) {
+ task_runner.PostTask(checkpoint);
+ },
+ producer_connected);
+ // We won't use the built-in fake producer and will start our own.
+ param.producer_thread = std::make_unique<FakeProducerThread>(
+ param.unix_sock_name, connected, noop, noop, param.producer_name);
+ param.producer_thread->Connect();
+ task_runner.RunUntilCheckpoint(checkpoint_name);
+ }
+
+ helper.ConnectConsumer();
+ helper.WaitForConsumerConnect();
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ trace_config.set_duration_ms(200);
+
+ static constexpr uint32_t kMsgSize = 1024;
+ static constexpr uint32_t kRandomSeed = 42;
+
+ // Enable the 1st producer.
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("perfetto.FakeProducer.1");
+ ds_config->set_target_buffer(0);
+ ds_config->mutable_for_testing()->set_message_count(12);
+ ds_config->mutable_for_testing()->set_message_size(kMsgSize);
+ ds_config->mutable_for_testing()->set_send_batch_on_register(true);
+ // Enable the 2nd producer.
+ ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("perfetto.FakeProducer.2");
+ ds_config->set_target_buffer(0);
+ ds_config->mutable_for_testing()->set_message_count(24);
+ ds_config->mutable_for_testing()->set_message_size(kMsgSize);
+ ds_config->mutable_for_testing()->set_send_batch_on_register(true);
+
+ helper.StartTracing(trace_config);
+ helper.WaitForTracingDisabled();
+
+ helper.ReadData();
+ helper.WaitForReadData();
+
+ const auto& packets = helper.trace();
+ ASSERT_EQ(packets.size(), 36u);
+
+ // The producer is connected from this process. The relay service will inject
+ // the SetPeerIdentity message using the pid and euid of the current process.
+ auto pid = static_cast<int32_t>(getpid());
+ auto uid = static_cast<int32_t>(geteuid());
+
+ std::minstd_rand0 rnd_engine(kRandomSeed);
+ std::map<uint32_t, size_t> packets_counts; // machine ID => count.
+
+ for (const auto& packet : packets) {
+ ASSERT_TRUE(packet.has_for_testing());
+ ASSERT_EQ(packet.trusted_pid(), pid);
+ ASSERT_EQ(packet.trusted_uid(), uid);
+ packets_counts[packet.machine_id()]++;
+ }
+
+ // Fake producer (1, 2) either gets machine ID (1, 2), or (2, 1), depending on
+ // which on is seen by the tracing service first.
+ ASSERT_EQ(packets_counts.size(), 2u);
+ auto count_1 = packets_counts.begin()->second;
+ auto count_2 = packets_counts.rbegin()->second;
+ ASSERT_TRUE(count_1 == 12u || count_1 == 24u);
+ ASSERT_EQ(count_1 + count_2, 36u);
+
+ for (auto& param : test_params) {
+ param.producer_thread = nullptr;
+ param.relay_service = nullptr;
}
}
diff --git a/src/traced_relay/relay_service_unittest.cc b/src/traced_relay/relay_service_unittest.cc
index 649f552..0508328 100644
--- a/src/traced_relay/relay_service_unittest.cc
+++ b/src/traced_relay/relay_service_unittest.cc
@@ -116,6 +116,7 @@
const auto& set_peer_identity = frame->set_peer_identity();
EXPECT_EQ(set_peer_identity.pid(), getpid());
EXPECT_EQ(set_peer_identity.uid(), static_cast<int32_t>(geteuid()));
+ EXPECT_TRUE(set_peer_identity.has_machine_id_hint());
frame = deserializer.PopNextFrame();
EXPECT_EQ(1u, frame->data_for_testing().size());
@@ -126,5 +127,34 @@
task_runner.RunUntilCheckpoint("peer_identity_recv");
}
+TEST(RelayServiceTest, MachineIDHint) {
+ base::TestTaskRunner task_runner;
+ auto relay_service = std::make_unique<RelayService>(&task_runner);
+
+ auto hint1 = relay_service->GetMachineIdHint();
+ auto hint2 =
+ relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
+ EXPECT_NE(hint1, hint2);
+
+ // Add a short sleep to verify that pseudo boot ID isn't affected.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+ relay_service = std::make_unique<RelayService>(&task_runner);
+ auto hint3 = relay_service->GetMachineIdHint();
+ auto hint4 =
+ relay_service->GetMachineIdHint(/*use_pseudo_boot_id_for_testing=*/true);
+ EXPECT_NE(hint3, hint4);
+
+ EXPECT_FALSE(hint1.empty());
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // This test can run on Android kernel 3.x, but pseudo boot ID uses statx(2)
+ // that requires kernel 4.11.
+ EXPECT_FALSE(hint2.empty());
+#endif
+
+ EXPECT_EQ(hint1, hint3);
+ EXPECT_EQ(hint2, hint4);
+}
+
} // namespace
} // namespace perfetto
diff --git a/src/traced_relay/socket_relay_handler_unittest.cc b/src/traced_relay/socket_relay_handler_unittest.cc
index a96308a..380c594 100644
--- a/src/traced_relay/socket_relay_handler_unittest.cc
+++ b/src/traced_relay/socket_relay_handler_unittest.cc
@@ -110,8 +110,17 @@
// Test the SocketRelayHander with randomized request and response data.
TEST_P(SocketRelayHandlerTest, RandomizedRequestResponse) {
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER)
+ // Reduce the test strength for sanitizer builds.
+ constexpr size_t kMaxMsgSizeRng = 1 << 16;
+ constexpr size_t kMaxNumRequests = 10;
+#else
// The max message size in the number of RNG calls.
- constexpr size_t kMaxMsgSizeRng = 1 << 20;
+ constexpr size_t kMaxMsgSizeRng = 1 << 18;
+ // The max number of requests.
+ constexpr size_t kMaxNumRequests = 25;
+#endif
// Create the threads for sending and receiving data through the
// SocketRelayHandler.
@@ -122,7 +131,7 @@
auto& rng = client.data_prng;
// The max number of requests.
- const size_t num_requests = rng() % 50;
+ const size_t num_requests = rng() % kMaxNumRequests;
for (size_t j = 0; j < num_requests; j++) {
auto& send_endpoint = client.endpoint_sockets.first;
@@ -196,9 +205,12 @@
}
}
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+INSTANTIATE_TEST_SUITE_P(ByConnections, SocketRelayHandlerTest, Values(1, 5));
+#else
INSTANTIATE_TEST_SUITE_P(ByConnections,
SocketRelayHandlerTest,
- Values(1, 5, 50));
-
+ Values(1, 5, 25));
+#endif
} // namespace
} // namespace perfetto
diff --git a/src/tracing/core/packet_stream_validator.cc b/src/tracing/core/packet_stream_validator.cc
index d84eed3..9bed9b7 100644
--- a/src/tracing/core/packet_stream_validator.cc
+++ b/src/tracing/core/packet_stream_validator.cc
@@ -40,6 +40,7 @@
protos::pbzero::TracePacket::kCompressedPacketsFieldNumber,
protos::pbzero::TracePacket::kSynchronizationMarkerFieldNumber,
protos::pbzero::TracePacket::kTrustedPidFieldNumber,
+ protos::pbzero::TracePacket::kMachineIdFieldNumber,
};
// This translation unit is quite subtle and perf-sensitive. Remember to check
diff --git a/src/tracing/core/packet_stream_validator_unittest.cc b/src/tracing/core/packet_stream_validator_unittest.cc
index 74de04c..62de98a 100644
--- a/src/tracing/core/packet_stream_validator_unittest.cc
+++ b/src/tracing/core/packet_stream_validator_unittest.cc
@@ -181,6 +181,26 @@
EXPECT_FALSE(PacketStreamValidator::Validate(seq));
}
+TEST(PacketStreamValidatorTest, SimplePacketWithMachineID) {
+ protos::gen::TracePacket proto;
+ proto.set_machine_id(123);
+ std::string ser_buf = proto.SerializeAsString();
+
+ Slices seq;
+ seq.emplace_back(&ser_buf[0], ser_buf.size());
+ EXPECT_FALSE(PacketStreamValidator::Validate(seq));
+}
+
+TEST(PacketStreamValidatorTest, SimplePacketWithZeroMachineID) {
+ protos::gen::TracePacket proto;
+ proto.set_machine_id(0);
+ std::string ser_buf = proto.SerializeAsString();
+
+ Slices seq;
+ seq.emplace_back(&ser_buf[0], ser_buf.size());
+ EXPECT_FALSE(PacketStreamValidator::Validate(seq));
+}
+
TEST(PacketStreamValidatorTest, ComplexPacketWithPid) {
protos::gen::TracePacket proto;
proto.mutable_for_testing()->set_str("string field");
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 1e4783a..1d469ac 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -2329,8 +2329,8 @@
PERFETTO_DCHECK(sequence_properties.producer_id_trusted != 0);
PERFETTO_DCHECK(sequence_properties.writer_id != 0);
PERFETTO_DCHECK(sequence_properties.client_identity_trusted.has_uid());
- // Not checking sequence_properties.producer_pid_trusted: it is
- // base::kInvalidPid if the platform doesn't support it.
+ // Not checking sequence_properties.client_identity_trusted.has_pid():
+ // it is false if the platform doesn't support it.
PERFETTO_DCHECK(packet.size() > 0);
if (!PacketStreamValidator::Validate(packet.slices())) {
@@ -2356,6 +2356,7 @@
static_cast<int32_t>(client_identity_trusted.uid()));
trusted_packet->set_trusted_packet_sequence_id(
tracing_session->GetPacketSequenceID(
+ client_identity_trusted.machine_id(),
sequence_properties.producer_id_trusted,
sequence_properties.writer_id));
if (client_identity_trusted.has_pid()) {
@@ -2363,6 +2364,9 @@
trusted_packet->set_trusted_pid(
static_cast<int32_t>(client_identity_trusted.pid()));
}
+ if (client_identity_trusted.has_non_default_machine_id()) {
+ trusted_packet->set_machine_id(client_identity_trusted.machine_id());
+ }
if (previous_packet_dropped)
trusted_packet->set_previous_packet_dropped(previous_packet_dropped);
slice.size = trusted_packet.Finalize();
@@ -3430,7 +3434,8 @@
}
}
auto* wri_stats = trace_stats.add_writer_stats();
- wri_stats->set_sequence_id(tracing_session->GetPacketSequenceID(p, w));
+ wri_stats->set_sequence_id(
+ tracing_session->GetPacketSequenceID(kDefaultMachineID, p, w));
for (size_t i = 0; i < hist.num_buckets(); ++i) {
wri_stats->add_chunk_payload_histogram_counts(hist.GetBucketCount(i));
wri_stats->add_chunk_payload_histogram_sum(hist.GetBucketSum(i));
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 2639875..0f99493 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -465,9 +465,10 @@
return timeout_ms ? timeout_ms : kDataSourceStopTimeoutMs;
}
- PacketSequenceID GetPacketSequenceID(ProducerID producer_id,
+ PacketSequenceID GetPacketSequenceID(MachineID machine_id,
+ ProducerID producer_id,
WriterID writer_id) {
- auto key = std::make_pair(producer_id, writer_id);
+ auto key = std::make_tuple(machine_id, producer_id, writer_id);
auto it = packet_sequence_ids.find(key);
if (it != packet_sequence_ids.end())
return it->second;
@@ -551,7 +552,7 @@
// many entries as |config.buffers_size()|.
std::vector<BufferID> buffers_index;
- std::map<std::pair<ProducerID, WriterID>, PacketSequenceID>
+ std::map<std::tuple<MachineID, ProducerID, WriterID>, PacketSequenceID>
packet_sequence_ids;
PacketSequenceID last_packet_sequence_id = kServicePacketSequenceID;
diff --git a/src/tracing/internal/system_tracing_backend.cc b/src/tracing/internal/system_tracing_backend.cc
index 9488495..1b83579 100644
--- a/src/tracing/internal/system_tracing_backend.cc
+++ b/src/tracing/internal/system_tracing_backend.cc
@@ -65,11 +65,12 @@
shm.get(), shmem_page_size_hint, SharedMemoryABI::ShmemMode::kDefault);
}
+ ipc::Client::ConnArgs conn_args(GetProducerSocket(), true);
auto endpoint = ProducerIPCClient::Connect(
- GetProducerSocket(), args.producer, args.producer_name, args.task_runner,
+ std::move(conn_args), args.producer, args.producer_name, args.task_runner,
TracingService::ProducerSMBScrapingMode::kEnabled, shmem_size_hint,
shmem_page_size_hint, std::move(shm), std::move(arbiter),
- ProducerIPCClient::ConnectionFlags::kRetryIfUnreachable);
+ args.create_socket_async);
PERFETTO_CHECK(endpoint);
return endpoint;
}
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index c0d88ac..80e451f 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -970,6 +970,7 @@
rb.producer_conn_args.shmem_size_hint_bytes = args.shmem_size_hint_kb * 1024;
rb.producer_conn_args.shmem_page_size_hint_bytes =
args.shmem_page_size_hint_kb * 1024;
+ rb.producer_conn_args.create_socket_async = args.create_socket_async;
rb.producer->Initialize(rb.backend->ConnectProducer(rb.producer_conn_args));
}
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
index dfd0eda..1423223 100644
--- a/src/tracing/ipc/BUILD.gn
+++ b/src/tracing/ipc/BUILD.gn
@@ -50,6 +50,7 @@
"../../../gn:default_deps",
"../../../include/perfetto/ext/ipc",
"../../../include/perfetto/ext/tracing/core",
+ "../../../include/perfetto/tracing",
"../../base",
]
}
diff --git a/src/tracing/ipc/default_socket.cc b/src/tracing/ipc/default_socket.cc
index f053756..81ff6df 100644
--- a/src/tracing/ipc/default_socket.cc
+++ b/src/tracing/ipc/default_socket.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "perfetto/tracing/default_socket.h"
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index ea87953..3959b0a 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -66,7 +66,7 @@
ProducerIPCClient::ConnectionFlags::kRetryIfUnreachable},
producer, producer_name, task_runner, smb_scraping_mode,
shared_memory_size_hint_bytes, shared_memory_page_size_hint_bytes,
- std::move(shm), std::move(shm_arbiter)));
+ std::move(shm), std::move(shm_arbiter), CreateSocketAsync()));
}
// static. (Declared in include/tracing/ipc/producer_ipc_client.h).
@@ -79,13 +79,14 @@
size_t shared_memory_size_hint_bytes,
size_t shared_memory_page_size_hint_bytes,
std::unique_ptr<SharedMemory> shm,
- std::unique_ptr<SharedMemoryArbiter> shm_arbiter) {
+ std::unique_ptr<SharedMemoryArbiter> shm_arbiter,
+ CreateSocketAsync create_socket_async) {
return std::unique_ptr<TracingService::ProducerEndpoint>(
- new ProducerIPCClientImpl(std::move(conn_args), producer, producer_name,
- task_runner, smb_scraping_mode,
- shared_memory_size_hint_bytes,
- shared_memory_page_size_hint_bytes,
- std::move(shm), std::move(shm_arbiter)));
+ new ProducerIPCClientImpl(
+ std::move(conn_args), producer, producer_name, task_runner,
+ smb_scraping_mode, shared_memory_size_hint_bytes,
+ shared_memory_page_size_hint_bytes, std::move(shm),
+ std::move(shm_arbiter), create_socket_async));
}
ProducerIPCClientImpl::ProducerIPCClientImpl(
@@ -97,13 +98,12 @@
size_t shared_memory_size_hint_bytes,
size_t shared_memory_page_size_hint_bytes,
std::unique_ptr<SharedMemory> shm,
- std::unique_ptr<SharedMemoryArbiter> shm_arbiter)
+ std::unique_ptr<SharedMemoryArbiter> shm_arbiter,
+ CreateSocketAsync create_socket_async)
: producer_(producer),
task_runner_(task_runner),
receive_shmem_fd_cb_fuchsia_(
std::move(conn_args.receive_shmem_fd_cb_fuchsia)),
- ipc_channel_(
- ipc::Client::CreateInstance(std::move(conn_args), task_runner)),
producer_port_(
new protos::gen::ProducerPortProxy(this /* event_listener */)),
shared_memory_(std::move(shm)),
@@ -124,7 +124,28 @@
shared_buffer_page_size_kb_ = shared_memory_page_size_hint_bytes_ / 1024;
}
- ipc_channel_->BindService(producer_port_->GetWeakPtr());
+ if (create_socket_async) {
+ PERFETTO_DCHECK(conn_args.socket_name);
+ auto weak_this = weak_factory_.GetWeakPtr();
+ create_socket_async(
+ [weak_this, task_runner = task_runner_](base::SocketHandle fd) {
+ task_runner->PostTask([weak_this, fd] {
+ base::ScopedSocketHandle handle(fd);
+ if (!weak_this) {
+ return;
+ }
+ ipc::Client::ConnArgs args(std::move(handle));
+ weak_this->ipc_channel_ = ipc::Client::CreateInstance(
+ std::move(args), weak_this->task_runner_);
+ weak_this->ipc_channel_->BindService(
+ weak_this->producer_port_->GetWeakPtr());
+ });
+ });
+ } else {
+ ipc_channel_ =
+ ipc::Client::CreateInstance(std::move(conn_args), task_runner);
+ ipc_channel_->BindService(producer_port_->GetWeakPtr());
+ }
PERFETTO_DCHECK_THREAD(thread_checker_);
}
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.h b/src/tracing/ipc/producer/producer_ipc_client_impl.h
index 664b698..f50dcd7 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -46,6 +46,8 @@
// IPC channel to the remote Service. This class is the glue layer between the
// generic Service interface exposed to the clients of the library and the
// actual IPC transport.
+// If create_socket_async is set, it will be called to create and connect to a
+// socket to the service. If unset, the producer will create and connect itself.
class ProducerIPCClientImpl : public TracingService::ProducerEndpoint,
public ipc::ServiceProxy::EventListener {
public:
@@ -57,7 +59,8 @@
size_t shared_memory_size_hint_bytes,
size_t shared_memory_page_size_hint_bytes,
std::unique_ptr<SharedMemory> shm,
- std::unique_ptr<SharedMemoryArbiter> shm_arbiter);
+ std::unique_ptr<SharedMemoryArbiter> shm_arbiter,
+ CreateSocketAsync create_socket_async);
~ProducerIPCClientImpl() override;
// TracingService::ProducerEndpoint implementation.
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index b24c632..1e9e316 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -117,7 +117,8 @@
}
// Copy the data fields to be emitted to trace packets into ClientIdentity.
- ClientIdentity client_identity(client_info.uid(), client_info.pid());
+ ClientIdentity client_identity(client_info.uid(), client_info.pid(),
+ client_info.machine_id());
// ConnectProducer will call OnConnect() on the next task.
producer->service_endpoint = core_service_->ConnectProducer(
producer.get(), client_identity, req.producer_name(),
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 66b2cff..0040714 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -16,6 +16,9 @@
#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
#include <chrono>
#include <condition_variable>
#include <fstream>
@@ -6303,6 +6306,7 @@
// Create a new trace session.
auto* tracing_session = NewTraceWithCategories({"test"});
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
// Emit another event after starting.
@@ -6323,6 +6327,7 @@
// Create a new trace session.
auto* tracing_session = NewTraceWithCategories({"test"});
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
// Emit another event after starting.
@@ -6340,6 +6345,7 @@
TRACE_EVENT_BEGIN("test", "Event");
auto* tracing_session = NewTraceWithCategories({"test"});
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
TRACE_EVENT_END("test");
@@ -6358,7 +6364,9 @@
ds_cfg->set_name("CustomDataSource");
SetupStartupTracing(cfg);
TRACE_EVENT_BEGIN("test", "TrackEvent.Startup");
+
auto* tracing_session = NewTraceWithCategories({"test"}, {}, cfg);
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
TRACE_EVENT_BEGIN("test", "TrackEvent.Main");
@@ -6397,7 +6405,9 @@
auto packet = ctx.NewTracePacket();
packet->set_for_testing()->set_str("CustomDataSource.Startup");
});
+
auto* tracing_session = NewTraceWithCategories({"test"}, {}, cfg);
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
TRACE_EVENT_BEGIN("test", "TrackEvent.Main");
@@ -6470,6 +6480,7 @@
TRACE_EVENT_BEGIN("test", "StartupEvent2");
auto* tracing_session = NewTraceWithCategories({"test"});
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
TRACE_EVENT_BEGIN("test", "MainEvent");
@@ -6555,7 +6566,9 @@
// during startup tracing session.
TEST_P(PerfettoStartupTracingApiTest, NoEventInStartupTracing) {
SetupStartupTracing();
+
auto* tracing_session = NewTraceWithCategories({"test"});
+ ASSERT_TRUE(WaitForOneProducerConnected(tracing_session->get()));
tracing_session->get()->StartBlocking();
// Emit an event now that the session was fully started. This should go
// strait to the SMB.
@@ -6797,6 +6810,198 @@
perfetto::Tracing::ResetForTesting();
}
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+namespace {
+
+int ConnectUnixSocket() {
+ std::string socket_name = perfetto::GetProducerSocket();
+ int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ struct sockaddr_un saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ memcpy(saddr.sun_path, socket_name.data(), socket_name.size());
+ saddr.sun_family = AF_UNIX;
+ auto size = static_cast<socklen_t>(__builtin_offsetof(sockaddr_un, sun_path) +
+ socket_name.size() + 1);
+ connect(fd, reinterpret_cast<const struct sockaddr*>(&saddr), size);
+ return fd;
+}
+
+} // namespace
+
+TEST(PerfettoApiInitTest, AsyncSocket) {
+ auto system_service = perfetto::test::SystemService::Start();
+ // If the system backend isn't supported, skip
+ if (!system_service.valid()) {
+ GTEST_SKIP();
+ }
+
+ EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+
+ perfetto::CreateSocketCallback socket_callback;
+ WaitableTestEvent create_socket_called;
+
+ TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ args.tracing_policy = g_test_tracing_policy;
+ args.create_socket_async = [&socket_callback, &create_socket_called](
+ perfetto::CreateSocketCallback cb) {
+ socket_callback = cb;
+ create_socket_called.Notify();
+ };
+
+ perfetto::Tracing::Initialize(args);
+ create_socket_called.Wait();
+
+ int fd = ConnectUnixSocket();
+ socket_callback(fd);
+
+ perfetto::test::SyncProducers();
+ EXPECT_TRUE(perfetto::Tracing::NewTrace(perfetto::kSystemBackend)
+ ->QueryServiceStateBlocking()
+ .success);
+
+ perfetto::Tracing::ResetForTesting();
+}
+
+TEST(PerfettoApiInitTest, AsyncSocketDisconnect) {
+ auto system_service = perfetto::test::SystemService::Start();
+ // If the system backend isn't supported, skip
+ if (!system_service.valid()) {
+ GTEST_SKIP();
+ }
+
+ EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+
+ perfetto::CreateSocketCallback socket_callback;
+ testing::MockFunction<perfetto::CreateSocketAsync> mock_create_socket;
+ WaitableTestEvent create_socket_called1, create_socket_called2;
+
+ TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ args.tracing_policy = g_test_tracing_policy;
+ args.create_socket_async = mock_create_socket.AsStdFunction();
+
+ EXPECT_CALL(mock_create_socket, Call)
+ .WillOnce(Invoke([&socket_callback, &create_socket_called1](
+ perfetto::CreateSocketCallback cb) {
+ socket_callback = cb;
+ create_socket_called1.Notify();
+ }))
+ .WillOnce(Invoke([&socket_callback, &create_socket_called2](
+ perfetto::CreateSocketCallback cb) {
+ socket_callback = cb;
+ create_socket_called2.Notify();
+ }));
+
+ perfetto::Tracing::Initialize(args);
+ create_socket_called1.Wait();
+ int fd = ConnectUnixSocket();
+ socket_callback(fd);
+
+ perfetto::test::SyncProducers();
+ EXPECT_TRUE(perfetto::Tracing::NewTrace(perfetto::kSystemBackend)
+ ->QueryServiceStateBlocking()
+ .success);
+
+ // Restart the system service. This will cause the producer and consumer to
+ // disconnect and reconnect. The create_socket_async function should be called
+ // for the second time.
+ system_service.Restart();
+ create_socket_called2.Wait();
+ fd = ConnectUnixSocket();
+ socket_callback(fd);
+
+ perfetto::test::SyncProducers();
+ EXPECT_TRUE(perfetto::Tracing::NewTrace(perfetto::kSystemBackend)
+ ->QueryServiceStateBlocking()
+ .success);
+
+ perfetto::Tracing::ResetForTesting();
+}
+
+TEST(PerfettoApiInitTest, AsyncSocketStartupTracing) {
+ auto system_service = perfetto::test::SystemService::Start();
+ // If the system backend isn't supported, skip
+ if (!system_service.valid()) {
+ GTEST_SKIP();
+ }
+
+ EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+
+ perfetto::CreateSocketCallback socket_callback;
+ WaitableTestEvent create_socket_called;
+
+ TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ args.tracing_policy = g_test_tracing_policy;
+ args.create_socket_async = [&socket_callback, &create_socket_called](
+ perfetto::CreateSocketCallback cb) {
+ socket_callback = cb;
+ create_socket_called.Notify();
+ };
+
+ perfetto::Tracing::Initialize(args);
+ perfetto::TrackEvent::Register();
+
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+
+ perfetto::protos::gen::TrackEventConfig te_cfg;
+ te_cfg.add_disabled_categories("*");
+ te_cfg.add_enabled_categories("test");
+ ds_cfg->set_track_event_config_raw(te_cfg.SerializeAsString());
+
+ perfetto::Tracing::SetupStartupTracingOpts opts;
+ opts.backend = perfetto::kSystemBackend;
+ auto startup_session =
+ perfetto::Tracing::SetupStartupTracingBlocking(cfg, std::move(opts));
+
+ // Emit a significant number of events to write >1 chunk of data.
+ constexpr size_t kNumEvents = 1000;
+ for (size_t i = 0; i < kNumEvents; i++) {
+ TRACE_EVENT_INSTANT("test", "StartupEvent");
+ }
+
+ // Now proceed with the connection to the service and wait until it completes.
+ int fd = ConnectUnixSocket();
+ socket_callback(fd);
+ perfetto::test::SyncProducers();
+
+ auto session = perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+ session->Setup(cfg);
+ session->StartBlocking();
+
+ // Write even more events, now with connection established.
+ for (size_t i = 0; i < kNumEvents; i++) {
+ TRACE_EVENT_INSTANT("test", "TraceEvent");
+ }
+
+ perfetto::TrackEvent::Flush();
+ session->StopBlocking();
+
+ auto raw_trace = session->ReadTraceBlocking();
+ perfetto::protos::gen::Trace parsed_trace;
+ EXPECT_TRUE(parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ size_t n_track_events = 0;
+ for (const auto& packet : parsed_trace.packet()) {
+ if (packet.has_track_event()) {
+ ++n_track_events;
+ }
+ }
+
+ // Events from both startup and service-initiated sessions should be retained.
+ EXPECT_EQ(n_track_events, kNumEvents * 2);
+
+ startup_session.reset();
+ session.reset();
+ perfetto::Tracing::ResetForTesting();
+}
+#endif // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
struct BackendTypeAsString {
std::string operator()(
const ::testing::TestParamInfo<perfetto::BackendType>& info) const {
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index 5da8337..a0ab7b2 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -20,6 +20,7 @@
#include <condition_variable>
#include <mutex>
+#include "perfetto/base/time.h"
#include "perfetto/ext/base/no_destructor.h"
#include "perfetto/ext/base/waitable_event.h"
#include "perfetto/tracing/internal/track_event_internal.h"
@@ -38,6 +39,7 @@
// static
void Tracing::InitializeInternal(const TracingInitArgs& args) {
+ base::InitializeTime();
std::unique_lock<std::mutex> lock(InitializedMutex());
// If it's the first time Initialize is called, set some global params.
if (!g_was_initialized) {
diff --git a/test/data/heap_graph_object_for_benchmarks.pftrace.sha256 b/test/data/heap_graph_object_for_benchmarks.pftrace.sha256
new file mode 100644
index 0000000..dd7380f
--- /dev/null
+++ b/test/data/heap_graph_object_for_benchmarks.pftrace.sha256
@@ -0,0 +1 @@
+d0ee1affa7afdb325620a251f20ff16d5e19a5dae76508bb6db746d55dabd1cb
\ No newline at end of file
diff --git a/test/data/heap_pgraph_object_for_benchmarks_query.csv.sha256 b/test/data/heap_pgraph_object_for_benchmarks_query.csv.sha256
new file mode 100644
index 0000000..35a79ea
--- /dev/null
+++ b/test/data/heap_pgraph_object_for_benchmarks_query.csv.sha256
@@ -0,0 +1 @@
+62d757e7de34b466929f0444f2e097123b857c675fbe160f300f998a9309a3ae
\ No newline at end of file
diff --git a/test/data/statsd_atoms_oem.pb.sha256 b/test/data/statsd_atoms_oem.pb.sha256
new file mode 100644
index 0000000..9ab0459
--- /dev/null
+++ b/test/data/statsd_atoms_oem.pb.sha256
@@ -0,0 +1 @@
+5c38eaf8133ca06b1e9ab800c54430ac4807c98aa4684be3da48c56175bca679
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
index e5e8fa3..a022411 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
@@ -1 +1 @@
-041ebd18b8b3f62f76b1613f548c8cf8ea5660f818b4ecfab787c4b08fd55b50
\ No newline at end of file
+35d52b18e2dd2805641fbfa121a66f62a0c602418a46926f5f61ddce91d450f8
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
index 6cee1b2..9ed3863 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
@@ -1 +1 @@
-ec0a00856b147b2e13d0fe18666a307eb085ac437d67f78787131d4ea4190581
\ No newline at end of file
+2d29987562fb3b106e0f1794d2cb341c72a4a51fb5d94bf347048bbe198b7302
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
index 6e1e7b7..d8f53e7 100644
--- a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
@@ -1 +1 @@
-875e17941e1dd5c362eef4bc679af41db5a9b2e37bbde5e5b2d1e90fd54b9e28
\ No newline at end of file
+a4b1867255b838c3ab73f6d00c84aa5bd7fc31bb53da5b83f52ceb84d99e96b2
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
index 9203ad1..6cbb409 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
@@ -1 +1 @@
-1d185424a99b85372cecac21728fbf782fe33abf6a0664791ad08a3902bcdc3e
\ No newline at end of file
+ec9873453c3834735d55eb21138bf510069cf863342509a799076d821a8f01dd
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
index 64e5c99..6667e5f 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
@@ -1 +1 @@
-1ae776d7033331f560685bfd61aa83a8a3f9639a400150bfe7be19642be3855a
\ No newline at end of file
+351edcf1ade9e6b1b7ced9d4f204e9a939370b7a56066f66793e56c9d93ec972
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
index 7481c80..ea7e6de 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
@@ -1 +1 @@
-1fa69f1c7b6098b1e7ab944f4900a3b52984522e18ce94a6c68f8c46c6c06154
\ No newline at end of file
+d9f429e9adad0ce321ab12dbdd062a9a8661c6dc3b6357646bcad5910a215407
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
index 9b5f546..108417f 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
@@ -1 +1 @@
-846bdc1f6e20082da46d483049322300d91cb38ef72f922c934d46cc5df3507d
\ No newline at end of file
+9605f5ae3dbcb6555520046a73ea276c27601dd74235454af797544097015b19
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
index 6710525..0f29bad 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
@@ -1 +1 @@
-5f6f302958f0b26df40b90740bd8786480d7e8aa89232baa4fe2d881c070ba3c
\ No newline at end of file
+c7bb5ef50e96b3bdd96d684cbc65025e60ff9dd3281b220e4b99e07c2a896afe
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
index e138b33..1672234 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
@@ -1 +1 @@
-1dd5883861cff0c03811e5e97c479097771b8ab6ee97b5320593eb5f850c9a25
\ No newline at end of file
+df3d897c8b07ef8707df034e264fea6d32d391ea0d42abdf9349614fa5e57b44
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
index 5aee346..89f4d6f 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
@@ -1 +1 @@
-f93e24fcd03c2f69570c4a01bc8df04b05260b35983d479ab24f7b912ebccd4d
\ No newline at end of file
+8b1beeabcb5321575c4513eb8b572838e61513935098ddde3be368a9325b60f5
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
index 38f1e1d..58840ee 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
@@ -1 +1 @@
-cf12d662d4137c081875afbb1c508827c9430e2837013bd917acd6eaadfac37c
\ No newline at end of file
+b9146d41d92c3e164adb9ada4ed0b384f225220ef458a70c152a14c6bd29aef3
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
index 895dc82..be09ae5 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
@@ -1 +1 @@
-177e27d4d86bee1a17fce48d651b160f1541434aeb0f9e8fc1bac2b8fb07ac6d
\ No newline at end of file
+b9f04ac7c1d9bc25023a22bd2e6d79c48cf44ac62da30db9454f3e367dc9a824
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
index 5294534..92f9c01 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+1bcbdbf6d1c2c1c2e77faa90be1c060b350f21472f67aed9477e6ba5e5219e81
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
index 766073d..88676c9 100644
--- a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
@@ -1 +1 @@
-133e44411f05ea57299a67258d307ec06d381c2852143bf012059365fd2a7716
\ No newline at end of file
+5192001c1342677f7aebade9574bd9de5622c004d88d1a2a4a9453e35a376b16
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
index b0f92e9..657b6fe 100644
--- a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
@@ -1 +1 @@
-3c80ba72b9bd0454af4aac3352c4e8f855f48feeba53d2a5ac7566333b4cf763
\ No newline at end of file
+1f40c5a9cfbd5e7eb6f8536fb7e51a95b2863443e0774d394716c97de2fa4b05
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
index 33f95ef..ff33850 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+7d8429af94229b4cb96f3e7691e987b4eb0ade9683acce9a2c14c41777415d73
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
index 5294534..92f9c01 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+1bcbdbf6d1c2c1c2e77faa90be1c060b350f21472f67aed9477e6ba5e5219e81
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
index 33f95ef..ff33850 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+7d8429af94229b4cb96f3e7691e987b4eb0ade9683acce9a2c14c41777415d73
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
index 33f95ef..ff33850 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+7d8429af94229b4cb96f3e7691e987b4eb0ade9683acce9a2c14c41777415d73
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
index 22444d8..0aacaa4 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
@@ -1 +1 @@
-52be49df180c482cad1a48979ce0bb2d20a7cdd27ad10cb972b5bad61b9865ca
\ No newline at end of file
+539226bd5412f6f573473f91ae2fc6edda026744c143bdc4b027a069094de96f
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
index 5949d49..f8f7010 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
@@ -1 +1 @@
-f650a9a968e978dfa37f900cc5509bdc9118e8f3c74197164982285acde6149f
\ No newline at end of file
+428b6a704a5c68df10bc6543112ed9e1c367abf07219c50ceaa8989421f3b373
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
index 5294534..92f9c01 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+1bcbdbf6d1c2c1c2e77faa90be1c060b350f21472f67aed9477e6ba5e5219e81
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
index 33f95ef..ff33850 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+7d8429af94229b4cb96f3e7691e987b4eb0ade9683acce9a2c14c41777415d73
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
index 33f95ef..ff33850 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+7d8429af94229b4cb96f3e7691e987b4eb0ade9683acce9a2c14c41777415d73
\ No newline at end of file
diff --git a/test/synth_common.py b/test/synth_common.py
index a239fa9..d9970c9 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -763,11 +763,19 @@
event.pid = pid
event.layer_name = layer_name
- def add_actual_surface_frame_start_event(self, ts, cookie, token,
- display_frame_token, pid, layer_name,
- present_type, on_time_finish,
- gpu_composition, jank_type,
- prediction_type):
+ def add_actual_surface_frame_start_event(self,
+ ts,
+ cookie,
+ token,
+ display_frame_token,
+ pid,
+ layer_name,
+ present_type,
+ on_time_finish,
+ gpu_composition,
+ jank_type,
+ prediction_type,
+ jank_severity_type=None):
packet = self.add_packet()
packet.timestamp = ts
event = packet.frame_timeline_event.actual_surface_frame_start
@@ -781,6 +789,12 @@
event.on_time_finish = on_time_finish
event.gpu_composition = gpu_composition
event.jank_type = jank_type
+ # jank severity type is not available on every trace.
+ # When not set, default to none if no jank; otherwise default to unknown
+ if jank_severity_type is None:
+ event.jank_severity_type = 1 if event.jank_type == 1 else 0
+ else:
+ event.jank_severity_type = jank_severity_type
event.prediction_type = prediction_type
def add_frame_end_event(self, ts, cookie):
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 0aea755..e585f47 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -51,6 +51,7 @@
from diff_tests.parser.android.tests_games import AndroidGames
from diff_tests.parser.android.tests_surfaceflinger_layers import SurfaceFlingerLayers
from diff_tests.parser.android.tests_surfaceflinger_transactions import SurfaceFlingerTransactions
+from diff_tests.parser.android.tests_shell_transitions import ShellTransitions
from diff_tests.parser.android_fs.tests import AndroidFs
from diff_tests.parser.atrace.tests import Atrace
from diff_tests.parser.atrace.tests_error_handling import AtraceErrorHandling
@@ -171,6 +172,8 @@
'SurfaceFlingerLayers').fetch(),
*SurfaceFlingerTransactions(index_path, 'parser/android',
'SurfaceFlingerTransactions').fetch(),
+ *ShellTransitions(index_path, 'parser/android',
+ 'ShellTransitions').fetch(),
*TrackEvent(index_path, 'parser/track_event', 'TrackEvent').fetch(),
*TranslatedArgs(index_path, 'parser/translated_args',
'TranslatedArgs').fetch(),
diff --git a/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto b/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto
new file mode 100644
index 0000000..9b0182a
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_handlers.textproto
@@ -0,0 +1,21 @@
+packet {
+ trusted_uid: 10225
+ trusted_packet_sequence_id: 12
+ previous_packet_dropped: true
+ trusted_pid: 3981
+ first_packet_on_sequence: true
+ shell_handler_mappings {
+ mapping {
+ id: 1
+ name: "DefaultTransitionHandler"
+ }
+ mapping {
+ id: 2
+ name: "RecentsTransitionHandler"
+ }
+ mapping {
+ id: 3
+ name: "FreeformTaskTransitionHandler"
+ }
+ }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto b/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto
new file mode 100644
index 0000000..b92eb39
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_transitions.textproto
@@ -0,0 +1,167 @@
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 2
+ previous_packet_dropped: true
+ trusted_pid: 1305
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 7
+ create_time_ns: 76799049027
+ send_time_ns: 76875395422
+ start_transaction_id: 5604932321952
+ finish_transaction_id: 5604932321954
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 2
+ trusted_pid: 1305
+ shell_transition {
+ id: 10
+ create_time_ns: 77854865352
+ send_time_ns: 77894307328
+ start_transaction_id: 5604932322158
+ finish_transaction_id: 5604932322159
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 2
+ trusted_pid: 1305
+ shell_transition {
+ id: 11
+ create_time_ns: 82498121051
+ send_time_ns: 82535513345
+ start_transaction_id: 5604932322346
+ finish_transaction_id: 5604932322347
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 3
+ previous_packet_dropped: true
+ trusted_pid: 1305
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 8
+ create_time_ns: 76955664017
+ send_time_ns: 77277756832
+ start_transaction_id: 5604932322028
+ finish_transaction_id: 5604932322029
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 4
+ previous_packet_dropped: true
+ trusted_pid: 1305
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 7
+ starting_window_remove_time_ns: 77706603918
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 5
+ previous_packet_dropped: true
+ trusted_pid: 1305
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 9
+ create_time_ns: 77825423417
+ send_time_ns: 77843436723
+ start_transaction_id: 5604932322137
+ finish_transaction_id: 5604932322138
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 5
+ trusted_pid: 1305
+ shell_transition {
+ id: 9
+ finish_time_ns: 77873935462
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 5
+ trusted_pid: 1305
+ shell_transition {
+ id: 10
+ finish_time_ns: 78621610429
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ previous_packet_dropped: true
+ trusted_pid: 2528
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 7
+ dispatch_time_ns: 76879063147
+ handler: 2
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 8
+ merge_time_ns: 77278725500
+ merge_target: 7
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 8
+ dispatch_time_ns: 77320527177
+ handler: 3
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 9
+ dispatch_time_ns: 77876414832
+ handler: 3
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 10
+ dispatch_time_ns: 77899001013
+ handler: 4
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 11
+ dispatch_time_ns: 82536817137
+ handler: 2
+ }
+}
+packet {
+ trusted_uid: 10241
+ trusted_packet_sequence_id: 6
+ trusted_pid: 2528
+ shell_transition {
+ id: 12
+ merge_time_ns: 82697060749
+ merge_target: 11
+ }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto b/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto
new file mode 100644
index 0000000..6c9cb65
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/shell_transitions_simple_merge.textproto
@@ -0,0 +1,62 @@
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 2
+ previous_packet_dropped: true
+ trusted_pid: 1336
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 15
+ create_time_ns: 2187614568227
+ send_time_ns: 2187671767120
+ start_transaction_id: 5738076308937
+ finish_transaction_id: 5738076308938
+ type: 1
+ targets {
+ mode: 1
+ layer_id: 244
+ window_id: 219481253
+ flags: 0
+ }
+ targets {
+ mode: 4
+ layer_id: 47
+ window_id: 54474511
+ flags: 1
+ }
+ flags: 0
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 3
+ previous_packet_dropped: true
+ trusted_pid: 1336
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 15
+ finish_time_ns: 2188195968659
+ }
+}
+packet {
+ trusted_uid: 1000
+ trusted_packet_sequence_id: 5
+ previous_packet_dropped: true
+ trusted_pid: 1336
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 15
+ starting_window_remove_time_ns: 2188652838898
+ }
+}
+packet {
+ trusted_uid: 10225
+ trusted_packet_sequence_id: 12
+ previous_packet_dropped: true
+ trusted_pid: 3981
+ first_packet_on_sequence: true
+ shell_transition {
+ id: 15
+ dispatch_time_ns: 2187673373973
+ handler: 2
+ }
+}
diff --git a/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
new file mode 100644
index 0000000..a8e0328
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/android/tests_shell_transitions.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path
+from python.generators.diff_tests.testing import Csv
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ShellTransitions(TestSuite):
+
+ def test_has_expected_transition_rows(self):
+ return DiffTestBlueprint(
+ trace=Path('shell_transitions.textproto'),
+ query="""
+ SELECT
+ id, transition_id
+ FROM
+ window_manager_shell_transitions;
+ """,
+ out=Csv("""
+ "id","transition_id"
+ 0,7
+ 1,10
+ 2,11
+ 3,8
+ 4,9
+ 5,12
+ """))
+
+ def test_has_expected_transition_args(self):
+ return DiffTestBlueprint(
+ trace=Path('shell_transitions_simple_merge.textproto'),
+ query="""
+ SELECT
+ args.key, args.display_value
+ FROM
+ window_manager_shell_transitions JOIN args ON window_manager_shell_transitions.arg_set_id = args.arg_set_id
+ WHERE window_manager_shell_transitions.transition_id = 15
+ ORDER BY args.key;
+ """,
+ out=Csv("""
+ "key","display_value"
+ "create_time_ns","2187614568227"
+ "dispatch_time_ns","2187673373973"
+ "finish_time_ns","2188195968659"
+ "finish_transaction_id","5738076308938"
+ "flags","0"
+ "handler","2"
+ "id","15"
+ "send_time_ns","2187671767120"
+ "start_transaction_id","5738076308937"
+ "starting_window_remove_time_ns","2188652838898"
+ "targets[0].flags","0"
+ "targets[0].layer_id","244"
+ "targets[0].mode","1"
+ "targets[0].window_id","219481253"
+ "targets[1].flags","1"
+ "targets[1].layer_id","47"
+ "targets[1].mode","4"
+ "targets[1].window_id","54474511"
+ "type","1"
+ """))
+
+ def test_has_shell_handlers(self):
+ return DiffTestBlueprint(
+ trace=Path('shell_handlers.textproto'),
+ query="""
+ SELECT
+ handler_id, handler_name
+ FROM
+ window_manager_shell_transition_handlers;
+ """,
+ out=Csv("""
+ "handler_id","handler_name"
+ 1,"DefaultTransitionHandler"
+ 2,"RecentsTransitionHandler"
+ 3,"FreeformTaskTransitionHandler"
+ """))
diff --git a/test/trace_processor/diff_tests/parser/graphics/actual_frame_timeline_events_test.sql b/test/trace_processor/diff_tests/parser/graphics/actual_frame_timeline_events_test.sql
index a46f6aa..269523c 100644
--- a/test/trace_processor/diff_tests/parser/graphics/actual_frame_timeline_events_test.sql
+++ b/test/trace_processor/diff_tests/parser/graphics/actual_frame_timeline_events_test.sql
@@ -13,8 +13,8 @@
-- See the License for the specific language governing permissions and
-- limitations under the License.
-SELECT ts, dur, process.pid AS pid, display_frame_token, surface_frame_token, layer_name,
- present_type, on_time_finish, gpu_composition, jank_type, prediction_type, jank_tag
+SELECT ts, dur, process.pid, display_frame_token, surface_frame_token, layer_name,
+ present_type, on_time_finish, gpu_composition, jank_type, prediction_type, jank_tag, jank_severity_type
FROM
(SELECT t.*, process_track.name AS track_name FROM
process_track LEFT JOIN actual_frame_timeline_slice t
diff --git a/test/trace_processor/diff_tests/parser/graphics/frame_timeline_events.py b/test/trace_processor/diff_tests/parser/graphics/frame_timeline_events.py
index 91590fb..90bae3a 100644
--- a/test/trace_processor/diff_tests/parser/graphics/frame_timeline_events.py
+++ b/test/trace_processor/diff_tests/parser/graphics/frame_timeline_events.py
@@ -33,6 +33,13 @@
JANK_DROPPED = 1024
+class JankSeverityType:
+ UNKNOWN = 0
+ NONE = 1
+ PARTIAL = 2
+ FULL = 3
+
+
class PresentType:
PRESENT_UNSPECIFIED = 0
PRESENT_ON_TIME = 1
@@ -135,6 +142,7 @@
on_time_finish=0,
gpu_composition=0,
jank_type=JankType.JANK_APP_DEADLINE_MISSED,
+ jank_severity_type=JankSeverityType.FULL,
prediction_type=PredictionType.PREDICTION_VALID)
trace.add_frame_end_event(ts=74, cookie=10)
@@ -276,6 +284,7 @@
on_time_finish=0,
gpu_composition=0,
jank_type=JankType.JANK_UNKNOWN,
+ jank_severity_type=JankSeverityType.PARTIAL,
prediction_type=PredictionType.PREDICTION_EXPIRED)
trace.add_frame_end_event(ts=190, cookie=25)
diff --git a/test/trace_processor/diff_tests/parser/graphics/tests.py b/test/trace_processor/diff_tests/parser/graphics/tests.py
index 3a2bf84..7fe4af1 100644
--- a/test/trace_processor/diff_tests/parser/graphics/tests.py
+++ b/test/trace_processor/diff_tests/parser/graphics/tests.py
@@ -110,22 +110,22 @@
trace=Path('frame_timeline_events.py'),
query=Path('actual_frame_timeline_events_test.sql'),
out=Csv("""
- "ts","dur","pid","display_frame_token","surface_frame_token","layer_name","present_type","on_time_finish","gpu_composition","jank_type","prediction_type","jank_tag"
- 20,6,666,2,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 21,16,1000,4,1,"Layer1","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 41,33,1000,6,5,"Layer1","Late Present",0,0,"App Deadline Missed","Valid Prediction","Self Jank"
- 42,5,666,4,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 80,110,1000,17,16,"Layer1","Unknown Present",0,0,"Unknown Jank","Expired Prediction","Self Jank"
- 81,7,666,6,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 90,16,1000,8,7,"Layer1","Early Present",1,0,"SurfaceFlinger Scheduling","Valid Prediction","Other Jank"
- 108,4,666,8,0,"[NULL]","Early Present",1,0,"SurfaceFlinger Scheduling","Valid Prediction","Self Jank"
- 148,8,666,12,0,"[NULL]","Late Present",0,0,"SurfaceFlinger Scheduling, SurfaceFlinger CPU Deadline Missed","Valid Prediction","Self Jank"
- 150,17,1000,15,14,"Layer1","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 150,17,1000,15,14,"Layer2","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 170,6,666,15,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 200,6,666,17,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank"
- 245,-1,666,18,0,"[NULL]","Late Present",0,0,"SurfaceFlinger Stuffing","Valid Prediction","SurfaceFlinger Stuffing"
- 245,15,666,18,0,"[NULL]","Dropped Frame",0,0,"Dropped Frame","Unspecified Prediction","Dropped Frame"
+ "ts","dur","pid","display_frame_token","surface_frame_token","layer_name","present_type","on_time_finish","gpu_composition","jank_type","prediction_type","jank_tag","jank_severity_type"
+ 20,6,666,2,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 21,16,1000,4,1,"Layer1","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 41,33,1000,6,5,"Layer1","Late Present",0,0,"App Deadline Missed","Valid Prediction","Self Jank","Full"
+ 42,5,666,4,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 80,110,1000,17,16,"Layer1","Unknown Present",0,0,"Unknown Jank","Expired Prediction","Self Jank","Partial"
+ 81,7,666,6,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 90,16,1000,8,7,"Layer1","Early Present",1,0,"SurfaceFlinger Scheduling","Valid Prediction","Other Jank","Unknown"
+ 108,4,666,8,0,"[NULL]","Early Present",1,0,"SurfaceFlinger Scheduling","Valid Prediction","Self Jank","Unknown"
+ 148,8,666,12,0,"[NULL]","Late Present",0,0,"SurfaceFlinger Scheduling, SurfaceFlinger CPU Deadline Missed","Valid Prediction","Self Jank","Unknown"
+ 150,17,1000,15,14,"Layer1","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 150,17,1000,15,14,"Layer2","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 170,6,666,15,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 200,6,666,17,0,"[NULL]","On-time Present",1,0,"None","Valid Prediction","No Jank","None"
+ 245,-1,666,18,0,"[NULL]","Late Present",0,0,"SurfaceFlinger Stuffing","Valid Prediction","SurfaceFlinger Stuffing","Unknown"
+ 245,15,666,18,0,"[NULL]","Dropped Frame",0,0,"Dropped Frame","Unspecified Prediction","Dropped Frame","Unknown"
"""))
# Video 4 Linux 2 related tests
diff --git a/test/trace_processor/diff_tests/parser/parsing/tests.py b/test/trace_processor/diff_tests/parser/parsing/tests.py
index 5261b09..90067a7 100644
--- a/test/trace_processor/diff_tests/parser/parsing/tests.py
+++ b/test/trace_processor/diff_tests/parser/parsing/tests.py
@@ -1110,6 +1110,16 @@
query=Path('all_atoms_test.sql'),
out=Path('statsd_atoms_all_atoms.out'))
+ # Statsd Atoms
+ def test_statsd_atoms_unknown_atoms(self):
+ return DiffTestBlueprint(
+ trace=DataPath('statsd_atoms_oem.pb'),
+ query=Path('all_atoms_test.sql'),
+ out=Csv("""
+ "name","key","display_value"
+ "atom_202001","field_1","1"
+ """))
+
# Kernel function tracing.
def test_funcgraph_trace_funcgraph(self):
return DiffTestBlueprint(
diff --git a/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
index b9d19fd..80b679c 100755
--- a/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/stdlib/chrome/tests_scroll_jank.py
@@ -105,6 +105,99 @@
1991,4687329240739,-28.999969,-175.999969
"""))
+ def test_chrome_janky_event_latencies_v3(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_input_with_frame_view.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_intervals;
+
+ SELECT
+ id,
+ ts,
+ dur,
+ track_id,
+ name,
+ cause_of_jank,
+ sub_cause_of_jank,
+ delayed_frame_count,
+ frame_jank_ts,
+ frame_jank_dur
+ FROM chrome_janky_event_latencies_v3
+ ORDER by id;
+ """,
+ out=Csv("""
+ "id","ts","dur","track_id","name","cause_of_jank","sub_cause_of_jank","delayed_frame_count","frame_jank_ts","frame_jank_dur"
+ 29926,174795897267797,48088000,1431,"EventLatency","RendererCompositorQueueingDelay","[NULL]",1,174795928261797,17094000
+ 38463,174796315541797,131289000,2163,"EventLatency","RendererCompositorFinishedToBeginImplFrame","[NULL]",5,174796362924797,83906000
+ 88876,174799556245797,49856000,4329,"EventLatency","RendererCompositorQueueingDelay","[NULL]",1,174799589065797,17036000
+ """))
+
+ def test_chrome_janky_frame_presentation_intervals(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_input_with_frame_view.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_intervals;
+
+ SELECT
+ id,
+ ts,
+ dur,
+ cause_of_jank,
+ sub_cause_of_jank,
+ delayed_frame_count,
+ event_latency_id
+ FROM chrome_janky_frame_presentation_intervals
+ ORDER by id;
+ """,
+ out=Csv("""
+ "id","ts","dur","cause_of_jank","sub_cause_of_jank","delayed_frame_count","event_latency_id"
+ 1,174795928261797,17094000,"RendererCompositorQueueingDelay","[NULL]",1,29926
+ 2,174796362924797,83906000,"RendererCompositorFinishedToBeginImplFrame","[NULL]",5,38463
+ 3,174799589065797,17036000,"RendererCompositorQueueingDelay","[NULL]",1,88876
+ """))
+
+ def test_chrome_scroll_stats(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_input_with_frame_view.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_intervals;
+
+ SELECT
+ scroll_id,
+ missed_vsyncs,
+ frame_count,
+ presented_frame_count,
+ janky_frame_count,
+ janky_frame_percent
+ FROM chrome_scroll_stats
+ ORDER by scroll_id;
+ """,
+ out=Csv("""
+ "scroll_id","missed_vsyncs","frame_count","presented_frame_count","janky_frame_count","janky_frame_percent"
+ 1186,6,110,105,2,1.900000
+ 1889,"[NULL]",101,102,0,0.000000
+ 2506,1,84,84,1,1.190000
+ """))
+
+ def test_chrome_scroll_jank_intervals_v3(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_input_with_frame_view.pftrace'),
+ query="""
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_intervals;
+
+ SELECT
+ id,
+ ts,
+ dur
+ FROM chrome_scroll_jank_intervals_v3
+ ORDER by id;
+ """,
+ out=Csv("""
+ "id","ts","dur"
+ 1,174795928261797,17094000
+ 2,174796362924797,83906000
+ 3,174799589065797,17036000
+ """))
def test_chrome_presented_scroll_offsets(self):
return DiffTestBlueprint(
trace=DataPath('scroll_offsets.pftrace'),
diff --git a/test/trace_processor/diff_tests/tables/tests.py b/test/trace_processor/diff_tests/tables/tests.py
index 03c7eb0..5a8f393 100644
--- a/test/trace_processor/diff_tests/tables/tests.py
+++ b/test/trace_processor/diff_tests/tables/tests.py
@@ -356,3 +356,26 @@
0,"flow",0,1,57,0
1,"flow",1,2,57,0
"""))
+
+ def test_clock_snapshot_table_multiplier(self):
+ return DiffTestBlueprint(
+ trace=TextProto("""
+ packet {
+ clock_snapshot {
+ clocks {
+ clock_id: 1
+ timestamp: 42
+ unit_multiplier_ns: 10
+ }
+ clocks {
+ clock_id: 6
+ timestamp: 0
+ }
+ }
+ }
+ """),
+ query="SELECT TO_REALTIME(0);",
+ out=Csv("""
+ "TO_REALTIME(0)"
+ 420
+ """))
diff --git a/tools/cpu_profile b/tools/cpu_profile
index 166daff..9654a2a 100755
--- a/tools/cpu_profile
+++ b/tools/cpu_profile
@@ -37,18 +37,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACECONV_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'traceconv',
'file_size':
- 9020880,
+ 9184800,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/traceconv',
'sha256':
- 'ae7903f9bb8a98b32fdf96c0124797cf9b4b58a9bcb45a05574d74d33b11ada9',
+ 'b651d0a5b5606c1c3e24723e94d8ecb233a01f0dfccc95a2c6a4e773cb8f52d7',
'platform':
'darwin',
'machine': ['x86_64']
@@ -58,11 +58,11 @@
'file_name':
'traceconv',
'file_size':
- 7580200,
+ 7761896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/traceconv',
'sha256':
- '619f31cd84a70fcb75e2a1cbe911f77feca0273ea0b1e706ae4be5ac020cdf7d',
+ '3b019f5ddd5293d3181f7c30f91dc7b08f3a2e83ebb3b52b8f3905dc5161747d',
'platform':
'darwin',
'machine': ['arm64']
@@ -72,11 +72,11 @@
'file_name':
'traceconv',
'file_size':
- 8773576,
+ 8928296,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/traceconv',
'sha256':
- 'abc0b4191abe5106e1d4382bc0d238e53c39a83e1ba91237fbed5f789ef1c82b',
+ '830d20ffec266218d49f6b6c8efed4538bc59b51d8d2f735cbbb6a1435131b50',
'platform':
'linux',
'machine': ['x86_64']
@@ -86,11 +86,11 @@
'file_name':
'traceconv',
'file_size':
- 6620748,
+ 6770204,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/traceconv',
'sha256':
- 'f2dd9f1bfabea567ff20aedd0798e90093eff2bfdc15aacbfafe0abe52aa1fd1',
+ '93a9e5ccb94559b871af8f6da45f858aee01801b31776703892dcf3d7ea769b7',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -100,11 +100,11 @@
'file_name':
'traceconv',
'file_size':
- 8240792,
+ 8393944,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/traceconv',
'sha256':
- '76428eca01ced4b769241915fb5233b43b9b59e9062917e45d523db10f33e546',
+ '88a92ccbcd8e851673e018b7f599514daf05dde9b7e4de9641fa5629124abf12',
'platform':
'linux',
'machine': ['aarch64']
@@ -114,55 +114,55 @@
'file_name':
'traceconv',
'file_size':
- 6247672,
+ 6378744,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/traceconv',
'sha256':
- '3eb80bbd78c6210b73ef20654b9d80994747e552b3d342a8d2ec4aa58dabcc33'
+ '6cb7d30d656aa4f172e6724f105a56e249e7043ecf637c65e1e3868885535cff'
}, {
'arch':
'android-arm64',
'file_name':
'traceconv',
'file_size':
- 7545032,
+ 7692488,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/traceconv',
'sha256':
- '30037aba74f7718f71273a83141373e623ddfe561294d01b41433dc9b572fee5'
+ '1668808efbdf8d5b116d4716d61d2bd002f71ce465206d3b83af4fcc7a4c19cd'
}, {
'arch':
'android-x86',
'file_name':
'traceconv',
'file_size':
- 8410300,
+ 8557756,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/traceconv',
'sha256':
- '9ad420368453699da12ce6359db514283e92b2caa2d415d9db61977cc79649a9'
+ '653733582cae0021eae0e1b5d8db387c1bae772d77b307f1e2111b78ec4ea67c'
}, {
'arch':
'android-x64',
'file_name':
'traceconv',
'file_size':
- 8544512,
+ 8708352,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/traceconv',
'sha256':
- '60a62f8712186c5f984ff2fbc4ff8c47b6226b62a83b31f878e0597c8bb90cd8'
+ '7fc564ac581b81d79573f57dae027c47bd7a857ff0f89df984380c3c657d5876'
}, {
'arch':
'windows-amd64',
'file_name':
'traceconv.exe',
'file_size':
- 8049664,
+ 8204288,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/traceconv.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/traceconv.exe',
'sha256':
- '9e89ccd0cdb466ea5fe960b71538b0ff8a2858b9e7ecdbcafa9ffe60839c9afc',
+ 'e33bad8061f08f9c3cfe6e91ef6f1696b6ac90d0799edcb57052f24888b436e2',
'platform':
'win32',
'machine': ['amd64']
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index eea7f09..c1c1ea1 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -114,6 +114,10 @@
'//:libperfetto_client_experimental',
]
+target_product_available = [
+ '//:libperfetto_client_experimental',
+]
+
# Proto target groups which will be made public.
proto_groups = {
'trace': [
@@ -256,8 +260,7 @@
],
'libperfetto_client_experimental': [
('apex_available', {
- '//apex_available:platform', 'com.android.art',
- 'com.android.art.debug', 'com.android.tethering'
+ '//apex_available:platform', '//apex_available:anyapex'
}),
('min_sdk_version', '30'),
('shared_libs', {'liblog'}),
@@ -332,7 +335,7 @@
def enable_sqlite(module):
if module.type == 'cc_binary_host':
- module.static_libs.add('libsqlite')
+ module.static_libs.add('libsqlite_static_noicu')
module.static_libs.add('sqlite_ext_percentile')
elif module.host_supported:
# Copy what the sqlite3 command line tool does.
@@ -341,7 +344,7 @@
module.android.shared_libs.add('liblog')
module.android.shared_libs.add('libutils')
module.android.static_libs.add('sqlite_ext_percentile')
- module.host.static_libs.add('libsqlite')
+ module.host.static_libs.add('libsqlite_static_noicu')
module.host.static_libs.add('sqlite_ext_percentile')
else:
module.shared_libs.add('libsqlite')
@@ -509,6 +512,7 @@
self.cmd: Optional[str] = None
self.host_supported = False
self.vendor_available = False
+ self.product_available = False
self.init_rc = set()
self.out = set()
self.export_include_dirs = set()
@@ -532,6 +536,7 @@
self.apex_available = set()
self.min_sdk_version = None
self.proto = dict()
+ self.output_extension: Optional[str] = None
# The genrule_XXX below are properties that must to be propagated back
# on the module(s) that depend on the genrule.
self.genrule_headers = set()
@@ -558,6 +563,8 @@
self._output_field(output, 'host_supported')
if self.vendor_available:
self._output_field(output, 'vendor_available')
+ if self.product_available:
+ self._output_field(output, 'product_available')
self._output_field(output, 'init_rc')
self._output_field(output, 'out')
self._output_field(output, 'export_include_dirs')
@@ -581,6 +588,7 @@
self._output_field(output, 'stubs')
self._output_field(output, 'proto')
self._output_field(output, 'main')
+ self._output_field(output, 'output_extension')
target_out = []
self._output_field(target_out, 'android')
@@ -1032,6 +1040,7 @@
blueprint.add_module(module)
module.host_supported = (name_without_toolchain in target_host_supported)
module.vendor_available = (name_without_toolchain in target_vendor_available)
+ module.product_available = (name_without_toolchain in target_product_available)
module.init_rc.update(target_initrc.get(target.name, []))
if target.type != 'proto_library':
# proto_library embeds a "root" filegroup in its srcs.
diff --git a/tools/heap_profile b/tools/heap_profile
index 17155d8..77885fc 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -34,18 +34,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACECONV_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'traceconv',
'file_size':
- 9020880,
+ 9184800,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/traceconv',
'sha256':
- 'ae7903f9bb8a98b32fdf96c0124797cf9b4b58a9bcb45a05574d74d33b11ada9',
+ 'b651d0a5b5606c1c3e24723e94d8ecb233a01f0dfccc95a2c6a4e773cb8f52d7',
'platform':
'darwin',
'machine': ['x86_64']
@@ -55,11 +55,11 @@
'file_name':
'traceconv',
'file_size':
- 7580200,
+ 7761896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/traceconv',
'sha256':
- '619f31cd84a70fcb75e2a1cbe911f77feca0273ea0b1e706ae4be5ac020cdf7d',
+ '3b019f5ddd5293d3181f7c30f91dc7b08f3a2e83ebb3b52b8f3905dc5161747d',
'platform':
'darwin',
'machine': ['arm64']
@@ -69,11 +69,11 @@
'file_name':
'traceconv',
'file_size':
- 8773576,
+ 8928296,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/traceconv',
'sha256':
- 'abc0b4191abe5106e1d4382bc0d238e53c39a83e1ba91237fbed5f789ef1c82b',
+ '830d20ffec266218d49f6b6c8efed4538bc59b51d8d2f735cbbb6a1435131b50',
'platform':
'linux',
'machine': ['x86_64']
@@ -83,11 +83,11 @@
'file_name':
'traceconv',
'file_size':
- 6620748,
+ 6770204,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/traceconv',
'sha256':
- 'f2dd9f1bfabea567ff20aedd0798e90093eff2bfdc15aacbfafe0abe52aa1fd1',
+ '93a9e5ccb94559b871af8f6da45f858aee01801b31776703892dcf3d7ea769b7',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -97,11 +97,11 @@
'file_name':
'traceconv',
'file_size':
- 8240792,
+ 8393944,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/traceconv',
'sha256':
- '76428eca01ced4b769241915fb5233b43b9b59e9062917e45d523db10f33e546',
+ '88a92ccbcd8e851673e018b7f599514daf05dde9b7e4de9641fa5629124abf12',
'platform':
'linux',
'machine': ['aarch64']
@@ -111,55 +111,55 @@
'file_name':
'traceconv',
'file_size':
- 6247672,
+ 6378744,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/traceconv',
'sha256':
- '3eb80bbd78c6210b73ef20654b9d80994747e552b3d342a8d2ec4aa58dabcc33'
+ '6cb7d30d656aa4f172e6724f105a56e249e7043ecf637c65e1e3868885535cff'
}, {
'arch':
'android-arm64',
'file_name':
'traceconv',
'file_size':
- 7545032,
+ 7692488,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/traceconv',
'sha256':
- '30037aba74f7718f71273a83141373e623ddfe561294d01b41433dc9b572fee5'
+ '1668808efbdf8d5b116d4716d61d2bd002f71ce465206d3b83af4fcc7a4c19cd'
}, {
'arch':
'android-x86',
'file_name':
'traceconv',
'file_size':
- 8410300,
+ 8557756,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/traceconv',
'sha256':
- '9ad420368453699da12ce6359db514283e92b2caa2d415d9db61977cc79649a9'
+ '653733582cae0021eae0e1b5d8db387c1bae772d77b307f1e2111b78ec4ea67c'
}, {
'arch':
'android-x64',
'file_name':
'traceconv',
'file_size':
- 8544512,
+ 8708352,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/traceconv',
'sha256':
- '60a62f8712186c5f984ff2fbc4ff8c47b6226b62a83b31f878e0597c8bb90cd8'
+ '7fc564ac581b81d79573f57dae027c47bd7a857ff0f89df984380c3c657d5876'
}, {
'arch':
'windows-amd64',
'file_name':
'traceconv.exe',
'file_size':
- 8049664,
+ 8204288,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/traceconv.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/traceconv.exe',
'sha256':
- '9e89ccd0cdb466ea5fe960b71538b0ff8a2858b9e7ecdbcafa9ffe60839c9afc',
+ 'e33bad8061f08f9c3cfe6e91ef6f1696b6ac90d0799edcb57052f24888b436e2',
'platform':
'win32',
'machine': ['amd64']
diff --git a/tools/install-build-deps b/tools/install-build-deps
index c3822f6..fd17b81 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -63,7 +63,7 @@
'buildtools/test_data', # Moved to test/data by r.android.com/1539381 .
'buildtools/d8', # Removed by r.android.com/1424334 .
- # Build toools moved to third_party/ by r.android.com/2327602 .
+ # Build tools moved to third_party/ by r.android.com/2327602 .
'buildtools/mac/clang-format',
'buildtools/mac/gn',
'buildtools/mac/ninja',
@@ -95,6 +95,11 @@
'f706aaa0676e3e22f5fc9ca482295d7caee8535d1869f99efa2358177b64f5cd',
'linux', 'x64'),
Dependency(
+ 'third_party/gn/gn',
+ 'https://storage.googleapis.com/perfetto/gn-linux-arm64-1968-0725d782',
+ 'c2a372cd4f911028d8bc351fbf24835c9b1194fcc92beadf6c5a2b3addae973c',
+ 'linux', 'arm64'),
+ Dependency(
'third_party/gn/gn.exe',
'https://storage.googleapis.com/perfetto/gn-win-1968-0725d782',
'001f777f023c7a6959c778fb3a6b6cfc63f6baef953410ecdeaec350fb12285b',
@@ -150,6 +155,11 @@
'https://storage.googleapis.com/perfetto/ninja-win-182',
'09ced0fcd1a4dec7d1b798a2cf9ce5d20e5d2fbc2337343827f192ce47d0f491',
'windows', 'x64'),
+ Dependency(
+ 'third_party/ninja/ninja',
+ 'https://storage.googleapis.com/perfetto/ninja-linux-arm64-1111',
+ '05031a734ec4310a51b2cfe9f0096b26fce25ab4ff19e5b51abe6371de066cc5',
+ 'linux', 'arm64'),
# Keep the revision in sync with Chrome's PACKAGE_VERSION in
# tools/clang/scripts/update.py.
@@ -463,6 +473,8 @@
arch = machine()
if arch == 'arm64':
return 'arm64'
+ elif arch == 'aarch64':
+ return 'arm64'
else:
# Assume everything else is x64 matching previous behaviour.
return 'x64'
diff --git a/tools/open_trace_in_ui b/tools/open_trace_in_ui
index 30e4b01..0bf8ea7 100755
--- a/tools/open_trace_in_ui
+++ b/tools/open_trace_in_ui
@@ -63,7 +63,7 @@
fname = os.path.basename(path)
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
- address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}'
+ address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}&referrer=open_trace_in_ui'
if open_browser:
webbrowser.open_new_tab(address)
else:
diff --git a/tools/record_android_trace b/tools/record_android_trace
index d463dd9..d4a740a 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -33,18 +33,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACEBOX_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'tracebox',
'file_size':
- 1498680,
+ 1498816,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/tracebox',
'sha256':
- '57252aaf73b6a82cfb3bbc6cd68a9ec953f61296cf2c2d651125bb437dc50144',
+ '185014447d35357edbd20e7ce9924842a0d5c6576bd2257abae2ed48b65fd3b8',
'platform':
'darwin',
'machine': ['x86_64']
@@ -54,11 +54,11 @@
'file_name':
'tracebox',
'file_size':
- 1376136,
+ 1392776,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/tracebox',
'sha256':
- '5f37217af7d47b39624c62625847ec6b98db2ebd1a512e85921eb16bcb402b35',
+ '082bb50e64df5e232673eebb1cd8b0dd752a394105f600cb0262730833f6b7f3',
'platform':
'darwin',
'machine': ['arm64']
@@ -68,11 +68,11 @@
'file_name':
'tracebox',
'file_size':
- 2218840,
+ 2229096,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/tracebox',
'sha256':
- '89b1412e66b0a227cd8d16e6bf3d7416609c153d4b5c64d6e1a7c600870cd27b',
+ 'c99120caedb845e1c3fad4428263a683b44c357c76d65848dd8e437250066e38',
'platform':
'linux',
'machine': ['x86_64']
@@ -82,11 +82,11 @@
'file_name':
'tracebox',
'file_size':
- 1332292,
+ 1339796,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/tracebox',
'sha256':
- 'b5a3a8e0869b35bc9ceb06f3e4ca5bb6c1e1b1c5e6ea23cfed541e40a45ad96a',
+ '6732165916b74f0b820991d1aaed2086a6b56e91f6c604291efe6636f0bdda71',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -96,11 +96,11 @@
'file_name':
'tracebox',
'file_size':
- 2147464,
+ 2157312,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/tracebox',
'sha256':
- '00fdbdfc5877352b9323b6ea1a119132b66e696941f380da117d0d47ae1baaf9',
+ '7d09865a6d7118e67d2acd0c56b2a94ce8bd5f614869d29a72fe633515ab1fbd',
'platform':
'linux',
'machine': ['aarch64']
@@ -110,11 +110,11 @@
'file_name':
'tracebox',
'file_size':
- 1230804,
+ 1247188,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/tracebox',
'sha256':
- '02ee157f995708e78f57d6e5e110054d2e2b407dbf2db120cc967c7c553081e7'
+ '4ecc192172ac2bca49557cbdbb1f7d660718d4fb4a7314fd19b2b2e52be8bc0c'
}, {
'arch':
'android-arm64',
@@ -123,9 +123,9 @@
'file_size':
1854120,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/tracebox',
'sha256':
- 'e0031963f623da25c387b78e20747e07829043732f5afe16cfdea9bad3c0f27c'
+ '1ca89113279d5c6a9ae273bde03b4d84373efe6923dc637cb840908f13b9639e'
}, {
'arch':
'android-x86',
@@ -134,9 +134,9 @@
'file_size':
1853356,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/tracebox',
'sha256':
- '6e9e4b7b0d3773ab5011e476eca883eeb0ab7538fc7d2cd4642b6abc01711ba9'
+ 'cf689a191c1252734ebbfda3106600da324610f761515cfbffbeac2ebdfee715'
}, {
'arch':
'android-x64',
@@ -145,9 +145,9 @@
'file_size':
2149032,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/tracebox',
'sha256':
- '4b494907a43e898047ce98271796464deb3010b6755cee8d9c8a6c341502979b'
+ '99e9ebdb5b5308d95551a4ad060d615d7defb6877c4061d21c783c45a71d372f'
}]
# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
@@ -718,7 +718,7 @@
fname = os.path.basename(path)
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
- address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}'
+ address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}&referrer=record_android_trace'
if open_browser:
webbrowser.open_new_tab(address)
else:
diff --git a/tools/trace_processor b/tools/trace_processor
index 6328084..60de377 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -30,18 +30,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/trace_processor_shell.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACE_PROCESSOR_SHELL_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'trace_processor_shell',
'file_size':
- 9830664,
+ 9978200,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/trace_processor_shell',
'sha256':
- 'd617f9237f77477a6e80065cbcfc84d6ce80505ee34851a0b435f0815aba2645',
+ 'f3e21eb29fb51cb2ea9b81b69132c5ae93ce3276c57ccd27fcf7c675306b4e41',
'platform':
'darwin',
'machine': ['x86_64']
@@ -51,11 +51,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 8328808,
+ 8493976,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/trace_processor_shell',
'sha256':
- 'cc5b81618ddfa0a6eb63e0d418c0a52e302ca2617906fae6743d71f82c454611',
+ '84f35765141374b8d883813ac533e0c004cf72d1c6f05aef0c973364ff541eb9',
'platform':
'darwin',
'machine': ['arm64']
@@ -65,11 +65,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 9682088,
+ 9830856,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/trace_processor_shell',
'sha256':
- '0beb26c1d744cb982630b23ce031a767d6ed7113b81dca0710f792111592ee9e',
+ 'b3dc0a9c641b84a57fa5d59637921ae2237e4f05b1778341a691df220faf0cd7',
'platform':
'linux',
'machine': ['x86_64']
@@ -79,11 +79,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 7087544,
+ 7231096,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/trace_processor_shell',
'sha256':
- '4df289643b09261bd5718d6894e301023b4101e94e2dd81ead63037fb454054d',
+ 'a21252830fb1bbb7b3fd9665ce6e70920cffa6b1e72c16589c90896c002c3348',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
'file_name':
'trace_processor_shell',
'file_size':
- 9090808,
+ 9238056,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/trace_processor_shell',
'sha256':
- '9cb0ce22fba1929bef599d89a197a2ea718061840006c4122032cf0fc5fc0c90',
+ 'f77519ec19743ec2c22ed78fe3a20106a482a28d77c4154378af108c5f7bdd4a',
'platform':
'linux',
'machine': ['aarch64']
@@ -107,55 +107,55 @@
'file_name':
'trace_processor_shell',
'file_size':
- 6723512,
+ 6870968,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/trace_processor_shell',
'sha256':
- 'c5949a736b1b5edb285f56c22e9af83e1d0ec6d331adaac77330a277d8bee07c'
+ '2c7055fb44085ec60ad8bb970d495c9c88070fce08902f11fcd44e0ae3369876'
}, {
'arch':
'android-arm64',
'file_name':
'trace_processor_shell',
'file_size':
- 8267112,
+ 8414568,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/trace_processor_shell',
'sha256':
- '32475b5fe7e1af557eab0d898accb37c657bc4b2dd221135b55114e61bd8be39'
+ 'd8ca0dc2bab7ea604a6721f0ac0e2b433b43261f247c6c98c510dc17aafe5a72'
}, {
'arch':
'android-x86',
'file_name':
'trace_processor_shell',
'file_size':
- 9164668,
+ 9328508,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/trace_processor_shell',
'sha256':
- '02aeff08c0c3e553c95c851401ade9aac6784ad2f19e312dabbb24801d02c0e5'
+ 'de6a6ea45769888e59a1678d37b6e355b27b834d34a0b9e4980a942d333b88cc'
}, {
'arch':
'android-x64',
'file_name':
'trace_processor_shell',
'file_size':
- 9430440,
+ 9577896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/trace_processor_shell',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/trace_processor_shell',
'sha256':
- 'de75622f96f2551a7bbc28a0f063e0dfcc39241cd4fe09c39d0b2541d860a30a'
+ 'cd4b16c5f78a060934204737ba8b312e824ff7cc28f3732daf7d64e733a727f9'
}, {
'arch':
'windows-amd64',
'file_name':
'trace_processor_shell.exe',
'file_size':
- 9100288,
+ 9248256,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/trace_processor_shell.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/trace_processor_shell.exe',
'sha256':
- 'f0065f1dfa3f8001959bf18f5cd30138aa58409e39a13ccdd52a1860384e34fb',
+ '26584b4bbab40f8b0ad991a869e7483f92d7223e1473b879a6ceafa49b76390a',
'platform':
'win32',
'machine': ['amd64']
diff --git a/tools/tracebox b/tools/tracebox
index df4911a..a4c278b 100755
--- a/tools/tracebox
+++ b/tools/tracebox
@@ -30,18 +30,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACEBOX_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'tracebox',
'file_size':
- 1498680,
+ 1498816,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/tracebox',
'sha256':
- '57252aaf73b6a82cfb3bbc6cd68a9ec953f61296cf2c2d651125bb437dc50144',
+ '185014447d35357edbd20e7ce9924842a0d5c6576bd2257abae2ed48b65fd3b8',
'platform':
'darwin',
'machine': ['x86_64']
@@ -51,11 +51,11 @@
'file_name':
'tracebox',
'file_size':
- 1376136,
+ 1392776,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/tracebox',
'sha256':
- '5f37217af7d47b39624c62625847ec6b98db2ebd1a512e85921eb16bcb402b35',
+ '082bb50e64df5e232673eebb1cd8b0dd752a394105f600cb0262730833f6b7f3',
'platform':
'darwin',
'machine': ['arm64']
@@ -65,11 +65,11 @@
'file_name':
'tracebox',
'file_size':
- 2218840,
+ 2229096,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/tracebox',
'sha256':
- '89b1412e66b0a227cd8d16e6bf3d7416609c153d4b5c64d6e1a7c600870cd27b',
+ 'c99120caedb845e1c3fad4428263a683b44c357c76d65848dd8e437250066e38',
'platform':
'linux',
'machine': ['x86_64']
@@ -79,11 +79,11 @@
'file_name':
'tracebox',
'file_size':
- 1332292,
+ 1339796,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/tracebox',
'sha256':
- 'b5a3a8e0869b35bc9ceb06f3e4ca5bb6c1e1b1c5e6ea23cfed541e40a45ad96a',
+ '6732165916b74f0b820991d1aaed2086a6b56e91f6c604291efe6636f0bdda71',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
'file_name':
'tracebox',
'file_size':
- 2147464,
+ 2157312,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/tracebox',
'sha256':
- '00fdbdfc5877352b9323b6ea1a119132b66e696941f380da117d0d47ae1baaf9',
+ '7d09865a6d7118e67d2acd0c56b2a94ce8bd5f614869d29a72fe633515ab1fbd',
'platform':
'linux',
'machine': ['aarch64']
@@ -107,11 +107,11 @@
'file_name':
'tracebox',
'file_size':
- 1230804,
+ 1247188,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/tracebox',
'sha256':
- '02ee157f995708e78f57d6e5e110054d2e2b407dbf2db120cc967c7c553081e7'
+ '4ecc192172ac2bca49557cbdbb1f7d660718d4fb4a7314fd19b2b2e52be8bc0c'
}, {
'arch':
'android-arm64',
@@ -120,9 +120,9 @@
'file_size':
1854120,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/tracebox',
'sha256':
- 'e0031963f623da25c387b78e20747e07829043732f5afe16cfdea9bad3c0f27c'
+ '1ca89113279d5c6a9ae273bde03b4d84373efe6923dc637cb840908f13b9639e'
}, {
'arch':
'android-x86',
@@ -131,9 +131,9 @@
'file_size':
1853356,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/tracebox',
'sha256':
- '6e9e4b7b0d3773ab5011e476eca883eeb0ab7538fc7d2cd4642b6abc01711ba9'
+ 'cf689a191c1252734ebbfda3106600da324610f761515cfbffbeac2ebdfee715'
}, {
'arch':
'android-x64',
@@ -142,9 +142,9 @@
'file_size':
2149032,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/tracebox',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/tracebox',
'sha256':
- '4b494907a43e898047ce98271796464deb3010b6755cee8d9c8a6c341502979b'
+ '99e9ebdb5b5308d95551a4ad060d615d7defb6877c4061d21c783c45a71d372f'
}]
# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
diff --git a/tools/traceconv b/tools/traceconv
index 5127ab5..0136e37 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -30,18 +30,18 @@
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: tools/roll-prebuilts 6ba75cc5d93c39d4f86e2777709a460b30ce06fc
+# This file has been generated by: tools/roll-prebuilts v40.0
TRACECONV_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
'traceconv',
'file_size':
- 9020880,
+ 9184800,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-amd64/traceconv',
'sha256':
- 'ae7903f9bb8a98b32fdf96c0124797cf9b4b58a9bcb45a05574d74d33b11ada9',
+ 'b651d0a5b5606c1c3e24723e94d8ecb233a01f0dfccc95a2c6a4e773cb8f52d7',
'platform':
'darwin',
'machine': ['x86_64']
@@ -51,11 +51,11 @@
'file_name':
'traceconv',
'file_size':
- 7580200,
+ 7761896,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/mac-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/mac-arm64/traceconv',
'sha256':
- '619f31cd84a70fcb75e2a1cbe911f77feca0273ea0b1e706ae4be5ac020cdf7d',
+ '3b019f5ddd5293d3181f7c30f91dc7b08f3a2e83ebb3b52b8f3905dc5161747d',
'platform':
'darwin',
'machine': ['arm64']
@@ -65,11 +65,11 @@
'file_name':
'traceconv',
'file_size':
- 8773576,
+ 8928296,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-amd64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-amd64/traceconv',
'sha256':
- 'abc0b4191abe5106e1d4382bc0d238e53c39a83e1ba91237fbed5f789ef1c82b',
+ '830d20ffec266218d49f6b6c8efed4538bc59b51d8d2f735cbbb6a1435131b50',
'platform':
'linux',
'machine': ['x86_64']
@@ -79,11 +79,11 @@
'file_name':
'traceconv',
'file_size':
- 6620748,
+ 6770204,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm/traceconv',
'sha256':
- 'f2dd9f1bfabea567ff20aedd0798e90093eff2bfdc15aacbfafe0abe52aa1fd1',
+ '93a9e5ccb94559b871af8f6da45f858aee01801b31776703892dcf3d7ea769b7',
'platform':
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
'file_name':
'traceconv',
'file_size':
- 8240792,
+ 8393944,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/linux-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/linux-arm64/traceconv',
'sha256':
- '76428eca01ced4b769241915fb5233b43b9b59e9062917e45d523db10f33e546',
+ '88a92ccbcd8e851673e018b7f599514daf05dde9b7e4de9641fa5629124abf12',
'platform':
'linux',
'machine': ['aarch64']
@@ -107,55 +107,55 @@
'file_name':
'traceconv',
'file_size':
- 6247672,
+ 6378744,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm/traceconv',
'sha256':
- '3eb80bbd78c6210b73ef20654b9d80994747e552b3d342a8d2ec4aa58dabcc33'
+ '6cb7d30d656aa4f172e6724f105a56e249e7043ecf637c65e1e3868885535cff'
}, {
'arch':
'android-arm64',
'file_name':
'traceconv',
'file_size':
- 7545032,
+ 7692488,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-arm64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-arm64/traceconv',
'sha256':
- '30037aba74f7718f71273a83141373e623ddfe561294d01b41433dc9b572fee5'
+ '1668808efbdf8d5b116d4716d61d2bd002f71ce465206d3b83af4fcc7a4c19cd'
}, {
'arch':
'android-x86',
'file_name':
'traceconv',
'file_size':
- 8410300,
+ 8557756,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x86/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x86/traceconv',
'sha256':
- '9ad420368453699da12ce6359db514283e92b2caa2d415d9db61977cc79649a9'
+ '653733582cae0021eae0e1b5d8db387c1bae772d77b307f1e2111b78ec4ea67c'
}, {
'arch':
'android-x64',
'file_name':
'traceconv',
'file_size':
- 8544512,
+ 8708352,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/android-x64/traceconv',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/android-x64/traceconv',
'sha256':
- '60a62f8712186c5f984ff2fbc4ff8c47b6226b62a83b31f878e0597c8bb90cd8'
+ '7fc564ac581b81d79573f57dae027c47bd7a857ff0f89df984380c3c657d5876'
}, {
'arch':
'windows-amd64',
'file_name':
'traceconv.exe',
'file_size':
- 8049664,
+ 8204288,
'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/6ba75cc5d93c39d4f86e2777709a460b30ce06fc/windows-amd64/traceconv.exe',
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v40.0/windows-amd64/traceconv.exe',
'sha256':
- '9e89ccd0cdb466ea5fe960b71538b0ff8a2858b9e7ecdbcafa9ffe60839c9afc',
+ 'e33bad8061f08f9c3cfe6e91ef6f1696b6ac90d0799edcb57052f24888b436e2',
'platform':
'win32',
'machine': ['amd64']
diff --git a/tools/update-statsd-descriptor b/tools/update-statsd-descriptor
index fe9ff0e..eca6c02 100755
--- a/tools/update-statsd-descriptor
+++ b/tools/update-statsd-descriptor
@@ -21,6 +21,7 @@
import tempfile
import contextlib
import argparse
+import itertools
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TOOLS_DIR = os.path.join(ROOT_DIR, "tools")
@@ -34,8 +35,10 @@
PROTO_LOGGING_URL = "https://android.googlesource.com/platform/frameworks/proto_logging.git"
ATOM_RE = r" message_type {\n. name: \"Atom\"(\n .+)+(\n })"
FIELD_RE = r" field {\n name: \"([^\"]+)\"\n number: ([0-9]+)"
+EXTENSIONS_RE = r" extension {\n name: \"([^\"]+)\"\n extendee: \".android.os.statsd.Atom\"\n number: ([0-9]+)(\n .+)+(\n })"
ATOM_IDS_PATH = os.path.join(ROOT_DIR, "protos", "perfetto", "config", "statsd",
"atom_ids.proto")
+
ATOM_IDS_TEMPLATE = """/*
* Copyright (C) 2022 The Android Open Source Project
*
@@ -75,6 +78,31 @@
exit(1)
+# Extract core atoms. To do this we regex the pbtext
+# of the descriptor. This is hopefully:
+# - more stable than regexing atom.proto directly
+# - less complicated than parsing finding, importing, and using the
+# Python protobuf library.
+def atoms_from_descriptor():
+ with contextlib.ExitStack() as stack:
+ descriptor_in = stack.enter_context(open(DESCRIPTOR_PATH))
+ pbtext = call(
+ PROTOC_PATH,
+ f"--proto_path={PROTOBUF_BUILTINS_DIR}",
+ f"{PROTOBUF_BUILTINS_DIR}/google/protobuf/descriptor.proto",
+ "--decode=google.protobuf.FileDescriptorSet",
+ stdin=descriptor_in).decode("utf8")
+
+ # Core atoms:
+ atom_pbtext = re.search(ATOM_RE, pbtext, re.MULTILINE)[0]
+ for m in re.finditer(FIELD_RE, atom_pbtext):
+ yield m[1], m[2]
+
+ # Extensions
+ for m in re.finditer(EXTENSIONS_RE, pbtext):
+ yield m[1], m[2]
+
+
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--atoms-checkout")
@@ -97,31 +125,29 @@
pathlib.Path(proto_logging_dir).mkdir(parents=True, exist_ok=True)
call("git", "clone", PROTO_LOGGING_URL, proto_logging_dir)
- atoms_path = os.path.join(proto_logging_dir, "stats", "atoms.proto")
- call(PROTOC_PATH, f"--proto_path={PROTOBUF_BUILTINS_DIR}",
- f"--proto_path={atoms_root}",
- f"--descriptor_set_out={DESCRIPTOR_PATH}", "--include_imports",
- atoms_path)
- # Extract and update atom_ids.proto. To do this we regex the pbtext
- # of the descriptor. This is hopefully:
- # - more stable than regexing atom.proto directly
- # - less complicated than parsing finding, importing, and using the
- # Python protobuf library.
- descriptor_in = stack.enter_context(open(DESCRIPTOR_PATH))
- pbtext = call(
- PROTOC_PATH,
- f"--proto_path={PROTOBUF_BUILTINS_DIR}",
- f"{PROTOBUF_BUILTINS_DIR}/google/protobuf/descriptor.proto",
- "--decode=google.protobuf.FileDescriptorSet",
- stdin=descriptor_in)
+ extensions_path = os.path.join(proto_logging_dir, "stats", "atoms")
+ extensions = []
+ if os.path.isdir(extensions_path):
+ for dirpath, dirnames, filenames in os.walk(extensions_path):
+ for name in filenames:
+ if name.endswith(".proto"):
+ path = os.path.join(dirpath, name)
+ extensions.append(path)
- atom_pbtext = re.search(ATOM_RE, pbtext.decode("utf8"), re.MULTILINE)[0]
+ cmd = [
+ f"--proto_path={PROTOBUF_BUILTINS_DIR}",
+ f"--proto_path={atoms_root}",
+ f"--descriptor_set_out={DESCRIPTOR_PATH}",
+ "--include_imports",
+ ] + extensions + [
+ os.path.join(proto_logging_dir, "stats", "atoms.proto")
+ ]
+ call(PROTOC_PATH, *cmd)
lines = []
- for m in re.finditer(FIELD_RE, atom_pbtext):
- name = "ATOM_" + m[1].upper()
- field = m[2]
+ for name, field in atoms_from_descriptor():
+ name = "ATOM_" + name.upper()
lines.append(f" {name} = {field};".format(name=name, field=field))
atom_ids_out = stack.enter_context(open(ATOM_IDS_PATH, "w"))
atom_ids_out.write(ATOM_IDS_TEMPLATE.format(atoms="\n".join(lines)))
diff --git a/ui/build.js b/ui/build.js
index ba02879..4afe0cd 100644
--- a/ui/build.js
+++ b/ui/build.js
@@ -352,6 +352,9 @@
function copyAssets(src, dst) {
addTask(cp, [src, pjoin(cfg.outDistDir, 'assets', dst)]);
+ if (cfg.bigtrace) {
+ addTask(cp, [src, pjoin(cfg.outBigtraceDistDir, 'assets', dst)]);
+ }
}
function copyUiTestArtifactsAssets(src, dst) {
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 91a0c0e..9ade987 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
"channels": [
{
"name": "stable",
- "rev": "c69b33b9abcc20fad9ad5f39de883216e4b43130"
+ "rev": "fa6ee75b6f4a9374ade506697c65a8a73edc54be"
},
{
"name": "canary",
- "rev": "c69b33b9abcc20fad9ad5f39de883216e4b43130"
+ "rev": "9ca89e30931314dec4af1131d516e07e39d8657d"
},
{
"name": "autopush",
diff --git a/ui/src/base/dom_utils.ts b/ui/src/base/dom_utils.ts
index d7c7582..5caf9d9 100644
--- a/ui/src/base/dom_utils.ts
+++ b/ui/src/base/dom_utils.ts
@@ -82,3 +82,25 @@
return {x: e.offsetX, y: e.offsetY};
}
+
+function calculateScrollbarWidth() {
+ const outer = document.createElement('div');
+ outer.style.overflowY = 'scroll';
+ const inner = document.createElement('div');
+ outer.appendChild(inner);
+ document.body.appendChild(outer);
+ const width =
+ outer.getBoundingClientRect().width - inner.getBoundingClientRect().width;
+ document.body.removeChild(outer);
+ return width;
+}
+
+let cachedScrollBarWidth: number|undefined = undefined;
+
+// Calculate the space a scrollbar takes up.
+export function getScrollbarWidth() {
+ if (cachedScrollBarWidth === undefined) {
+ cachedScrollBarWidth = calculateScrollbarWidth();
+ }
+ return cachedScrollBarWidth;
+}
diff --git a/ui/src/base/mithril_utils.ts b/ui/src/base/mithril_utils.ts
index 576ea24..7458026 100644
--- a/ui/src/base/mithril_utils.ts
+++ b/ui/src/base/mithril_utils.ts
@@ -19,3 +19,34 @@
return Array.isArray(children) && children.length > 0 &&
children.some((value) => value);
}
+
+// A component which simply passes through it's children.
+// Can be used for having something to attach lifecycle hooks to without having
+// to add an extra HTML element to the DOM.
+export const Passthrough = {
+ view({children}: m.VnodeDOM) {
+ return children;
+ },
+};
+
+export interface GateAttrs {
+ open: boolean;
+}
+
+// The gate component is a wrapper which can either be open or closed.
+// - When open, children are rendered inside a div where display = contents.
+// - When closed, children are rendered inside a div where display = none, and
+// the children's view() function(s) will not be called.
+// Use this component when we want to conditionally render certain children,
+// but we want to maintain their state.
+export const Gate = {
+ view({attrs, children}: m.VnodeDOM<GateAttrs>) {
+ return m(
+ '',
+ {
+ style: {display: attrs.open ? 'contents' : 'none'},
+ },
+ m(Passthrough, {onbeforeupdate: () => attrs.open}, children),
+ );
+ },
+};
diff --git a/ui/src/base/static_initializers.ts b/ui/src/base/static_initializers.ts
index 5abfcd9..f927471 100644
--- a/ui/src/base/static_initializers.ts
+++ b/ui/src/base/static_initializers.ts
@@ -38,7 +38,7 @@
// from the global state (which is frozen) and later try to update the copies.
// By doing so, we accidentally the local copy of global state, which is
// supposed to be immutable.
- setAutoFreeze(false);
+ setAutoFreeze(true);
}
function initializeProtobuf() {
diff --git a/ui/src/common/canvas_utils.ts b/ui/src/common/canvas_utils.ts
index 2d09617..ad78f96 100644
--- a/ui/src/common/canvas_utils.ts
+++ b/ui/src/common/canvas_utils.ts
@@ -75,7 +75,11 @@
x: number,
y: number,
width: number,
- height: number) {
+ height: number,
+ showGradient: boolean = true) {
+ if (width <= 0 || height <= 0) {
+ return;
+ }
ctx.beginPath();
const triangleSize = height / 4;
ctx.moveTo(x, y);
@@ -92,10 +96,12 @@
const fillStyle = ctx.fillStyle;
if (isString(fillStyle)) {
- const gradient = ctx.createLinearGradient(x, y, x + width, y + height);
- gradient.addColorStop(0.66, fillStyle);
- gradient.addColorStop(1, '#FFFFFF');
- ctx.fillStyle = gradient;
+ if (showGradient) {
+ const gradient = ctx.createLinearGradient(x, y, x + width, y + height);
+ gradient.addColorStop(0.66, fillStyle);
+ gradient.addColorStop(1, '#FFFFFF');
+ ctx.fillStyle = gradient;
+ }
} else {
throw new Error(
`drawIncompleteSlice() expects fillStyle to be a simple color not ${
diff --git a/ui/src/common/color.ts b/ui/src/common/color.ts
new file mode 100644
index 0000000..127bfbb
--- /dev/null
+++ b/ui/src/common/color.ts
@@ -0,0 +1,290 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {hsluvToRgb} from 'hsluv';
+
+import {clamp} from '../base/math_utils';
+
+// This file contains a library for working with colors in various color spaces
+// and formats.
+
+const LIGHTNESS_MIN = 0;
+const LIGHTNESS_MAX = 100;
+
+const SATURATION_MIN = 0;
+const SATURATION_MAX = 100;
+
+// Most color formats can be defined using 3 numbers in a standardized order, so
+// this tuple serves as a compact way to store various color formats.
+// E.g. HSL, RGB
+type ColorTuple = [number, number, number];
+
+// Definition of an HSL color with named fields.
+interface HSL {
+ readonly h: number; // 0-360
+ readonly s: number; // 0-100
+ readonly l: number; // 0-100
+}
+
+// Defines an interface to an immutable color object, which can be defined in
+// any arbitrary format or color space and provides function to modify the color
+// and conversions to CSS compatible style strings.
+// Because this color object is effectively immutable, a new color object is
+// returned when modifying the color, rather than editing the current object
+// in-place.
+// Also, because these objects are immutable, it's expected that readonly
+// properties such as |cssString| are efficient, as they can be computed at
+// creation time, so they may be used in the hot path (render loop).
+export interface Color {
+ readonly cssString: string;
+
+ // The perceived brightness of the color using a weighted average of the
+ // r, g and b channels based on human perception.
+ readonly perceivedBrightness: number;
+
+ // Bring up the lightness by |percent| percent.
+ lighten(percent: number, max?: number): Color;
+
+ // Bring down the lightness by |percent| percent.
+ darken(percent: number, min?: number): Color;
+
+ // Bring up the saturation by |percent| percent.
+ saturate(percent: number, max?: number): Color;
+
+ // Bring down the saturation by |percent| percent.
+ desaturate(percent: number, min?: number): Color;
+
+ // Set one or more HSL values.
+ setHSL(hsl: Partial<HSL>): Color;
+
+ setAlpha(alpha: number|undefined): Color;
+}
+
+// Common base class for HSL colors. Avoids code duplication.
+abstract class HSLColorBase<T extends Color> {
+ readonly hsl: ColorTuple;
+ readonly alpha?: number;
+
+ // Values are in the range:
+ // Hue: 0-360
+ // Saturation: 0-100
+ // Lightness: 0-100
+ // Alpha: 0-1
+ constructor(init: ColorTuple|HSL|string, alpha?: number) {
+ if (Array.isArray(init)) {
+ this.hsl = init;
+ } else if (typeof init === 'string') {
+ const rgb = hexToRgb(init);
+ this.hsl = rgbToHsl(rgb);
+ } else {
+ this.hsl = [init.h, init.s, init.l];
+ }
+ this.alpha = alpha;
+ }
+
+ // Subclasses should implement this to teach the base class how to create a
+ // new object of the subclass type.
+ abstract create(hsl: ColorTuple|HSL, alpha?: number): T;
+
+ lighten(amount: number, max = LIGHTNESS_MAX): T {
+ const [h, s, l] = this.hsl;
+ const newLightness = clamp(l + amount, LIGHTNESS_MIN, max);
+ return this.create([h, s, newLightness], this.alpha);
+ }
+
+ darken(amount: number, min = LIGHTNESS_MIN): T {
+ const [h, s, l] = this.hsl;
+ const newLightness = clamp(l - amount, min, LIGHTNESS_MAX);
+ return this.create([h, s, newLightness], this.alpha);
+ }
+
+ saturate(amount: number, max = SATURATION_MAX): T {
+ const [h, s, l] = this.hsl;
+ const newSaturation = clamp(s + amount, SATURATION_MIN, max);
+ return this.create([h, newSaturation, l], this.alpha);
+ }
+
+ desaturate(amount: number, min = SATURATION_MIN): T {
+ const [h, s, l] = this.hsl;
+ const newSaturation = clamp(s - amount, min, SATURATION_MAX);
+ return this.create([h, newSaturation, l], this.alpha);
+ }
+
+ setHSL(hsl: Partial<HSL>): T {
+ const [h, s, l] = this.hsl;
+ return this.create({h, s, l, ...hsl}, this.alpha);
+ }
+
+ setAlpha(alpha: number|undefined): T {
+ return this.create(this.hsl, alpha);
+ }
+}
+
+// Describes a color defined in standard HSL color space.
+export class HSLColor extends HSLColorBase<HSLColor> implements Color {
+ readonly cssString: string;
+ readonly perceivedBrightness: number;
+
+ // Values are in the range:
+ // Hue: 0-360
+ // Saturation: 0-100
+ // Lightness: 0-100
+ // Alpha: 0-1
+ constructor(hsl: ColorTuple|HSL|string, alpha?: number) {
+ super(hsl, alpha);
+
+ const [r, g, b] = hslToRgb(...this.hsl);
+
+ this.perceivedBrightness = perceivedBrightness(r, g, b);
+
+ if (this.alpha === undefined) {
+ this.cssString = `rgb(${r} ${g} ${b})`;
+ } else {
+ this.cssString = `rgb(${r} ${g} ${b} / ${this.alpha})`;
+ }
+ }
+
+ create(values: ColorTuple|HSL, alpha?: number|undefined): HSLColor {
+ return new HSLColor(values, alpha);
+ }
+}
+
+// Describes a color defined in HSLuv color space.
+// See: https://www.hsluv.org/
+export class HSLuvColor extends HSLColorBase<HSLuvColor> implements Color {
+ readonly cssString: string;
+ readonly perceivedBrightness: number;
+
+ constructor(hsl: ColorTuple|HSL, alpha?: number) {
+ super(hsl, alpha);
+
+ const rgb = hsluvToRgb(this.hsl);
+ const r = Math.floor(rgb[0] * 255);
+ const g = Math.floor(rgb[1] * 255);
+ const b = Math.floor(rgb[2] * 255);
+
+ this.perceivedBrightness = perceivedBrightness(r, g, b);
+
+ if (this.alpha === undefined) {
+ this.cssString = `rgb(${r} ${g} ${b})`;
+ } else {
+ this.cssString = `rgb(${r} ${g} ${b} / ${this.alpha})`;
+ }
+ }
+
+ create(raw: ColorTuple|HSL, alpha?: number|undefined): HSLuvColor {
+ return new HSLuvColor(raw, alpha);
+ }
+}
+
+// Hue: 0-360
+// Saturation: 0-100
+// Lightness: 0-100
+// RGB: 0-255
+export function hslToRgb(h: number, s: number, l: number): ColorTuple {
+ h = h;
+ s = s / SATURATION_MAX;
+ l = l / LIGHTNESS_MAX;
+
+ const c = (1 - Math.abs(2 * l - 1)) * s;
+ const x = c * (1 - Math.abs((h / 60) % 2 - 1));
+ const m = l - c / 2;
+
+ let [r, g, b] = [0, 0, 0];
+
+ if (0 <= h && h < 60) {
+ [r, g, b] = [c, x, 0];
+ } else if (60 <= h && h < 120) {
+ [r, g, b] = [x, c, 0];
+ } else if (120 <= h && h < 180) {
+ [r, g, b] = [0, c, x];
+ } else if (180 <= h && h < 240) {
+ [r, g, b] = [0, x, c];
+ } else if (240 <= h && h < 300) {
+ [r, g, b] = [x, 0, c];
+ } else if (300 <= h && h < 360) {
+ [r, g, b] = [c, 0, x];
+ }
+
+ // Convert to 0-255 range
+ r = Math.round((r + m) * 255);
+ g = Math.round((g + m) * 255);
+ b = Math.round((b + m) * 255);
+
+ return [r, g, b];
+}
+
+export function hexToRgb(hex: string): ColorTuple {
+ // Convert hex to RGB first
+ let r: number = 0;
+ let g: number = 0;
+ let b: number = 0;
+
+ if (hex.length === 4) {
+ r = parseInt(hex[1] + hex[1], 16);
+ g = parseInt(hex[2] + hex[2], 16);
+ b = parseInt(hex[3] + hex[3], 16);
+ } else if (hex.length === 7) {
+ r = parseInt(hex.substring(1, 3), 16);
+ g = parseInt(hex.substring(3, 5), 16);
+ b = parseInt(hex.substring(5, 7), 16);
+ }
+
+ return [r, g, b];
+}
+
+export function rgbToHsl(rgb: ColorTuple): ColorTuple {
+ let [r, g, b] = rgb;
+ r /= 255;
+ g /= 255;
+ b /= 255;
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ let h: number = (max + min) / 2;
+ let s: number = (max + min) / 2;
+ const l: number = (max + min) / 2;
+
+ if (max === min) {
+ h = s = 0; // achromatic
+ } else {
+ const d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+
+ return [h * 360, s * 100, l * 100];
+}
+
+// Return the perceived brightness of a color using a weighted average of the
+// r, g and b channels based on human perception.
+function perceivedBrightness(r: number, g: number, b: number): number {
+ // YIQ calculation from https://24ways.org/2010/calculating-color-contrast
+ return ((r * 299) + (g * 587) + (b * 114)) / 1000;
+}
+
+// Comparison function used for sorting colors.
+export function colorCompare(a: Color, b: Color): number {
+ return a.cssString.localeCompare(b.cssString);
+}
diff --git a/ui/src/common/color_unittest.ts b/ui/src/common/color_unittest.ts
new file mode 100644
index 0000000..4973308
--- /dev/null
+++ b/ui/src/common/color_unittest.ts
@@ -0,0 +1,113 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {HSLColor, hslToRgb, HSLuvColor} from './color';
+
+describe('HSLColor', () => {
+ const col = new HSLColor({h: 123, s: 66, l: 45});
+
+ test('cssString', () => {
+ expect(col.cssString).toBe('rgb(39 190 47)');
+ expect(new HSLColor({h: 0, s: 0, l: 0}).cssString).toBe('rgb(0 0 0)');
+ expect(new HSLColor({h: 0, s: 100, l: 100}).cssString)
+ .toBe('rgb(255 255 255)');
+ expect(new HSLColor({h: 90, s: 25, l: 55}).cssString)
+ .toBe('rgb(140 169 112)');
+ expect(new HSLColor({h: 180, s: 80, l: 40}, 0.7).cssString)
+ .toBe('rgb(20 184 184 / 0.7)');
+ });
+
+ test('lighten', () => {
+ expect(col.lighten(20).hsl).toEqual([123, 66, 65]);
+ expect(col.lighten(100).hsl).toEqual([123, 66, 100]);
+ expect(col.lighten(-100).hsl).toEqual([123, 66, 0]);
+ });
+
+ test('saturate', () => {
+ expect(col.saturate(20).hsl).toEqual([123, 86, 45]);
+ expect(col.saturate(100).hsl).toEqual([123, 100, 45]);
+ expect(col.saturate(-100).hsl).toEqual([123, 0, 45]);
+ });
+
+ test('setAlpha', () => {
+ expect(col.setAlpha(.7).alpha).toEqual(.7);
+ expect(col.setAlpha(undefined).alpha).toEqual(undefined);
+ });
+
+ test('perceivedBrightness', () => {
+ // Test a few obviously light/dark colours.
+ expect(new HSLColor({h: 0, s: 0, l: 0}).perceivedBrightness)
+ .toBeLessThan(128);
+ expect(new HSLColor({h: 0, s: 0, l: 100}).perceivedBrightness)
+ .toBeGreaterThan(128);
+
+ expect(new HSLColor({h: 0, s: 0, l: 40}).perceivedBrightness)
+ .toBeLessThan(128);
+ expect(new HSLColor({h: 0, s: 0, l: 60}).perceivedBrightness)
+ .toBeGreaterThan(128);
+ });
+});
+
+describe('HSLuvColor', () => {
+ const col = new HSLuvColor({h: 123, s: 66, l: 45});
+
+ test('cssString', () => {
+ expect(col.cssString).toBe('rgb(69 117 58)');
+ expect(new HSLColor({h: 0, s: 0, l: 0}).cssString).toBe('rgb(0 0 0)');
+ expect(new HSLColor({h: 0, s: 100, l: 100}).cssString)
+ .toBe('rgb(255 255 255)');
+ expect(new HSLuvColor({h: 90, s: 25, l: 55}).cssString)
+ .toBe('rgb(131 133 112)');
+ expect(new HSLuvColor({h: 240, s: 100, l: 100}, 0.3).cssString)
+ .toBe('rgb(254 255 255 / 0.3)');
+ });
+
+ test('lighten', () => {
+ expect(col.lighten(20).hsl).toEqual([123, 66, 65]);
+ expect(col.lighten(100).hsl).toEqual([123, 66, 100]);
+ expect(col.lighten(-100).hsl).toEqual([123, 66, 0]);
+ });
+
+ test('saturate', () => {
+ expect(col.saturate(20).hsl).toEqual([123, 86, 45]);
+ expect(col.saturate(100).hsl).toEqual([123, 100, 45]);
+ expect(col.saturate(-100).hsl).toEqual([123, 0, 45]);
+ });
+
+ test('setAlpha', () => {
+ expect(col.setAlpha(.7).alpha).toEqual(.7);
+ expect(col.setAlpha(undefined).alpha).toEqual(undefined);
+ });
+
+ test('perceivedBrightness', () => {
+ // Test a few obviously light/dark colours.
+ expect(new HSLuvColor({h: 0, s: 0, l: 0}).perceivedBrightness)
+ .toBeLessThan(128);
+ expect(new HSLuvColor({h: 0, s: 0, l: 100}).perceivedBrightness)
+ .toBeGreaterThan(128);
+
+ expect(new HSLuvColor({h: 0, s: 0, l: 40}).perceivedBrightness)
+ .toBeLessThan(128);
+ expect(new HSLuvColor({h: 0, s: 0, l: 60}).perceivedBrightness)
+ .toBeGreaterThan(128);
+ });
+});
+
+test('hslToRGB', () => {
+ // Pick a few well-known conversions to check we're in the right ballpark.
+ expect(hslToRgb(0, 0, 0)).toEqual([0, 0, 0]);
+ expect(hslToRgb(0, 100, 50)).toEqual([255, 0, 0]);
+ expect(hslToRgb(120, 100, 50)).toEqual([0, 255, 0]);
+ expect(hslToRgb(240, 100, 50)).toEqual([0, 0, 255]);
+});
diff --git a/ui/src/common/colorizer.ts b/ui/src/common/colorizer.ts
index 3c20520..584deb5 100644
--- a/ui/src/common/colorizer.ts
+++ b/ui/src/common/colorizer.ts
@@ -13,91 +13,161 @@
// limitations under the License.
import {hsl} from 'color-convert';
-import {hsluvToRgb} from 'hsluv';
-import {clamp} from '../base/math_utils';
import {hash} from '../common/hash';
-import {cachedHsluvToHex} from '../frontend/hsluv_cache';
+import {featureFlags} from '../core/feature_flags';
-export interface Color {
- h: number;
- s: number;
- l: number;
- a?: number;
+import {Color, HSLColor, HSLuvColor} from './color';
+
+// 128 would provide equal weighting between dark and light text, but we want to
+// slightly prefer light text for stylistic reasons.
+// 140 means we must be brighter on average before switching to dark text.
+const PERCEIVED_BRIGHTNESS_LIMIT = 140;
+
+// This file defines some opinionated colors and provides functions to access
+// random but predictable colors based on a seed, as well as standardized ways
+// to access colors for core objects such as slices and thread states.
+
+// We have, over the years, accumulated a number of different color palettes
+// which are used for different parts of the UI.
+// It would be nice to combine these into a single palette in the future, but
+// changing colors is difficult especially for slice colors, as folks get used
+// to certain slices being certain colors and are resistant to change.
+// However we do it, we should make it possible for folks to switch back the a
+// previous palette, or define their own.
+
+const USE_CONSISTENT_COLORS = featureFlags.register({
+ id: 'useConsistentColors',
+ name: 'Use common color palette for timeline elements',
+ description: 'Use the same color palette for all timeline elements.',
+ defaultValue: false,
+});
+
+// |ColorScheme| defines a collection of colors which can be used for various UI
+// elements. In the future we would expand this interface to include light and
+// dark variants.
+export interface ColorScheme {
+ // The base color to be used for the bulk of the element.
+ readonly base: Color;
+
+ // A variant on the base color, commonly used for highlighting.
+ readonly variant: Color;
+
+ // Grayed out color to represent a disabled state.
+ readonly disabled: Color;
+
+ // Appropriate colors for text to be displayed on top of the above colors.
+ readonly textBase: Color;
+ readonly textVariant: Color;
+ readonly textDisabled: Color;
}
-const MD_PALETTE: Color[] = [
- {h: 4, s: 90, l: 58},
- {h: 340, s: 82, l: 52},
- {h: 291, s: 64, l: 42},
- {h: 262, s: 52, l: 47},
- {h: 231, s: 48, l: 48},
- {h: 207, s: 90, l: 54},
- {h: 199, s: 98, l: 48},
- {h: 187, s: 100, l: 42},
- {h: 174, s: 100, l: 29},
- {h: 122, s: 39, l: 49},
- {h: 88, s: 50, l: 53},
- {h: 66, s: 70, l: 54},
- {h: 45, s: 100, l: 51},
- {h: 36, s: 100, l: 50},
- {h: 14, s: 100, l: 57},
- {h: 16, s: 25, l: 38},
- {h: 200, s: 18, l: 46},
- {h: 54, s: 100, l: 62},
+const MD_PALETTE_RAW: Color[] = [
+ new HSLColor({h: 4, s: 90, l: 58}),
+ new HSLColor({h: 340, s: 82, l: 52}),
+ new HSLColor({h: 291, s: 64, l: 42}),
+ new HSLColor({h: 262, s: 52, l: 47}),
+ new HSLColor({h: 231, s: 48, l: 48}),
+ new HSLColor({h: 207, s: 90, l: 54}),
+ new HSLColor({h: 199, s: 98, l: 48}),
+ new HSLColor({h: 187, s: 100, l: 42}),
+ new HSLColor({h: 174, s: 100, l: 29}),
+ new HSLColor({h: 122, s: 39, l: 49}),
+ new HSLColor({h: 88, s: 50, l: 53}),
+ new HSLColor({h: 66, s: 70, l: 54}),
+ new HSLColor({h: 45, s: 100, l: 51}),
+ new HSLColor({h: 36, s: 100, l: 50}),
+ new HSLColor({h: 14, s: 100, l: 57}),
+ new HSLColor({h: 16, s: 25, l: 38}),
+ new HSLColor({h: 200, s: 18, l: 46}),
+ new HSLColor({h: 54, s: 100, l: 62}),
];
-export const GRAY_COLOR: Color = {
- h: 0,
- s: 0,
- l: 62,
-};
+const WHITE_COLOR = new HSLColor([0, 0, 100]);
+const BLACK_COLOR = new HSLColor([0, 0, 0]);
+const GRAY_COLOR = new HSLColor([0, 0, 90]);
+
+const MD_PALETTE: ColorScheme[] = MD_PALETTE_RAW.map((color): ColorScheme => {
+ const base = color.lighten(10, 60).desaturate(20);
+ const variant = base.lighten(30, 80).desaturate(20);
+
+ return {
+ base,
+ variant,
+ disabled: GRAY_COLOR,
+ textBase: WHITE_COLOR, // White text suits MD colors quite well
+ textVariant: WHITE_COLOR,
+ textDisabled: WHITE_COLOR, // Low contrast is on purpose
+ };
+});
+
+// Create a color scheme based on a single color, which defines the variant
+// color as a slightly darker and more saturated version of the base color.
+export function makeColorScheme(base: Color, variant?: Color): ColorScheme {
+ variant = variant ?? base.darken(15).saturate(15);
+
+ return {
+ base,
+ variant,
+ disabled: GRAY_COLOR,
+ textBase: base.perceivedBrightness >= PERCEIVED_BRIGHTNESS_LIMIT ?
+ BLACK_COLOR :
+ WHITE_COLOR,
+ textVariant: variant.perceivedBrightness >= PERCEIVED_BRIGHTNESS_LIMIT ?
+ BLACK_COLOR :
+ WHITE_COLOR,
+ textDisabled: WHITE_COLOR, // Low contrast is on purpose
+ };
+}
+
+const GRAY = makeColorScheme(new HSLColor([0, 0, 62]));
+const DESAT_RED = makeColorScheme(new HSLColor([3, 30, 49]));
+const DARK_GREEN = makeColorScheme(new HSLColor([120, 44, 34]));
+const LIME_GREEN = makeColorScheme(new HSLColor([75, 55, 47]));
+const TRANSPARENT_WHITE = makeColorScheme(new HSLColor([0, 1, 97], 0.55));
+const ORANGE = makeColorScheme(new HSLColor([36, 100, 50]));
+const INDIGO = makeColorScheme(new HSLColor([231, 48, 48]));
// A piece of wisdom from a long forgotten blog post: "Don't make
// colors you want to change something normal like grey."
-export const UNEXPECTED_PINK_COLOR: Color = {
- h: 330,
- s: 1.0,
- l: 0.706,
-};
+export const UNEXPECTED_PINK = makeColorScheme(new HSLColor([330, 100, 70]));
-export function hueForCpu(cpu: number): number {
- return (128 + (32 * cpu)) % 256;
+// Selects a predictable color scheme from a palette of material design colors,
+// based on a string seed.
+function materialColorScheme(seed: string): ColorScheme {
+ const colorIdx = hash(seed, MD_PALETTE.length);
+ return MD_PALETTE[colorIdx];
}
-const DESAT_RED: Color = {
- h: 3,
- s: 30,
- l: 49,
-};
-const DARK_GREEN: Color = {
- h: 120,
- s: 44,
- l: 34,
-};
-const LIME_GREEN: Color = {
- h: 75,
- s: 55,
- l: 47,
-};
-const TRANSPARENT_WHITE: Color = {
- h: 0,
- s: 1,
- l: 97,
- a: 0.55,
-};
-const ORANGE: Color = {
- h: 36,
- s: 100,
- l: 50,
-};
-const INDIGO: Color = {
- h: 231,
- s: 48,
- l: 48,
-};
+const proceduralColorCache = new Map<string, ColorScheme>();
-export function colorForState(state: string): Readonly<Color> {
+// Procedurally generates a predictable color scheme based on a string seed.
+function proceduralColorScheme(seed: string): ColorScheme {
+ const colorScheme = proceduralColorCache.get(seed);
+ if (colorScheme) {
+ return colorScheme;
+ } else {
+ const hue = hash(seed, 360);
+ // Saturation 100 would give the most differentiation between colors, but
+ // it's garish.
+ const saturation = 80;
+
+ // Prefer using HSLuv, not the browser's built-in vanilla HSL handling. This
+ // is because this function chooses hue/lightness uniform at random, but HSL
+ // is not perceptually uniform.
+ // See https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-Beings/.
+ const base =
+ new HSLuvColor({h: hue, s: saturation, l: hash(seed + 'x', 40) + 40});
+ const variant = new HSLuvColor({h: hue, s: saturation, l: 30});
+ const colorScheme = makeColorScheme(base, variant);
+
+ proceduralColorCache.set(seed, colorScheme);
+
+ return colorScheme;
+ }
+}
+
+export function colorForState(state: string): ColorScheme {
if (state === 'Running') {
return DARK_GREEN;
} else if (state.startsWith('Runnable')) {
@@ -113,162 +183,59 @@
return INDIGO;
}
-export function textColorForState(stateCode: string): string {
- const background = colorForState(stateCode);
- return background.l > 80 ? '#404040' : '#fff';
+export function colorForTid(tid: number): ColorScheme {
+ return materialColorScheme(tid.toString());
}
-export function colorForString(identifier: string): Color {
- const colorIdx = hash(identifier, MD_PALETTE.length);
- return Object.assign({}, MD_PALETTE[colorIdx]);
-}
-
-export function colorForTid(tid: number): Color {
- return colorForString(tid.toString());
-}
-
-export function colorForThread(thread?: {pid?: number, tid: number}): Color {
+export function colorForThread(thread?: {pid?: number, tid: number}):
+ ColorScheme {
if (thread === undefined) {
- return Object.assign({}, GRAY_COLOR);
+ return GRAY;
}
const tid = thread.pid ? thread.pid : thread.tid;
return colorForTid(tid);
}
-// 40 different random hues 9 degrees apart.
+export function colorForCpu(cpu: number): Color {
+ if (USE_CONSISTENT_COLORS.get()) {
+ return materialColorScheme(cpu.toString()).base;
+ } else {
+ const hue = (128 + (32 * cpu)) % 256;
+ return new HSLColor({h: hue, s: 50, l: 50});
+ }
+}
+
export function randomColor(): string {
- const hue = Math.floor(Math.random() * 40) * 9;
- return '#' + hsl.hex([hue, 90, 30]);
+ const rand = Math.random();
+ if (USE_CONSISTENT_COLORS.get()) {
+ return materialColorScheme(rand.toString()).base.cssString;
+ } else {
+ // 40 different random hues 9 degrees apart.
+ const hue = Math.floor(rand * 40) * 9;
+ return '#' + hsl.hex([hue, 90, 30]);
+ }
}
-// Chooses a color uniform at random based on hash(sliceName). Returns [hue,
-// saturation, lightness].
-//
-// Prefer converting this to an RGB color using hsluv, not the browser's
-// built-in vanilla HSL handling. This is because this function chooses
-// hue/lightness uniform at random, but HSL is not perceptually uniform. See
-// https://www.boronine.com/2012/03/26/Color-Spaces-for-Human-Beings/.
-//
-// If isSelected, the color will be particularly dark, making it stand out.
-export function hslForSlice(
- sliceName: string, isSelected: boolean|null): [number, number, number] {
- const hue = hash(sliceName, 360);
- // Saturation 100 would give the most differentiation between colors, but it's
- // garish.
- const saturation = 80;
- const lightness = isSelected ? 30 : hash(sliceName + 'x', 40) + 40;
- return [hue, saturation, lightness];
-}
-
-// Lightens the color for thread slices to represent wall time.
-export function colorForThreadIdleSlice(
- hue: number,
- saturation: number,
- lightness: number,
- isSelected: boolean|null): string {
- // Increase lightness by 80% when selected and 40% otherwise,
- // without exceeding 88.
- let newLightness = isSelected ? lightness * 1.8 : lightness * 1.4;
- newLightness = Math.min(newLightness, 88);
- return cachedHsluvToHex(hue, saturation, newLightness);
-}
-
-export function colorCompare(x: Color, y: Color): number {
- return (x.h - y.h) || (x.s - y.s) || (x.l - y.l);
-}
-
-// Return true if two colors have the same value.
-export function colorsEqual(a: Color, b: Color): boolean {
- return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a;
-}
-
-export function getColorForSlice(
- sliceName: string, hasFocus: boolean|null): Color {
+export function getColorForSlice(sliceName: string): ColorScheme {
const name = sliceName.replace(/( )?\d+/g, '');
- const [hue, saturation, lightness] = hslForSlice(name, hasFocus);
-
- return {
- h: hue,
- s: saturation,
- l: lightness,
- };
+ if (USE_CONSISTENT_COLORS.get()) {
+ return materialColorScheme(name);
+ } else {
+ return proceduralColorScheme(name);
+ }
}
-const LIGHTNESS_MAX = 100;
-const LIGHTNESS_MIN = 0;
-
-// Lighten color by a percentage.
-export function colorLighten(color: Color, amount: number): Color {
- return {
- ...color,
- l: clamp(color.l + amount, LIGHTNESS_MIN, LIGHTNESS_MAX),
- };
+export function colorForFtrace(name: string): ColorScheme {
+ return materialColorScheme(name);
}
-// Darken color by a percentage.
-export function colorDarken(color: Color, amount: number): Color {
- return colorLighten(color, -amount);
-}
-
-const SATURATION_MAX = 100;
-const SATURATION_MIN = 0;
-
-// Saturate color by a percentage.
-export function colorSaturate(color: Color, amount: number): Color {
- return {
- ...color,
- s: clamp(color.s + amount, SATURATION_MIN, SATURATION_MAX),
- };
-}
-
-// Desaturate color by a percentage.
-export function colorDesaturate(color: Color, amount: number): Color {
- return colorSaturate(color, -amount);
-}
-
-// Convert color to RGB values in the range 0-255
-export function colorToRGB(color: Color): [number, number, number] {
- const h = color.h;
- const s = color.s / SATURATION_MAX;
- const l = color.l / LIGHTNESS_MAX;
-
- const c = (1 - Math.abs(2 * l - 1)) * s;
- const x = c * (1 - Math.abs((h / 60) % 2 - 1));
- const m = l - c / 2;
-
- let [r, g, b] = [0, 0, 0];
-
- if (0 <= h && h < 60) {
- [r, g, b] = [c, x, 0];
- } else if (60 <= h && h < 120) {
- [r, g, b] = [x, c, 0];
- } else if (120 <= h && h < 180) {
- [r, g, b] = [0, c, x];
- } else if (180 <= h && h < 240) {
- [r, g, b] = [0, x, c];
- } else if (240 <= h && h < 300) {
- [r, g, b] = [x, 0, c];
- } else if (300 <= h && h < 360) {
- [r, g, b] = [c, 0, x];
+export function colorForSample(callsiteId: number, isHovered: boolean): string {
+ let colorScheme;
+ if (USE_CONSISTENT_COLORS.get()) {
+ colorScheme = materialColorScheme(String(callsiteId));
+ } else {
+ colorScheme = proceduralColorScheme(String(callsiteId));
}
- // Convert to 0-255 range
- r = Math.round((r + m) * 255);
- g = Math.round((g + m) * 255);
- b = Math.round((b + m) * 255);
-
- return [r, g, b];
-}
-
-// Get whether a color should be considered "light" based on its perceived
-// brightness.
-export function colorIsLight(color: Color): boolean {
- // YIQ calculation from https://24ways.org/2010/calculating-color-contrast
- const [r, g, b] = hsluvToRgb([color.h, color.s, color.l]);
- const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
- return (yiq >= 128);
-}
-
-export function colorIsDark(color: Color): boolean {
- return !colorIsLight(color);
+ return isHovered ? colorScheme.variant.cssString : colorScheme.base.cssString;
}
diff --git a/ui/src/common/colorizer_unittest.ts b/ui/src/common/colorizer_unittest.ts
index bfd0eb1..ffc931e 100644
--- a/ui/src/common/colorizer_unittest.ts
+++ b/ui/src/common/colorizer_unittest.ts
@@ -13,15 +13,8 @@
// limitations under the License.
import {
- Color,
- colorCompare,
+ colorForCpu,
colorForThread,
- colorIsLight,
- colorLighten,
- colorSaturate,
- colorsEqual,
- colorToRGB,
- hueForCpu,
} from './colorizer';
const PROCESS_A_THREAD_A = {
@@ -61,66 +54,12 @@
expect(colorUnkA).toEqual(colorUnkB);
});
-test('it copies colors', () => {
+test('it doesn\'t copy colors', () => {
const a = colorForThread(PROCESS_A_THREAD_A);
const b = colorForThread(PROCESS_A_THREAD_A);
- expect(a === b).toEqual(false);
+ expect(a).toBe(b);
});
test('it gives different cpus different hues', () => {
- expect(hueForCpu(0)).not.toEqual(hueForCpu(1));
-});
-
-test('colorCompare', () => {
- const col: Color = {h: 123, s: 66, l: 45};
-
- expect(colorCompare({...col}, col)).toBe(0);
-
- expect(colorCompare({...col, h: 34}, col)).toBeLessThan(0);
- expect(colorCompare({...col, h: 156}, col)).toBeGreaterThan(0);
-
- expect(colorCompare({...col, s: 22}, col)).toBeLessThan(0);
- expect(colorCompare({...col, s: 100}, col)).toBeGreaterThan(0);
-
- expect(colorCompare({...col, l: 22}, col)).toBeLessThan(0);
- expect(colorCompare({...col, l: 76}, col)).toBeGreaterThan(0);
-});
-
-test('colorsEqual', () => {
- const col: Color = {h: 123, s: 66, l: 45};
- expect(colorsEqual(col, {h: 123, s: 66, l: 45})).toBeTruthy();
- expect(colorsEqual(col, {h: 86, s: 66, l: 45})).toBeFalsy();
- expect(colorsEqual(col, {h: 123, s: 43, l: 45})).toBeFalsy();
- expect(colorsEqual(col, {h: 123, s: 43, l: 78})).toBeFalsy();
-});
-
-test('colorLighten', () => {
- const col: Color = {h: 123, s: 66, l: 45};
- expect(colorLighten(col, 20)).toEqual({...col, l: 65});
- expect(colorLighten(col, 100)).toEqual({...col, l: 100});
- expect(colorLighten(col, -100)).toEqual({...col, l: 0});
-});
-
-test('colorSaturate', () => {
- const col: Color = {h: 123, s: 66, l: 45};
- expect(colorSaturate(col, 20)).toEqual({...col, s: 86});
- expect(colorSaturate(col, 100)).toEqual({...col, s: 100});
- expect(colorSaturate(col, -100)).toEqual({...col, s: 0});
-});
-
-test('colorToRGB', () => {
- // Pick a few well-known conversions to check we're in the right ballpark.
- expect(colorToRGB({h: 0, s: 0, l: 0})).toEqual([0, 0, 0]);
- expect(colorToRGB({h: 0, s: 100, l: 50})).toEqual([255, 0, 0]);
- expect(colorToRGB({h: 120, s: 100, l: 50})).toEqual([0, 255, 0]);
- expect(colorToRGB({h: 240, s: 100, l: 50})).toEqual([0, 0, 255]);
-});
-
-test('lightness calculations', () => {
- // Pick a few obvious light/dark colours to check we're in the right ballpark.
- expect(colorIsLight({h: 0, s: 0, l: 0})).toBeFalsy();
- expect(colorIsLight({h: 0, s: 0, l: 100})).toBeTruthy();
-
- expect(colorIsLight({h: 0, s: 0, l: 49})).toBeFalsy();
- expect(colorIsLight({h: 0, s: 0, l: 51})).toBeTruthy();
+ expect(colorForCpu(0)).not.toEqual(colorForCpu(1));
});
diff --git a/ui/src/common/track_adapter.ts b/ui/src/common/track_adapter.ts
index 8dbb089..9afa02b 100644
--- a/ui/src/common/track_adapter.ts
+++ b/ui/src/common/track_adapter.ts
@@ -16,12 +16,12 @@
import {v4 as uuidv4} from 'uuid';
import {assertExists} from '../base/logging';
-import {duration, Span, time} from '../base/time';
-import {PxSpan, TimeScale} from '../frontend/time_scale';
-import {NewTrackArgs, SliceRect} from '../frontend/track';
+import {duration, time} from '../base/time';
+import {NewTrackArgs} from '../frontend/track';
+import {SliceRect} from '../public';
import {EngineProxy} from '../trace_processor/engine';
-import {BasicAsyncTrack} from './basic_async_track';
+import {TrackHelperLEGACY} from './track_helper';
export {Store} from '../frontend/store';
export {EngineProxy} from '../trace_processor/engine';
@@ -37,7 +37,7 @@
// This is an adapter to convert old style controller based tracks to new style
// tracks.
export class TrackWithControllerAdapter<Config, Data> extends
- BasicAsyncTrack<Data> {
+ TrackHelperLEGACY<Data> {
private track: TrackAdapter<Config, Data>;
private controller: TrackControllerAdapter<Config, Data>;
private isSetup = false;
@@ -63,12 +63,8 @@
super.onDestroy();
}
- getSliceRect(
- visibleTimeScale: TimeScale, visibleWindow: Span<time, bigint>,
- windowSpan: PxSpan, tStart: time, tEnd: time, depth: number): SliceRect
- |undefined {
- return this.track.getSliceRect(
- visibleTimeScale, visibleWindow, windowSpan, tStart, tEnd, depth);
+ getSliceRect(tStart: time, tEnd: time, depth: number): SliceRect|undefined {
+ return this.track.getSliceRect(tStart, tEnd, depth);
}
getHeight(): number {
@@ -139,10 +135,8 @@
abstract renderCanvas(ctx: CanvasRenderingContext2D): void;
- getSliceRect(
- _visibleTimeScale: TimeScale, _visibleWindow: Span<time, bigint>,
- _windowSpan: PxSpan, _tStart: time, _tEnd: time,
- _depth: number): SliceRect|undefined {
+ getSliceRect(_tStart: time, _tEnd: time, _depth: number): SliceRect
+ |undefined {
return undefined;
}
diff --git a/ui/src/common/basic_async_track.ts b/ui/src/common/track_helper.ts
similarity index 76%
rename from ui/src/common/basic_async_track.ts
rename to ui/src/common/track_helper.ts
index 41dfe3d..f634627 100644
--- a/ui/src/common/basic_async_track.ts
+++ b/ui/src/common/track_helper.ts
@@ -14,12 +14,10 @@
import m from 'mithril';
-import {duration, Span, Time, time} from '../base/time';
+import {duration, Time, time} from '../base/time';
import {raf} from '../core/raf_scheduler';
import {globals} from '../frontend/globals';
-import {PxSpan, TimeScale} from '../frontend/time_scale';
-import {SliceRect} from '../frontend/track';
-import {Track, TrackContext} from '../public';
+import {SliceRect, Track, TrackContext} from '../public';
import {TrackData} from './track_data';
@@ -34,12 +32,22 @@
STR_NULL,
} from '../trace_processor/query_result';
-// This shim track provides the base for async style tracks implementing the new
-// plugin track interface.
-// This provides the logic to perform data reloads at appropriate times as the
-// window is panned and zoomed about.
-// The extending class need only define renderCanvas() and onBoundsChange().
-export abstract class BasicAsyncTrack<Data> implements Track {
+// A helper class which provides a base track implementation for tracks which
+// load their content asynchronously from the trace.
+//
+// Tracks extending this base class need only define |renderCanvas()| and
+// |onBoundsChange()|. This helper provides sensible default implementations for
+// all the |Track| interface methods which subclasses may also choose to
+// override if necessary.
+//
+// This helper provides the logic to call |onBoundsChange()| only when more data
+// is needed as the visible window is panned and zoomed about, and includes an
+// FSM to ensure onBoundsChange is not re-entered, and that the track doesn't
+// render stale data.
+//
+// Note: This class is deprecated and should not be used for new tracks. Use
+// |BaseSliceTrack| instead.
+export abstract class TrackHelperLEGACY<Data> implements Track {
private requestingData = false;
private queuedRequest = false;
private currentState?: TrackData;
@@ -57,10 +65,8 @@
// only for track types that support slices e.g. chrome_slice, async_slices
// tStart - slice start time in seconds, tEnd - slice end time in seconds,
// depth - slice depth
- getSliceRect(
- _visibleTimeScale: TimeScale, _visibleWindow: Span<time, duration>,
- _windowSpan: PxSpan, _tStart: time, _tEnd: time,
- _depth: number): SliceRect|undefined {
+ getSliceRect(_tStart: time, _tEnd: time, _depth: number): SliceRect
+ |undefined {
return undefined;
}
diff --git a/ui/src/controller/aggregation/frame_aggregation_controller.ts b/ui/src/controller/aggregation/frame_aggregation_controller.ts
index e51bde9..c45a40c 100644
--- a/ui/src/controller/aggregation/frame_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/frame_aggregation_controller.ts
@@ -17,7 +17,7 @@
import {Area, Sorting} from '../../common/state';
import {globals} from '../../frontend/globals';
import {Engine} from '../../trace_processor/engine';
-import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../../tracks/actual_frames';
+import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../../tracks/frames';
import {AggregationController} from './aggregation_controller';
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index 4de7003..7543110 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -17,7 +17,9 @@
import {Area, Sorting} from '../../common/state';
import {globals} from '../../frontend/globals';
import {Engine} from '../../trace_processor/engine';
-import {ASYNC_SLICE_TRACK_KIND} from '../../tracks/async_slices';
+import {
+ ASYNC_SLICE_TRACK_KIND,
+} from '../../tracks/async_slices/async_slice_track';
import {SLICE_TRACK_KIND} from '../../tracks/chrome_slices';
import {AggregationController} from './aggregation_controller';
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index 89d870e..a7ad34c 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -21,8 +21,8 @@
import {asSliceSqlId} from '../frontend/sql_types';
import {Engine} from '../trace_processor/engine';
import {LONG, NUM, STR_NULL} from '../trace_processor/query_result';
-import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../tracks/actual_frames';
import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
+import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../tracks/frames';
import {Controller} from './controller';
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index b75ad6f..232f446 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -309,14 +309,12 @@
// UI track id for slice tracks this would be unnecessary.
let trackKey = '';
for (const track of Object.values(globals.state.tracks)) {
- if (track.uri) {
- const trackInfo = pluginManager.resolveTrackInfo(track.uri);
- if (trackInfo?.kind === SLICE_TRACK_KIND) {
- const trackIds = trackInfo?.trackIds;
- if (trackIds && trackIds.length > 0 && trackIds[0] === trackId) {
- trackKey = track.key;
- break;
- }
+ const trackInfo = pluginManager.resolveTrackInfo(track.uri);
+ if (trackInfo?.kind === SLICE_TRACK_KIND) {
+ const trackIds = trackInfo?.trackIds;
+ if (trackIds && trackIds.length > 0 && trackIds[0] === trackId) {
+ trackKey = track.key;
+ break;
}
}
}
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index c1ce372..4f80835 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -780,39 +780,43 @@
private async loadTimelineOverview(trace: Span<time, duration>) {
clearOverviewData();
-
const engine = assertExists<Engine>(this.engine);
const stepSize = Duration.max(1n, trace.duration / 100n);
- let hasSchedOverview = false;
- for (let start = trace.start; start < trace.end;
- start = Time.add(start, stepSize)) {
- const progress = start - trace.start;
- const ratio = Number(progress) / Number(trace.duration);
- this.updateStatus(
- 'Loading overview ' +
- `${Math.round(ratio * 100)}%`);
- const end = Time.add(start, stepSize);
-
- // Sched overview.
- const schedResult = await engine.query(
- `select cast(sum(dur) as float)/${
- stepSize} as load, cpu from sched ` +
- `where ts >= ${start} and ts < ${end} and utid != 0 ` +
- 'group by cpu order by cpu');
- const schedData: {[key: string]: QuantizedLoad} = {};
- const it = schedResult.iter({load: NUM, cpu: NUM});
- for (; it.valid(); it.next()) {
- const load = it.load;
- const cpu = it.cpu;
- schedData[cpu] = {start, end, load};
- hasSchedOverview = true;
- }
- publishOverviewData(schedData);
- }
-
+ const hasSchedSql = 'select ts from sched limit 1';
+ const hasSchedOverview = (await engine.query(hasSchedSql)).numRows() > 0;
if (hasSchedOverview) {
+ const stepPromises = [];
+ for (let start = trace.start; start < trace.end;
+ start = Time.add(start, stepSize)) {
+ const progress = start - trace.start;
+ const ratio = Number(progress) / Number(trace.duration);
+ this.updateStatus(
+ 'Loading overview ' +
+ `${Math.round(ratio * 100)}%`);
+ const end = Time.add(start, stepSize);
+ // The (async() => {})() queues all the 100 async promises in one batch.
+ // Without that, we would wait for each step to be rendered before
+ // kicking off the next one. That would interleave an animation frame
+ // between each step, slowing down significantly the overall process.
+ stepPromises.push((async () => {
+ const schedResult = await engine.query(
+ `select cast(sum(dur) as float)/${
+ stepSize} as load, cpu from sched ` +
+ `where ts >= ${start} and ts < ${end} and utid != 0 ` +
+ 'group by cpu order by cpu');
+ const schedData: {[key: string]: QuantizedLoad} = {};
+ const it = schedResult.iter({load: NUM, cpu: NUM});
+ for (; it.valid(); it.next()) {
+ const load = it.load;
+ const cpu = it.cpu;
+ schedData[cpu] = {start, end, load};
+ }
+ publishOverviewData(schedData);
+ })());
+ } // for(start = ...)
+ await Promise.all(stepPromises);
return;
- }
+ } // if (hasSchedOverview)
// Slices overview.
const sliceResult = await engine.query(`select
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index c123e78..1df6bee 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -38,8 +38,7 @@
STR,
STR_NULL,
} from '../trace_processor/query_result';
-import {ACTUAL_FRAMES_SLICE_TRACK_KIND} from '../tracks/actual_frames';
-import {ASYNC_SLICE_TRACK_KIND} from '../tracks/async_slices';
+import {ASYNC_SLICE_TRACK_KIND} from '../tracks/async_slices/async_slice_track';
import {
ENABLE_SCROLL_JANK_PLUGIN_V2,
getScrollJankTracks,
@@ -49,7 +48,10 @@
} from '../tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track';
import {SLICE_TRACK_KIND} from '../tracks/chrome_slices';
import {COUNTER_TRACK_KIND} from '../tracks/counter';
-import {EXPECTED_FRAMES_SLICE_TRACK_KIND} from '../tracks/expected_frames';
+import {
+ ACTUAL_FRAMES_SLICE_TRACK_KIND,
+ EXPECTED_FRAMES_SLICE_TRACK_KIND,
+} from '../tracks/frames';
import {NULL_TRACK_URI} from '../tracks/null_track';
import {
decideTracks as screenshotDecideTracks,
@@ -60,7 +62,7 @@
id: 'tracksV2.1',
name: 'Tracks V2',
description: 'Show tracks built on top of the Track V2 API.',
- defaultValue: false,
+ defaultValue: true,
});
const TRACKS_V2_COMPARE_FLAG = featureFlags.register({
@@ -288,14 +290,25 @@
}
}
- const track: AddTrackArgs = {
- uri: `perfetto.AsyncSlices#${rawName}`,
- trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
- trackGroup,
- name,
- };
+ if (showV1()) {
+ const track: AddTrackArgs = {
+ uri: `perfetto.AsyncSlices#${rawName}`,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ trackGroup,
+ name,
+ };
+ this.tracksToAdd.push(track);
+ }
- this.tracksToAdd.push(track);
+ if (showV2()) {
+ const track: AddTrackArgs = {
+ uri: `perfetto.AsyncSlices#${rawName}.v2`,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ trackGroup,
+ name,
+ };
+ this.tracksToAdd.push(track);
+ }
}
}
@@ -849,12 +862,13 @@
}
const priority = InThreadTrackSortKey.THREAD_SCHEDULING_STATE_TRACK;
+ const name =
+ getTrackName({utid, tid, threadName, kind: THREAD_STATE_TRACK_KIND});
if (showV1()) {
- const kind = THREAD_STATE_TRACK_KIND;
this.tracksToAdd.push({
uri: `perfetto.ThreadState#${upid}.${utid}`,
- name: getTrackName({utid, tid, threadName, kind}),
+ name,
trackGroup: uuid,
trackSortKey: {
utid,
@@ -866,8 +880,7 @@
if (showV2()) {
this.tracksToAdd.push({
uri: `perfetto.ThreadState#${utid}.v2`,
- name:
- getTrackName({utid, tid, threadName, kind: 'ThreadStateTrackV2'}),
+ name,
trackGroup: uuid,
trackSortKey: {
utid,
@@ -1022,12 +1035,24 @@
processName,
kind: ASYNC_SLICE_TRACK_KIND,
});
- this.tracksToAdd.push({
- uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}`,
- name,
- trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
- trackGroup: uuid,
- });
+
+ if (showV1()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
+
+ if (showV2()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}.v2`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
}
}
@@ -1078,12 +1103,24 @@
const kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
const name =
getTrackName({name: trackName, upid, pid, processName, kind});
- this.tracksToAdd.push({
- uri: `perfetto.ActualFrames#${upid}`,
- name,
- trackSortKey: PrimaryTrackSortKey.ACTUAL_FRAMES_SLICE_TRACK,
- trackGroup: uuid,
- });
+
+ if (showV1()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.ActualFrames#${upid}`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.ACTUAL_FRAMES_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
+
+ if (showV2()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.ActualFrames#${upid}.v2`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.ACTUAL_FRAMES_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
}
}
@@ -1135,12 +1172,24 @@
const kind = EXPECTED_FRAMES_SLICE_TRACK_KIND;
const name =
getTrackName({name: trackName, upid, pid, processName, kind});
- this.tracksToAdd.push({
- uri: `perfetto.ExpectedFrames#${upid}`,
- name,
- trackSortKey: PrimaryTrackSortKey.EXPECTED_FRAMES_SLICE_TRACK,
- trackGroup: uuid,
- });
+
+ if (showV1()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.ExpectedFrames#${upid}`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.EXPECTED_FRAMES_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
+
+ if (showV2()) {
+ this.tracksToAdd.push({
+ uri: `perfetto.ExpectedFrames#${upid}.v2`,
+ name,
+ trackSortKey: PrimaryTrackSortKey.EXPECTED_FRAMES_SLICE_TRACK,
+ trackGroup: uuid,
+ });
+ }
}
}
diff --git a/ui/src/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts
index ea90a3f..fed608c 100644
--- a/ui/src/frontend/aggregation_panel.ts
+++ b/ui/src/frontend/aggregation_panel.ts
@@ -20,7 +20,7 @@
Column,
ThreadStateExtra,
} from '../common/aggregation_data';
-import {colorForState, textColorForState} from '../common/colorizer';
+import {colorForState} from '../common/colorizer';
import {translateState} from '../common/thread_state';
import {globals} from './globals';
@@ -122,15 +122,14 @@
if (data === undefined) return undefined;
const states = [];
for (let i = 0; i < data.states.length; i++) {
- const color = colorForState(data.states[i]);
- const textColor = textColorForState(data.states[i]);
+ const colorScheme = colorForState(data.states[i]);
const width = data.values[i] / data.totalMs * 100;
states.push(
m('.state',
{
style: {
- background: `hsl(${color.h},${color.s}%,${color.l}%)`,
- color: `${textColor}`,
+ background: colorScheme.base.cssString,
+ color: colorScheme.textBase.cssString,
width: `${width}%`,
},
},
diff --git a/ui/src/frontend/analytics.ts b/ui/src/frontend/analytics.ts
index 8b01523..74c42fc 100644
--- a/ui/src/frontend/analytics.ts
+++ b/ui/src/frontend/analytics.ts
@@ -16,11 +16,25 @@
import {VERSION} from '../gen/perfetto_version';
import {globals} from './globals';
+import {Router} from './router';
type TraceCategories = 'Trace Actions'|'Record Trace'|'User Actions';
const ANALYTICS_ID = 'G-BD89KT2P3C';
const PAGE_TITLE = 'no-page-title';
+// Get the referrer from either:
+// - If present: the referrer argument if present
+// - document.referrer
+function getReferrer(): string {
+ const route = Router.parseUrl(window.location.href);
+ const referrer = route.args.referrer;
+ if (referrer) {
+ return referrer;
+ } else {
+ return document.referrer.split('?')[0];
+ }
+}
+
export function initAnalytics() {
// Only initialize logging on the official site and on localhost (to catch
// analytics bugs when testing locally).
@@ -95,14 +109,14 @@
console.log(
`GA initialized. route=${route}`,
`isInternalUser=${globals.isInternalUser}`);
- // GA's reccomendation for SPAs is to disable automatic page views and
+ // GA's recommendation for SPAs is to disable automatic page views and
// manually send page_view events. See:
// https://developers.google.com/analytics/devguides/collection/gtagjs/pages#manual_pageviews
gtagGlobals.gtag('config', ANALYTICS_ID, {
allow_google_signals: false,
anonymize_ip: true,
page_location: route,
- referrer: document.referrer.split('?')[0],
+ page_referrer: getReferrer(),
send_page_view: false,
page_title: PAGE_TITLE,
perfetto_is_internal_user: globals.isInternalUser ? '1' : '0',
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index e4a6bd7..ae7145d 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -61,7 +61,7 @@
yBoundaries: 'strict'|'human_readable';
}
-export abstract class BaseCounterTrack<Config = {}> extends TrackBase<Config> {
+export abstract class BaseCounterTrack extends TrackBase {
protected readonly tableName: string;
// This is the over-skirted cached bounds:
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index cd3ccc3..2c873da 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -17,7 +17,6 @@
import {clamp, floatEqual} from '../base/math_utils';
import {
duration,
- Span,
Time,
time,
} from '../base/time';
@@ -27,27 +26,19 @@
drawIncompleteSlice,
drawTrackHoverTooltip,
} from '../common/canvas_utils';
-import {
- Color,
- colorCompare,
- colorDesaturate,
- colorIsLight,
- colorLighten,
- colorsEqual,
- UNEXPECTED_PINK_COLOR,
-} from '../common/colorizer';
+import {colorCompare} from '../common/color';
+import {UNEXPECTED_PINK} from '../common/colorizer';
import {Selection, SelectionKind} from '../common/state';
+import {featureFlags} from '../core/feature_flags';
import {raf} from '../core/raf_scheduler';
+import {Slice, SliceRect} from '../public';
import {LONG, NUM} from '../trace_processor/query_result';
import {checkerboardExcept} from './checkerboard';
import {globals} from './globals';
-import {cachedHsluvToHex} from './hsluv_cache';
-import {Slice} from './slice';
import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
import {constraintsToQuerySuffix} from './sql_utils';
-import {PxSpan, TimeScale} from './time_scale';
-import {NewTrackArgs, SliceRect, TrackBase} from './track';
+import {NewTrackArgs, TrackBase} from './track';
import {BUCKETS_PER_PIXEL, CacheKey, TrackCache} from './track_cache';
// The common class that underpins all tracks drawing slices.
@@ -59,7 +50,15 @@
const SLICE_MIN_WIDTH_FOR_TEXT_PX = 5;
const SLICE_MIN_WIDTH_PX = 1 / BUCKETS_PER_PIXEL;
const CHEVRON_WIDTH_PX = 10;
-const DEFAULT_SLICE_COLOR = UNEXPECTED_PINK_COLOR;
+const DEFAULT_SLICE_COLOR = UNEXPECTED_PINK;
+const INCOMPLETE_SLICE_WIDTH_PX = 20;
+
+export const CROP_INCOMPLETE_SLICE_FLAG = featureFlags.register({
+ id: 'cropIncompleteSlice',
+ name: 'Crop incomplete Slice',
+ description: 'Display incomplete slice in short form',
+ defaultValue: false,
+});
// Exposed and standalone to allow for testing without making this
// visible to subclasses.
@@ -150,7 +149,7 @@
// slices at depth 0..N.
// If you need temporally overlapping slices, look at AsyncSliceTrack, which
// merges several tracks into one visual track.
-export const BASE_SLICE_ROW = {
+export const BASE_ROW = {
id: NUM, // The slice ID, for selection / lookups.
ts: LONG, // Start time in nanoseconds.
dur: LONG, // Duration in nanoseconds. -1 = incomplete, 0 = instant.
@@ -161,7 +160,7 @@
tsqEnd: LONG, // Quantized |ts+dur|. The end bucket.
};
-export type BaseSliceRow = typeof BASE_SLICE_ROW;
+export type BaseRow = typeof BASE_ROW;
// These properties change @ 60FPS and shouldn't be touched by the subclass.
// since the Impl doesn't see every frame attempting to reason on them in a
@@ -182,13 +181,11 @@
// Derived classes can extend this interface to override these types if needed.
export interface BaseSliceTrackTypes {
slice: Slice;
- row: BaseSliceRow;
- config: {};
+ row: BaseRow;
}
-export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes =
- BaseSliceTrackTypes> extends
- TrackBase<T['config']> {
+export abstract class BaseSliceTrack<
+ T extends BaseSliceTrackTypes = BaseSliceTrackTypes> extends TrackBase {
protected sliceLayout: SliceLayout = {...DEFAULT_SLICE_LAYOUT};
// This is the over-skirted cached bounds:
@@ -260,7 +257,7 @@
abstract getSqlSource(): string;
getRowSpec(): T['row'] {
- return BASE_SLICE_ROW;
+ return BASE_ROW;
}
onSliceOver(_args: OnSliceOverArgs<T['slice']>): void {}
onSliceOut(_args: OnSliceOutArgs<T['slice']>): void {}
@@ -287,7 +284,7 @@
// This is the union of the embedder-defined columns and the base columns
// we know about (ts, dur, ...).
const allCols = Object.keys(this.getRowSpec());
- const baseCols = Object.keys(BASE_SLICE_ROW);
+ const baseCols = Object.keys(BASE_ROW);
this.extraSqlColumns = allCols.filter((key) => !baseCols.includes(key));
}
@@ -401,8 +398,16 @@
slice.x -= CHEVRON_WIDTH_PX / 2;
slice.w = CHEVRON_WIDTH_PX;
} else if (slice.flags & SLICE_FLAGS_INCOMPLETE) {
- slice.x = Math.max(slice.x, 0);
- slice.w = pxEnd - slice.x;
+ let widthPx;
+ if (CROP_INCOMPLETE_SLICE_FLAG.get()) {
+ widthPx = slice.x > 0 ? Math.min(pxEnd, INCOMPLETE_SLICE_WIDTH_PX) :
+ Math.max(0, INCOMPLETE_SLICE_WIDTH_PX + slice.x);
+ slice.x = Math.max(slice.x, 0);
+ } else {
+ slice.x = Math.max(slice.x, 0);
+ widthPx = pxEnd - slice.x;
+ }
+ slice.w = widthPx;
} else {
// If the slice is an actual slice, intersect the slice geometry with
// the visible viewport (this affects only the first and last slice).
@@ -425,20 +430,24 @@
// Second pass: fill slices by color.
const vizSlicesByColor = vizSlices.slice();
- vizSlicesByColor.sort((a, b) => colorCompare(a.color, b.color));
+ vizSlicesByColor.sort(
+ (a, b) => colorCompare(a.colorScheme.base, b.colorScheme.base));
let lastColor = undefined;
for (const slice of vizSlicesByColor) {
- if (slice.color !== lastColor) {
- lastColor = slice.color;
- const {h, s, l} = slice.color;
- ctx.fillStyle = cachedHsluvToHex(h, s, l);
+ const color = slice.isHighlighted ? slice.colorScheme.variant.cssString :
+ slice.colorScheme.base.cssString;
+ if (color !== lastColor) {
+ lastColor = color;
+ ctx.fillStyle = color;
}
const y = padding + slice.depth * (sliceHeight + rowSpacing);
if (slice.flags & SLICE_FLAGS_INSTANT) {
this.drawChevron(ctx, slice.x, y, sliceHeight);
} else if (slice.flags & SLICE_FLAGS_INCOMPLETE) {
- const w = Math.max(slice.w - 2, 2);
- drawIncompleteSlice(ctx, slice.x, y, w, sliceHeight);
+ const w = CROP_INCOMPLETE_SLICE_FLAG.get() ? slice.w :
+ Math.max(slice.w - 2, 2);
+ drawIncompleteSlice(
+ ctx, slice.x, y, w, sliceHeight, !CROP_INCOMPLETE_SLICE_FLAG.get());
} else {
const w = Math.max(slice.w, SLICE_MIN_WIDTH_PX);
ctx.fillRect(slice.x, y, w, sliceHeight);
@@ -446,7 +455,7 @@
}
// Pass 2.5: Draw fillRatio light section.
- let prevColor: Color|undefined;
+ ctx.fillStyle = `#FFFFFF50`;
for (const slice of vizSlicesByColor) {
// Can't draw fill ratio on incomplete or instant slices.
if (slice.flags & (SLICE_FLAGS_INCOMPLETE | SLICE_FLAGS_INSTANT)) {
@@ -470,17 +479,6 @@
continue;
}
- // Lighten and desaturate the slice color
- const color = getFillRatioLightColor(slice.color);
-
- // Set color if not set previously
- // Slices are sorted by color and light tint is a pure function of slice
- // color so we should be able to re-use colors quite frequently
- if (prevColor === undefined || !colorsEqual(color, prevColor)) {
- ctx.fillStyle = cachedHsluvToHex(color.h, color.s, color.l);
- prevColor = color;
- }
-
const y = padding + slice.depth * (sliceHeight + rowSpacing);
const x = slice.x + (sliceDrawWidth - lightSectionDrawWidth);
ctx.fillRect(x, y, lightSectionDrawWidth, sliceHeight);
@@ -497,7 +495,9 @@
}
// Change the title color dynamically depending on contrast.
- ctx.fillStyle = colorIsLight(slice.color) ? 'black' : 'white';
+ const textColor = slice.isHighlighted ? slice.colorScheme.textVariant :
+ slice.colorScheme.textBase;
+ ctx.fillStyle = textColor.cssString;
const title = cropText(slice.title, charWidth, slice.w);
const rectXCenter = slice.x + slice.w / 2;
const y = padding + slice.depth * (sliceHeight + rowSpacing);
@@ -529,9 +529,9 @@
// Draw a thicker border around the selected slice (or chevron).
const slice = discoveredSelection;
- const color = slice.color;
+ const color = slice.colorScheme;
const y = padding + slice.depth * (sliceHeight + rowSpacing);
- ctx.strokeStyle = cachedHsluvToHex(color.h, 100, 10);
+ ctx.strokeStyle = color.base.setHSL({s: 100, l: 10}).cssString;
ctx.beginPath();
const THICKNESS = 3;
ctx.lineWidth = THICKNESS;
@@ -788,8 +788,8 @@
// The derived class doesn't need to initialize these. They are
// rewritten on every renderCanvas() call. We just need to initialize
// them to something.
- baseColor: DEFAULT_SLICE_COLOR,
- color: DEFAULT_SLICE_COLOR,
+ colorScheme: DEFAULT_SLICE_COLOR,
+ isHighlighted: false,
};
}
@@ -815,7 +815,15 @@
}
for (const slice of this.incomplete) {
- if (slice.depth === depth && slice.x <= x) {
+ const visibleTimeScale = globals.frontendLocalState.visibleTimeScale;
+ const startPx = CROP_INCOMPLETE_SLICE_FLAG.get() ?
+ visibleTimeScale.timeToPx(slice.startNsQ) :
+ slice.x;
+ const cropUnfinishedSlicesCondition = CROP_INCOMPLETE_SLICE_FLAG.get() ?
+ startPx + INCOMPLETE_SLICE_WIDTH_PX >= x : true;
+
+ if (slice.depth === depth && startPx <= x &&
+ cropUnfinishedSlicesCondition) {
return slice;
}
}
@@ -948,15 +956,7 @@
for (const slice of slices) {
const isHovering = globals.state.highlightedSliceId === slice.id ||
(this.hoveredSlice && this.hoveredSlice.title === slice.title);
- if (isHovering) {
- slice.color = {
- h: slice.baseColor.h,
- s: slice.baseColor.s,
- l: 30,
- };
- } else {
- slice.color = slice.baseColor;
- }
+ slice.isHighlighted = !!isHovering;
}
}
@@ -965,17 +965,20 @@
return this.computedTrackHeight;
}
- getSliceRect(
- visibleTimeScale: TimeScale, visibleWindow: Span<time, duration>,
- windowSpan: PxSpan, tStart: time, tEnd: time, depth: number): SliceRect
- |undefined {
+ getSliceRect(tStart: time, tEnd: time, depth: number): SliceRect|undefined {
this.updateSliceAndTrackHeight();
+ const {
+ windowSpan,
+ visibleTimeScale,
+ visibleTimeSpan,
+ } = globals.frontendLocalState;
+
const pxEnd = windowSpan.end;
const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
const right = Math.min(visibleTimeScale.timeToPx(tEnd), pxEnd);
- const visible = visibleWindow.intersects(tStart, tEnd);
+ const visible = visibleTimeSpan.intersects(tStart, tEnd);
const totalSliceHeight = this.computedRowSpacing + this.computedSliceHeight;
@@ -1009,7 +1012,3 @@
// Input args (BaseSliceTrack -> Impl):
slice: S; // The slice which is clicked.
}
-
-function getFillRatioLightColor(color: Color): Color {
- return colorLighten(colorDesaturate(color, 15), 15);
-}
diff --git a/ui/src/frontend/base_slice_track_unittest.ts b/ui/src/frontend/base_slice_track_unittest.ts
index b179d7e..73fca19 100644
--- a/ui/src/frontend/base_slice_track_unittest.ts
+++ b/ui/src/frontend/base_slice_track_unittest.ts
@@ -13,12 +13,12 @@
// limitations under the License.
import {Time} from '../base/time';
-import {GRAY_COLOR} from '../common/colorizer';
+import {UNEXPECTED_PINK} from '../common/colorizer';
+import {Slice} from '../public';
import {
filterVisibleSlicesForTesting as filterVisibleSlices,
} from './base_slice_track';
-import {Slice} from './slice';
function slice(start: number, duration: number): Slice {
const startNsQ = Time.fromRaw(BigInt(start));
@@ -35,9 +35,9 @@
flags: 0,
title: '',
subTitle: '',
- baseColor: GRAY_COLOR,
- color: GRAY_COLOR,
+ colorScheme: UNEXPECTED_PINK,
fillRatio: 1,
+ isHighlighted: false,
};
}
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index 7f83b36..5311cb2 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -35,7 +35,7 @@
NewBottomTabArgs,
} from './bottom_tab';
import {FlowPoint, globals} from './globals';
-import {renderArguments} from './slice_args';
+import {hasArgs, renderArguments} from './slice_args';
import {renderDetails} from './slice_details';
import {getSlice, SliceDetails, SliceRef} from './sql/slice';
import {
@@ -288,7 +288,10 @@
private renderRhs(engine: EngineProxy, slice: SliceDetails): m.Children {
const precFlows = this.renderPrecedingFlows(slice);
const followingFlows = this.renderFollowingFlows(slice);
- const args = renderArguments(engine, slice);
+ const args = hasArgs(slice) &&
+ m(Section,
+ {title: 'Arguments'},
+ m(Tree, renderArguments(engine, slice)));
if (precFlows ?? followingFlows ?? args) {
return m(
GridLayoutColumn,
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 209583c..83c4865 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -15,6 +15,7 @@
import m from 'mithril';
import {Trash} from '../base/disposable';
+import {Gate} from '../base/mithril_utils';
import {Actions} from '../common/actions';
import {isEmptyData} from '../common/aggregation_data';
import {LogExists, LogExistsKey} from '../common/logs';
@@ -410,11 +411,16 @@
}),
currentTabKey: currentTabDetails?.key,
}),
- m('.details-panel-container',
- {
- style: {height: `${this.detailsHeight}px`},
- },
- panel),
+ m(
+ '.details-panel-container',
+ {
+ style: {height: `${this.detailsHeight}px`},
+ },
+ detailsPanels.map((tab) => {
+ const active = tab === currentTabDetails;
+ return m(Gate, {open: active}, tab.vnode);
+ }),
+ ),
];
}
}
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index ed47282..12ce93c 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -15,12 +15,12 @@
import {time} from '../base/time';
import {pluginManager} from '../common/plugins';
import {TrackState} from '../common/state';
+import {SliceRect} from '../public';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {ALL_CATEGORIES, getFlowCategories} from './flow_events_panel';
import {Flow, FlowPoint, globals} from './globals';
import {PanelVNode} from './panel';
-import {SliceRect} from './track';
import {TrackGroupPanel} from './track_group_panel';
import {TrackPanel} from './track_panel';
@@ -81,17 +81,6 @@
for (const trackId of getTrackIds(track)) {
this.trackIdToTrackPanel.set(trackId, {panel: panel.state, yStart});
}
-
- // Register new "plugin track" ids
- const trackState = globals.state.tracks[panel.attrs.trackKey];
- if (trackState.uri) {
- const trackInfo = pluginManager.resolveTrackInfo(trackState.uri);
- if (trackInfo?.trackIds) {
- for (const trackId of trackInfo.trackIds) {
- this.trackIdToTrackPanel.set(trackId, {panel: panel.state, yStart});
- }
- }
- }
} else if (
panel.state instanceof TrackGroupPanel &&
hasTrackGroupId(panel.attrs)) {
@@ -151,19 +140,12 @@
private getSliceRect(args: FlowEventsRendererArgs, point: FlowPoint):
SliceRect|undefined {
- const {visibleTimeScale, visibleTimeSpan, windowSpan} =
- globals.frontendLocalState;
const trackPanel = args.trackIdToTrackPanel.get(point.trackId) ?.panel;
if (!trackPanel) {
return undefined;
}
return trackPanel.getSliceRect(
- visibleTimeScale,
- visibleTimeSpan,
- windowSpan,
- point.sliceStartTs,
- point.sliceEndTs,
- point.depth);
+ point.sliceStartTs, point.sliceEndTs, point.depth);
}
render(ctx: CanvasRenderingContext2D, args: FlowEventsRendererArgs) {
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 86c30db..a649966 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -26,7 +26,6 @@
VisibleState,
} from '../common/state';
import {raf} from '../core/raf_scheduler';
-import {HttpRpcState} from '../trace_processor/http_rpc_engine';
import {globals} from './globals';
import {ratelimit} from './rate_limiters';
@@ -47,20 +46,6 @@
return current;
}
-// Calculate the space a scrollbar takes up so that we can subtract it from
-// the canvas width.
-function calculateScrollbarWidth() {
- const outer = document.createElement('div');
- outer.style.overflowY = 'scroll';
- const inner = document.createElement('div');
- outer.appendChild(inner);
- document.body.appendChild(outer);
- const width =
- outer.getBoundingClientRect().width - inner.getBoundingClientRect().width;
- document.body.removeChild(outer);
- return width;
-}
-
// Immutable object describing a (high precision) time window, providing methods
// for common mutation operations (pan, zoom), and accessors for common
// properties such as spans and durations in several formats.
@@ -155,17 +140,10 @@
private visibleWindow = new TimeWindow();
private _timeScale = this.visibleWindow.createTimeScale(0, 0);
private _windowSpan = PxSpan.ZERO;
- showPanningHint = false;
- showCookieConsent = false;
- scrollToTrackKey?: string|number;
- httpRpcState: HttpRpcState = {connected: false};
- newVersionAvailable = false;
// This is used to calculate the tracks within a Y range for area selection.
areaY: Range = {};
- private scrollBarWidth?: number;
-
private _visibleState: VisibleState = {
lastUpdate: 0,
start: Time.ZERO,
@@ -179,18 +157,6 @@
// and a |timeScale| have a notion of time range. That should live in one
// place only.
- getScrollbarWidth() {
- if (this.scrollBarWidth === undefined) {
- this.scrollBarWidth = calculateScrollbarWidth();
- }
- return this.scrollBarWidth;
- }
-
- setHttpRpcState(httpRpcState: HttpRpcState) {
- this.httpRpcState = httpRpcState;
- raf.scheduleFullRedraw();
- }
-
zoomVisibleWindow(ratio: number, centerPoint: number) {
this.visibleWindow = this.visibleWindow.zoom(ratio, centerPoint);
this._timeScale = this.visibleWindow.createTimeScale(
@@ -231,7 +197,6 @@
assertTrue(
end >= start,
`Impossible select area: start [${start}] >= end [${end}]`);
- this.showPanningHint = true;
this._selectedArea = {start, end, tracks};
raf.scheduleFullRedraw();
}
diff --git a/ui/src/frontend/ftrace_panel.ts b/ui/src/frontend/ftrace_panel.ts
index c313726..a77d31b 100644
--- a/ui/src/frontend/ftrace_panel.ts
+++ b/ui/src/frontend/ftrace_panel.ts
@@ -16,7 +16,7 @@
import {time, Time} from '../base/time';
import {Actions} from '../common/actions';
-import {colorForString} from '../common/colorizer';
+import {colorForFtrace} from '../common/colorizer';
import {StringListPatch} from '../common/state';
import {DetailsShell} from '../widgets/details_shell';
import {
@@ -177,12 +177,7 @@
const rank = i + offset;
- const color = colorForString(name);
- const hsl = `hsl(
- ${color.h},
- ${color.s - 20}%,
- ${Math.min(color.l + 10, 60)}%
- )`;
+ const color = colorForFtrace(name).base.cssString;
rows.push(m(
`.row`,
@@ -192,7 +187,7 @@
onmouseout: this.onRowOut.bind(this),
},
m('.cell', timestamp),
- m('.cell', m('span.colour', {style: {background: hsl}}), name),
+ m('.cell', m('span.colour', {style: {background: color}}), name),
m('.cell', cpu),
m('.cell', process),
m('.cell', args),
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 7323ba5..435bf99 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -47,6 +47,7 @@
import {setPerfHooks} from '../core/perf';
import {raf} from '../core/raf_scheduler';
import {Engine} from '../trace_processor/engine';
+import {HttpRpcState} from '../trace_processor/http_rpc_engine';
import {Analytics, initAnalytics} from './analytics';
import {BottomTabList} from './bottom_tab';
@@ -283,6 +284,11 @@
private _utcOffset = Time.ZERO;
private _openQueryHandler?: OpenQueryHandler;
+ scrollToTrackKey?: string|number;
+ httpRpcState: HttpRpcState = {connected: false};
+ newVersionAvailable = false;
+ showPanningHint = false;
+
// TODO(hjd): Remove once we no longer need to update UUID on redraw.
private _publishRedraw?: () => void = undefined;
diff --git a/ui/src/frontend/hsluv_cache.ts b/ui/src/frontend/hsluv_cache.ts
deleted file mode 100644
index 44cafa6..0000000
--- a/ui/src/frontend/hsluv_cache.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {hsluvToHex} from 'hsluv';
-
-class HsluvCache {
- storage = new Map<number, string>();
-
- get(hue: number, saturation: number, lightness: number): string {
- const key = hue * 1e6 + saturation * 1e3 + lightness;
- const value = this.storage.get(key);
-
- if (value === undefined) {
- const computed = hsluvToHex([hue, saturation, lightness]);
- this.storage.set(key, computed);
- return computed;
- }
-
- return value;
- }
-}
-
-const cache = new HsluvCache();
-
-export function cachedHsluvToHex(
- hue: number, saturation: number, lightness: number): string {
- return cache.get(hue, saturation, lightness);
-}
diff --git a/ui/src/frontend/named_slice_track.ts b/ui/src/frontend/named_slice_track.ts
index 588e9c4..d8c3514 100644
--- a/ui/src/frontend/named_slice_track.ts
+++ b/ui/src/frontend/named_slice_track.ts
@@ -19,7 +19,7 @@
import {STR_NULL} from '../trace_processor/query_result';
import {
- BASE_SLICE_ROW,
+ BASE_ROW,
BaseSliceTrack,
BaseSliceTrackTypes,
OnSliceClickArgs,
@@ -28,17 +28,17 @@
import {globals} from './globals';
import {NewTrackArgs} from './track';
-export const NAMED_SLICE_ROW = {
+export const NAMED_ROW = {
// Base columns (tsq, ts, dur, id, depth).
- ...BASE_SLICE_ROW,
+ ...BASE_ROW,
// Impl-specific columns.
name: STR_NULL,
};
-export type NamedSliceRow = typeof NAMED_SLICE_ROW;
+export type NamedRow = typeof NAMED_ROW;
export interface NamedSliceTrackTypes extends BaseSliceTrackTypes {
- row: NamedSliceRow;
+ row: NamedRow;
}
export abstract class NamedSliceTrack<
@@ -50,7 +50,7 @@
// This is used by the base class to call iter().
getRowSpec(): T['row'] {
- return NAMED_SLICE_ROW;
+ return NAMED_ROW;
}
// Converts a SQL result row to an "Impl" Slice.
@@ -58,8 +58,8 @@
const baseSlice = super.rowToSlice(row);
// Ignore PIDs or numeric arguments when hashing.
const name = row.name || '';
- const baseColor = getColorForSlice(name, false);
- return {...baseSlice, title: name, baseColor};
+ const colorScheme = getColorForSlice(name);
+ return {...baseSlice, title: name, colorScheme};
}
onSliceOver(args: OnSliceOverArgs<T['slice']>) {
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 7ad81ff..edda1d3 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -20,7 +20,7 @@
Time,
time,
} from '../base/time';
-import {hueForCpu} from '../common/colorizer';
+import {colorForCpu} from '../common/colorizer';
import {timestampFormat, TimestampFormat} from '../common/timestamp_format';
import {
@@ -135,7 +135,8 @@
const xEnd = Math.ceil(this.timeScale.timeToPx(loads[i].end));
const yOff = Math.floor(headerHeight + y * trackHeight);
const lightness = Math.ceil((1 - loads[i].load * 0.7) * 100);
- ctx.fillStyle = `hsl(${hueForCpu(y)}, 50%, ${lightness}%)`;
+ const color = colorForCpu(y).setHSL({s: 50, l: lightness});
+ ctx.fillStyle = color.cssString;
ctx.fillRect(xStart, yOff, xEnd - xStart, Math.ceil(trackHeight));
}
y++;
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 14550e7..5497d8e 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -15,6 +15,7 @@
import m from 'mithril';
import {Trash} from '../base/disposable';
+import {getScrollbarWidth} from '../base/dom_utils';
import {assertExists, assertFalse} from '../base/logging';
import {SimpleResizeObserver} from '../base/resize_observer';
import {
@@ -333,8 +334,7 @@
// On non-MacOS if there is a solid scroll bar it can cover important
// pixels, reduce the size of the canvas so it doesn't overlap with
// the scroll bar.
- this.parentWidth =
- clientRect.width - globals.frontendLocalState.getScrollbarWidth();
+ this.parentWidth = clientRect.width - getScrollbarWidth();
this.parentHeight = clientRect.height;
return this.parentHeight !== oldHeight || this.parentWidth !== oldWidth;
}
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
index 8ed0c2d..d23be62 100644
--- a/ui/src/frontend/publish.ts
+++ b/ui/src/frontend/publish.ts
@@ -25,6 +25,7 @@
import {MetricResult} from '../common/metric_data';
import {CurrentSearchResults, SearchSummary} from '../common/search_data';
import {raf} from '../core/raf_scheduler';
+import {HttpRpcState} from '../trace_processor/http_rpc_engine';
import {
CounterDetails,
@@ -81,6 +82,11 @@
globals.publishRedraw();
}
+export function publishHttpRpcState(httpRpcState: HttpRpcState) {
+ globals.httpRpcState = httpRpcState;
+ raf.scheduleFullRedraw();
+}
+
export function publishCounterDetails(click: CounterDetails) {
globals.counterDetails = click;
globals.publishRedraw();
@@ -216,3 +222,8 @@
globals.ftracePanelData = data;
globals.publishRedraw();
}
+
+export function publishShowPanningHint() {
+ globals.showPanningHint = true;
+ globals.publishRedraw();
+}
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index 27ab289..5c7a8ff 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -63,9 +63,14 @@
// DEPRECATED: for #!/record?p=cpu subpages (b/191255021).
p: optStr,
- // For fetching traces from Cloud Storage.
+ // For fetching traces from Cloud Storage or local servers
+ // as with record_android_trace.
url: optStr,
+ // Override the referrer. Useful for scripts such as
+ // record_android_trace to record where the trace is coming from.
+ referrer: optStr,
+
// For the 'mode' of the UI. For example when the mode is 'embedded'
// some features are disabled.
mode: oneOf<Mode>(modes, undefined),
diff --git a/ui/src/frontend/rpc_http_dialog.ts b/ui/src/frontend/rpc_http_dialog.ts
index c2a64a2..54796b8 100644
--- a/ui/src/frontend/rpc_http_dialog.ts
+++ b/ui/src/frontend/rpc_http_dialog.ts
@@ -22,6 +22,7 @@
import {globals} from './globals';
import {showModal} from './modal';
+import {publishHttpRpcState} from './publish';
const CURRENT_API_VERSION =
TraceProcessorApiVersion.TRACE_PROCESSOR_CURRENT_API_VERSION;
@@ -79,7 +80,7 @@
// having to open a trace).
export async function CheckHttpRpcConnection(): Promise<void> {
const state = await HttpRpcEngine.checkConnection();
- globals.frontendLocalState.setHttpRpcState(state);
+ publishHttpRpcState(state);
if (!state.connected) return;
const tpStatus = assertExists(state.status);
diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts
index dccad06..b514b61 100644
--- a/ui/src/frontend/scroll_helper.ts
+++ b/ui/src/frontend/scroll_helper.ts
@@ -135,7 +135,7 @@
// group and scroll to the track or just scroll to the track group.
if (openGroup) {
// After the track exists in the dom, it will be scrolled to.
- globals.frontendLocalState.scrollToTrackKey = trackKey;
+ globals.scrollToTrackKey = trackKey;
globals.dispatch(Actions.toggleTrackGroupCollapsed({trackGroupId}));
return;
} else {
diff --git a/ui/src/frontend/service_worker_controller.ts b/ui/src/frontend/service_worker_controller.ts
index a6082fe..de5323e 100644
--- a/ui/src/frontend/service_worker_controller.ts
+++ b/ui/src/frontend/service_worker_controller.ts
@@ -88,7 +88,7 @@
// Ctrl+Shift+R). In these cases, we are already at the last
// version.
if (sw !== this._initialWorker && this._initialWorker) {
- globals.frontendLocalState.newVersionAvailable = true;
+ globals.newVersionAvailable = true;
}
}
}
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index c0c23bf..6d3e79b 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -631,7 +631,7 @@
// RPC server is shut down after we load the UI and cached httpRpcState)
// this will eventually become consistent once the engine is created.
if (mode === undefined) {
- if (globals.frontendLocalState.httpRpcState.connected &&
+ if (globals.httpRpcState.connected &&
globals.state.newEngineMode === 'USE_HTTP_RPC_IF_AVAILABLE') {
mode = 'HTTP_RPC';
} else {
diff --git a/ui/src/frontend/slice.ts b/ui/src/frontend/slice.ts
deleted file mode 100644
index 7e82268..0000000
--- a/ui/src/frontend/slice.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {duration, time} from '../base/time';
-import {Color} from '../common/colorizer';
-
-export interface Slice {
- // These properties are updated only once per query result when the Slice
- // object is created and don't change afterwards.
- readonly id: number;
- readonly startNsQ: time;
- readonly endNsQ: time;
- readonly durNsQ: duration;
- readonly ts: time;
- readonly dur: duration;
- readonly depth: number;
- readonly flags: number;
-
- // Each slice can represent some extra numerical information by rendering a
- // portion of the slice with a lighter tint.
- // |fillRatio\ describes the ratio of the normal area to the tinted area
- // width of the slice, normalized between 0.0 -> 1.0.
- // 0.0 means the whole slice is tinted.
- // 1.0 means none of the slice is tinted.
- // E.g. If |fillRatio| = 0.65 the slice will be rendered like this:
- // [############|*******]
- // ^------------^-------^
- // Normal Light
- readonly fillRatio: number;
-
- // These can be changed by the Impl.
- title: string;
- subTitle: string;
- baseColor: Color;
- color: Color;
-}
diff --git a/ui/src/frontend/slice_args.ts b/ui/src/frontend/slice_args.ts
index 70beacb..9e1798f 100644
--- a/ui/src/frontend/slice_args.ts
+++ b/ui/src/frontend/slice_args.ts
@@ -30,8 +30,7 @@
} from '../tracks/visualised_args';
import {Anchor} from '../widgets/anchor';
import {MenuItem, PopupMenu2} from '../widgets/menu';
-import {Section} from '../widgets/section';
-import {Tree, TreeNode} from '../widgets/tree';
+import {TreeNode} from '../widgets/tree';
import {addTab} from './bottom_tab';
import {globals} from './globals';
@@ -40,20 +39,21 @@
import {SqlTableTab} from './sql_table/tab';
import {SqlTables} from './sql_table/well_known_tables';
-// Renders slice arguments (key/value pairs) into a Tree widget.
+// Renders slice arguments (key/value pairs) as a subtree.
export function renderArguments(
engine: EngineProxy, slice: SliceDetails): m.Children {
if (slice.args && slice.args.length > 0) {
const tree = convertArgsToTree(slice.args);
- return m(
- Section,
- {title: 'Arguments'},
- m(Tree, renderArgTreeNodes(engine, tree)));
+ return renderArgTreeNodes(engine, tree);
} else {
return undefined;
}
}
+export function hasArgs(slice: SliceDetails): boolean {
+ return exists(slice.args) && slice.args.length > 0;
+}
+
function renderArgTreeNodes(
engine: EngineProxy, args: ArgNode<Arg>[]): m.Children {
return args.map((arg) => {
diff --git a/ui/src/frontend/slice_track_base.ts b/ui/src/frontend/slice_track.ts
similarity index 80%
rename from ui/src/frontend/slice_track_base.ts
rename to ui/src/frontend/slice_track.ts
index 37c2ec8..0264d07 100644
--- a/ui/src/frontend/slice_track_base.ts
+++ b/ui/src/frontend/slice_track.ts
@@ -12,28 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {duration, Span, Time, time} from '../base/time';
+import {Time, time} from '../base/time';
import {Actions} from '../common/actions';
-import {BasicAsyncTrack} from '../common/basic_async_track';
import {cropText, drawIncompleteSlice} from '../common/canvas_utils';
-import {
- colorForThreadIdleSlice,
- getColorForSlice,
-} from '../common/colorizer';
+import {getColorForSlice} from '../common/colorizer';
import {HighPrecisionTime} from '../common/high_precision_time';
import {TrackData} from '../common/track_data';
+import {TrackHelperLEGACY} from '../common/track_helper';
+import {SliceRect} from '../public';
+import {CROP_INCOMPLETE_SLICE_FLAG} from './base_slice_track';
import {checkerboardExcept} from './checkerboard';
import {globals} from './globals';
-import {cachedHsluvToHex} from './hsluv_cache';
-import {PxSpan, TimeScale} from './time_scale';
-import {SliceRect} from './track';
export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
const SLICE_HEIGHT = 18;
const TRACK_PADDING = 2;
const CHEVRON_WIDTH_PX = 10;
const HALF_CHEVRON_WIDTH_PX = CHEVRON_WIDTH_PX / 2;
+const INCOMPLETE_SLICE_WIDTH_PX = 20;
export interface SliceData extends TrackData {
// Slices are stored in a columnar fashion.
@@ -55,7 +52,9 @@
// tracks before they are ported to v2.
// Slice tracks should extend this class and implement the abstract methods,
// notably onBoundsChange().
-export abstract class SliceTrackBase extends BasicAsyncTrack<SliceData> {
+// Note: This class is deprecated and should not be used for new tracks. Use
+// |BaseSliceTrack| instead.
+export abstract class SliceTrackLEGACY extends TrackHelperLEGACY<SliceData> {
constructor(
private maxDepth: number, protected trackKey: string,
private tableName: string, private namespace?: string) {
@@ -119,18 +118,23 @@
if (isIncomplete) { // incomplete slice
// TODO(stevegolton): This isn't exactly equivalent, ideally we should
// choose tEnd once we've converted to screen space coords.
- tEnd = visibleWindowTime.end.toTime('ceil');
+ tEnd = this.getEndTimeIfInComplete(tStart);
}
if (!visibleTimeSpan.intersects(tStart, tEnd)) {
continue;
}
- const rect = this.getSliceRect(
- visibleTimeScale, visibleTimeSpan, windowSpan, tStart, tEnd, depth);
- if (!rect || !rect.visible) {
- continue;
- }
+ const pxEnd = windowSpan.end;
+ const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
+ const right = Math.min(visibleTimeScale.timeToPx(tEnd), pxEnd);
+
+ const rect = {
+ left,
+ width: Math.max(right - left, 1),
+ top: TRACK_PADDING + depth * SLICE_HEIGHT,
+ height: SLICE_HEIGHT,
+ };
const currentSelection = globals.state.currentSelection;
const isSelected = currentSelection &&
@@ -141,11 +145,14 @@
globals.state.highlightedSliceId === sliceId;
const hasFocus = highlighted || isSelected;
- const colorObj = getColorForSlice(title, hasFocus);
+ const colorScheme = getColorForSlice(title);
+ const colorObj = hasFocus ? colorScheme.variant : colorScheme.base;
+ const textColor =
+ hasFocus ? colorScheme.textVariant : colorScheme.textBase;
let color: string;
if (colorOverride === undefined) {
- color = cachedHsluvToHex(colorObj.h, colorObj.s, colorObj.l);
+ color = colorObj.cssString;
} else {
color = colorOverride;
}
@@ -165,7 +172,7 @@
ctx.translate(rect.left, rect.top);
// Draw a rectangle around the selected slice
- ctx.strokeStyle = cachedHsluvToHex(colorObj.h, 100, 10);
+ ctx.strokeStyle = colorObj.setHSL({s: 100, l: 10}).cssString;
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(
@@ -188,7 +195,13 @@
}
if (isIncomplete && rect.width > SLICE_HEIGHT / 4) {
- drawIncompleteSlice(ctx, rect.left, rect.top, rect.width, SLICE_HEIGHT);
+ drawIncompleteSlice(
+ ctx,
+ rect.left,
+ rect.top,
+ rect.width,
+ SLICE_HEIGHT,
+ !CROP_INCOMPLETE_SLICE_FLAG.get());
} else if (
data.cpuTimeRatio !== undefined && data.cpuTimeRatio[i] < 1 - 1e-9) {
// We draw two rectangles, representing the ratio between wall time and
@@ -196,9 +209,8 @@
const cpuTimeRatio = data.cpuTimeRatio![i];
const firstPartWidth = rect.width * cpuTimeRatio;
const secondPartWidth = rect.width * (1 - cpuTimeRatio);
- ctx.fillRect(rect.left, rect.top, firstPartWidth, SLICE_HEIGHT);
- ctx.fillStyle = colorForThreadIdleSlice(
- colorObj.h, colorObj.s, colorObj.l, hasFocus);
+ ctx.fillRect(rect.left, rect.top, rect.width, SLICE_HEIGHT);
+ ctx.fillStyle = '#FFFFFF50';
ctx.fillRect(
rect.left + firstPartWidth,
rect.top,
@@ -211,7 +223,7 @@
// Selected case
if (isSelected) {
drawRectOnSelected = () => {
- ctx.strokeStyle = cachedHsluvToHex(colorObj.h, 100, 10);
+ ctx.strokeStyle = colorObj.setHSL({s: 100, l: 10}).cssString;
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(
@@ -222,7 +234,7 @@
// Don't render text when we have less than 5px to play with.
if (rect.width >= 5) {
- ctx.fillStyle = colorObj.l > 65 ? '#404040' : 'white';
+ ctx.fillStyle = textColor.cssString;
const displayText = cropText(title, charWidth, rect.width);
const rectXCenter = rect.left + rect.width / 2;
ctx.textBaseline = 'middle';
@@ -250,7 +262,6 @@
if (data === undefined) return;
const {
visibleTimeScale: timeScale,
- visibleWindowTime: visibleHPTimeSpan,
} = globals.frontendLocalState;
if (y < TRACK_PADDING) return;
const instantWidthTime = timeScale.pxDeltaToDuration(HALF_CHEVRON_WIDTH_PX);
@@ -271,7 +282,8 @@
const end = Time.fromRaw(data.ends[i]);
let tEnd = HighPrecisionTime.fromTime(end);
if (data.isIncomplete[i]) {
- tEnd = visibleHPTimeSpan.end;
+ const endTime = this.getEndTimeIfInComplete(start);
+ tEnd = HighPrecisionTime.fromTime(endTime);
}
if (tStart.lte(t) && t.lte(tEnd)) {
return i;
@@ -280,6 +292,20 @@
}
}
+ getEndTimeIfInComplete(start: time): time {
+ const {visibleTimeScale, visibleWindowTime} = globals.frontendLocalState;
+
+ let end = visibleWindowTime.end.toTime('ceil');
+ if (CROP_INCOMPLETE_SLICE_FLAG.get()) {
+ const widthTime =
+ visibleTimeScale.pxDeltaToDuration(INCOMPLETE_SLICE_WIDTH_PX)
+ .toTime();
+ end = Time.add(start, widthTime);
+ }
+
+ return end;
+ }
+
onMouseMove({x, y}: {x: number, y: number}) {
this.hoveredTitleId = -1;
globals.dispatch(Actions.setHighlightedSliceId({sliceId: -1}));
@@ -318,15 +344,18 @@
return SLICE_HEIGHT * (this.maxDepth + 1) + 2 * TRACK_PADDING;
}
- getSliceRect(
- visibleTimeScale: TimeScale, visibleWindow: Span<time, duration>,
- windowSpan: PxSpan, tStart: time, tEnd: time, depth: number): SliceRect
- |undefined {
+ getSliceRect(tStart: time, tEnd: time, depth: number): SliceRect|undefined {
+ const {
+ windowSpan,
+ visibleTimeScale,
+ visibleTimeSpan,
+ } = globals.frontendLocalState;
+
const pxEnd = windowSpan.end;
const left = Math.max(visibleTimeScale.timeToPx(tStart), 0);
const right = Math.min(visibleTimeScale.timeToPx(tEnd), pxEnd);
- const visible = visibleWindow.intersects(tStart, tEnd);
+ const visible = visibleTimeSpan.intersects(tStart, tEnd);
return {
left,
diff --git a/ui/src/frontend/sql_table/argument_selector.ts b/ui/src/frontend/sql_table/argument_selector.ts
index 2c86138..f7cd374 100644
--- a/ui/src/frontend/sql_table/argument_selector.ts
+++ b/ui/src/frontend/sql_table/argument_selector.ts
@@ -69,7 +69,7 @@
this.argList = [];
const it = queryResult.iter({key: STR});
for (; it.valid(); it.next()) {
- const arg = argColumn(attrs.argSetId, it.key);
+ const arg = argColumn(attrs.tableName, attrs.argSetId, it.key);
if (attrs.alreadySelectedColumns.has(arg.alias)) continue;
this.argList.push(it.key);
}
diff --git a/ui/src/frontend/sql_table/column.ts b/ui/src/frontend/sql_table/column.ts
index f4a6543..133c5fb 100644
--- a/ui/src/frontend/sql_table/column.ts
+++ b/ui/src/frontend/sql_table/column.ts
@@ -46,10 +46,11 @@
};
}
-export function argColumn(c: ArgSetIdColumn, argName: string): Column {
+export function argColumn(
+ tableName: string, c: ArgSetIdColumn, argName: string): Column {
const escape = (name: string) => name.replace(/[^A-Za-z0-9]/g, '_');
return {
- expression: `extract_arg(${c.name}, ${sqliteString(argName)})`,
+ expression: `extract_arg(${tableName}.${c.name}, ${sqliteString(argName)})`,
alias: `_arg_${c.name}_${escape(argName)}`,
title: `${c.title ?? c.name} ${argName}`,
};
diff --git a/ui/src/frontend/sql_table/column_unittest.ts b/ui/src/frontend/sql_table/column_unittest.ts
index 8fd944e..849eb03 100644
--- a/ui/src/frontend/sql_table/column_unittest.ts
+++ b/ui/src/frontend/sql_table/column_unittest.ts
@@ -72,11 +72,12 @@
},
});
- expect(argColumn(table.columns[3] as ArgSetIdColumn, 'foo.bar')).toEqual({
- expression: 'extract_arg(arg_set_id, \'foo.bar\')',
- alias: '_arg_arg_set_id_foo_bar',
- title: 'Arg foo.bar',
- });
+ expect(argColumn('slice', table.columns[3] as ArgSetIdColumn, 'foo.bar'))
+ .toEqual({
+ expression: 'extract_arg(slice.arg_set_id, \'foo.bar\')',
+ alias: '_arg_arg_set_id_foo_bar',
+ title: 'Arg foo.bar',
+ });
});
function formatSqlProjectionsForColumn(c: Column): string {
diff --git a/ui/src/frontend/sql_table/table.ts b/ui/src/frontend/sql_table/table.ts
index de1c9cb..d6de8e3 100644
--- a/ui/src/frontend/sql_table/table.ts
+++ b/ui/src/frontend/sql_table/table.ts
@@ -89,7 +89,7 @@
constraints: this.state.getQueryConstraints(),
alreadySelectedColumns: existingColumns,
onArgumentSelected: (argument: string) => {
- addColumn(argColumn(column, argument));
+ addColumn(argColumn(this.table.name, column, argument));
},
})));
continue;
diff --git a/ui/src/frontend/tables/table.ts b/ui/src/frontend/tables/table.ts
index 4d1b773..eb12883 100644
--- a/ui/src/frontend/tables/table.ts
+++ b/ui/src/frontend/tables/table.ts
@@ -89,6 +89,11 @@
return new ColumnDescriptor<T>(name, getter, {contextMenu, sortKey: getter});
}
+export function widgetColumn<T>(
+ name: string, getter: (t: T) => m.Child): ColumnDescriptor<T> {
+ return new ColumnDescriptor<T>(name, getter);
+}
+
interface SortingInfo<T> {
columnId: string;
direction: SortDirection;
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index 81b1346..977b608 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -181,7 +181,6 @@
let trackKey: string|number|undefined;
for (const track of Object.values(globals.state.tracks)) {
const trackDesc = pluginManager.resolveTrackInfo(track.uri);
- // TODO(stevegolton): Handle v2.
if (trackDesc && trackDesc.kind === THREAD_STATE_TRACK_KIND &&
trackDesc.utid === vnode.attrs.utid) {
trackKey = track.key;
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 70aed74..4bea985 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -45,7 +45,7 @@
m('button.notification-btn.preferred',
{
onclick: () => {
- globals.frontendLocalState.newVersionAvailable = false;
+ globals.newVersionAvailable = false;
raf.scheduleFullRedraw();
},
},
@@ -61,7 +61,7 @@
// does not persist for iFrames. The host is responsible for communicating
// to users that they can press '?' for help.
if (globals.embeddedMode || dismissed === 'true' ||
- !globals.frontendLocalState.showPanningHint) {
+ !globals.showPanningHint) {
return;
}
return m(
@@ -72,7 +72,7 @@
m('button.hint-dismiss-button',
{
onclick: () => {
- globals.frontendLocalState.showPanningHint = false;
+ globals.showPanningHint = false;
localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
raf.scheduleFullRedraw();
},
@@ -113,9 +113,7 @@
return m(
'.topbar',
{class: globals.state.sidebarVisible ? '' : 'hide-sidebar'},
- globals.frontendLocalState.newVersionAvailable ?
- m(NewVersionNotification) :
- omnibox,
+ globals.newVersionAvailable ? m(NewVersionNotification) : omnibox,
m(Progress),
m(HelpPanningNotification),
m(TraceErrorIcon));
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index ad2336b..7fcce0b 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -14,53 +14,20 @@
import m from 'mithril';
-import {assertExists} from '../base/logging';
-import {duration, Span, time} from '../base/time';
-import {Track, TrackContext} from '../public';
+import {time} from '../base/time';
+import {SliceRect, Track, TrackContext} from '../public';
import {EngineProxy} from '../trace_processor/engine';
-import {PxSpan, TimeScale} from './time_scale';
-
// Args passed to the track constructors when creating a new track.
export interface NewTrackArgs {
trackKey: string;
engine: EngineProxy;
}
-// This interface forces track implementations to have some static properties.
-// Typescript does not have abstract static members, which is why this needs to
-// be in a separate interface.
-export interface TrackCreator {
- // Store the kind explicitly as a string as opposed to using class.kind in
- // case we ever minify our code.
- readonly kind: string;
-
- // We need the |create| method because the stored value in the registry can be
- // an abstract class, and we cannot call 'new' on an abstract class.
- create(args: NewTrackArgs): TrackBase;
-}
-
-export interface SliceRect {
- left: number;
- width: number;
- top: number;
- height: number;
- visible: boolean;
-}
-
// The abstract class that needs to be implemented by all tracks.
-export abstract class TrackBase<Config = {}> implements Track {
+export abstract class TrackBase implements Track {
protected readonly trackKey: string;
protected readonly engine: EngineProxy;
- private _config?: Config;
-
- get config(): Config {
- return assertExists(this._config);
- }
-
- set config(x: Config) {
- this._config = x;
- }
constructor(args: NewTrackArgs) {
this.trackKey = args.trackKey;
@@ -103,10 +70,8 @@
// only for track types that support slices e.g. chrome_slice, async_slices
// tStart - slice start time in seconds, tEnd - slice end time in seconds,
// depth - slice depth
- getSliceRect(
- _visibleTimeScale: TimeScale, _visibleWindow: Span<time, duration>,
- _windowSpan: PxSpan, _tStart: time, _tEnd: time,
- _depth: number): SliceRect|undefined {
+ getSliceRect(_tStart: time, _tEnd: time, _depth: number): SliceRect
+ |undefined {
return undefined;
}
}
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index a524dfb..ec6e7e6 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -24,7 +24,7 @@
TrackGroupState,
TrackState,
} from '../common/state';
-import {Migrate, Track, TrackContext} from '../public';
+import {Migrate, Track, TrackContext, TrackTags} from '../public';
import {globals} from './globals';
import {drawGridLines} from './gridline_helper';
@@ -44,6 +44,7 @@
private shellWidth = 0;
private backgroundColor = '#ffffff'; // Updated from CSS later.
private summaryTrack?: Track;
+ private summaryTrackTags?: TrackTags;
constructor({attrs}: m.CVnode<Attrs>) {
super();
@@ -70,6 +71,7 @@
};
this.summaryTrack = pluginManager.createTrack(uri, ctx);
+ this.summaryTrackTags = pluginManager.resolveTrackInfo(uri)?.tags;
}
get trackGroupState(): TrackGroupState {
@@ -148,7 +150,7 @@
'h1.track-title',
{title: name},
name,
- renderChips(this.summaryTrackState),
+ renderChips(this.summaryTrackTags),
),
(this.trackGroupState.collapsed && child !== null) ?
m('h2.track-subtitle', child) :
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 90a593a..dec3a65 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -17,20 +17,19 @@
import {currentTargetOffset} from '../base/dom_utils';
import {Icons} from '../base/semantic_icons';
-import {duration, Span, time} from '../base/time';
+import {time} from '../base/time';
import {Actions} from '../common/actions';
import {pluginManager} from '../common/plugins';
import {TrackState} from '../common/state';
import {raf} from '../core/raf_scheduler';
-import {Migrate, Track, TrackContext} from '../public';
+import {Migrate, SliceRect, Track, TrackContext, TrackTags} from '../public';
+import {checkerboard} from './checkerboard';
import {SELECTION_FILL_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
import {globals} from './globals';
import {drawGridLines} from './gridline_helper';
import {Panel, PanelSize} from './panel';
import {verticalScrollToTrack} from './scroll_helper';
-import {PxSpan, TimeScale} from './time_scale';
-import {SliceRect} from './track';
import {
drawVerticalLineAtTime,
} from './vertical_line_helper';
@@ -76,29 +75,24 @@
}
}
-export function renderChips({uri}: TrackState) {
- const tagElements: m.Children = [];
- const trackInfo = pluginManager.resolveTrackInfo(uri);
- const tags = trackInfo?.tags;
- tags?.metric && tagElements.push(m(TrackChip, {text: 'metric'}));
- tags?.debuggable && tagElements.push(m(TrackChip, {text: 'debuggable'}));
- return tagElements;
+export function renderChips(tags?: TrackTags) {
+ return [
+ tags?.metric && m(TrackChip, {text: 'metric'}),
+ tags?.debuggable && m(TrackChip, {text: 'debuggable'}),
+ ];
}
interface TrackShellAttrs {
- track: Track;
- trackState: TrackState;
+ trackKey: string;
+ title: string;
+ buttons: m.Children;
+ tags?: TrackTags;
}
class TrackShell implements m.ClassComponent<TrackShellAttrs> {
// Set to true when we click down and drag the
private dragging = false;
private dropping: 'before'|'after'|undefined = undefined;
- private attrs?: TrackShellAttrs;
-
- oninit(vnode: m.Vnode<TrackShellAttrs>) {
- this.attrs = vnode.attrs;
- }
view({attrs}: m.CVnode<TrackShellAttrs>) {
// The shell should be highlighted if the current search result is inside
@@ -107,7 +101,7 @@
const searchIndex = globals.state.searchIndex;
if (searchIndex !== -1) {
const trackKey = globals.currentSearchResults.trackKeys[searchIndex];
- if (trackKey === attrs.trackState.key) {
+ if (trackKey === attrs.trackKey) {
highlightClass = 'flash';
}
}
@@ -118,34 +112,34 @@
`.track-shell[draggable=true]`,
{
class: `${highlightClass} ${dragClass} ${dropClass}`,
- ondragstart: this.ondragstart.bind(this),
+ ondragstart: (e: DragEvent) => this.ondragstart(e, attrs.trackKey),
ondragend: this.ondragend.bind(this),
ondragover: this.ondragover.bind(this),
ondragleave: this.ondragleave.bind(this),
- ondrop: this.ondrop.bind(this),
+ ondrop: (e: DragEvent) => this.ondrop(e, attrs.trackKey),
},
m(
'h1',
{
- title: attrs.trackState.name,
+ title: attrs.title,
style: {
- 'font-size': getTitleSize(attrs.trackState.name),
+ 'font-size': getTitleSize(attrs.title),
},
},
- attrs.trackState.name,
- renderChips(attrs.trackState),
+ attrs.title,
+ renderChips(attrs.tags),
),
m('.track-buttons',
- attrs.track.getTrackShellButtons(),
+ attrs.buttons,
m(TrackButton, {
action: () => {
globals.dispatch(
- Actions.toggleTrackPinned({trackKey: attrs.trackState.key}));
+ Actions.toggleTrackPinned({trackKey: attrs.trackKey}));
},
i: Icons.Pin,
- filledIcon: isPinned(attrs.trackState.key),
- tooltip: isPinned(attrs.trackState.key) ? 'Unpin' : 'Pin to top',
- showButton: isPinned(attrs.trackState.key),
+ filledIcon: isPinned(attrs.trackKey),
+ tooltip: isPinned(attrs.trackKey) ? 'Unpin' : 'Pin to top',
+ showButton: isPinned(attrs.trackKey),
fullHeight: true,
}),
globals.state.currentSelection !== null &&
@@ -153,25 +147,24 @@
m(TrackButton, {
action: (e: MouseEvent) => {
globals.dispatch(Actions.toggleTrackSelection(
- {id: attrs.trackState.key, isTrackGroup: false}));
+ {id: attrs.trackKey, isTrackGroup: false}));
e.stopPropagation();
},
- i: isSelected(attrs.trackState.key) ? Icons.Checkbox :
- Icons.BlankCheckbox,
- tooltip: isSelected(attrs.trackState.key) ?
- 'Remove track' :
- 'Add track to selection',
+ i: isSelected(attrs.trackKey) ? Icons.Checkbox :
+ Icons.BlankCheckbox,
+ tooltip: isSelected(attrs.trackKey) ? 'Remove track' :
+ 'Add track to selection',
showButton: true,
}) :
''));
}
- ondragstart(e: DragEvent) {
+ ondragstart(e: DragEvent, trackKey: string) {
const dataTransfer = e.dataTransfer;
if (dataTransfer === null) return;
this.dragging = true;
raf.scheduleFullRedraw();
- dataTransfer.setData('perfetto/track', `${this.attrs!.trackState.key}`);
+ dataTransfer.setData('perfetto/track', `${trackKey}`);
dataTransfer.setDragImage(new Image(), 0, 0);
}
@@ -204,13 +197,13 @@
raf.scheduleFullRedraw();
}
- ondrop(e: DragEvent) {
+ ondrop(e: DragEvent, trackKey: string) {
if (this.dropping === undefined) return;
const dataTransfer = e.dataTransfer;
if (dataTransfer === null) return;
raf.scheduleFullRedraw();
const srcId = dataTransfer.getData('perfetto/track');
- const dstId = this.attrs!.trackState.key;
+ const dstId = trackKey;
globals.dispatch(Actions.moveTrack({srcId, op: this.dropping, dstId}));
this.dropping = undefined;
}
@@ -275,9 +268,14 @@
}
interface TrackComponentAttrs {
- trackState: TrackState;
- track: Track;
+ trackKey: string;
+ heightPx?: number;
+ title: string;
+ buttons?: m.Children;
+ tags?: TrackTags;
+ track?: Track;
}
+
class TrackComponent implements m.ClassComponent<TrackComponentAttrs> {
view({attrs}: m.CVnode<TrackComponentAttrs>) {
// TODO(hjd): The min height below must match the track_shell_title
@@ -287,20 +285,25 @@
'.track',
{
style: {
- height: `${Math.max(18, attrs.track.getHeight())}px`,
+ height: `${Math.max(18, attrs.heightPx ?? 0)}px`,
},
- id: 'track_' + attrs.trackState.key,
+ id: 'track_' + attrs.trackKey,
},
[
- m(TrackShell, {track: attrs.track, trackState: attrs.trackState}),
- m(TrackContent, {track: attrs.track}),
+ m(TrackShell, {
+ buttons: attrs.buttons,
+ title: attrs.title,
+ trackKey: attrs.trackKey,
+ tags: attrs.tags,
+ }),
+ attrs.track && m(TrackContent, {track: attrs.track}),
]);
}
oncreate({attrs}: m.CVnode<TrackComponentAttrs>) {
- if (globals.frontendLocalState.scrollToTrackKey === attrs.trackState.key) {
- verticalScrollToTrack(attrs.trackState.key);
- globals.frontendLocalState.scrollToTrackKey = undefined;
+ if (globals.scrollToTrackKey === attrs.trackKey) {
+ verticalScrollToTrack(attrs.trackKey);
+ globals.scrollToTrackKey = undefined;
}
}
}
@@ -342,6 +345,7 @@
// has disappeared.
private track: Track|undefined;
private trackState: TrackState|undefined;
+ private tags: TrackTags|undefined;
private tryLoadTrack(vnode: m.CVnode<TrackPanelAttrs>) {
const trackKey = vnode.attrs.trackKey;
@@ -365,6 +369,7 @@
};
this.track = pluginManager.createTrack(uri, trackCtx);
+ this.tags = pluginManager.resolveTrackInfo(uri)?.tags;
this.track?.onCreate(trackCtx);
this.trackState = trackState;
@@ -376,9 +381,19 @@
}
if (this.track === undefined || this.trackState === undefined) {
- return m('div', 'No such track');
+ return m(TrackComponent, {
+ trackKey: vnode.attrs.trackKey,
+ title: this.trackState?.name ?? 'Loading...',
+ });
}
- return m(TrackComponent, {trackState: this.trackState, track: this.track});
+ return m(TrackComponent, {
+ tags: this.tags,
+ heightPx: this.track.getHeight(),
+ title: this.trackState.name,
+ trackKey: this.trackState.key,
+ buttons: this.track.getTrackShellButtons(),
+ track: this.track,
+ });
}
oncreate() {
@@ -430,6 +445,8 @@
ctx.translate(TRACK_SHELL_WIDTH, 0);
if (this.track !== undefined) {
this.track.render(ctx);
+ } else {
+ checkerboard(ctx, size.height, 0, size.width - TRACK_SHELL_WIDTH);
}
ctx.restore();
@@ -492,14 +509,10 @@
}
}
- getSliceRect(
- visibleTimeScale: TimeScale, visibleWindow: Span<time, duration>,
- windowSpan: PxSpan, tStart: time, tDur: time, depth: number): SliceRect
- |undefined {
+ getSliceRect(tStart: time, tDur: time, depth: number): SliceRect|undefined {
if (this.track === undefined) {
return undefined;
}
- return this.track.getSliceRect(
- visibleTimeScale, visibleWindow, windowSpan, tStart, tDur, depth);
+ return this.track.getSliceRect(tStart, tDur, depth);
}
}
diff --git a/ui/src/frontend/track_registry.ts b/ui/src/frontend/track_registry.ts
deleted file mode 100644
index 92007d7..0000000
--- a/ui/src/frontend/track_registry.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Registry} from '../common/registry';
-import {TrackCreator} from './track';
-
-/**
- * Global registry that maps types to TrackCreator.
- */
-export const trackRegistry = Registry.kindRegistry<TrackCreator>();
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 8aeabde..57aa0a3 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -14,6 +14,7 @@
import m from 'mithril';
+import {getScrollbarWidth} from '../base/dom_utils';
import {clamp} from '../base/math_utils';
import {Time} from '../base/time';
import {Actions} from '../common/actions';
@@ -28,6 +29,7 @@
import {createPage} from './pages';
import {PanAndZoomHandler} from './pan_and_zoom_handler';
import {AnyAttrsVnode, PanelContainer} from './panel_container';
+import {publishShowPanningHint} from './publish';
import {TickmarkPanel} from './tickmark_panel';
import {TimeAxisPanel} from './time_axis_panel';
import {TimeSelectionPanel} from './time_selection_panel';
@@ -96,9 +98,7 @@
const updateDimensions = () => {
const rect = vnode.dom.getBoundingClientRect();
frontendLocalState.updateLocalLimits(
- 0,
- rect.width - TRACK_SHELL_WIDTH -
- frontendLocalState.getScrollbarWidth());
+ 0, rect.width - TRACK_SHELL_WIDTH - getScrollbarWidth());
};
updateDimensions();
@@ -193,6 +193,7 @@
);
frontendLocalState.areaY.start = dragStartY;
frontendLocalState.areaY.end = currentY;
+ publishShowPanningHint();
}
raf.scheduleRedraw();
},
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
index 7658fb6..a521724 100644
--- a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
@@ -58,6 +58,59 @@
SELECT * FROM android_binder_graph(-1000, 1000, -1000, 1000)`,
'all process binder graph'),
});
+
+ ctx.registerCommand({
+ id: 'dev.perfetto.AndroidPerf#ThreadClusterDistribution',
+ name: 'Run query: runtime cluster distribution for a thread',
+ callback: async (tid) => {
+ if (tid === undefined) {
+ tid = prompt('Enter a thread tid', '');
+ if (tid === null) return;
+ }
+ ctx.tabs.openQuery(`
+ INCLUDE PERFETTO MODULE common.cpus;
+ WITH
+ total_runtime AS (
+ SELECT sum(dur) AS total_runtime
+ FROM sched s
+ LEFT JOIN thread t
+ USING (utid)
+ WHERE t.tid = ${tid}
+ )
+ SELECT
+ c.size AS cluster,
+ sum(dur)/1e6 AS total_dur_ms,
+ sum(dur) * 1.0 / (SELECT * FROM total_runtime) AS percentage
+ FROM sched s
+ LEFT JOIN thread t
+ USING (utid)
+ LEFT JOIN cpus c
+ ON s.cpu = c.cpu_index
+ WHERE t.tid = ${tid}
+ GROUP BY 1`, `runtime cluster distrubtion for tid ${tid}`);
+ },
+ });
+
+ ctx.registerCommand({
+ id: 'dev.perfetto.AndroidPerf#SchedLatency',
+ name: 'Run query: top 50 sched latency for a thread',
+ callback: async (tid) => {
+ if (tid === undefined) {
+ tid = prompt('Enter a thread tid', '');
+ if (tid === null) return;
+ }
+ ctx.tabs.openQuery(`
+ SELECT ts.*, t.tid, t.name, tt.id AS track_id
+ FROM thread_state ts
+ LEFT JOIN thread_track tt
+ USING (utid)
+ LEFT JOIN thread t
+ USING (utid)
+ WHERE ts.state IN ('R', 'R+') AND tid = ${tid}
+ ORDER BY dur DESC
+ LIMIT 50`, `top 50 sched latency slice for tid ${tid}`);
+ },
+ });
}
}
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/OWNERS b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/OWNERS
new file mode 100644
index 0000000..e5632b1
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/OWNERS
@@ -0,0 +1 @@
+lukechang@google.com
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts
new file mode 100644
index 0000000..46fbb46
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.AndroidPerfTraceCounters/index.ts
@@ -0,0 +1,109 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {
+ Plugin,
+ PluginContext,
+ PluginContextTrace,
+ PluginDescriptor,
+} from '../../public';
+import {addDebugSliceTrack} from '../../tracks/debug/slice_track';
+
+class AndroidPerfTraceCounters implements Plugin {
+
+ onActivate(_: PluginContext): void {}
+
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ ctx.registerCommand({
+ id: 'dev.perfetto.AndroidPerfTraceCounters#ThreadRuntimeIPC',
+ name: 'Add a track to show a thread runtime ipc',
+ callback: async (tid) => {
+ if (tid === undefined) {
+ tid = prompt('Enter a thread tid', '');
+ if (tid === null) return;
+ }
+ const sql_prefix = `
+WITH
+ sched_switch_ipc AS (
+ SELECT
+ ts,
+ EXTRACT_ARG(arg_set_id, 'prev_pid') AS tid,
+ EXTRACT_ARG(arg_set_id, 'prev_comm') AS thread_name,
+ EXTRACT_ARG(arg_set_id, 'inst') / (EXTRACT_ARG(arg_set_id, 'cyc') * 1.0) AS ipc,
+ EXTRACT_ARG(arg_set_id, 'inst') AS instruction,
+ EXTRACT_ARG(arg_set_id, 'cyc') AS cycle,
+ EXTRACT_ARG(arg_set_id, 'stallbm') AS stall_backend_mem,
+ EXTRACT_ARG(arg_set_id, 'l3dm') AS l3_cache_miss
+ FROM ftrace_event
+ WHERE name = 'sched_switch_with_ctrs' AND tid = ${tid}
+ ),
+ target_thread_sched_slice AS (
+ SELECT s.*, t.tid, t.name FROM sched s LEFT JOIN thread t USING (utid) WHERE t.tid = ${tid}
+ ),
+ target_thread_ipc_slice AS (
+ SELECT
+ (
+ SELECT
+ ts
+ FROM target_thread_sched_slice ts
+ WHERE ts.tid = ssi.tid AND ts.ts < ssi.ts
+ ORDER BY ts.ts DESC
+ LIMIT 1
+ ) AS ts,
+ (
+ SELECT
+ dur
+ FROM target_thread_sched_slice ts
+ WHERE ts.tid = ssi.tid AND ts.ts < ssi.ts
+ ORDER BY ts.ts DESC
+ LIMIT 1
+ ) AS dur,
+ ssi.ipc,
+ ssi.instruction,
+ ssi.cycle,
+ ssi.stall_backend_mem,
+ ssi.l3_cache_miss
+ FROM sched_switch_ipc ssi
+ )
+`
+
+ await addDebugSliceTrack(
+ ctx.engine,
+ {
+ sqlSource: sql_prefix + `
+SELECT * FROM target_thread_ipc_slice WHERE ts IS NOT NULL`,
+ },
+ 'Rutime IPC:' + tid,
+ {ts: 'ts', dur: 'dur', name: 'ipc'},
+ ['instruction', 'cycle', 'stall_backend_mem', 'l3_cache_miss' ],
+ );
+ ctx.tabs.openQuery(sql_prefix + `
+SELECT
+ (sum(instruction) * 1.0 / sum(cycle)*1.0) AS avg_ipc,
+ sum(dur)/1e6 as total_runtime_ms,
+ sum(instruction) AS total_instructions,
+ sum(cycle) AS total_cycles,
+ sum(stall_backend_mem) as total_stall_backend_mem,
+ sum(l3_cache_miss) as total_l3_cache_miss
+FROM target_thread_ipc_slice WHERE ts IS NOT NULL`,
+ 'target thread ipc statistic');
+ },
+ });
+ }
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'dev.perfetto.AndroidPerfTraceCounters',
+ plugin: AndroidPerfTraceCounters,
+};
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index 725c526..81f9225 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -15,10 +15,9 @@
import m from 'mithril';
import {Hotkey} from '../base/hotkeys';
-import {duration, Span, time} from '../base/time';
+import {duration, time} from '../base/time';
+import {ColorScheme} from '../common/colorizer';
import {Store} from '../frontend/store';
-import {PxSpan, TimeScale} from '../frontend/time_scale';
-import {SliceRect} from '../frontend/track';
import {EngineProxy} from '../trace_processor/engine';
export {createStore, Store} from '../frontend/store';
@@ -32,6 +31,37 @@
STR_NULL,
} from '../trace_processor/query_result';
+export interface Slice {
+ // These properties are updated only once per query result when the Slice
+ // object is created and don't change afterwards.
+ readonly id: number;
+ readonly startNsQ: time;
+ readonly endNsQ: time;
+ readonly durNsQ: duration;
+ readonly ts: time;
+ readonly dur: duration;
+ readonly depth: number;
+ readonly flags: number;
+
+ // Each slice can represent some extra numerical information by rendering a
+ // portion of the slice with a lighter tint.
+ // |fillRatio\ describes the ratio of the normal area to the tinted area
+ // width of the slice, normalized between 0.0 -> 1.0.
+ // 0.0 means the whole slice is tinted.
+ // 1.0 means none of the slice is tinted.
+ // E.g. If |fillRatio| = 0.65 the slice will be rendered like this:
+ // [############|*******]
+ // ^------------^-------^
+ // Normal Light
+ readonly fillRatio: number;
+
+ // These can be changed by the Impl.
+ title: string;
+ subTitle: string;
+ colorScheme: ColorScheme;
+ isHighlighted: boolean;
+}
+
export interface Command {
// A unique id for this command.
id: string;
@@ -132,14 +162,19 @@
mountStore<State>(migrate: Migrate<State>): Store<State>;
}
+export interface SliceRect {
+ left: number;
+ width: number;
+ top: number;
+ height: number;
+ visible: boolean;
+}
+
export interface Track {
onCreate(ctx: TrackContext): void;
render(ctx: CanvasRenderingContext2D): void;
onFullRedraw(): void;
- getSliceRect(
- visibleTimeScale: TimeScale, visibleWindow: Span<time, duration>,
- windowSpan: PxSpan, tStart: time, tEnd: time, depth: number): SliceRect
- |undefined;
+ getSliceRect(tStart: time, tEnd: time, depth: number): SliceRect|undefined;
getHeight(): number;
getTrackShellButtons(): m.Children;
onMouseMove(position: {x: number, y: number}): void;
diff --git a/ui/src/trace_processor/query_result.ts b/ui/src/trace_processor/query_result.ts
index c568488..798b42f 100644
--- a/ui/src/trace_processor/query_result.ts
+++ b/ui/src/trace_processor/query_result.ts
@@ -150,7 +150,7 @@
}
toString() {
- return `Query: ${this.query}\n` + super.toString();
+ return `${super.toString()}\nQuery:\n${this.query}`;
}
}
@@ -510,6 +510,10 @@
return assertExists(this.allRowsPromise);
}
+ get errorInfo(): QueryErrorInfo {
+ return this._errorInfo;
+ }
+
private resolveOrReject(promise: Deferred<QueryResult>, arg: QueryResult) {
if (this._error === undefined) {
promise.resolve(arg);
@@ -689,11 +693,14 @@
return this.isValid;
}
+ private makeError(message: string): QueryError {
+ return new QueryError(message, this.resultObj.errorInfo);
+ }
get(columnName: string): ColumnType {
const res = this.rowData[columnName];
if (res === undefined) {
- throw new Error(
+ throw this.makeError(
`Column '${columnName}' doesn't exist. ` +
`Actual columns: [${this.columnNames.join(',')}]`);
}
@@ -769,7 +776,7 @@
break;
default:
- throw new Error(`Invalid cell type ${cellType}`);
+ throw this.makeError(`Invalid cell type ${cellType}`);
}
} // For (cells)
this.isValid = true;
@@ -802,7 +809,7 @@
// Check that all the expected columns are present.
for (const expectedCol of Object.keys(this.rowSpec)) {
if (this.columnNames.indexOf(expectedCol) < 0) {
- throw new Error(
+ throw this.makeError(
`Column ${expectedCol} not found in the SQL result ` +
`set {${this.columnNames.join(' ')}}`);
}
@@ -845,9 +852,9 @@
}
}
if (err.length > 0) {
- throw new Error(
- `Error @ row: ${Math.floor(i / numColumns)} col: '` +
- `${colName}': ${err}`);
+ const row = Math.floor(i / numColumns);
+ const message = `Error @ row: ${row} col: '${colName}': ${err}`;
+ throw this.makeError(message);
}
}
return true;
diff --git a/ui/src/tracks/async_slices/async_slice_track.ts b/ui/src/tracks/async_slices/async_slice_track.ts
new file mode 100644
index 0000000..d3d8f92
--- /dev/null
+++ b/ui/src/tracks/async_slices/async_slice_track.ts
@@ -0,0 +1,119 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {BigintMath as BIMath} from '../../base/bigint_math';
+import {duration, time} from '../../base/time';
+import {SliceData, SliceTrackLEGACY} from '../../frontend/slice_track';
+import {EngineProxy} from '../../public';
+import {
+ LONG,
+ LONG_NULL,
+ NUM,
+ STR,
+} from '../../trace_processor/query_result';
+
+export const ASYNC_SLICE_TRACK_KIND = 'AsyncSliceTrack';
+
+export class AsyncSliceTrack extends SliceTrackLEGACY {
+ private maxDurNs: duration = 0n;
+
+ constructor(
+ private engine: EngineProxy, maxDepth: number, trackKey: string,
+ private trackIds: number[], namespace?: string) {
+ // TODO is 'slice' right here?
+ super(maxDepth, trackKey, 'slice', namespace);
+ }
+
+ async onBoundsChange(start: time, end: time, resolution: duration):
+ Promise<SliceData> {
+ if (this.maxDurNs === 0n) {
+ const maxDurResult = await this.engine.query(`
+ select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts,
+ dur)) as maxDur from experimental_slice_layout where filter_track_ids
+ = '${this.trackIds.join(',')}'
+ `);
+ this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
+ }
+
+ const queryRes = await this.engine.query(`
+ SELECT
+ (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
+ ts,
+ max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as
+ dur, layout_depth as depth, ifnull(name, '[null]') as name, id, dur =
+ 0 as isInstant, dur = -1 as isIncomplete
+ from experimental_slice_layout
+ where
+ filter_track_ids = '${this.trackIds.join(',')}' and
+ ts >= ${start - this.maxDurNs} and
+ ts <= ${end}
+ group by tsq, layout_depth
+ order by tsq, layout_depth
+ `);
+
+ const numRows = queryRes.numRows();
+ const slices: SliceData = {
+ start,
+ end,
+ resolution,
+ length: numRows,
+ strings: [],
+ sliceIds: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
+ depths: new Uint16Array(numRows),
+ titles: new Uint16Array(numRows),
+ isInstant: new Uint16Array(numRows),
+ isIncomplete: new Uint16Array(numRows),
+ };
+
+ const stringIndexes = new Map<string, number>();
+ function internString(str: string) {
+ let idx = stringIndexes.get(str);
+ if (idx !== undefined) return idx;
+ idx = slices.strings.length;
+ slices.strings.push(str);
+ stringIndexes.set(str, idx);
+ return idx;
+ }
+
+ const it = queryRes.iter({
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
+ depth: NUM,
+ name: STR,
+ id: NUM,
+ isInstant: NUM,
+ isIncomplete: NUM,
+ });
+ for (let row = 0; it.valid(); it.next(), row++) {
+ const startQ = it.tsq;
+ const start = it.ts;
+ const dur = it.dur;
+ const end = start + dur;
+ const minEnd = startQ + resolution;
+ const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
+
+ slices.starts[row] = startQ;
+ slices.ends[row] = endQ;
+ slices.depths[row] = it.depth;
+ slices.titles[row] = internString(it.name);
+ slices.sliceIds[row] = it.id;
+ slices.isInstant[row] = it.isInstant;
+ slices.isIncomplete[row] = it.isIncomplete;
+ }
+ return slices;
+ }
+}
diff --git a/ui/src/tracks/async_slices/async_slice_track_v2.ts b/ui/src/tracks/async_slices/async_slice_track_v2.ts
new file mode 100644
index 0000000..4e87c9b
--- /dev/null
+++ b/ui/src/tracks/async_slices/async_slice_track_v2.ts
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {NamedSliceTrack} from '../../frontend/named_slice_track';
+import {NewTrackArgs} from '../../frontend/track';
+import {Slice} from '../../public';
+
+export class AsyncSliceTrackV2 extends NamedSliceTrack {
+ constructor(
+ args: NewTrackArgs, maxDepth: number, private trackIds: number[]) {
+ super(args);
+ this.sliceLayout.maxDepth = maxDepth + 1;
+ }
+
+ getSqlSource(): string {
+ return `
+ select
+ ts,
+ dur,
+ layout_depth as depth,
+ ifnull(name, '[null]') as name,
+ id,
+ thread_dur as threadDur
+ from experimental_slice_layout
+ where filter_track_ids = '${this.trackIds.join(',')}'
+ `;
+ }
+
+ onUpdatedSlices(slices: Slice[]) {
+ for (const slice of slices) {
+ slice.isHighlighted = (slice === this.hoveredSlice);
+ }
+ }
+}
diff --git a/ui/src/tracks/async_slices/index.ts b/ui/src/tracks/async_slices/index.ts
index 97d43fa..eb3ad88 100644
--- a/ui/src/tracks/async_slices/index.ts
+++ b/ui/src/tracks/async_slices/index.ts
@@ -12,11 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {BigintMath as BIMath} from '../../base/bigint_math';
-import {duration, time} from '../../base/time';
-import {SliceData, SliceTrackBase} from '../../frontend/slice_track_base';
import {
- EngineProxy,
Plugin,
PluginContext,
PluginContextTrace,
@@ -24,109 +20,17 @@
} from '../../public';
import {getTrackName} from '../../public/utils';
import {
- LONG,
- LONG_NULL,
NUM,
NUM_NULL,
STR,
STR_NULL,
} from '../../trace_processor/query_result';
+import {AsyncSliceTrack} from './async_slice_track';
+import {AsyncSliceTrackV2} from './async_slice_track_v2';
+
export const ASYNC_SLICE_TRACK_KIND = 'AsyncSliceTrack';
-class AsyncSliceTrack extends SliceTrackBase {
- private maxDurNs: duration = 0n;
-
- constructor(
- private engine: EngineProxy, maxDepth: number, trackKey: string,
- private trackIds: number[], namespace?: string) {
- // TODO is 'slice' right here?
- super(maxDepth, trackKey, 'slice', namespace);
- }
-
- async onBoundsChange(start: time, end: time, resolution: duration):
- Promise<SliceData> {
- if (this.maxDurNs === 0n) {
- const maxDurResult = await this.engine.query(`
- select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts,
- dur)) as maxDur from experimental_slice_layout where filter_track_ids
- = '${this.trackIds.join(',')}'
- `);
- this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
- }
-
- const queryRes = await this.engine.query(`
- SELECT
- (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
- ts,
- max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as
- dur, layout_depth as depth, ifnull(name, '[null]') as name, id, dur =
- 0 as isInstant, dur = -1 as isIncomplete
- from experimental_slice_layout
- where
- filter_track_ids = '${this.trackIds.join(',')}' and
- ts >= ${start - this.maxDurNs} and
- ts <= ${end}
- group by tsq, layout_depth
- order by tsq, layout_depth
- `);
-
- const numRows = queryRes.numRows();
- const slices: SliceData = {
- start,
- end,
- resolution,
- length: numRows,
- strings: [],
- sliceIds: new Float64Array(numRows),
- starts: new BigInt64Array(numRows),
- ends: new BigInt64Array(numRows),
- depths: new Uint16Array(numRows),
- titles: new Uint16Array(numRows),
- isInstant: new Uint16Array(numRows),
- isIncomplete: new Uint16Array(numRows),
- };
-
- const stringIndexes = new Map<string, number>();
- function internString(str: string) {
- let idx = stringIndexes.get(str);
- if (idx !== undefined) return idx;
- idx = slices.strings.length;
- slices.strings.push(str);
- stringIndexes.set(str, idx);
- return idx;
- }
-
- const it = queryRes.iter({
- tsq: LONG,
- ts: LONG,
- dur: LONG,
- depth: NUM,
- name: STR,
- id: NUM,
- isInstant: NUM,
- isIncomplete: NUM,
- });
- for (let row = 0; it.valid(); it.next(), row++) {
- const startQ = it.tsq;
- const start = it.ts;
- const dur = it.dur;
- const end = start + dur;
- const minEnd = startQ + resolution;
- const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
-
- slices.starts[row] = startQ;
- slices.ends[row] = endQ;
- slices.depths[row] = it.depth;
- slices.titles[row] = internString(it.name);
- slices.sliceIds[row] = it.id;
- slices.isInstant[row] = it.isInstant;
- slices.isIncomplete[row] = it.isIncomplete;
- }
- return slices;
- }
-}
-
class AsyncSlicePlugin implements Plugin {
onActivate(_ctx: PluginContext) {}
@@ -220,6 +124,20 @@
);
},
});
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.AsyncSlices#${rawName}.v2`,
+ displayName,
+ trackIds,
+ kind: ASYNC_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new AsyncSliceTrackV2(
+ {engine, trackKey},
+ maxDepth,
+ trackIds,
+ );
+ },
+ });
}
}
@@ -288,6 +206,20 @@
);
},
});
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.AsyncSlices#process.${pid}${rawTrackIds}.v2`,
+ displayName,
+ trackIds,
+ kind: ASYNC_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new AsyncSliceTrackV2(
+ {engine: ctx.engine, trackKey},
+ maxDepth,
+ trackIds,
+ );
+ },
+ });
}
}
}
diff --git a/ui/src/tracks/chrome_critical_user_interactions/index.ts b/ui/src/tracks/chrome_critical_user_interactions/index.ts
index f77b967..8ddb3d4 100644
--- a/ui/src/tracks/chrome_critical_user_interactions/index.ts
+++ b/ui/src/tracks/chrome_critical_user_interactions/index.ts
@@ -20,17 +20,17 @@
import {GenericSliceDetailsTab} from '../../frontend/generic_slice_details_tab';
import {globals} from '../../frontend/globals';
import {
- NAMED_SLICE_ROW,
+ NAMED_ROW,
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {Slice} from '../../frontend/slice';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
import {
+ NUM,
Plugin,
PluginContext,
PluginContextTrace,
PluginDescriptor,
PrimaryTrackSortKey,
+ Slice,
STR,
} from '../../public';
import {
@@ -41,30 +41,33 @@
} from '../custom_sql_table_slices';
import {PageLoadDetailsPanel} from './page_load_details_panel';
+import {StartupDetailsPanel} from './startup_details_panel';
export const CRITICAL_USER_INTERACTIONS_KIND =
'org.chromium.CriticalUserInteraction.track';
-export const CRITICAL_USER_INTERACTIONS_SLICE_ROW = {
- ...NAMED_SLICE_ROW,
+export const CRITICAL_USER_INTERACTIONS_ROW = {
+ ...NAMED_ROW,
+ scopedId: NUM,
type: STR,
};
-export type CriticalUserInteractionSliceRow =
- typeof CRITICAL_USER_INTERACTIONS_SLICE_ROW;
+export type CriticalUserInteractionRow = typeof CRITICAL_USER_INTERACTIONS_ROW;
export interface CriticalUserInteractionSlice extends Slice {
+ scopedId: number;
type: string;
}
export interface CriticalUserInteractionSliceTrackTypes extends
NamedSliceTrackTypes {
slice: CriticalUserInteractionSlice;
- row: CriticalUserInteractionSliceRow;
+ row: CriticalUserInteractionRow;
}
enum CriticalUserInteractionType {
UNKNOWN = 'Unknown',
PAGE_LOAD = 'chrome_page_loads',
+ STARTUP = 'chrome_startups',
}
function convertToCriticalUserInteractionType(cujType: string):
@@ -72,6 +75,8 @@
switch (cujType) {
case CriticalUserInteractionType.PAGE_LOAD:
return CriticalUserInteractionType.PAGE_LOAD;
+ case CriticalUserInteractionType.STARTUP:
+ return CriticalUserInteractionType.STARTUP;
default:
return CriticalUserInteractionType.UNKNOWN;
}
@@ -81,13 +86,19 @@
CustomSqlTableSliceTrack<CriticalUserInteractionSliceTrackTypes> {
static readonly kind = CRITICAL_USER_INTERACTIONS_KIND;
- static create(args: NewTrackArgs): TrackBase {
- return new CriticalUserInteractionTrack(args);
- }
-
getSqlDataSource(): CustomSqlTableDefConfig {
return {
- columns: ['scoped_id AS id', 'name', 'ts', 'dur', 'type'],
+ columns: [
+ // The scoped_id is not a unique identifier within the table; generate
+ // a unique id from type and scoped_id on the fly to use for slice
+ // selection.
+ 'hash(type, scoped_id) AS id',
+ 'scoped_id AS scopedId',
+ 'name',
+ 'ts',
+ 'dur',
+ 'type',
+ ],
sqlTableName: 'chrome_interactions',
};
}
@@ -113,12 +124,37 @@
},
};
break;
+ case CriticalUserInteractionType.STARTUP:
+ detailsPanel = {
+ kind: StartupDetailsPanel.kind,
+ config: {
+ sqlTableName: this.tableName,
+ title: 'Chrome Startup',
+ },
+ };
+ break;
default:
break;
}
return detailsPanel;
}
+ onSliceClick(
+ args: OnSliceClickArgs<CriticalUserInteractionSliceTrackTypes['slice']>) {
+ const detailsPanelConfig = this.getDetailsPanel(args);
+ globals.makeSelection(Actions.selectGenericSlice({
+ id: args.slice.scopedId,
+ sqlTableName: this.tableName,
+ start: args.slice.ts,
+ duration: args.slice.dur,
+ trackKey: this.trackKey,
+ detailsPanelConfig: {
+ kind: detailsPanelConfig.kind,
+ config: detailsPanelConfig.config,
+ },
+ }));
+ }
+
getSqlImports(): CustomSqlImportConfig {
return {
modules: ['chrome.interactions'],
@@ -126,14 +162,15 @@
}
getRowSpec(): CriticalUserInteractionSliceTrackTypes['row'] {
- return CRITICAL_USER_INTERACTIONS_SLICE_ROW;
+ return CRITICAL_USER_INTERACTIONS_ROW;
}
rowToSlice(row: CriticalUserInteractionSliceTrackTypes['row']):
CriticalUserInteractionSliceTrackTypes['slice'] {
const baseSlice = super.rowToSlice(row);
+ const scopedId = row.scopedId;
const type = row.type;
- return {...baseSlice, type};
+ return {...baseSlice, scopedId, type};
}
}
diff --git a/ui/src/tracks/chrome_critical_user_interactions/startup_details_panel.ts b/ui/src/tracks/chrome_critical_user_interactions/startup_details_panel.ts
new file mode 100644
index 0000000..dbcde70
--- /dev/null
+++ b/ui/src/tracks/chrome_critical_user_interactions/startup_details_panel.ts
@@ -0,0 +1,147 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+
+import {duration, Time, time} from '../../base/time';
+import {
+ BottomTab,
+ bottomTabRegistry,
+ NewBottomTabArgs,
+} from '../../frontend/bottom_tab';
+import {
+ GenericSliceDetailsTabConfig,
+} from '../../frontend/generic_slice_details_tab';
+import {DurationWidget} from '../../frontend/widgets/duration';
+import {Timestamp} from '../../frontend/widgets/timestamp';
+import {LONG, NUM, STR, STR_NULL} from '../../trace_processor/query_result';
+import {DetailsShell} from '../../widgets/details_shell';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {SqlRef} from '../../widgets/sql_ref';
+import {dictToTreeNodes, Tree} from '../../widgets/tree';
+import {asUpid, Upid} from '../../frontend/sql_types';
+
+interface Data {
+ startupId: number;
+ eventName: string;
+ startupBeginTs: time;
+ durToFirstVisibleContent: duration;
+ launchCause?: string;
+ upid: Upid;
+}
+
+export class StartupDetailsPanel extends
+ BottomTab<GenericSliceDetailsTabConfig> {
+ static readonly kind = 'org.perfetto.StartupDetailsPanel';
+ private loaded = false;
+ private data: Data|undefined;
+
+ static create(args: NewBottomTabArgs): StartupDetailsPanel {
+ return new StartupDetailsPanel(args);
+ }
+
+ constructor(args: NewBottomTabArgs) {
+ super(args);
+ this.loadData();
+ }
+
+ private async loadData() {
+ const queryResult = await this.engine.query(`
+ SELECT
+ activity_id AS startupId,
+ name,
+ startup_begin_ts AS startupBeginTs,
+ CASE
+ WHEN first_visible_content_ts IS NULL THEN 0
+ ELSE first_visible_content_ts - startup_begin_ts
+ END AS durTofirstVisibleContent,
+ launch_cause AS launchCause,
+ browser_upid AS upid
+ FROM chrome_startups
+ WHERE id = ${this.config.id};
+ `);
+
+ const iter = queryResult.firstRow({
+ startupId: NUM,
+ name: STR,
+ startupBeginTs: LONG,
+ durTofirstVisibleContent: LONG,
+ launchCause: STR_NULL,
+ upid: NUM,
+ });
+
+ this.data = {
+ startupId: iter.startupId,
+ eventName: iter.name,
+ startupBeginTs: Time.fromRaw(iter.startupBeginTs),
+ durToFirstVisibleContent: iter.durTofirstVisibleContent,
+ upid: asUpid(iter.upid),
+ };
+
+ if (iter.launchCause) {
+ this.data.launchCause = iter.launchCause;
+ }
+
+ this.loaded = true;
+ }
+
+ private getDetailsDictionary() {
+ const details: {[key: string]: m.Child} = {};
+ if (this.data === undefined) return details;
+ details['Activity ID'] = this.data.startupId;
+ details['Browser Upid'] = this.data.upid;
+ details['Startup Event'] = this.data.eventName;
+ details['Startup Timestamp'] = m(Timestamp, {ts: this.data.startupBeginTs});
+ details['Duration to First Visible Content'] =
+ m(DurationWidget, {dur: this.data.durToFirstVisibleContent});
+ if (this.data.launchCause) {
+ details['Launch Cause'] = this.data.launchCause;
+ }
+ details['SQL ID'] =
+ m(SqlRef, {table: 'chrome_startups', id: this.config.id});
+ return details;
+ }
+
+ viewTab() {
+ if (this.isLoading()) {
+ return m('h2', 'Loading');
+ }
+
+ return m(
+ DetailsShell,
+ {
+ title: this.getTitle(),
+ },
+ m(GridLayout,
+ m(
+ GridLayoutColumn,
+ m(
+ Section,
+ {title: 'Details'},
+ m(Tree, dictToTreeNodes(this.getDetailsDictionary())),
+ ),
+ )));
+ }
+
+ getTitle(): string {
+ return this.config.title;
+ }
+
+ isLoading() {
+ return !this.loaded;
+ }
+}
+
+bottomTabRegistry.register(StartupDetailsPanel);
diff --git a/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts b/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
index 95d1924..5b87694 100644
--- a/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
@@ -17,7 +17,7 @@
NamedSliceTrack,
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {NewTrackArgs} from '../../frontend/track';
import {Engine} from '../../trace_processor/engine';
import {NUM} from '../../trace_processor/query_result';
@@ -35,9 +35,6 @@
export class ChromeTasksScrollJankTrack extends
NamedSliceTrack<ChromeTasksScrollJankTrackTypes> {
static readonly kind = 'org.chromium.ScrollJank.BrowserUIThreadLongTasks';
- static create(args: NewTrackArgs): TrackBase {
- return new ChromeTasksScrollJankTrack(args);
- }
constructor(args: NewTrackArgs) {
super(args);
diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
index 5014d14..0510eeb 100644
--- a/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
@@ -14,7 +14,7 @@
import m from 'mithril';
-import {exists} from '../../base/utils';
+import {duration, time} from '../../base/time';
import {raf} from '../../core/raf_scheduler';
import {
BottomTab,
@@ -27,8 +27,14 @@
import {renderArguments} from '../../frontend/slice_args';
import {renderDetails} from '../../frontend/slice_details';
import {getSlice, SliceDetails, sliceRef} from '../../frontend/sql/slice';
-import {asSliceSqlId} from '../../frontend/sql_types';
-import {NUM} from '../../trace_processor/query_result';
+import {asSliceSqlId, SliceSqlId} from '../../frontend/sql_types';
+import {
+ ColumnDescriptor,
+ Table,
+ TableData,
+ widgetColumn,
+} from '../../frontend/tables/table';
+import {NUM, STR} from '../../trace_processor/query_result';
import {DetailsShell} from '../../widgets/details_shell';
import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
import {Section} from '../../widgets/section';
@@ -36,6 +42,14 @@
import {Tree, TreeNode} from '../../widgets/tree';
import {
+ EventLatencyCauseThreadTracks,
+ EventLatencyStage,
+ getCauseLink,
+ getEventLatencyCauseTracks,
+ getScrollJankCauseStage,
+} from './scroll_jank_cause_link_utils';
+import {ScrollJankCauseMap} from './scroll_jank_cause_map';
+import {
getScrollJankSlices,
getSliceForTrack,
ScrollJankSlice,
@@ -47,10 +61,22 @@
static readonly kind = 'dev.perfetto.EventLatencySliceDetailsPanel';
private loaded = false;
+ private name = '';
+ private topEventLatencyId: SliceSqlId|undefined = undefined;
private sliceDetails?: SliceDetails;
private jankySlice?: ScrollJankSlice;
+ // Whether this stage has caused jank. This is also true for top level
+ // EventLatency slices where a descendant is a cause of jank.
+ private isJankStage = false;
+
+ // For top level EventLatency slices - if any descendant is a cause of jank,
+ // this field stores information about that descendant slice. Otherwise, this
+ // is stores information about the current stage;
+ private relevantThreadStage: EventLatencyStage|undefined;
+ private relevantThreadTracks: EventLatencyCauseThreadTracks[] = [];
+
static create(args: NewBottomTabArgs): EventLatencySliceDetailsPanel {
return new EventLatencySliceDetailsPanel(args);
}
@@ -62,8 +88,22 @@
}
async loadData() {
+ const queryResult = await this.engine.query(`
+ SELECT
+ name
+ FROM ${this.config.sqlTableName}
+ WHERE id = ${this.config.id}
+ `);
+
+ const iter = queryResult.firstRow({
+ name: STR,
+ });
+
+ this.name = iter.name;
+
await this.loadSlice();
await this.loadJankSlice();
+ await this.loadRelevantThreads();
this.loaded = true;
}
@@ -74,37 +114,152 @@
}
async loadJankSlice() {
- if (exists(this.sliceDetails)) {
- // Get the id for the top-level EventLatency slice (this or parent), as
- // this id is used in the ScrollJankV3 track to identify the corresponding
- // janky interval.
- let eventLatencyId = -1;
- if (this.sliceDetails.name == 'EventLatency') {
- eventLatencyId = this.sliceDetails.id;
- } else {
- const queryResult = await this.engine.query(`
- SELECT
- id
- FROM ancestor_slice(${this.sliceDetails.id})
- WHERE name = 'EventLatency'
- `);
- const it = queryResult.iter({
- id: NUM,
- });
- for (; it.valid(); it.next()) {
- eventLatencyId = it.id;
- break;
- }
- }
-
- const possibleSlices =
- await getScrollJankSlices(this.engine, eventLatencyId);
- // We may not get any slices if the EventLatency doesn't indicate any
- // jank occurred.
- if (possibleSlices.length > 0) {
- this.jankySlice = possibleSlices[0];
- }
+ if (!this.sliceDetails) return;
+ // Get the id for the top-level EventLatency slice (this or parent), as
+ // this id is used in the ScrollJankV3 track to identify the corresponding
+ // janky interval.
+ if (this.sliceDetails.name === 'EventLatency') {
+ this.topEventLatencyId = this.sliceDetails.id;
+ } else {
+ this.topEventLatencyId =
+ asSliceSqlId(await this.getOldestAncestorSliceId());
}
+
+ const possibleSlices =
+ await getScrollJankSlices(this.engine, this.topEventLatencyId);
+ // We may not get any slices if the EventLatency doesn't indicate any
+ // jank occurred.
+ if (possibleSlices.length > 0) {
+ this.jankySlice = possibleSlices[0];
+ }
+ }
+
+ async loadRelevantThreads() {
+ if (!this.sliceDetails) return;
+ if (!this.topEventLatencyId) return;
+
+ // Relevant threads should only be available on a "Janky" EventLatency
+ // slice to allow the user to jump to the possible cause of jank.
+ if (this.sliceDetails.name === 'EventLatency' && !this.jankySlice) return;
+
+ const possibleScrollJankStage =
+ await getScrollJankCauseStage(this.engine, this.topEventLatencyId);
+ if (this.sliceDetails.name === 'EventLatency') {
+ this.isJankStage = true;
+ this.relevantThreadStage = possibleScrollJankStage;
+ } else {
+ if (possibleScrollJankStage &&
+ this.sliceDetails.name === possibleScrollJankStage.name) {
+ this.isJankStage = true;
+ }
+ this.relevantThreadStage = {
+ name: this.sliceDetails.name,
+ eventLatencyId: this.topEventLatencyId,
+ ts: this.sliceDetails.ts,
+ dur: this.sliceDetails.dur,
+ };
+ }
+
+ if (this.relevantThreadStage) {
+ this.relevantThreadTracks = await getEventLatencyCauseTracks(
+ this.engine, this.relevantThreadStage);
+ }
+ }
+
+ private getRelevantLinks(): m.Child {
+ if (!this.sliceDetails) return undefined;
+
+ // Relevant threads should only be available on a "Janky" EventLatency
+ // slice to allow the user to jump to the possible cause of jank.
+ if (this.sliceDetails.name === 'EventLatency' &&
+ !this.relevantThreadStage) {
+ return undefined;
+ }
+
+ const name = this.relevantThreadStage ? this.relevantThreadStage.name :
+ this.sliceDetails.name;
+ const ts = this.relevantThreadStage ? this.relevantThreadStage.ts :
+ this.sliceDetails.ts;
+ const dur = this.relevantThreadStage ? this.relevantThreadStage.dur :
+ this.sliceDetails.dur;
+ const stageDetails = ScrollJankCauseMap.getEventLatencyDetails(name);
+ if (stageDetails === undefined) return undefined;
+
+ const childWidgets: m.Child[] = [];
+ childWidgets.push(m(TextParagraph, {text: stageDetails.description}));
+
+ interface RelevantThreadRow {
+ description: string;
+ tracks: EventLatencyCauseThreadTracks;
+ ts: time;
+ dur: duration;
+ }
+
+ const columns: ColumnDescriptor<RelevantThreadRow>[] = [
+ widgetColumn<RelevantThreadRow>(
+ 'Relevant Thread', (x) => getCauseLink(x.tracks, x.ts, x.dur)),
+ widgetColumn<RelevantThreadRow>(
+ 'Description',
+ (x) => {
+ if (x.description === '') {
+ return x.description;
+ } else {
+ return m(TextParagraph, {text: x.description});
+ }
+ }),
+ ];
+
+ const trackLinks: RelevantThreadRow[] = [];
+
+ for (let i = 0; i < this.relevantThreadTracks.length; i++) {
+ const track = this.relevantThreadTracks[i];
+ let description = '';
+ if (i == 0 || track.thread != this.relevantThreadTracks[i - 1].thread) {
+ description = track.causeDescription;
+ }
+ trackLinks.push({
+ description: description,
+ tracks: this.relevantThreadTracks[i],
+ ts: ts,
+ dur: dur,
+ });
+ }
+
+ const tableData = new TableData(trackLinks);
+
+ if (trackLinks.length > 0) {
+ childWidgets.push(m(Table, {
+ data: tableData,
+ columns: columns,
+ }));
+ }
+
+ return m(
+ Section,
+ {title: this.isJankStage ? `Jank Cause: ${name}` : name},
+ childWidgets);
+ }
+
+ private async getOldestAncestorSliceId(): Promise<number> {
+ let eventLatencyId = -1;
+ if (!this.sliceDetails) return eventLatencyId;
+ const queryResult = await this.engine.query(`
+ SELECT
+ id
+ FROM ancestor_slice(${this.sliceDetails.id})
+ WHERE name = 'EventLatency'
+ `);
+
+ const it = queryResult.iter({
+ id: NUM,
+ });
+
+ for (; it.valid(); it.next()) {
+ eventLatencyId = it.id;
+ break;
+ }
+
+ return eventLatencyId;
}
private getLinksSection(): m.Child {
@@ -114,20 +269,20 @@
m(
Tree,
m(TreeNode, {
- left: exists(this.sliceDetails) ?
+ left: this.sliceDetails ?
sliceRef(
this.sliceDetails,
'EventLatency in context of other Input events') :
'EventLatency in context of other Input events',
- right: exists(this.sliceDetails) ? '' : 'N/A',
+ right: this.sliceDetails ? '' : 'N/A',
}),
m(TreeNode, {
- left: exists(this.jankySlice) ? getSliceForTrack(
- this.jankySlice,
- ScrollJankV3Track.kind,
- 'Jank Interval') :
- 'Jank Interval',
- right: exists(this.jankySlice) ? '' : 'N/A',
+ left: this.jankySlice ? getSliceForTrack(
+ this.jankySlice,
+ ScrollJankV3Track.kind,
+ 'Jank Interval') :
+ 'Jank Interval',
+ right: this.jankySlice ? '' : 'N/A',
}),
),
);
@@ -160,23 +315,34 @@
}
viewTab() {
- if (exists(this.sliceDetails)) {
+ if (this.sliceDetails) {
const slice = this.sliceDetails;
+
+ const rightSideWidgets: m.Child[] = [];
+ rightSideWidgets.push(
+ m(Section,
+ {title: 'Description'},
+ m('.div', this.getDescriptionText())));
+
+ const stageWidget = this.getRelevantLinks();
+ if (stageWidget) {
+ rightSideWidgets.push(stageWidget);
+ }
+ rightSideWidgets.push(this.getLinksSection());
+
return m(
DetailsShell,
{
title: 'Slice',
- description: slice.name,
+ description: this.name,
},
m(GridLayout,
m(GridLayoutColumn,
renderDetails(slice),
- renderArguments(this.engine, slice)),
- m(GridLayoutColumn,
m(Section,
- {title: 'Description'},
- m('.div', this.getDescriptionText())),
- this.getLinksSection())),
+ {title: 'Arguments'},
+ m(Tree, renderArguments(this.engine, slice)))),
+ m(GridLayoutColumn, rightSideWidgets)),
);
} else {
return m(DetailsShell, {title: 'Slice', description: 'Loading...'});
diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
index fcd0b5d..b85116f 100644
--- a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts
@@ -12,15 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {
- getColorForSlice,
-} from '../../common/colorizer';
import {globals} from '../../frontend/globals';
-import {
- NamedSliceTrackTypes,
-} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
-import {PrimaryTrackSortKey} from '../../public';
+import {NamedRow, NamedSliceTrackTypes} from '../../frontend/named_slice_track';
+import {NewTrackArgs} from '../../frontend/track';
+import {PrimaryTrackSortKey, Slice} from '../../public';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
@@ -33,7 +28,7 @@
ScrollJankPluginState,
ScrollJankTracks as DecideTracksResult,
} from './index';
-import {DEEP_RED_COLOR, RED_COLOR} from './jank_colors';
+import {JANK_COLOR} from './jank_colors';
export const JANKY_LATENCY_NAME = 'Janky EventLatency';
@@ -48,11 +43,7 @@
CustomSqlTableSliceTrack<EventLatencyTrackTypes> {
static readonly kind = CHROME_EVENT_LATENCY_TRACK_KIND;
- static create(args: NewTrackArgs): TrackBase {
- return new EventLatencyTrack(args);
- }
-
- constructor(args: NewTrackArgs) {
+ constructor(args: NewTrackArgs, private baseTable: string) {
super(args);
ScrollJankPluginState.getInstance().registerTrack({
kind: EventLatencyTrack.kind,
@@ -68,7 +59,7 @@
}
getSqlSource(): string {
- return `SELECT * FROM ${this.config.baseTable}`;
+ return `SELECT * FROM ${this.baseTable}`;
}
getDetailsPanel(): CustomSqlDetailsPanelConfig {
@@ -80,21 +71,18 @@
getSqlDataSource(): CustomSqlTableDefConfig {
return {
- sqlTableName: this.config.baseTable,
+ sqlTableName: this.baseTable,
};
}
- // TODO(stevegolton): The janky event color should be assigned in rowToSlice.
- // For example:
-
- // rowToSlice(row: NamedSliceRow): Slice {
- // const baseSlice = super.rowToSlice(row);
- // if (baseSlice.title === JANKY_LATENCY_NAME) {
- // return {...baseSlice, baseColor: RED_COLOR};
- // } else {
- // return baseSlice;
- // }
- // }
+ rowToSlice(row: NamedRow): Slice {
+ const baseSlice = super.rowToSlice(row);
+ if (baseSlice.title === JANKY_LATENCY_NAME) {
+ return {...baseSlice, colorScheme: JANK_COLOR};
+ } else {
+ return baseSlice;
+ }
+ }
onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) {
for (const slice of slices) {
@@ -105,16 +93,7 @@
const highlighted = globals.state.highlightedSliceId === slice.id;
const hasFocus = highlighted || isSelected;
-
- if (slice.title === JANKY_LATENCY_NAME) {
- if (hasFocus) {
- slice.baseColor = DEEP_RED_COLOR;
- } else {
- slice.baseColor = RED_COLOR;
- }
- } else {
- slice.baseColor = getColorForSlice(slice.title, hasFocus);
- }
+ slice.isHighlighted = !!hasFocus;
}
super.onUpdatedSlices(slices);
}
diff --git a/ui/src/tracks/chrome_scroll_jank/index.ts b/ui/src/tracks/chrome_scroll_jank/index.ts
index bec5a05..34df4e1 100644
--- a/ui/src/tracks/chrome_scroll_jank/index.ts
+++ b/ui/src/tracks/chrome_scroll_jank/index.ts
@@ -38,6 +38,7 @@
EventLatencyTrack,
JANKY_LATENCY_NAME,
} from './event_latency_track';
+import {ScrollJankCauseMap} from './scroll_jank_cause_map';
import {
addScrollJankV3ScrollTrack,
ScrollJankV3Track,
@@ -117,7 +118,7 @@
}
}
-export async function getScrollJankTracks(_engine: Engine):
+export async function getScrollJankTracks(engine: Engine):
Promise<ScrollJankTrackGroup> {
const result: ScrollJankTracks = {
tracksToAdd: [],
@@ -149,6 +150,7 @@
fixedOrdering: true,
});
+ await ScrollJankCauseMap.initialize(engine);
return {tracks: result, addTrackGroup};
}
@@ -238,9 +240,11 @@
'FIRST_GESTURE_SCROLL_UPDATE',
'GESTURE_SCROLL_UPDATE',
'INERTIAL_GESTURE_SCROLL_UPDATE')
- AND HAS_DESCENDANT_SLICE_WITH_NAME(
+ AND has_descendant_slice_with_name(
id,
- 'SubmitCompositorFrameToPresentationCompositorFrame')`,
+ 'SubmitCompositorFrameToPresentationCompositorFrame')
+ AND name = 'EventLatency'
+ AND depth = 0`,
});
// Table name must be unique - it cannot include '-' characters or begin
@@ -248,22 +252,22 @@
const baseTable =
`table_${uuidv4().split('-').join('_')}_janky_event_latencies_v3`;
const tableDefSql = `CREATE TABLE ${baseTable} AS
- WITH event_latencies AS (
+ WITH
+ event_latencies AS MATERIALIZED (
${subTableSql}
- ), latency_stages AS (
- SELECT
- d.id,
- d.ts,
- d.dur,
- d.track_id,
- d.name,
- d.depth,
- min(a.id) AS parent_id
- FROM slice s
- JOIN descendant_slice(s.id) d
- JOIN ancestor_slice(d.id) a
- WHERE s.id IN (SELECT id FROM event_latencies)
- GROUP BY d.id, d.ts, d.dur, d.track_id, d.name, d.parent_id, d.depth)
+ ),
+ latency_stages AS (
+ SELECT
+ stage.id,
+ stage.ts,
+ stage.dur,
+ stage.track_id,
+ stage.name,
+ stage.depth,
+ event.id as event_latency_id
+ FROM event_latencies event
+ JOIN descendant_slice(event.id) stage
+ )
SELECT
id,
ts,
@@ -279,14 +283,17 @@
FROM event_latencies
UNION ALL
SELECT
- ls.id,
- ls.ts,
- ls.dur,
- ls.name,
- depth + (
- (SELECT depth FROM event_latencies
- WHERE id = ls.parent_id LIMIT 1) * 3) AS depth
- FROM latency_stages ls;`;
+ stage.id,
+ stage.ts,
+ stage.dur,
+ stage.name,
+ stage.depth + (
+ (
+ SELECT depth FROM event_latencies
+ WHERE id = stage.event_latency_id
+ ) * 3
+ ) AS depth
+ FROM latency_stages stage;`;
await ctx.engine.query(
`INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_intervals`);
@@ -297,16 +304,7 @@
displayName: 'Chrome Scroll Input Latencies',
kind: EventLatencyTrack.kind,
track: ({trackKey}) => {
- const track = new EventLatencyTrack({
- engine: ctx.engine,
- trackKey,
- });
-
- track.config = {
- baseTable,
- };
-
- return track;
+ return new EventLatencyTrack({engine: ctx.engine, trackKey}, baseTable);
},
});
}
diff --git a/ui/src/tracks/chrome_scroll_jank/jank_colors.ts b/ui/src/tracks/chrome_scroll_jank/jank_colors.ts
index f556f7d..22d508e 100644
--- a/ui/src/tracks/chrome_scroll_jank/jank_colors.ts
+++ b/ui/src/tracks/chrome_scroll_jank/jank_colors.ts
@@ -12,16 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Color} from '../../common/colorizer';
+import {HSLColor} from '../../common/color';
+import {makeColorScheme} from '../../common/colorizer';
-export const RED_COLOR: Color = {
- h: 7,
- s: 100,
- l: 46,
-};
-
-export const DEEP_RED_COLOR: Color = {
- h: 11,
- s: 100,
- l: 32,
-};
+export const JANK_COLOR = makeColorScheme(new HSLColor([343, 100, 43]));
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
index 63e913f..2837ce1 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
@@ -31,6 +31,7 @@
numberColumn,
Table,
TableData,
+ widgetColumn,
} from '../../frontend/tables/table';
import {DurationWidget} from '../../frontend/widgets/duration';
import {Timestamp} from '../../frontend/widgets/timestamp';
@@ -55,11 +56,6 @@
} from './scroll_jank_slice';
import {ScrollJankV3Track} from './scroll_jank_v3_track';
-function widgetColumn<T>(
- name: string, getter: (t: T) => m.Child): ColumnDescriptor<T> {
- return new ColumnDescriptor<T>(name, getter);
-}
-
interface Data {
// Scroll ID.
id: number;
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
new file mode 100644
index 0000000..7209356
--- /dev/null
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
@@ -0,0 +1,218 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+
+import {Icons} from '../../base/semantic_icons';
+import {duration, Time, time} from '../../base/time';
+import {exists} from '../../base/utils';
+import {Actions} from '../../common/actions';
+import {globals} from '../../frontend/globals';
+import {
+ focusHorizontalRange,
+ verticalScrollToTrack,
+} from '../../frontend/scroll_helper';
+import {SliceSqlId} from '../../frontend/sql_types';
+import {EngineProxy} from '../../trace_processor/engine';
+import {LONG, NUM, STR} from '../../trace_processor/query_result';
+import {Anchor} from '../../widgets/anchor';
+
+import {
+ CauseProcess,
+ CauseThread,
+ ScrollJankCauseMap,
+} from './scroll_jank_cause_map';
+
+const UNKNOWN_NAME = 'Unknown';
+
+export interface EventLatencyStage {
+ name: string;
+ // Slice id of the top level EventLatency slice (not a stage).
+ eventLatencyId: SliceSqlId;
+ ts: time;
+ dur: duration;
+}
+
+export interface EventLatencyCauseThreadTracks {
+ // A thread may have multiple tracks associated with it (e.g. from ATrace
+ // events).
+ trackIds: number[];
+ thread: CauseThread;
+ causeDescription: string;
+}
+
+export async function getScrollJankCauseStage(
+ engine: EngineProxy,
+ eventLatencyId: SliceSqlId): Promise<EventLatencyStage|undefined> {
+ const queryResult = await engine.query(`
+ SELECT
+ IFNULL(cause_of_jank, '${UNKNOWN_NAME}') AS causeOfJank,
+ IFNULL(sub_cause_of_jank, '${UNKNOWN_NAME}') AS subCauseOfJank,
+ IFNULL(substage.ts, -1) AS ts,
+ IFNULL(substage.dur, -1) AS dur
+ FROM chrome_janky_frame_presentation_intervals
+ JOIN descendant_slice(event_latency_id) substage
+ WHERE event_latency_id = ${eventLatencyId}
+ AND substage.name = COALESCE(sub_cause_of_jank, cause_of_jank)
+ `);
+
+ const causeIt = queryResult.iter({
+ causeOfJank: STR,
+ subCauseOfJank: STR,
+ ts: LONG,
+ dur: LONG,
+ });
+
+ for (; causeIt.valid(); causeIt.next()) {
+ const causeOfJank = causeIt.causeOfJank;
+ const subCauseOfJank = causeIt.subCauseOfJank;
+
+ if (causeOfJank == '' || causeOfJank == UNKNOWN_NAME) return undefined;
+ const cause = subCauseOfJank == UNKNOWN_NAME ? causeOfJank : subCauseOfJank;
+ const stageDetails: EventLatencyStage = {
+ name: cause,
+ eventLatencyId: eventLatencyId,
+ ts: Time.fromRaw(causeIt.ts),
+ dur: causeIt.dur,
+ };
+
+ return stageDetails;
+ }
+
+ return undefined;
+}
+
+export async function getEventLatencyCauseTracks(
+ engine: EngineProxy, scrollJankCauseStage: EventLatencyStage):
+ Promise<EventLatencyCauseThreadTracks[]> {
+ const threadTracks: EventLatencyCauseThreadTracks[] = [];
+ const causeDetails =
+ ScrollJankCauseMap.getEventLatencyDetails(scrollJankCauseStage.name);
+ if (causeDetails === undefined) return threadTracks;
+
+ for (const cause of causeDetails.jankCauses) {
+ switch (cause.process) {
+ case CauseProcess.RENDERER:
+ case CauseProcess.BROWSER:
+ case CauseProcess.GPU:
+ const tracksForProcess = await getChromeCauseTracks(
+ engine,
+ scrollJankCauseStage.eventLatencyId,
+ cause.process,
+ cause.thread);
+ for (const track of tracksForProcess) {
+ track.causeDescription = cause.description;
+ threadTracks.push(track);
+ }
+ break;
+ case CauseProcess.UNKNOWN:
+ default:
+ break;
+ }
+ }
+
+ return threadTracks;
+}
+
+async function getChromeCauseTracks(
+ engine: EngineProxy,
+ eventLatencySliceId: number,
+ processName: CauseProcess,
+ threadName: CauseThread): Promise<EventLatencyCauseThreadTracks[]> {
+ const queryResult = await engine.query(`
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_cause_utils;
+
+ SELECT DISTINCT
+ utid,
+ id AS trackId
+ FROM thread_track
+ WHERE utid IN (
+ SELECT DISTINCT
+ utid
+ FROM chrome_select_scroll_jank_cause_thread(
+ ${eventLatencySliceId},
+ '${processName}',
+ '${threadName}'
+ )
+ );
+ `);
+
+ const it = queryResult.iter({
+ utid: NUM,
+ trackId: NUM,
+ });
+
+ const threadsWithTrack: {[id: number]: EventLatencyCauseThreadTracks;} = {};
+ const utids: number[] = [];
+ for (; it.valid(); it.next()) {
+ const utid = it.utid;
+ if (!(utid in threadsWithTrack)) {
+ threadsWithTrack[utid] = {
+ trackIds: [it.trackId],
+ thread: threadName,
+ causeDescription: '',
+ };
+ utids.push(utid);
+ } else {
+ threadsWithTrack[utid].trackIds.push(it.trackId);
+ }
+ }
+
+ return utids.map((each) => threadsWithTrack[each]);
+}
+
+export function getCauseLink(
+ threadTracks: EventLatencyCauseThreadTracks,
+ ts: time|undefined,
+ dur: duration|undefined): m.Child {
+ const trackKeys: string[] = [];
+ for (const trackId of threadTracks.trackIds) {
+ const trackKey = globals.state.trackKeyByTrackId[trackId];
+ if (trackKey === undefined) {
+ return `Could not locate track ${trackId} for thread ${
+ threadTracks.thread} in the global state`;
+ }
+ trackKeys.push(trackKey);
+ }
+
+ if (trackKeys.length == 0) {
+ return `No valid tracks for thread ${threadTracks.thread}.`;
+ }
+
+ // Fixed length of a container to ensure that the icon does not overlap with
+ // the text due to table formatting.
+ return m(
+ `div[style='width:250px']`,
+ m(Anchor,
+ {
+ icon: Icons.UpdateSelection,
+ onclick: () => {
+ verticalScrollToTrack(trackKeys[0], true);
+ if (exists(ts) && exists(dur)) {
+ focusHorizontalRange(ts, Time.fromRaw(ts + dur), 0.3);
+ globals.frontendLocalState.selectArea(
+ ts, Time.fromRaw(ts + dur), trackKeys);
+
+ globals.dispatch(Actions.selectArea({
+ area: {
+ start: ts,
+ end: Time.fromRaw(ts + dur),
+ tracks: trackKeys,
+ },
+ }));
+ }
+ },
+ },
+ threadTracks.thread));
+}
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_map.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_map.ts
new file mode 100644
index 0000000..c4842ef
--- /dev/null
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_cause_map.ts
@@ -0,0 +1,149 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {exists} from '../../base/utils';
+import {Engine} from '../../trace_processor/engine';
+import {STR} from '../../trace_processor/query_result';
+
+export enum CauseProcess {
+ UNKNOWN,
+ BROWSER = 'Browser',
+ RENDERER = 'Renderer',
+ GPU = 'GPU',
+}
+
+export enum CauseThread {
+ UNKNOWN,
+ BROWSER_MAIN = 'CrBrowserMain',
+ RENDERER_MAIN = 'CrRendererMain',
+ COMPOSITOR = 'Compositor',
+ CHROME_CHILD_IO_THREAD = 'Chrome_ChildIOThread',
+ VIZ_COMPOSITOR = 'VizCompositorThread',
+ SURFACE_FLINGER = 'surfaceflinger'
+}
+
+export interface ScrollJankCause {
+ description: string;
+ process: CauseProcess;
+ thread: CauseThread;
+}
+
+export interface EventLatencyStageDetails {
+ description: string;
+ jankCauses: ScrollJankCause[];
+}
+
+export interface ScrollJankCauseMapInternal {
+ // Key corresponds with the EventLatency stage.
+ [key: string]: EventLatencyStageDetails;
+}
+
+function getScrollJankProcess(process: string): CauseProcess {
+ switch (process) {
+ case CauseProcess.BROWSER:
+ return CauseProcess.BROWSER;
+ case CauseProcess.RENDERER:
+ return CauseProcess.RENDERER;
+ case CauseProcess.GPU:
+ return CauseProcess.GPU;
+ default:
+ return CauseProcess.UNKNOWN;
+ }
+}
+
+function getScrollJankThread(thread: string): CauseThread {
+ switch (thread) {
+ case CauseThread.BROWSER_MAIN:
+ return CauseThread.BROWSER_MAIN;
+ case CauseThread.RENDERER_MAIN:
+ return CauseThread.RENDERER_MAIN;
+ case CauseThread.CHROME_CHILD_IO_THREAD:
+ return CauseThread.CHROME_CHILD_IO_THREAD;
+ case CauseThread.COMPOSITOR:
+ return CauseThread.COMPOSITOR;
+ case CauseThread.VIZ_COMPOSITOR:
+ return CauseThread.VIZ_COMPOSITOR;
+ case CauseThread.SURFACE_FLINGER:
+ return CauseThread.SURFACE_FLINGER;
+ default:
+ return CauseThread.UNKNOWN;
+ }
+}
+
+export class ScrollJankCauseMap {
+ private static instance: ScrollJankCauseMap;
+ private causes: ScrollJankCauseMapInternal;
+
+ private constructor() {
+ this.causes = {};
+ }
+
+ private async initializeCauseMap(engine: Engine) {
+ const queryResult = await engine.query(`
+ INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_cause_map;
+
+ SELECT
+ IFNULL(name, '') AS name,
+ IFNULL(description, '') AS description,
+ IFNULL(cause_process, '') AS causeProcess,
+ IFNULL(cause_thread, '') AS causeThread,
+ IFNULL(cause_description, '') AS causeDescription
+ FROM chrome_scroll_jank_causes_with_event_latencies;
+ `);
+
+ const iter = queryResult.iter({
+ name: STR,
+ description: STR,
+ causeProcess: STR,
+ causeThread: STR,
+ causeDescription: STR,
+ });
+
+ for (; iter.valid(); iter.next()) {
+ const eventLatencyStage = iter.name;
+ if (!(eventLatencyStage in this.causes)) {
+ this.causes[eventLatencyStage] = {
+ description: iter.description,
+ jankCauses: [] as ScrollJankCause[],
+ };
+ }
+
+ const causeProcess = getScrollJankProcess(iter.causeProcess);
+ const causeThread = getScrollJankThread(iter.causeThread);
+
+ this.causes[eventLatencyStage].jankCauses.push({
+ description: iter.causeDescription,
+ process: causeProcess,
+ thread: causeThread,
+ });
+ }
+ }
+
+ // Must be called before this item is accessed, as the object is populated
+ // from SQL data.
+ public static async initialize(engine: Engine) {
+ if (!exists(ScrollJankCauseMap.instance)) {
+ ScrollJankCauseMap.instance = new ScrollJankCauseMap();
+ await ScrollJankCauseMap.instance.initializeCauseMap(engine);
+ }
+ }
+
+ public static getEventLatencyDetails(eventLatency: string):
+ EventLatencyStageDetails|undefined {
+ if (eventLatency in ScrollJankCauseMap.instance.causes) {
+ return ScrollJankCauseMap.instance.causes[eventLatency];
+ }
+ return undefined;
+ }
+}
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
index 70f6f0a..14d296a 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
@@ -12,13 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {
- getColorForSlice,
-} from '../../common/colorizer';
import {globals} from '../../frontend/globals';
-import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
-import {PrimaryTrackSortKey} from '../../public';
+import {NamedRow, NamedSliceTrackTypes} from '../../frontend/named_slice_track';
+import {NewTrackArgs} from '../../frontend/track';
+import {PrimaryTrackSortKey, Slice} from '../../public';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
@@ -31,7 +28,7 @@
ScrollJankPluginState,
ScrollJankTracks as DecideTracksResult,
} from './index';
-import {DEEP_RED_COLOR, RED_COLOR} from './jank_colors';
+import {JANK_COLOR} from './jank_colors';
import {ScrollJankV3DetailsPanel} from './scroll_jank_v3_details_panel';
const UNKNOWN_SLICE_NAME = 'Unknown';
@@ -41,10 +38,6 @@
CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
static readonly kind = 'org.chromium.ScrollJank.scroll_jank_v3_track';
- static create(args: NewTrackArgs): TrackBase {
- return new ScrollJankV3Track(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
ScrollJankPluginState.getInstance().registerTrack({
@@ -87,6 +80,24 @@
ScrollJankPluginState.getInstance().unregisterTrack(ScrollJankV3Track.kind);
}
+ rowToSlice(row: NamedRow): Slice {
+ const slice = super.rowToSlice(row);
+
+ let stage = slice.title.substring(0, slice.title.indexOf(JANK_SLICE_NAME));
+ // Stage may include substage, in which case we use the substage for
+ // color selection.
+ const separator = '::';
+ if (stage.indexOf(separator) != -1) {
+ stage = stage.substring(stage.indexOf(separator) + separator.length);
+ }
+
+ if (stage == UNKNOWN_SLICE_NAME) {
+ return {...slice, colorScheme: JANK_COLOR};
+ } else {
+ return slice;
+ }
+ }
+
onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) {
for (const slice of slices) {
const currentSelection = globals.state.currentSelection;
@@ -96,25 +107,7 @@
const highlighted = globals.state.highlightedSliceId === slice.id;
const hasFocus = highlighted || isSelected;
-
- let stage =
- slice.title.substring(0, slice.title.indexOf(JANK_SLICE_NAME));
- // Stage may include substage, in which case we use the substage for
- // color selection.
- const separator = '::';
- if (stage.indexOf(separator) != -1) {
- stage = stage.substring(stage.indexOf(separator) + separator.length);
- }
-
- if (stage == UNKNOWN_SLICE_NAME) {
- if (hasFocus) {
- slice.baseColor = DEEP_RED_COLOR;
- } else {
- slice.baseColor = RED_COLOR;
- }
- } else {
- slice.baseColor = getColorForSlice(stage, hasFocus);
- }
+ slice.isHighlighted = !!hasFocus;
}
super.onUpdatedSlices(slices);
}
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
index 7eaba65..a888844 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
@@ -13,13 +13,14 @@
// limitations under the License.
import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {NewTrackArgs} from '../../frontend/track';
import {PrimaryTrackSortKey} from '../../public';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
CustomSqlTableSliceTrack,
} from '../custom_sql_table_slices';
+
import {
SCROLL_JANK_GROUP_ID,
ScrollJankPluginState,
@@ -33,9 +34,6 @@
export class TopLevelScrollTrack extends
CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
public static kind = CHROME_TOPLEVEL_SCROLLS_KIND;
- static create(args: NewTrackArgs): TrackBase {
- return new TopLevelScrollTrack(args);
- }
getSqlDataSource(): CustomSqlTableDefConfig {
return {
diff --git a/ui/src/tracks/chrome_slices/generic_slice_track.ts b/ui/src/tracks/chrome_slices/generic_slice_track.ts
index d19f3db..1e817a8 100644
--- a/ui/src/tracks/chrome_slices/generic_slice_track.ts
+++ b/ui/src/tracks/chrome_slices/generic_slice_track.ts
@@ -18,26 +18,13 @@
} from '../../frontend/named_slice_track';
import {NewTrackArgs} from '../../frontend/track';
-export interface GenericSliceTrackConfig {
- sqlTrackId: number;
-}
-
-export interface GenericSliceTrackTypes extends NamedSliceTrackTypes {
- config: GenericSliceTrackConfig;
-}
-
-export class GenericSliceTrack extends NamedSliceTrack<GenericSliceTrackTypes> {
- static readonly kind = 'GenericSliceTrack';
- static create(args: NewTrackArgs) {
- return new GenericSliceTrack(args);
- }
-
- constructor(args: NewTrackArgs) {
+export class GenericSliceTrack extends NamedSliceTrack<NamedSliceTrackTypes> {
+ constructor(args: NewTrackArgs, private sqlTrackId: number) {
super(args);
}
getSqlSource(): string {
return `select ts, dur, id, depth, ifnull(name, '') as name
- from slice where track_id = ${this.config.sqlTrackId}`;
+ from slice where track_id = ${this.sqlTrackId}`;
}
}
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index 35e9da0..8d36341 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -16,14 +16,14 @@
import {clamp} from '../../base/math_utils';
import {Duration, duration, time} from '../../base/time';
import {
- NAMED_SLICE_ROW,
+ NAMED_ROW,
NamedSliceTrack,
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
import {
SliceData,
- SliceTrackBase,
-} from '../../frontend/slice_track_base';
+ SliceTrackLEGACY,
+} from '../../frontend/slice_track';
import {NewTrackArgs} from '../../frontend/track';
import {
EngineProxy,
@@ -44,7 +44,7 @@
export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
-export class ChromeSliceTrack extends SliceTrackBase {
+export class ChromeSliceTrack extends SliceTrackLEGACY {
private maxDurNs: duration = 0n;
constructor(
@@ -153,7 +153,7 @@
export const CHROME_SLICE_ROW = {
// Base columns (tsq, ts, dur, id, depth).
- ...NAMED_SLICE_ROW,
+ ...NAMED_ROW,
// Chrome-specific columns.
threadDur: LONG_NULL,
@@ -201,14 +201,7 @@
onUpdatedSlices(slices: ChromeSliceTrackTypes['slice'][]) {
for (const slice of slices) {
- if (slice === this.hoveredSlice) {
- slice.color = {
- ...slice.baseColor,
- l: 30,
- };
- } else {
- slice.color = slice.baseColor;
- }
+ slice.isHighlighted = (slice === this.hoveredSlice);
}
}
}
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 21ef5e6..4f2b7f4 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -20,13 +20,13 @@
import {isString} from '../../base/object_utils';
import {duration, time, Time} from '../../base/time';
import {Actions} from '../../common/actions';
-import {
- BasicAsyncTrack,
- NUM_NULL,
- STR_NULL,
-} from '../../common/basic_async_track';
import {drawTrackHoverTooltip} from '../../common/canvas_utils';
import {TrackData} from '../../common/track_data';
+import {
+ NUM_NULL,
+ STR_NULL,
+ TrackHelperLEGACY,
+} from '../../common/track_helper';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {
@@ -126,7 +126,7 @@
}
}
-export class CounterTrack extends BasicAsyncTrack<Data> {
+export class CounterTrack extends TrackHelperLEGACY<Data> {
private maximumValueSeen = 0;
private minimumValueSeen = 0;
private maximumDeltaSeen = 0;
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index 83bc30b..aa421f6 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -18,7 +18,7 @@
import {duration, time, Time} from '../../base/time';
import {calcCachedBucketSize} from '../../common/cache_utils';
import {drawTrackHoverTooltip} from '../../common/canvas_utils';
-import {hueForCpu} from '../../common/colorizer';
+import {colorForCpu} from '../../common/colorizer';
import {
TrackAdapter,
TrackControllerAdapter,
@@ -274,10 +274,6 @@
const RECT_HEIGHT = 20;
class CpuFreqTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuFreqTrack {
- return new CpuFreqTrack(args);
- }
-
private mousePos = {x: 0, y: 0};
private hoveredValue: number|undefined = undefined;
private hoveredTs: time|undefined = undefined;
@@ -326,13 +322,14 @@
const yLabel = `${num} ${kUnits[unitGroup + 1]}Hz`;
// Draw the CPU frequency graph.
- const hue = hueForCpu(this.config.cpu);
+ const color = colorForCpu(this.config.cpu);
let saturation = 45;
if (globals.state.hoveredUtid !== -1) {
saturation = 0;
}
- ctx.fillStyle = `hsl(${hue}, ${saturation}%, 70%)`;
- ctx.strokeStyle = `hsl(${hue}, ${saturation}%, 55%)`;
+
+ ctx.fillStyle = color.setHSL({s: saturation, l: 70}).cssString;
+ ctx.strokeStyle = color.setHSL({s: saturation, l: 55}).cssString;
const calculateX = (timestamp: time) => {
return Math.floor(visibleTimeScale.timeToPx(timestamp));
@@ -386,7 +383,7 @@
// Draw CPU idle rectangles that overlay the CPU freq graph.
ctx.fillStyle = `rgba(240, 240, 240, 1)`;
- for (let i = 0; i < data.lastIdleValues.length; i++) {
+ for (let i = startIdx; i < endIdx; i++) {
if (data.lastIdleValues[i] < 0) {
continue;
}
@@ -412,8 +409,8 @@
if (this.hoveredValue !== undefined && this.hoveredTs !== undefined) {
let text = `${this.hoveredValue.toLocaleString()}kHz`;
- ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
- ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
+ ctx.fillStyle = color.setHSL({s: 45, l: 75}).cssString;
+ ctx.strokeStyle = color.setHSL({s: 45, l: 45}).cssString;
const xStart = Math.floor(visibleTimeScale.timeToPx(this.hoveredTs));
const xEnd = this.hoveredTsEnd === undefined ?
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index d03a842..59ad373 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -16,7 +16,7 @@
import {searchSegment} from '../../base/binary_search';
import {duration, Time, time} from '../../base/time';
import {Actions} from '../../common/actions';
-import {hslForSlice} from '../../common/colorizer';
+import {colorForSample} from '../../common/colorizer';
import {
TrackAdapter,
TrackControllerAdapter,
@@ -24,7 +24,6 @@
} from '../../common/track_adapter';
import {TrackData} from '../../common/track_data';
import {globals} from '../../frontend/globals';
-import {cachedHsluvToHex} from '../../frontend/hsluv_cache';
import {TimeScale} from '../../frontend/time_scale';
import {NewTrackArgs} from '../../frontend/track';
import {
@@ -90,17 +89,7 @@
}
}
-function colorForSample(callsiteId: number, isHovered: boolean): string {
- const [hue, saturation, lightness] =
- hslForSlice(String(callsiteId), isHovered);
- return cachedHsluvToHex(hue, saturation, lightness);
-}
-
class CpuProfileTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuProfileTrack {
- return new CpuProfileTrack(args);
- }
-
private centerY = this.getHeight() / 2 + BAR_HEIGHT;
private markerWidth = (this.getHeight() - MARGIN_TOP - BAR_HEIGHT) / 2;
private hoveredTs: time|undefined = undefined;
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 45babfd..0e9c470 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -24,6 +24,7 @@
drawIncompleteSlice,
drawTrackHoverTooltip,
} from '../../common/canvas_utils';
+import {Color} from '../../common/color';
import {colorForThread} from '../../common/colorizer';
import {
TrackAdapter,
@@ -205,10 +206,6 @@
const TRACK_HEIGHT = MARGIN_TOP * 2 + RECT_HEIGHT;
class CpuSliceTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuSliceTrack {
- return new CpuSliceTrack(args);
- }
-
private mousePos?: {x: number, y: number};
private utidHoveredInThisTrack = -1;
@@ -285,20 +282,22 @@
const isHovering = globals.state.hoveredUtid !== -1;
const isThreadHovered = globals.state.hoveredUtid === utid;
const isProcessHovered = globals.state.hoveredPid === pid;
- const color = colorForThread(threadInfo);
+ const colorScheme = colorForThread(threadInfo);
+ let color: Color;
+ let textColor: Color;
if (isHovering && !isThreadHovered) {
if (!isProcessHovered) {
- color.l = 90;
- color.s = 0;
+ color = colorScheme.disabled;
+ textColor = colorScheme.textDisabled;
} else {
- color.l = Math.min(color.l + 30, 80);
- color.s -= 20;
+ color = colorScheme.variant;
+ textColor = colorScheme.textVariant;
}
} else {
- color.l = Math.min(color.l + 10, 60);
- color.s -= 20;
+ color = colorScheme.base;
+ textColor = colorScheme.textBase;
}
- ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
+ ctx.fillStyle = color.cssString;
if (data.isIncomplete[i]) {
drawIncompleteSlice(ctx, rectStart, MARGIN_TOP, rectWidth, RECT_HEIGHT);
} else {
@@ -330,10 +329,10 @@
title = cropText(title, charWidth, visibleWidth);
subTitle = cropText(subTitle, charWidth, visibleWidth);
const rectXCenter = left + visibleWidth / 2;
- ctx.fillStyle = '#fff';
+ ctx.fillStyle = textColor.cssString;
ctx.font = '12px Roboto Condensed';
ctx.fillText(title, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 - 1);
- ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
+ ctx.fillStyle = textColor.setAlpha(0.6).cssString;
ctx.font = '10px Roboto Condensed';
ctx.fillText(subTitle, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 + 9);
}
@@ -352,7 +351,7 @@
const rectWidth = Math.max(1, rectEnd - rectStart);
// Draw a rectangle around the slice that is currently selected.
- ctx.strokeStyle = `hsl(${color.h}, ${color.s}%, 30%)`;
+ ctx.strokeStyle = color.base.setHSL({l: 30}).cssString;
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(rectStart, MARGIN_TOP - 1.5, rectWidth, RECT_HEIGHT + 3);
diff --git a/ui/src/tracks/debug/counter_track.ts b/ui/src/tracks/debug/counter_track.ts
index 840364c..9af2db3 100644
--- a/ui/src/tracks/debug/counter_track.ts
+++ b/ui/src/tracks/debug/counter_track.ts
@@ -36,16 +36,15 @@
columns: CounterColumns;
}
-export class DebugCounterTrack extends
- BaseCounterTrack<CounterDebugTrackConfig> {
- constructor(engine: EngineProxy, trackKey: string) {
+export class DebugCounterTrack extends BaseCounterTrack {
+ private config: CounterDebugTrackConfig;
+
+ constructor(engine: EngineProxy, ctx: TrackContext) {
super({
engine,
- trackKey,
+ trackKey: ctx.trackKey,
});
- }
- onCreate(ctx: TrackContext): void {
// TODO(stevegolton): Validate params before type asserting.
// TODO(stevegolton): Avoid just pushing this config up for some base
// class to use. Be more explicit.
diff --git a/ui/src/tracks/debug/details_tab.ts b/ui/src/tracks/debug/details_tab.ts
index 3523f4f..5616a6b 100644
--- a/ui/src/tracks/debug/details_tab.ts
+++ b/ui/src/tracks/debug/details_tab.ts
@@ -24,6 +24,7 @@
import {
GenericSliceDetailsTabConfig,
} from '../../frontend/generic_slice_details_tab';
+import {hasArgs, renderArguments} from '../../frontend/slice_args';
import {
getSlice,
SliceDetails,
@@ -162,11 +163,24 @@
left: sliceRef(this.slice, 'Slice'),
right: '',
},
- renderTreeContents({
- 'Name': this.slice.name,
- 'Thread': getThreadName(this.slice.thread),
- 'Process': getProcessName(this.slice.process),
- }));
+ m(TreeNode, {
+ left: 'Name',
+ right: this.slice.name,
+ }),
+ m(TreeNode, {
+ left: 'Thread',
+ right: getThreadName(this.slice.thread),
+ }),
+ m(TreeNode, {
+ left: 'Process',
+ right: getProcessName(this.slice.process),
+ }),
+ hasArgs(this.slice) &&
+ m(TreeNode,
+ {
+ left: 'Args',
+ },
+ renderArguments(this.engine, this.slice)));
}
diff --git a/ui/src/tracks/debug/index.ts b/ui/src/tracks/debug/index.ts
index 985f068..cc03350 100644
--- a/ui/src/tracks/debug/index.ts
+++ b/ui/src/tracks/debug/index.ts
@@ -31,11 +31,11 @@
async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
ctx.registerTrack({
uri: DEBUG_SLICE_TRACK_URI,
- track: ({trackKey}) => new DebugTrackV2(ctx.engine, trackKey),
+ track: (trackCtx) => new DebugTrackV2(ctx.engine, trackCtx),
});
ctx.registerTrack({
uri: DEBUG_COUNTER_TRACK_URI,
- track: ({trackKey}) => new DebugCounterTrack(ctx.engine, trackKey),
+ track: (trackCtx) => new DebugCounterTrack(ctx.engine, trackCtx),
});
}
}
diff --git a/ui/src/tracks/debug/slice_track.ts b/ui/src/tracks/debug/slice_track.ts
index 30dfe3c..e023655 100644
--- a/ui/src/tracks/debug/slice_track.ts
+++ b/ui/src/tracks/debug/slice_track.ts
@@ -19,9 +19,7 @@
import {Actions} from '../../common/actions';
import {SCROLLING_TRACK_GROUP} from '../../common/state';
import {globals} from '../../frontend/globals';
-import {
- NamedSliceTrackTypes,
-} from '../../frontend/named_slice_track';
+import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
import {TrackButton} from '../../frontend/track_panel';
import {PrimaryTrackSortKey, TrackContext} from '../../public';
import {EngineProxy} from '../../trace_processor/engine';
@@ -47,19 +45,16 @@
columns: SliceColumns;
}
-interface DebugTrackV2Types extends NamedSliceTrackTypes {
- config: DebugTrackV2Config;
-}
+export class DebugTrackV2 extends
+ CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
+ private config: DebugTrackV2Config;
-export class DebugTrackV2 extends CustomSqlTableSliceTrack<DebugTrackV2Types> {
- constructor(engine: EngineProxy, trackKey: string) {
+ constructor(engine: EngineProxy, ctx: TrackContext) {
super({
engine,
- trackKey,
+ trackKey: ctx.trackKey,
});
- }
- onCreate(ctx: TrackContext): void {
// TODO(stevegolton): Validate params before type asserting.
// TODO(stevegolton): Avoid just pushing this config up for some base
// class to use. Be more explicit.
@@ -68,7 +63,7 @@
getSqlDataSource(): CustomSqlTableDefConfig {
return {
- sqlTableName: this.config.sqlTableName,
+ sqlTableName: this.config!.sqlTableName,
};
}
@@ -76,7 +71,7 @@
return {
kind: DebugSliceDetailsTab.kind,
config: {
- sqlTableName: this.config.sqlTableName,
+ sqlTableName: this.config!.sqlTableName,
title: 'Debug Slice',
},
};
diff --git a/ui/src/tracks/actual_frames/index.ts b/ui/src/tracks/frames/actual_frames_track.ts
similarity index 65%
rename from ui/src/tracks/actual_frames/index.ts
rename to ui/src/tracks/frames/actual_frames_track.ts
index fbc3cf7..4c46619 100644
--- a/ui/src/tracks/actual_frames/index.ts
+++ b/ui/src/tracks/frames/actual_frames_track.ts
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,22 +14,15 @@
import {BigintMath as BIMath} from '../../base/bigint_math';
import {duration, time} from '../../base/time';
-import {SliceData, SliceTrackBase} from '../../frontend/slice_track_base';
+import {SliceData, SliceTrackLEGACY} from '../../frontend/slice_track';
import {
EngineProxy,
- Plugin,
- PluginContext,
- PluginContextTrace,
- PluginDescriptor,
} from '../../public';
-import {getTrackName} from '../../public/utils';
import {
LONG,
LONG_NULL,
NUM,
- NUM_NULL,
STR,
- STR_NULL,
} from '../../trace_processor/query_result';
export const ACTUAL_FRAMES_SLICE_TRACK_KIND = 'ActualFramesSliceTrack';
@@ -41,7 +34,7 @@
const LIGHT_GREEN_COLOR = '#C0D588'; // Light Green 500
const PINK_COLOR = '#F515E0'; // Pink 500
-class SliceTrack extends SliceTrackBase {
+export class ActualFramesTrack extends SliceTrackLEGACY {
private maxDur = 0n;
constructor(
@@ -151,79 +144,3 @@
return slices;
}
}
-
-class ActualFrames implements Plugin {
- onActivate(_ctx: PluginContext): void {}
-
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- const {engine} = ctx;
- const result = await engine.query(`
- with process_async_tracks as materialized (
- select
- process_track.upid as upid,
- process_track.name as trackName,
- process.name as processName,
- process.pid as pid,
- group_concat(process_track.id) as trackIds,
- count(1) as trackCount
- from process_track
- left join process using(upid)
- where process_track.name = "Actual Timeline"
- group by
- process_track.upid,
- process_track.name
- )
- select
- t.*,
- max_layout_depth(t.trackCount, t.trackIds) as maxDepth
- from process_async_tracks t;
- `);
-
- const it = result.iter({
- upid: NUM,
- trackName: STR_NULL,
- trackIds: STR,
- processName: STR_NULL,
- pid: NUM_NULL,
- maxDepth: NUM_NULL,
- });
- for (; it.valid(); it.next()) {
- const upid = it.upid;
- const trackName = it.trackName;
- const rawTrackIds = it.trackIds;
- const trackIds = rawTrackIds.split(',').map((v) => Number(v));
- const processName = it.processName;
- const pid = it.pid;
- const maxDepth = it.maxDepth;
-
- if (maxDepth === null) {
- // If there are no slices in this track, skip it.
- continue;
- }
-
- const kind = 'ActualFrames';
- const displayName =
- getTrackName({name: trackName, upid, pid, processName, kind});
-
- ctx.registerStaticTrack({
- uri: `perfetto.ActualFrames#${upid}`,
- displayName,
- trackIds,
- kind: ACTUAL_FRAMES_SLICE_TRACK_KIND,
- track: ({trackKey}) => {
- return new SliceTrack(
- engine,
- maxDepth,
- trackKey,
- trackIds,
- );
- },
- });
- }
- }
-}
-
-export const plugin: PluginDescriptor = {
- pluginId: 'perfetto.ActualFrames',
- plugin: ActualFrames,
-};
diff --git a/ui/src/tracks/frames/actual_frames_track_v2.ts b/ui/src/tracks/frames/actual_frames_track_v2.ts
new file mode 100644
index 0000000..c30e673
--- /dev/null
+++ b/ui/src/tracks/frames/actual_frames_track_v2.ts
@@ -0,0 +1,96 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {HSLColor} from '../../common/color';
+import {ColorScheme, makeColorScheme} from '../../common/colorizer';
+import {
+ NAMED_ROW,
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {EngineProxy, Slice, STR_NULL} from '../../public';
+
+const BLUE = makeColorScheme(new HSLColor('#03A9F4')); // Blue 500
+const GREEN = makeColorScheme(new HSLColor('#4CAF50')); // Green 500
+const YELLOW = makeColorScheme(new HSLColor('#FFEB3B')); // Yellow 500
+const RED = makeColorScheme(new HSLColor('#FF5722')); // Red 500
+const LIGHT_GREEN =
+ makeColorScheme(new HSLColor('#C0D588')); // Light Green 500
+const PINK = makeColorScheme(new HSLColor('#F515E0')); // Pink 500
+
+export const ACTUAL_FRAME_ROW = {
+ // Base columns (tsq, ts, dur, id, depth).
+ ...NAMED_ROW,
+
+ // Chrome-specific columns.
+ jankTag: STR_NULL,
+};
+export type ActualFrameRow = typeof ACTUAL_FRAME_ROW;
+
+export interface ActualFrameTrackTypes extends NamedSliceTrackTypes {
+ row: ActualFrameRow;
+}
+
+export class ActualFramesTrack extends NamedSliceTrack<ActualFrameTrackTypes> {
+ constructor(
+ engine: EngineProxy, maxDepth: number, trackKey: string,
+ private trackIds: number[]) {
+ super({engine, trackKey});
+ this.sliceLayout.maxDepth = maxDepth + 1;
+ }
+
+ // This is used by the base class to call iter().
+ getRowSpec() {
+ return ACTUAL_FRAME_ROW;
+ }
+
+ getSqlSource(): string {
+ return `
+ SELECT
+ s.ts as ts,
+ s.dur as dur,
+ s.layout_depth as depth,
+ s.name as name,
+ s.id as id,
+ afs.jank_tag as jankTag
+ from experimental_slice_layout s
+ join actual_frame_timeline_slice afs using(id)
+ where
+ filter_track_ids = '${this.trackIds.join(',')}'
+ `;
+ }
+
+ rowToSlice(row: ActualFrameRow): Slice {
+ const baseSlice = super.rowToSlice(row);
+ return {...baseSlice, colorScheme: getColorSchemeForJank(row.jankTag)};
+ }
+}
+
+function getColorSchemeForJank(jankTag: string|null): ColorScheme {
+ switch (jankTag) {
+ case 'Self Jank':
+ return RED;
+ case 'Other Jank':
+ return YELLOW;
+ case 'Dropped Frame':
+ return BLUE;
+ case 'Buffer Stuffing':
+ case 'SurfaceFlinger Stuffing':
+ return LIGHT_GREEN;
+ case 'No Jank':
+ return GREEN;
+ default:
+ return PINK;
+ }
+}
diff --git a/ui/src/tracks/expected_frames/index.ts b/ui/src/tracks/frames/expected_frames_track.ts
similarity index 60%
rename from ui/src/tracks/expected_frames/index.ts
rename to ui/src/tracks/frames/expected_frames_track.ts
index 8cc1833..05af78a 100644
--- a/ui/src/tracks/expected_frames/index.ts
+++ b/ui/src/tracks/frames/expected_frames_track.ts
@@ -14,30 +14,16 @@
import {BigintMath as BIMath} from '../../base/bigint_math';
import {Duration, duration, time} from '../../base/time';
-import {
- SliceData,
- SliceTrackBase,
-} from '../../frontend/slice_track_base';
-import {
- EngineProxy,
- Plugin,
- PluginContext,
- PluginContextTrace,
- PluginDescriptor,
-} from '../../public';
-import {getTrackName} from '../../public/utils';
+import {SliceData, SliceTrackLEGACY} from '../../frontend/slice_track';
+import {EngineProxy} from '../../public';
import {
LONG,
LONG_NULL,
NUM,
- NUM_NULL,
STR,
- STR_NULL,
} from '../../trace_processor/query_result';
-export const EXPECTED_FRAMES_SLICE_TRACK_KIND = 'ExpectedFramesSliceTrack';
-
-class SliceTrack extends SliceTrackBase {
+export class ExpectedFramesTrack extends SliceTrackLEGACY {
private maxDur = Duration.ZERO;
constructor(
@@ -135,79 +121,3 @@
return slices;
}
}
-
-class ExpectedFramesPlugin implements Plugin {
- onActivate(_ctx: PluginContext): void {}
-
- async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
- const {engine} = ctx;
- const result = await engine.query(`
- with process_async_tracks as materialized (
- select
- process_track.upid as upid,
- process_track.name as trackName,
- process.name as processName,
- process.pid as pid,
- group_concat(process_track.id) as trackIds,
- count(1) as trackCount
- from process_track
- left join process using(upid)
- where process_track.name = "Expected Timeline"
- group by
- process_track.upid,
- process_track.name
- )
- select
- t.*,
- max_layout_depth(t.trackCount, t.trackIds) as maxDepth
- from process_async_tracks t;
- `);
-
- const it = result.iter({
- upid: NUM,
- trackName: STR_NULL,
- trackIds: STR,
- processName: STR_NULL,
- pid: NUM_NULL,
- maxDepth: NUM_NULL,
- });
-
- for (; it.valid(); it.next()) {
- const upid = it.upid;
- const trackName = it.trackName;
- const rawTrackIds = it.trackIds;
- const trackIds = rawTrackIds.split(',').map((v) => Number(v));
- const processName = it.processName;
- const pid = it.pid;
- const maxDepth = it.maxDepth;
-
- if (maxDepth === null) {
- // If there are no slices in this track, skip it.
- continue;
- }
-
- const displayName = getTrackName(
- {name: trackName, upid, pid, processName, kind: 'ExpectedFrames'});
-
- ctx.registerStaticTrack({
- uri: `perfetto.ExpectedFrames#${upid}`,
- displayName,
- trackIds,
- kind: EXPECTED_FRAMES_SLICE_TRACK_KIND,
- track: ({trackKey}) => {
- return new SliceTrack(
- engine,
- maxDepth,
- trackKey,
- trackIds,
- );
- },
- });
- }
- }
-}
-
-export const plugin: PluginDescriptor = {
- pluginId: 'perfetto.ExpectedFrames',
- plugin: ExpectedFramesPlugin,
-};
diff --git a/ui/src/tracks/frames/expected_frames_track_v2.ts b/ui/src/tracks/frames/expected_frames_track_v2.ts
new file mode 100644
index 0000000..268b4b6
--- /dev/null
+++ b/ui/src/tracks/frames/expected_frames_track_v2.ts
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {HSLColor} from '../../common/color';
+import {makeColorScheme} from '../../common/colorizer';
+import {NamedRow, NamedSliceTrack} from '../../frontend/named_slice_track';
+import {EngineProxy, Slice} from '../../public';
+
+const GREEN = makeColorScheme(new HSLColor('#4CAF50')); // Green 500
+
+export class ExpectedFramesTrack extends NamedSliceTrack {
+ constructor(
+ engine: EngineProxy, maxDepth: number, trackKey: string,
+ private trackIds: number[]) {
+ super({engine, trackKey});
+ this.sliceLayout.maxDepth = maxDepth + 1;
+ }
+
+ getSqlSource(): string {
+ return `
+ SELECT
+ ts,
+ dur,
+ layout_depth as depth,
+ name,
+ id
+ from experimental_slice_layout
+ where
+ filter_track_ids = '${this.trackIds.join(',')}'
+ `;
+ }
+
+ rowToSlice(row: NamedRow): Slice {
+ const baseSlice = super.rowToSlice(row);
+ return {...baseSlice, colorScheme: GREEN};
+ }
+}
diff --git a/ui/src/tracks/frames/index.ts b/ui/src/tracks/frames/index.ts
new file mode 100644
index 0000000..e1cfc85
--- /dev/null
+++ b/ui/src/tracks/frames/index.ts
@@ -0,0 +1,217 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {
+ Plugin,
+ PluginContext,
+ PluginContextTrace,
+ PluginDescriptor,
+} from '../../public';
+import {getTrackName} from '../../public/utils';
+import {
+ NUM,
+ NUM_NULL,
+ STR,
+ STR_NULL,
+} from '../../trace_processor/query_result';
+
+import {ActualFramesTrack} from './actual_frames_track';
+import {
+ ActualFramesTrack as ActualFramesTrackV2,
+} from './actual_frames_track_v2';
+import {ExpectedFramesTrack} from './expected_frames_track';
+import {
+ ExpectedFramesTrack as ExpectedFramesTrackV2,
+} from './expected_frames_track_v2';
+
+export const EXPECTED_FRAMES_SLICE_TRACK_KIND = 'ExpectedFramesSliceTrack';
+export const ACTUAL_FRAMES_SLICE_TRACK_KIND = 'ActualFramesSliceTrack';
+
+class FramesPlugin implements Plugin {
+ onActivate(_ctx: PluginContext): void {}
+
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ this.addExpectedFrames(ctx);
+ this.addActualFrames(ctx);
+ }
+
+ async addExpectedFrames(ctx: PluginContextTrace): Promise<void> {
+ const {engine} = ctx;
+ const result = await engine.query(`
+ with process_async_tracks as materialized (
+ select
+ process_track.upid as upid,
+ process_track.name as trackName,
+ process.name as processName,
+ process.pid as pid,
+ group_concat(process_track.id) as trackIds,
+ count(1) as trackCount
+ from process_track
+ left join process using(upid)
+ where process_track.name = "Expected Timeline"
+ group by
+ process_track.upid,
+ process_track.name
+ )
+ select
+ t.*,
+ max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+ from process_async_tracks t;
+ `);
+
+ const it = result.iter({
+ upid: NUM,
+ trackName: STR_NULL,
+ trackIds: STR,
+ processName: STR_NULL,
+ pid: NUM_NULL,
+ maxDepth: NUM_NULL,
+ });
+
+ for (; it.valid(); it.next()) {
+ const upid = it.upid;
+ const trackName = it.trackName;
+ const rawTrackIds = it.trackIds;
+ const trackIds = rawTrackIds.split(',').map((v) => Number(v));
+ const processName = it.processName;
+ const pid = it.pid;
+ const maxDepth = it.maxDepth;
+
+ if (maxDepth === null) {
+ // If there are no slices in this track, skip it.
+ continue;
+ }
+
+ const displayName = getTrackName(
+ {name: trackName, upid, pid, processName, kind: 'ExpectedFrames'});
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.ExpectedFrames#${upid}`,
+ displayName,
+ trackIds,
+ kind: EXPECTED_FRAMES_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new ExpectedFramesTrack(
+ engine,
+ maxDepth,
+ trackKey,
+ trackIds,
+ );
+ },
+ });
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.ExpectedFrames#${upid}.v2`,
+ displayName,
+ trackIds,
+ kind: EXPECTED_FRAMES_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new ExpectedFramesTrackV2(
+ engine,
+ maxDepth,
+ trackKey,
+ trackIds,
+ );
+ },
+ });
+ }
+ }
+
+ async addActualFrames(ctx: PluginContextTrace): Promise<void> {
+ const {engine} = ctx;
+ const result = await engine.query(`
+ with process_async_tracks as materialized (
+ select
+ process_track.upid as upid,
+ process_track.name as trackName,
+ process.name as processName,
+ process.pid as pid,
+ group_concat(process_track.id) as trackIds,
+ count(1) as trackCount
+ from process_track
+ left join process using(upid)
+ where process_track.name = "Actual Timeline"
+ group by
+ process_track.upid,
+ process_track.name
+ )
+ select
+ t.*,
+ max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+ from process_async_tracks t;
+ `);
+
+ const it = result.iter({
+ upid: NUM,
+ trackName: STR_NULL,
+ trackIds: STR,
+ processName: STR_NULL,
+ pid: NUM_NULL,
+ maxDepth: NUM_NULL,
+ });
+ for (; it.valid(); it.next()) {
+ const upid = it.upid;
+ const trackName = it.trackName;
+ const rawTrackIds = it.trackIds;
+ const trackIds = rawTrackIds.split(',').map((v) => Number(v));
+ const processName = it.processName;
+ const pid = it.pid;
+ const maxDepth = it.maxDepth;
+
+ if (maxDepth === null) {
+ // If there are no slices in this track, skip it.
+ continue;
+ }
+
+ const kind = 'ActualFrames';
+ const displayName =
+ getTrackName({name: trackName, upid, pid, processName, kind});
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.ActualFrames#${upid}`,
+ displayName,
+ trackIds,
+ kind: ACTUAL_FRAMES_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new ActualFramesTrack(
+ engine,
+ maxDepth,
+ trackKey,
+ trackIds,
+ );
+ },
+ });
+
+ ctx.registerStaticTrack({
+ uri: `perfetto.ActualFrames#${upid}.v2`,
+ displayName,
+ trackIds,
+ kind: ACTUAL_FRAMES_SLICE_TRACK_KIND,
+ track: ({trackKey}) => {
+ return new ActualFramesTrackV2(
+ engine,
+ maxDepth,
+ trackKey,
+ trackIds,
+ );
+ },
+ });
+ }
+ }
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: 'perfetto.Frames',
+ plugin: FramesPlugin,
+};
diff --git a/ui/src/tracks/ftrace/index.ts b/ui/src/tracks/ftrace/index.ts
index 5dd646c..75c741e 100644
--- a/ui/src/tracks/ftrace/index.ts
+++ b/ui/src/tracks/ftrace/index.ts
@@ -13,9 +13,9 @@
// limitations under the License.
import {duration, Time, time} from '../../base/time';
-import {BasicAsyncTrack} from '../../common/basic_async_track';
-import {colorForString} from '../../common/colorizer';
+import {colorForFtrace} from '../../common/colorizer';
import {LIMIT, TrackData} from '../../common/track_data';
+import {TrackHelperLEGACY} from '../../common/track_helper';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {
@@ -42,7 +42,7 @@
const RECT_HEIGHT = 18;
const TRACK_HEIGHT = (RECT_HEIGHT) + (2 * MARGIN);
-class FtraceRawTrack extends BasicAsyncTrack<Data> {
+class FtraceRawTrack extends TrackHelperLEGACY<Data> {
constructor(private engine: EngineProxy, private cpu: number) {
super();
}
@@ -116,13 +116,7 @@
for (let i = 0; i < data.timestamps.length; i++) {
const name = data.names[i];
- const color = colorForString(name);
- const hsl = `hsl(
- ${color.h},
- ${color.s - 20}%,
- ${Math.min(color.l + 10, 60)}%
- )`;
- ctx.fillStyle = hsl;
+ ctx.fillStyle = colorForFtrace(name).base.cssString;
const timestamp = Time.fromRaw(data.timestamps[i]);
const xPos = Math.floor(visibleTimeScale.timeToPx(timestamp));
diff --git a/ui/src/tracks/heap_profile/index.ts b/ui/src/tracks/heap_profile/index.ts
index 494dae7..f35d176 100644
--- a/ui/src/tracks/heap_profile/index.ts
+++ b/ui/src/tracks/heap_profile/index.ts
@@ -16,36 +16,36 @@
import {ProfileType, Selection} from '../../common/state';
import {profileType} from '../../controller/flamegraph_controller';
import {
- BASE_SLICE_ROW,
+ BASE_ROW,
BaseSliceTrack,
BaseSliceTrackTypes,
OnSliceClickArgs,
OnSliceOverArgs,
} from '../../frontend/base_slice_track';
import {globals} from '../../frontend/globals';
-import {Slice} from '../../frontend/slice';
import {NewTrackArgs} from '../../frontend/track';
import {
Plugin,
PluginContext,
PluginContextTrace,
PluginDescriptor,
+ Slice,
} from '../../public';
import {NUM, STR} from '../../trace_processor/query_result';
export const HEAP_PROFILE_TRACK_KIND = 'HeapProfileTrack';
-const HEAP_PROFILE_SLICE_ROW = {
- ...BASE_SLICE_ROW,
+const HEAP_PROFILE_ROW = {
+ ...BASE_ROW,
type: STR,
};
-type HeapProfileSliceRow = typeof HEAP_PROFILE_SLICE_ROW;
+type HeapProfileRow = typeof HEAP_PROFILE_ROW;
interface HeapProfileSlice extends Slice {
type: ProfileType;
}
interface HeapProfileTrackTypes extends BaseSliceTrackTypes {
- row: HeapProfileSliceRow;
+ row: HeapProfileRow;
slice: HeapProfileSlice;
}
@@ -80,11 +80,11 @@
)`;
}
- getRowSpec(): HeapProfileSliceRow {
- return HEAP_PROFILE_SLICE_ROW;
+ getRowSpec(): HeapProfileRow {
+ return HEAP_PROFILE_ROW;
}
- rowToSlice(row: HeapProfileSliceRow): HeapProfileSlice {
+ rowToSlice(row: HeapProfileRow): HeapProfileSlice {
const slice = super.rowToSlice(row);
let type = row.type;
if (type === 'heap_profile:libc.malloc,com.android.art') {
diff --git a/ui/src/tracks/null_track/index.ts b/ui/src/tracks/null_track/index.ts
index 1b2bb24..7bc77b5 100644
--- a/ui/src/tracks/null_track/index.ts
+++ b/ui/src/tracks/null_track/index.ts
@@ -28,10 +28,6 @@
super(args);
}
- static create(args: NewTrackArgs): NullTrack {
- return new NullTrack(args);
- }
-
getHeight(): number {
return 30;
}
@@ -50,7 +46,7 @@
uri: NULL_TRACK_URI,
displayName: 'Null Track',
kind: NULL_TRACK_KIND,
- track: ({trackKey}) => NullTrack.create({
+ track: ({trackKey}) => new NullTrack({
engine: ctx.engine,
trackKey,
}),
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index 5da1f71..dd30045 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -87,10 +87,6 @@
const RECT_HEIGHT = 30.5;
class PerfSamplesProfileTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): PerfSamplesProfileTrack {
- return new PerfSamplesProfileTrack(args);
- }
-
private centerY = this.getHeight() / 2;
private markerWidth = (this.getHeight() - MARGIN_TOP) / 2;
private hoveredTs: time|undefined = undefined;
diff --git a/ui/src/tracks/process_summary/process_scheduling_track.ts b/ui/src/tracks/process_summary/process_scheduling_track.ts
index 0d65539..a955278 100644
--- a/ui/src/tracks/process_summary/process_scheduling_track.ts
+++ b/ui/src/tracks/process_summary/process_scheduling_track.ts
@@ -19,6 +19,7 @@
import {Actions} from '../../common/actions';
import {calcCachedBucketSize} from '../../common/cache_utils';
import {drawTrackHoverTooltip} from '../../common/canvas_utils';
+import {Color} from '../../common/color';
import {colorForThread} from '../../common/colorizer';
import {
TrackAdapter,
@@ -188,10 +189,6 @@
}
export class ProcessSchedulingTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ProcessSchedulingTrack {
- return new ProcessSchedulingTrack(args);
- }
-
private mousePos?: {x: number, y: number};
private utidHoveredInThisTrack = -1;
@@ -250,20 +247,18 @@
const isHovering = globals.state.hoveredUtid !== -1;
const isThreadHovered = globals.state.hoveredUtid === utid;
const isProcessHovered = globals.state.hoveredPid === pid;
- const color = colorForThread(threadInfo);
+ const colorScheme = colorForThread(threadInfo);
+ let color: Color;
if (isHovering && !isThreadHovered) {
if (!isProcessHovered) {
- color.l = 90;
- color.s = 0;
+ color = colorScheme.disabled;
} else {
- color.l = Math.min(color.l + 30, 80);
- color.s -= 20;
+ color = colorScheme.variant;
}
} else {
- color.l = Math.min(color.l + 10, 60);
- color.s -= 20;
+ color = colorScheme.base;
}
- ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
+ ctx.fillStyle = color.cssString;
const y = MARGIN_TOP + cpuTrackHeight * cpu + cpu;
ctx.fillRect(rectStart, y, rectEnd - rectStart, cpuTrackHeight);
}
diff --git a/ui/src/tracks/process_summary/process_summary_track.ts b/ui/src/tracks/process_summary/process_summary_track.ts
index 0b29eea..ee0bcc8 100644
--- a/ui/src/tracks/process_summary/process_summary_track.ts
+++ b/ui/src/tracks/process_summary/process_summary_track.ts
@@ -142,10 +142,6 @@
const SUMMARY_HEIGHT = TRACK_HEIGHT - MARGIN_TOP;
export class ProcessSummaryTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ProcessSummaryTrack {
- return new ProcessSummaryTrack(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
}
@@ -184,10 +180,7 @@
// TODO(hjd): Dedupe this math.
const color = colorForTid(this.config.pidForColor);
- color.l = Math.min(color.l + 10, 60);
- color.s -= 20;
-
- ctx.fillStyle = `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
+ ctx.fillStyle = color.base.cssString;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
for (let i = 0; i < data.utilizations.length; i++) {
diff --git a/ui/src/tracks/sched/active_cpu_count.ts b/ui/src/tracks/sched/active_cpu_count.ts
index 9606137..6dc2103 100644
--- a/ui/src/tracks/sched/active_cpu_count.ts
+++ b/ui/src/tracks/sched/active_cpu_count.ts
@@ -25,8 +25,7 @@
} from '../../frontend/base_counter_track';
import {CloseTrackButton} from '../../frontend/close_track_button';
import {globals} from '../../frontend/globals';
-import {NewTrackArgs} from '../../frontend/track';
-import {PrimaryTrackSortKey, TrackContext} from '../../public';
+import {EngineProxy, PrimaryTrackSortKey, TrackContext} from '../../public';
export function addActiveCPUCountTrack(cpuType?: string) {
const cpuTypeName = cpuType === undefined ? '' : ` ${cpuType} `;
@@ -52,15 +51,17 @@
cpuType?: string;
}
-export class ActiveCPUCountTrack extends
- BaseCounterTrack<ActiveCPUCountTrackConfig> {
+export class ActiveCPUCountTrack extends BaseCounterTrack {
+ private config: ActiveCPUCountTrackConfig;
+
static readonly kind = 'dev.perfetto.Sched.ActiveCPUCount';
- constructor(args: NewTrackArgs) {
- super(args);
- }
+ constructor(ctx: TrackContext, engine: EngineProxy) {
+ super({
+ engine,
+ trackKey: ctx.trackKey,
+ });
- onCreate(ctx: TrackContext): void {
// TODO(stevegolton): Validate params before type asserting.
// TODO(stevegolton): Avoid just pushing this config up for some base
// class to use. Be more explicit.
@@ -87,10 +88,10 @@
}
getSqlSource() {
- const sourceTable = this.config.cpuType === undefined ?
+ const sourceTable = this.config!.cpuType === undefined ?
'sched_active_cpu_count' :
`sched_active_cpu_count_for_core_type(${
- sqliteString(this.config.cpuType)})`;
+ sqliteString(this.config!.cpuType)})`;
return `
select
ts,
diff --git a/ui/src/tracks/sched/index.ts b/ui/src/tracks/sched/index.ts
index af411ed..d9d5ed2 100644
--- a/ui/src/tracks/sched/index.ts
+++ b/ui/src/tracks/sched/index.ts
@@ -37,11 +37,7 @@
});
ctx.registerTrack({
uri: ActiveCPUCountTrack.kind,
- track: (trackCtx) => {
- const track = new ActiveCPUCountTrack(
- {engine: ctx.engine, trackKey: trackCtx.trackKey});
- return track;
- },
+ track: (trackCtx) => new ActiveCPUCountTrack(trackCtx, ctx.engine),
});
}
diff --git a/ui/src/tracks/screenshots/index.ts b/ui/src/tracks/screenshots/index.ts
index 32df756..4af3c94 100644
--- a/ui/src/tracks/screenshots/index.ts
+++ b/ui/src/tracks/screenshots/index.ts
@@ -16,7 +16,6 @@
import {
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
import {
Plugin,
PluginContext,
@@ -36,9 +35,6 @@
class ScreenshotsTrack extends CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
static readonly kind = 'dev.perfetto.ScreenshotsTrack';
- static create(args: NewTrackArgs): TrackBase {
- return new ScreenshotsTrack(args);
- }
getSqlDataSource(): CustomSqlTableDefConfig {
return {
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index 2b5e2ef..7544ddb 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -48,7 +48,6 @@
} from './thread_state_v2';
export const THREAD_STATE_TRACK_KIND = 'ThreadStateTrack';
-export const THREAD_STATE_TRACK_V2_KIND = 'ThreadStateTrackV2';
interface Data extends TrackData {
strings: string[];
@@ -180,10 +179,6 @@
const EXCESS_WIDTH = 10;
class ThreadStateTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ThreadStateTrack {
- return new ThreadStateTrack(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
}
@@ -246,21 +241,15 @@
currentSelection.kind === 'THREAD_STATE' &&
currentSelection.id === data.ids[i];
- const color = colorForState(state);
-
- let colorStr = `hsl(${color.h},${color.s}%,${color.l}%)`;
- if (color.a) {
- colorStr = `hsla(${color.h},${color.s}%,${color.l}%, ${color.a})`;
- }
- ctx.fillStyle = colorStr;
-
+ const colorScheme = colorForState(state);
+ ctx.fillStyle = colorScheme.base.cssString;
ctx.fillRect(rectStart, MARGIN_TOP, rectWidth, RECT_HEIGHT);
// Don't render text when we have less than 10px to play with.
if (rectWidth < 10 || state === 'Sleeping') continue;
const title = cropText(state, charWidth, rectWidth);
const rectXCenter = rectStart + rectWidth / 2;
- ctx.fillStyle = color.l > 80 ? '#404040' : '#fff';
+ ctx.fillStyle = colorScheme.textBase.cssString;
ctx.fillText(title, rectXCenter, MARGIN_TOP + RECT_HEIGHT / 2 + 3);
if (isSelected) {
@@ -269,8 +258,7 @@
Math.max(0 - EXCESS_WIDTH, timeScale.timeToPx(tStart));
const rectEnd =
Math.min(windowSpan.end + EXCESS_WIDTH, timeScale.timeToPx(tEnd));
- const color = colorForState(state);
- ctx.strokeStyle = `hsl(${color.h},${color.s}%,${color.l * 0.7}%)`;
+ ctx.strokeStyle = colorScheme.base.cssString;
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeRect(
@@ -352,15 +340,15 @@
ctx.registerStaticTrack({
uri: `perfetto.ThreadState#${utid}.v2`,
displayName,
- kind: THREAD_STATE_TRACK_V2_KIND,
+ kind: THREAD_STATE_TRACK_KIND,
utid,
track: ({trackKey}) => {
- const track = ThreadStateTrackV2.create({
- engine: ctx.engine,
- trackKey,
- });
- track.config = {utid};
- return track;
+ return new ThreadStateTrackV2(
+ {
+ engine: ctx.engine,
+ trackKey,
+ },
+ utid);
},
});
}
diff --git a/ui/src/tracks/thread_state/thread_state_track_v2.ts b/ui/src/tracks/thread_state/thread_state_track_v2.ts
deleted file mode 100644
index ffcd8d2..0000000
--- a/ui/src/tracks/thread_state/thread_state_track_v2.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import {Actions} from '../../common/actions';
-import {Color, colorForState} from '../../common/colorizer';
-import {Selection} from '../../common/state';
-import {translateState} from '../../common/thread_state';
-import {
- BASE_SLICE_ROW,
- BaseSliceTrack,
- BaseSliceTrackTypes,
- OnSliceClickArgs,
-} from '../../frontend/base_slice_track';
-import {globals} from '../../frontend/globals';
-import {
- SLICE_LAYOUT_FLAT_DEFAULTS,
- SliceLayout,
-} from '../../frontend/slice_layout';
-import {NewTrackArgs} from '../../frontend/track';
-import {NUM_NULL, STR} from '../../trace_processor/query_result';
-
-export const THREAD_STATE_ROW = {
- ...BASE_SLICE_ROW,
- state: STR,
- ioWait: NUM_NULL,
-};
-
-export type ThreadStateRow = typeof THREAD_STATE_ROW;
-
-export interface ThreadStateTrackConfig {
- utid: number;
-}
-
-export interface ThreadStateTrackTypes extends BaseSliceTrackTypes {
- row: ThreadStateRow;
- config: ThreadStateTrackConfig;
-}
-
-export class ThreadStateTrack extends BaseSliceTrack<ThreadStateTrackTypes> {
- static create(args: NewTrackArgs) {
- return new ThreadStateTrack(args);
- }
-
- protected sliceLayout: SliceLayout = {...SLICE_LAYOUT_FLAT_DEFAULTS};
-
- constructor(args: NewTrackArgs) {
- super(args);
- }
-
- // This is used by the base class to call iter().
- getRowSpec(): ThreadStateTrackTypes['row'] {
- return THREAD_STATE_ROW;
- }
-
- getSqlSource(): string {
- // Do not display states 'x' and 'S' (dead & sleeping).
- const sql = `
- select
- id,
- ts,
- dur,
- cpu,
- state,
- io_wait as ioWait,
- 0 as depth
- from thread_state
- where
- utid = ${this.config.utid} and
- state != 'x' and
- state != 'S'
- `;
- return sql;
- }
-
- rowToSlice(row: ThreadStateTrackTypes['row']):
- ThreadStateTrackTypes['slice'] {
- const baseSlice = super.rowToSlice(row);
- const ioWait = row.ioWait === null ? undefined : !!row.ioWait;
- const title = translateState(row.state, ioWait);
- const baseColor: Color = colorForState(title);
- return {...baseSlice, title, baseColor};
- }
-
- onUpdatedSlices(slices: ThreadStateTrackTypes['slice'][]) {
- for (const slice of slices) {
- if (slice === this.hoveredSlice) {
- slice.color = {
- h: slice.baseColor.h,
- s: slice.baseColor.s,
- l: 30,
- };
- } else {
- slice.color = slice.baseColor;
- }
- }
- }
-
- onSliceClick(args: OnSliceClickArgs<ThreadStateTrackTypes['slice']>) {
- globals.makeSelection(Actions.selectThreadState({
- id: args.slice.id,
- trackKey: this.trackKey,
- }));
- }
-
- protected isSelectionHandled(selection: Selection): boolean {
- return selection.kind === 'THREAD_STATE';
- }
-}
diff --git a/ui/src/tracks/thread_state/thread_state_v2.ts b/ui/src/tracks/thread_state/thread_state_v2.ts
index 0f93074..c35ed5c 100644
--- a/ui/src/tracks/thread_state/thread_state_v2.ts
+++ b/ui/src/tracks/thread_state/thread_state_v2.ts
@@ -13,11 +13,11 @@
// limitations under the License.
import {Actions} from '../../common/actions';
-import {Color, colorForState} from '../../common/colorizer';
+import {colorForState} from '../../common/colorizer';
import {Selection} from '../../common/state';
import {translateState} from '../../common/thread_state';
import {
- BASE_SLICE_ROW,
+ BASE_ROW,
BaseSliceTrack,
BaseSliceTrackTypes,
OnSliceClickArgs,
@@ -31,32 +31,21 @@
import {NUM_NULL, STR} from '../../trace_processor/query_result';
export const THREAD_STATE_ROW = {
- ...BASE_SLICE_ROW,
+ ...BASE_ROW,
state: STR,
ioWait: NUM_NULL,
};
+
export type ThreadStateRow = typeof THREAD_STATE_ROW;
-
-export interface ThreadStateTrackConfig {
- utid: number;
-}
-
export interface ThreadStateTrackTypes extends BaseSliceTrackTypes {
row: ThreadStateRow;
- config: ThreadStateTrackConfig;
}
-export const THREAD_STATE_TRACK_V2_KIND = 'ThreadStateTrackV2';
-
export class ThreadStateTrack extends BaseSliceTrack<ThreadStateTrackTypes> {
- static create(args: NewTrackArgs) {
- return new ThreadStateTrack(args);
- }
-
protected sliceLayout: SliceLayout = {...SLICE_LAYOUT_FLAT_DEFAULTS};
- constructor(args: NewTrackArgs) {
+ constructor(args: NewTrackArgs, private utid: number) {
super(args);
}
@@ -67,18 +56,22 @@
getSqlSource(): string {
// Do not display states 'x' and 'S' (dead & sleeping).
+ // Note: Thread state tracks V1 basically ignores incomplete slices, faking
+ // their duration as 1 instead. Let's just do this here as well for now to
+ // achieve feature parity with tracks V1 and tackle the issue of overlapping
+ // incomplete slices later.
return `
select
id,
ts,
- dur,
+ max(dur, 1) as dur,
cpu,
state,
io_wait as ioWait,
0 as depth
from thread_state
where
- utid = ${this.config.utid} and
+ utid = ${this.utid} and
state != 'x' and
state != 'S'
`;
@@ -89,21 +82,13 @@
const baseSlice = super.rowToSlice(row);
const ioWait = row.ioWait === null ? undefined : !!row.ioWait;
const title = translateState(row.state, ioWait);
- const baseColor: Color = colorForState(title);
- return {...baseSlice, title, baseColor};
+ const color = colorForState(title);
+ return {...baseSlice, title, colorScheme: color};
}
onUpdatedSlices(slices: ThreadStateTrackTypes['slice'][]) {
for (const slice of slices) {
- if (slice === this.hoveredSlice) {
- slice.color = {
- h: slice.baseColor.h,
- s: slice.baseColor.s,
- l: 30,
- };
- } else {
- slice.color = slice.baseColor;
- }
+ slice.isHighlighted = (slice === this.hoveredSlice);
}
}