Merge remote-tracking branch 'origin/master' into ui-canary
Commands:
$ git fetch origin
$ git checkout -B ui-canary -t origin/ui-canary
$ git merge --strategy=ours origin/master
$ git diff --binary origin/master | git apply --reverse --index
$ git commit --amend
End state:
$ git diff ui-canary origin/master
[no output]
$ git rev-list --count ui-canary..origin/master
0
Change-Id: I94521f6c83096b4a55996ffd7749539da856a811
diff --git a/Android.bp b/Android.bp
index e3a94e7..4265016 100644
--- a/Android.bp
+++ b/Android.bp
@@ -593,6 +593,7 @@
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_core_service",
+ ":perfetto_src_tracing_core_zlib_compressor",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
@@ -660,10 +661,19 @@
defaults: [
"perfetto_defaults",
],
+ cflags: [
+ "-DZLIB_IMPLEMENTATION",
+ ],
target: {
android: {
shared_libs: [
"liblog",
+ "libz",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libz",
],
},
},
@@ -679,6 +689,7 @@
"src/android_internal/health_hal.cc",
"src/android_internal/incident_service.cc",
"src/android_internal/power_stats.cc",
+ "src/android_internal/statsd.cc",
"src/android_internal/statsd_logging.cc",
"src/android_internal/tracing_service_proxy.cc",
],
@@ -696,6 +707,7 @@
"libincident",
"liblog",
"libservices",
+ "libstatspull",
"libstatssocket",
"libtracingproxy",
"libutils",
@@ -2023,6 +2035,8 @@
":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",
":perfetto_src_trace_processor_importers_common_common",
@@ -2052,11 +2066,13 @@
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
":perfetto_src_trace_processor_prelude_functions_functions",
+ ":perfetto_src_trace_processor_prelude_functions_interface",
":perfetto_src_trace_processor_prelude_operators_operators",
+ ":perfetto_src_trace_processor_prelude_table_functions_interface",
":perfetto_src_trace_processor_prelude_table_functions_table_functions",
":perfetto_src_trace_processor_sorter_sorter",
+ ":perfetto_src_trace_processor_sqlite_query_constraints",
":perfetto_src_trace_processor_sqlite_sqlite",
- ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
":perfetto_src_trace_processor_storage_minimal",
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
@@ -8409,6 +8425,7 @@
"src/base/scoped_file_unittest.cc",
"src/base/small_vector_unittest.cc",
"src/base/status_or_unittest.cc",
+ "src/base/status_unittest.cc",
"src/base/string_splitter_unittest.cc",
"src/base/string_utils_unittest.cc",
"src/base/string_view_unittest.cc",
@@ -9384,20 +9401,54 @@
srcs: [
"src/trace_processor/db/column.cc",
"src/trace_processor/db/column_storage.cc",
- "src/trace_processor/db/numeric_storage.cc",
- "src/trace_processor/db/storage.cc",
+ "src/trace_processor/db/null_overlay.cc",
"src/trace_processor/db/table.cc",
"src/trace_processor/db/view.cc",
],
}
+// GN: //src/trace_processor/db/overlays:overlays
+filegroup {
+ name: "perfetto_src_trace_processor_db_overlays_overlays",
+ srcs: [
+ "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/null_overlay_unittest.cc",
+ "src/trace_processor/db/overlays/selector_overlay_unittest.cc",
+ ],
+}
+
+// GN: //src/trace_processor/db/storage:storage
+filegroup {
+ name: "perfetto_src_trace_processor_db_storage_storage",
+ srcs: [
+ "src/trace_processor/db/storage/numeric_storage.cc",
+ "src/trace_processor/db/storage/storage.cc",
+ ],
+}
+
+// GN: //src/trace_processor/db/storage:unittests
+filegroup {
+ name: "perfetto_src_trace_processor_db_storage_unittests",
+ srcs: [
+ "src/trace_processor/db/storage/storage_unittest.cc",
+ ],
+}
+
// GN: //src/trace_processor/db:unittests
filegroup {
name: "perfetto_src_trace_processor_db_unittests",
srcs: [
"src/trace_processor/db/column_storage_overlay_unittest.cc",
"src/trace_processor/db/compare_unittest.cc",
- "src/trace_processor/db/storage_unittest.cc",
"src/trace_processor/db/view_unittest.cc",
],
}
@@ -10023,6 +10074,7 @@
"src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
"src/trace_processor/metrics/sql/android/java_heap_stats.sql",
"src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+ "src/trace_processor/metrics/sql/android/network_activity_template.sql",
"src/trace_processor/metrics/sql/android/p_state.sql",
"src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
"src/trace_processor/metrics/sql/android/power_profile_data.sql",
@@ -10151,14 +10203,22 @@
"src/trace_processor/prelude/functions/create_view_function.cc",
"src/trace_processor/prelude/functions/import.cc",
"src/trace_processor/prelude/functions/layout_functions.cc",
+ "src/trace_processor/prelude/functions/math.cc",
"src/trace_processor/prelude/functions/pprof_functions.cc",
- "src/trace_processor/prelude/functions/register_function.cc",
"src/trace_processor/prelude/functions/sqlite3_str_split.cc",
"src/trace_processor/prelude/functions/stack_functions.cc",
"src/trace_processor/prelude/functions/to_ftrace.cc",
],
}
+// GN: //src/trace_processor/prelude/functions:interface
+filegroup {
+ name: "perfetto_src_trace_processor_prelude_functions_interface",
+ srcs: [
+ "src/trace_processor/prelude/functions/sql_function.cc",
+ ],
+}
+
// GN: //src/trace_processor/prelude/functions:unittests
filegroup {
name: "perfetto_src_trace_processor_prelude_functions_unittests",
@@ -10184,6 +10244,14 @@
],
}
+// GN: //src/trace_processor/prelude/table_functions:interface
+filegroup {
+ name: "perfetto_src_trace_processor_prelude_table_functions_interface",
+ srcs: [
+ "src/trace_processor/prelude/table_functions/table_function.cc",
+ ],
+}
+
// GN: //src/trace_processor/prelude/table_functions:table_functions
filegroup {
name: "perfetto_src_trace_processor_prelude_table_functions_table_functions",
@@ -10198,7 +10266,6 @@
"src/trace_processor/prelude/table_functions/experimental_sched_upid.cc",
"src/trace_processor/prelude/table_functions/experimental_slice_layout.cc",
"src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc",
- "src/trace_processor/prelude/table_functions/table_function.cc",
"src/trace_processor/prelude/table_functions/view.cc",
],
}
@@ -10232,6 +10299,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -10312,33 +10380,38 @@
],
}
+// GN: //src/trace_processor/sqlite:query_constraints
+filegroup {
+ name: "perfetto_src_trace_processor_sqlite_query_constraints",
+ srcs: [
+ "src/trace_processor/sqlite/query_constraints.cc",
+ ],
+}
+
// GN: //src/trace_processor/sqlite:sqlite
filegroup {
name: "perfetto_src_trace_processor_sqlite_sqlite",
srcs: [
"src/trace_processor/sqlite/db_sqlite_table.cc",
+ "src/trace_processor/sqlite/perfetto_sql_engine.cc",
+ "src/trace_processor/sqlite/perfetto_sql_parser.cc",
"src/trace_processor/sqlite/sql_stats_table.cc",
"src/trace_processor/sqlite/sqlite_engine.cc",
+ "src/trace_processor/sqlite/sqlite_table.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer.cc",
"src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/stats_table.cc",
],
}
-// GN: //src/trace_processor/sqlite:sqlite_minimal
-filegroup {
- name: "perfetto_src_trace_processor_sqlite_sqlite_minimal",
- srcs: [
- "src/trace_processor/sqlite/query_constraints.cc",
- "src/trace_processor/sqlite/sqlite_table.cc",
- ],
-}
-
// GN: //src/trace_processor/sqlite:unittests
filegroup {
name: "perfetto_src_trace_processor_sqlite_unittests",
srcs: [
"src/trace_processor/sqlite/db_sqlite_table_unittest.cc",
+ "src/trace_processor/sqlite/perfetto_sql_parser_unittest.cc",
"src/trace_processor/sqlite/query_constraints_unittest.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc",
"src/trace_processor/sqlite/sqlite_utils_unittest.cc",
],
}
@@ -10348,15 +10421,21 @@
name: "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
srcs: [
"src/trace_processor/stdlib/android/battery.sql",
+ "src/trace_processor/stdlib/android/battery_stats.sql",
"src/trace_processor/stdlib/android/binder.sql",
"src/trace_processor/stdlib/android/monitor_contention.sql",
+ "src/trace_processor/stdlib/android/network_packets.sql",
"src/trace_processor/stdlib/android/process_metadata.sql",
"src/trace_processor/stdlib/android/slices.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql",
"src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql",
"src/trace_processor/stdlib/android/startup/startups.sql",
+ "src/trace_processor/stdlib/android/statsd.sql",
+ "src/trace_processor/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+ "src/trace_processor/stdlib/chrome/histograms.sql",
+ "src/trace_processor/stdlib/chrome/speedometer.sql",
"src/trace_processor/stdlib/common/counters.sql",
"src/trace_processor/stdlib/common/cpus.sql",
"src/trace_processor/stdlib/common/metadata.sql",
@@ -10443,6 +10522,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -10458,6 +10538,7 @@
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
+ "src/trace_processor/tables/sched_tables_py.h",
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
@@ -10477,6 +10558,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -11299,6 +11381,15 @@
"src/tracing/core/trace_packet_unittest.cc",
"src/tracing/core/trace_writer_impl_unittest.cc",
"src/tracing/core/tracing_service_impl_unittest.cc",
+ "src/tracing/core/zlib_compressor_unittest.cc",
+ ],
+}
+
+// GN: //src/tracing/core:zlib_compressor
+filegroup {
+ name: "perfetto_src_tracing_core_zlib_compressor",
+ srcs: [
+ "src/tracing/core/zlib_compressor.cc",
],
}
@@ -11988,6 +12079,10 @@
":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_storage",
+ ":perfetto_src_trace_processor_db_storage_unittests",
":perfetto_src_trace_processor_db_unittests",
":perfetto_src_trace_processor_export_json",
":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -12027,17 +12122,19 @@
":perfetto_src_trace_processor_metrics_metrics",
":perfetto_src_trace_processor_metrics_unittests",
":perfetto_src_trace_processor_prelude_functions_functions",
+ ":perfetto_src_trace_processor_prelude_functions_interface",
":perfetto_src_trace_processor_prelude_functions_unittests",
":perfetto_src_trace_processor_prelude_operators_operators",
":perfetto_src_trace_processor_prelude_operators_unittests",
+ ":perfetto_src_trace_processor_prelude_table_functions_interface",
":perfetto_src_trace_processor_prelude_table_functions_table_functions",
":perfetto_src_trace_processor_prelude_table_functions_unittests",
":perfetto_src_trace_processor_rpc_rpc",
":perfetto_src_trace_processor_rpc_unittests",
":perfetto_src_trace_processor_sorter_sorter",
":perfetto_src_trace_processor_sorter_unittests",
+ ":perfetto_src_trace_processor_sqlite_query_constraints",
":perfetto_src_trace_processor_sqlite_sqlite",
- ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
":perfetto_src_trace_processor_sqlite_unittests",
":perfetto_src_trace_processor_storage_minimal",
":perfetto_src_trace_processor_storage_storage",
@@ -12115,6 +12212,7 @@
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_core_test_support",
":perfetto_src_tracing_core_unittests",
+ ":perfetto_src_tracing_core_zlib_compressor",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
":perfetto_src_tracing_ipc_default_socket",
@@ -12682,6 +12780,8 @@
":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",
":perfetto_src_trace_processor_importers_common_common",
@@ -12711,13 +12811,15 @@
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
":perfetto_src_trace_processor_prelude_functions_functions",
+ ":perfetto_src_trace_processor_prelude_functions_interface",
":perfetto_src_trace_processor_prelude_operators_operators",
+ ":perfetto_src_trace_processor_prelude_table_functions_interface",
":perfetto_src_trace_processor_prelude_table_functions_table_functions",
":perfetto_src_trace_processor_rpc_httpd",
":perfetto_src_trace_processor_rpc_rpc",
":perfetto_src_trace_processor_sorter_sorter",
+ ":perfetto_src_trace_processor_sqlite_query_constraints",
":perfetto_src_trace_processor_sqlite_sqlite",
- ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
":perfetto_src_trace_processor_storage_minimal",
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
@@ -12905,6 +13007,8 @@
":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",
":perfetto_src_trace_processor_importers_common_common",
@@ -12934,11 +13038,13 @@
":perfetto_src_trace_processor_metatrace",
":perfetto_src_trace_processor_metrics_metrics",
":perfetto_src_trace_processor_prelude_functions_functions",
+ ":perfetto_src_trace_processor_prelude_functions_interface",
":perfetto_src_trace_processor_prelude_operators_operators",
+ ":perfetto_src_trace_processor_prelude_table_functions_interface",
":perfetto_src_trace_processor_prelude_table_functions_table_functions",
":perfetto_src_trace_processor_sorter_sorter",
+ ":perfetto_src_trace_processor_sqlite_query_constraints",
":perfetto_src_trace_processor_sqlite_sqlite",
- ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
":perfetto_src_trace_processor_storage_minimal",
":perfetto_src_trace_processor_storage_storage",
":perfetto_src_trace_processor_tables_tables",
@@ -13043,15 +13149,22 @@
"src/traced/service/main.cc",
],
shared_libs: [
- "liblog",
"libperfetto",
],
+ host_supported: true,
init_rc: [
"perfetto.rc",
],
defaults: [
"perfetto_defaults",
],
+ target: {
+ android: {
+ shared_libs: [
+ "liblog",
+ ],
+ },
+ },
}
// GN: //src/profiling/perf:traced_perf
@@ -13236,9 +13349,9 @@
"src/traced/probes/main.cc",
],
shared_libs: [
- "liblog",
"libperfetto",
],
+ host_supported: true,
defaults: [
"perfetto_defaults",
],
@@ -13248,6 +13361,13 @@
"traced_perf",
"trigger_perfetto",
],
+ target: {
+ android: {
+ shared_libs: [
+ "liblog",
+ ],
+ },
+ },
}
// GN: //src/perfetto_cmd:trigger_perfetto
diff --git a/BUILD b/BUILD
index a8826c5..f217e0a 100644
--- a/BUILD
+++ b/BUILD
@@ -64,6 +64,154 @@
linkstatic = True,
)
+# GN target: //src/cloud_trace_processor:cloud_trace_processor
+perfetto_cc_library(
+ name = "cloud_trace_processor",
+ srcs = [
+ ":src_base_threading_threading",
+ ":src_cloud_trace_processor_sources",
+ ":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",
+ ":src_trace_processor_importers_common_common",
+ ":src_trace_processor_importers_common_parser_types",
+ ":src_trace_processor_importers_common_trace_parser_hdr",
+ ":src_trace_processor_importers_ftrace_ftrace_descriptors",
+ ":src_trace_processor_importers_ftrace_full",
+ ":src_trace_processor_importers_ftrace_minimal",
+ ":src_trace_processor_importers_fuchsia_fuchsia_record",
+ ":src_trace_processor_importers_fuchsia_full",
+ ":src_trace_processor_importers_fuchsia_minimal",
+ ":src_trace_processor_importers_gzip_full",
+ ":src_trace_processor_importers_i2c_full",
+ ":src_trace_processor_importers_json_full",
+ ":src_trace_processor_importers_json_minimal",
+ ":src_trace_processor_importers_memory_tracker_graph_processor",
+ ":src_trace_processor_importers_ninja_ninja",
+ ":src_trace_processor_importers_proto_full",
+ ":src_trace_processor_importers_proto_minimal",
+ ":src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+ ":src_trace_processor_importers_proto_proto_importer_module",
+ ":src_trace_processor_importers_syscalls_full",
+ ":src_trace_processor_importers_systrace_full",
+ ":src_trace_processor_importers_systrace_systrace_line",
+ ":src_trace_processor_importers_systrace_systrace_parser",
+ ":src_trace_processor_lib",
+ ":src_trace_processor_metatrace",
+ ":src_trace_processor_metrics_metrics",
+ ":src_trace_processor_prelude_functions_functions",
+ ":src_trace_processor_prelude_functions_interface",
+ ":src_trace_processor_prelude_operators_operators",
+ ":src_trace_processor_prelude_table_functions_interface",
+ ":src_trace_processor_prelude_table_functions_table_functions",
+ ":src_trace_processor_prelude_table_functions_tables",
+ ":src_trace_processor_rpc_rpc",
+ ":src_trace_processor_sorter_sorter",
+ ":src_trace_processor_sqlite_query_constraints",
+ ":src_trace_processor_sqlite_sqlite",
+ ":src_trace_processor_storage_minimal",
+ ":src_trace_processor_storage_storage",
+ ":src_trace_processor_tables_tables",
+ ":src_trace_processor_tables_tables_python",
+ ":src_trace_processor_types_types",
+ ":src_trace_processor_util_bump_allocator",
+ ":src_trace_processor_util_descriptors",
+ ":src_trace_processor_util_glob",
+ ":src_trace_processor_util_gzip",
+ ":src_trace_processor_util_interned_message_view",
+ ":src_trace_processor_util_profile_builder",
+ ":src_trace_processor_util_proto_profiler",
+ ":src_trace_processor_util_proto_to_args_parser",
+ ":src_trace_processor_util_protozero_to_text",
+ ":src_trace_processor_util_sql_argument",
+ ":src_trace_processor_util_stack_traces_util",
+ ":src_trace_processor_util_stdlib",
+ ":src_trace_processor_util_util",
+ ":src_trace_processor_util_zip_reader",
+ ":src_trace_processor_views_views",
+ ],
+ hdrs = [
+ ":include_perfetto_base_base",
+ ":include_perfetto_ext_base_base",
+ ":include_perfetto_ext_base_threading_threading",
+ ":include_perfetto_ext_cloud_trace_processor_cloud_trace_processor",
+ ":include_perfetto_ext_trace_processor_demangle",
+ ":include_perfetto_ext_trace_processor_export_json",
+ ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
+ ":include_perfetto_ext_traced_sys_stats_counters",
+ ":include_perfetto_protozero_protozero",
+ ":include_perfetto_public_abi_base",
+ ":include_perfetto_public_base",
+ ":include_perfetto_public_protozero",
+ ":include_perfetto_trace_processor_basic_types",
+ ":include_perfetto_trace_processor_storage",
+ ":include_perfetto_trace_processor_trace_processor",
+ ],
+ deps = [
+ ":protos_perfetto_cloud_trace_processor_lite",
+ ":protos_perfetto_common_lite",
+ ":protos_perfetto_common_zero",
+ ":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_ftrace_zero",
+ ":protos_perfetto_config_gpu_zero",
+ ":protos_perfetto_config_inode_file_zero",
+ ":protos_perfetto_config_interceptors_zero",
+ ":protos_perfetto_config_power_zero",
+ ":protos_perfetto_config_process_stats_zero",
+ ":protos_perfetto_config_profiling_zero",
+ ":protos_perfetto_config_statsd_zero",
+ ":protos_perfetto_config_sys_stats_zero",
+ ":protos_perfetto_config_system_info_zero",
+ ":protos_perfetto_config_track_event_zero",
+ ":protos_perfetto_config_zero",
+ ":protos_perfetto_trace_android_zero",
+ ":protos_perfetto_trace_chrome_zero",
+ ":protos_perfetto_trace_filesystem_zero",
+ ":protos_perfetto_trace_ftrace_zero",
+ ":protos_perfetto_trace_gpu_zero",
+ ":protos_perfetto_trace_interned_data_zero",
+ ":protos_perfetto_trace_minimal_zero",
+ ":protos_perfetto_trace_non_minimal_zero",
+ ":protos_perfetto_trace_perfetto_zero",
+ ":protos_perfetto_trace_power_zero",
+ ":protos_perfetto_trace_processor_lite",
+ ":protos_perfetto_trace_processor_metrics_impl_zero",
+ ":protos_perfetto_trace_processor_zero",
+ ":protos_perfetto_trace_profiling_zero",
+ ":protos_perfetto_trace_ps_zero",
+ ":protos_perfetto_trace_statsd_zero",
+ ":protos_perfetto_trace_sys_stats_zero",
+ ":protos_perfetto_trace_system_info_zero",
+ ":protos_perfetto_trace_track_event_zero",
+ ":protos_perfetto_trace_translation_zero",
+ ":protos_third_party_pprof_zero",
+ ":protozero",
+ ":src_base_base",
+ ":src_base_version",
+ ":src_trace_processor_containers_containers",
+ ":src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+ ":src_trace_processor_importers_proto_gen_cc_config_descriptor",
+ ":src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+ ":src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+ ":src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
+ ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
+ ":src_trace_processor_metrics_gen_cc_all_webview_metrics_descriptor",
+ ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
+ ":src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+ ":src_trace_processor_prelude_tables_views_tables_views",
+ ":src_trace_processor_stdlib_gen_amalgamated_stdlib",
+ ] + PERFETTO_CONFIG.deps.jsoncpp +
+ PERFETTO_CONFIG.deps.sqlite +
+ PERFETTO_CONFIG.deps.sqlite_ext_percentile +
+ PERFETTO_CONFIG.deps.zlib +
+ PERFETTO_CONFIG.deps.demangle_wrapper,
+ linkstatic = True,
+)
+
# GN target: //src/ipc/protoc_plugin:ipc_plugin
perfetto_cc_binary(
name = "ipc_plugin",
@@ -279,6 +427,7 @@
":src_tracing_common",
":src_tracing_core_core",
":src_tracing_core_service",
+ ":src_tracing_core_zlib_compressor",
":src_tracing_ipc_common",
":src_tracing_ipc_default_socket",
":src_tracing_ipc_producer_producer",
@@ -355,7 +504,7 @@
":protozero",
":src_base_base",
":src_base_version",
- ],
+ ] + PERFETTO_CONFIG.deps.zlib,
linkstatic = True,
)
@@ -387,6 +536,22 @@
],
)
+# GN target: //include/perfetto/ext/base/threading:threading
+perfetto_filegroup(
+ name = "include_perfetto_ext_base_threading_threading",
+ srcs = [
+ "include/perfetto/ext/base/threading/channel.h",
+ "include/perfetto/ext/base/threading/future.h",
+ "include/perfetto/ext/base/threading/future_combinators.h",
+ "include/perfetto/ext/base/threading/poll.h",
+ "include/perfetto/ext/base/threading/spawn.h",
+ "include/perfetto/ext/base/threading/stream.h",
+ "include/perfetto/ext/base/threading/stream_combinators.h",
+ "include/perfetto/ext/base/threading/thread_pool.h",
+ "include/perfetto/ext/base/threading/util.h",
+ ],
+)
+
# GN target: //include/perfetto/ext/base:base
perfetto_filegroup(
name = "include_perfetto_ext_base_base",
@@ -446,6 +611,16 @@
],
)
+# GN target: //include/perfetto/ext/cloud_trace_processor:cloud_trace_processor
+perfetto_filegroup(
+ name = "include_perfetto_ext_cloud_trace_processor_cloud_trace_processor",
+ srcs = [
+ "include/perfetto/ext/cloud_trace_processor/environment.h",
+ "include/perfetto/ext/cloud_trace_processor/orchestrator.h",
+ "include/perfetto/ext/cloud_trace_processor/worker.h",
+ ],
+)
+
# GN target: //include/perfetto/ext/ipc:ipc
perfetto_filegroup(
name = "include_perfetto_ext_ipc_ipc",
@@ -758,6 +933,16 @@
linkstatic = True,
)
+# GN target: //src/base/threading:threading
+perfetto_filegroup(
+ name = "src_base_threading_threading",
+ srcs = [
+ "src/base/threading/spawn.cc",
+ "src/base/threading/stream_combinators.cc",
+ "src/base/threading/thread_pool.cc",
+ ],
+)
+
# GN target: //src/base:base
perfetto_cc_library(
name = "src_base_base",
@@ -849,6 +1034,19 @@
],
)
+# GN target: //src/cloud_trace_processor:sources
+perfetto_filegroup(
+ name = "src_cloud_trace_processor_sources",
+ srcs = [
+ "src/cloud_trace_processor/orchestrator_impl.cc",
+ "src/cloud_trace_processor/orchestrator_impl.h",
+ "src/cloud_trace_processor/trace_processor_wrapper.cc",
+ "src/cloud_trace_processor/trace_processor_wrapper.h",
+ "src/cloud_trace_processor/worker_impl.cc",
+ "src/cloud_trace_processor/worker_impl.h",
+ ],
+)
+
# GN target: //src/ipc:client
perfetto_filegroup(
name = "src_ipc_client",
@@ -1075,6 +1273,32 @@
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/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/numeric_storage.cc",
+ "src/trace_processor/db/storage/numeric_storage.h",
+ "src/trace_processor/db/storage/storage.cc",
+ "src/trace_processor/db/storage/storage.h",
+ "src/trace_processor/db/storage/types.h",
+ ],
+)
+
# GN target: //src/trace_processor/db:db
perfetto_filegroup(
name = "src_trace_processor_db_db",
@@ -1082,19 +1306,12 @@
"src/trace_processor/db/base_id.h",
"src/trace_processor/db/column.cc",
"src/trace_processor/db/column.h",
- "src/trace_processor/db/column_overlay.h",
"src/trace_processor/db/column_storage.cc",
"src/trace_processor/db/column_storage.h",
"src/trace_processor/db/column_storage_overlay.h",
"src/trace_processor/db/compare.h",
+ "src/trace_processor/db/null_overlay.cc",
"src/trace_processor/db/null_overlay.h",
- "src/trace_processor/db/numeric_storage.cc",
- "src/trace_processor/db/numeric_storage.h",
- "src/trace_processor/db/sorting_overlay.h",
- "src/trace_processor/db/storage.cc",
- "src/trace_processor/db/storage.h",
- "src/trace_processor/db/storage_overlay.h",
- "src/trace_processor/db/storage_variants.h",
"src/trace_processor/db/table.cc",
"src/trace_processor/db/table.h",
"src/trace_processor/db/typed_column.h",
@@ -1588,6 +1805,7 @@
"src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
"src/trace_processor/metrics/sql/android/java_heap_stats.sql",
"src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+ "src/trace_processor/metrics/sql/android/network_activity_template.sql",
"src/trace_processor/metrics/sql/android/p_state.sql",
"src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
"src/trace_processor/metrics/sql/android/power_profile_data.sql",
@@ -1801,10 +2019,10 @@
"src/trace_processor/prelude/functions/import.h",
"src/trace_processor/prelude/functions/layout_functions.cc",
"src/trace_processor/prelude/functions/layout_functions.h",
+ "src/trace_processor/prelude/functions/math.cc",
+ "src/trace_processor/prelude/functions/math.h",
"src/trace_processor/prelude/functions/pprof_functions.cc",
"src/trace_processor/prelude/functions/pprof_functions.h",
- "src/trace_processor/prelude/functions/register_function.cc",
- "src/trace_processor/prelude/functions/register_function.h",
"src/trace_processor/prelude/functions/sqlite3_str_split.cc",
"src/trace_processor/prelude/functions/sqlite3_str_split.h",
"src/trace_processor/prelude/functions/stack_functions.cc",
@@ -1816,6 +2034,15 @@
],
)
+# GN target: //src/trace_processor/prelude/functions:interface
+perfetto_filegroup(
+ name = "src_trace_processor_prelude_functions_interface",
+ srcs = [
+ "src/trace_processor/prelude/functions/sql_function.cc",
+ "src/trace_processor/prelude/functions/sql_function.h",
+ ],
+)
+
# GN target: //src/trace_processor/prelude/operators:operators
perfetto_filegroup(
name = "src_trace_processor_prelude_operators_operators",
@@ -1827,6 +2054,15 @@
],
)
+# GN target: //src/trace_processor/prelude/table_functions:interface
+perfetto_filegroup(
+ name = "src_trace_processor_prelude_table_functions_interface",
+ srcs = [
+ "src/trace_processor/prelude/table_functions/table_function.cc",
+ "src/trace_processor/prelude/table_functions/table_function.h",
+ ],
+)
+
# GN target: //src/trace_processor/prelude/table_functions:table_functions
perfetto_filegroup(
name = "src_trace_processor_prelude_table_functions_table_functions",
@@ -1851,8 +2087,6 @@
"src/trace_processor/prelude/table_functions/experimental_slice_layout.h",
"src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc",
"src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.h",
- "src/trace_processor/prelude/table_functions/table_function.cc",
- "src/trace_processor/prelude/table_functions/table_function.h",
"src/trace_processor/prelude/table_functions/view.cc",
"src/trace_processor/prelude/table_functions/view.h",
],
@@ -1924,17 +2158,35 @@
],
)
+# GN target: //src/trace_processor/sqlite:query_constraints
+perfetto_filegroup(
+ name = "src_trace_processor_sqlite_query_constraints",
+ srcs = [
+ "src/trace_processor/sqlite/query_constraints.cc",
+ "src/trace_processor/sqlite/query_constraints.h",
+ ],
+)
+
# GN target: //src/trace_processor/sqlite:sqlite
perfetto_filegroup(
name = "src_trace_processor_sqlite_sqlite",
srcs = [
"src/trace_processor/sqlite/db_sqlite_table.cc",
"src/trace_processor/sqlite/db_sqlite_table.h",
+ "src/trace_processor/sqlite/perfetto_sql_engine.cc",
+ "src/trace_processor/sqlite/perfetto_sql_engine.h",
+ "src/trace_processor/sqlite/perfetto_sql_parser.cc",
+ "src/trace_processor/sqlite/perfetto_sql_parser.h",
"src/trace_processor/sqlite/query_cache.h",
+ "src/trace_processor/sqlite/scoped_db.h",
"src/trace_processor/sqlite/sql_stats_table.cc",
"src/trace_processor/sqlite/sql_stats_table.h",
"src/trace_processor/sqlite/sqlite_engine.cc",
"src/trace_processor/sqlite/sqlite_engine.h",
+ "src/trace_processor/sqlite/sqlite_table.cc",
+ "src/trace_processor/sqlite/sqlite_table.h",
+ "src/trace_processor/sqlite/sqlite_tokenizer.cc",
+ "src/trace_processor/sqlite/sqlite_tokenizer.h",
"src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/sqlite_utils.h",
"src/trace_processor/sqlite/stats_table.cc",
@@ -1942,19 +2194,6 @@
],
)
-# GN target: //src/trace_processor/sqlite:sqlite_minimal
-perfetto_filegroup(
- name = "src_trace_processor_sqlite_sqlite_minimal",
- srcs = [
- "src/trace_processor/sqlite/query_constraints.cc",
- "src/trace_processor/sqlite/query_constraints.h",
- "src/trace_processor/sqlite/scoped_db.h",
- "src/trace_processor/sqlite/sqlite_table.cc",
- "src/trace_processor/sqlite/sqlite_table.h",
- "src/trace_processor/sqlite/sqlite_utils.h",
- ],
-)
-
# GN target: //src/trace_processor/stdlib/android/startup:startup
perfetto_filegroup(
name = "src_trace_processor_stdlib_android_startup_startup",
@@ -1971,10 +2210,13 @@
name = "src_trace_processor_stdlib_android_android",
srcs = [
"src/trace_processor/stdlib/android/battery.sql",
+ "src/trace_processor/stdlib/android/battery_stats.sql",
"src/trace_processor/stdlib/android/binder.sql",
"src/trace_processor/stdlib/android/monitor_contention.sql",
+ "src/trace_processor/stdlib/android/network_packets.sql",
"src/trace_processor/stdlib/android/process_metadata.sql",
"src/trace_processor/stdlib/android/slices.sql",
+ "src/trace_processor/stdlib/android/statsd.sql",
],
)
@@ -1982,7 +2224,10 @@
perfetto_filegroup(
name = "src_trace_processor_stdlib_chrome_chrome_sql",
srcs = [
+ "src/trace_processor/stdlib/chrome/chrome_scrolls.sql",
"src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+ "src/trace_processor/stdlib/chrome/histograms.sql",
+ "src/trace_processor/stdlib/chrome/speedometer.sql",
],
)
@@ -2063,6 +2308,7 @@
"src/trace_processor/tables/memory_tables.py",
"src/trace_processor/tables/metadata_tables.py",
"src/trace_processor/tables/profiler_tables.py",
+ "src/trace_processor/tables/sched_tables.py",
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
@@ -2074,6 +2320,7 @@
"src/trace_processor/tables/memory_tables_py.h",
"src/trace_processor/tables/metadata_tables_py.h",
"src/trace_processor/tables/profiler_tables_py.h",
+ "src/trace_processor/tables/sched_tables_py.h",
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
@@ -2632,6 +2879,15 @@
],
)
+# GN target: //src/tracing/core:zlib_compressor
+perfetto_filegroup(
+ name = "src_tracing_core_zlib_compressor",
+ srcs = [
+ "src/tracing/core/zlib_compressor.cc",
+ "src/tracing/core/zlib_compressor.h",
+ ],
+)
+
# GN target: //src/tracing/ipc/consumer:consumer
perfetto_filegroup(
name = "src_tracing_ipc_consumer_consumer",
@@ -3044,6 +3300,31 @@
],
)
+# GN target: //protos/perfetto/cloud_trace_processor:lite
+perfetto_cc_proto_library(
+ name = "protos_perfetto_cloud_trace_processor_lite",
+ deps = [
+ ":protos_perfetto_cloud_trace_processor_protos",
+ ],
+)
+
+# GN target: //protos/perfetto/cloud_trace_processor:source_set
+perfetto_proto_library(
+ name = "protos_perfetto_cloud_trace_processor_protos",
+ srcs = [
+ "protos/perfetto/cloud_trace_processor/common.proto",
+ "protos/perfetto/cloud_trace_processor/orchestrator.proto",
+ "protos/perfetto/cloud_trace_processor/worker.proto",
+ ],
+ visibility = [
+ PERFETTO_CONFIG.proto_library_visibility,
+ ],
+ deps = [
+ ":protos_perfetto_common_protos",
+ ":protos_perfetto_trace_processor_protos",
+ ],
+)
+
# GN target: //protos/perfetto/common:cpp
perfetto_cc_protocpp_library(
name = "protos_perfetto_common_cpp",
@@ -3052,6 +3333,14 @@
],
)
+# GN target: //protos/perfetto/common:lite
+perfetto_cc_proto_library(
+ name = "protos_perfetto_common_lite",
+ deps = [
+ ":protos_perfetto_common_protos",
+ ],
+)
+
# GN target: //protos/perfetto/common:source_set
perfetto_proto_library(
name = "protos_perfetto_common_protos",
@@ -4193,6 +4482,14 @@
],
)
+# GN target: //protos/perfetto/trace_processor:lite
+perfetto_cc_proto_library(
+ name = "protos_perfetto_trace_processor_lite",
+ deps = [
+ ":protos_perfetto_trace_processor_protos",
+ ],
+)
+
# GN target: //protos/perfetto/trace_processor:metrics_impl_source_set
perfetto_proto_library(
name = "protos_perfetto_trace_processor_metrics_impl_protos",
@@ -4724,6 +5021,8 @@
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",
":src_trace_processor_importers_common_common",
@@ -4753,12 +5052,14 @@
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
":src_trace_processor_prelude_functions_functions",
+ ":src_trace_processor_prelude_functions_interface",
":src_trace_processor_prelude_operators_operators",
+ ":src_trace_processor_prelude_table_functions_interface",
":src_trace_processor_prelude_table_functions_table_functions",
":src_trace_processor_prelude_table_functions_tables",
":src_trace_processor_sorter_sorter",
+ ":src_trace_processor_sqlite_query_constraints",
":src_trace_processor_sqlite_sqlite",
- ":src_trace_processor_sqlite_sqlite_minimal",
":src_trace_processor_storage_minimal",
":src_trace_processor_storage_storage",
":src_trace_processor_tables_tables",
@@ -4878,6 +5179,8 @@
":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",
":src_trace_processor_importers_common_common",
@@ -4907,14 +5210,16 @@
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
":src_trace_processor_prelude_functions_functions",
+ ":src_trace_processor_prelude_functions_interface",
":src_trace_processor_prelude_operators_operators",
+ ":src_trace_processor_prelude_table_functions_interface",
":src_trace_processor_prelude_table_functions_table_functions",
":src_trace_processor_prelude_table_functions_tables",
":src_trace_processor_rpc_httpd",
":src_trace_processor_rpc_rpc",
":src_trace_processor_sorter_sorter",
+ ":src_trace_processor_sqlite_query_constraints",
":src_trace_processor_sqlite_sqlite",
- ":src_trace_processor_sqlite_sqlite_minimal",
":src_trace_processor_storage_minimal",
":src_trace_processor_storage_storage",
":src_trace_processor_tables_tables",
@@ -5091,6 +5396,8 @@
":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",
":src_trace_processor_importers_common_common",
@@ -5120,12 +5427,14 @@
":src_trace_processor_metatrace",
":src_trace_processor_metrics_metrics",
":src_trace_processor_prelude_functions_functions",
+ ":src_trace_processor_prelude_functions_interface",
":src_trace_processor_prelude_operators_operators",
+ ":src_trace_processor_prelude_table_functions_interface",
":src_trace_processor_prelude_table_functions_table_functions",
":src_trace_processor_prelude_table_functions_tables",
":src_trace_processor_sorter_sorter",
+ ":src_trace_processor_sqlite_query_constraints",
":src_trace_processor_sqlite_sqlite",
- ":src_trace_processor_sqlite_sqlite_minimal",
":src_trace_processor_storage_minimal",
":src_trace_processor_storage_storage",
":src_trace_processor_tables_tables",
diff --git a/CHANGELOG b/CHANGELOG
index 6e371a4..107a26b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
Unreleased:
Tracing service and probes:
+ * Compression has been moved from perfetto_cmd to traced. Now compression is
+ supported even with write_into_file. The `compress_from_cli` config option
+ can be used to restore the old behavior.
+ Trace Processor:
*
UI:
*
@@ -10,6 +14,8 @@
Tracing service and probes:
* --continuous-dump in tools/java_heap_dump now keeps recording until it
receives CTRL+C.
+ * Add CLONE_SNAPSHOT triggers for non-destructive snapshots of the trace
+ buffer without tracing interruption.
Trace Processor:
*
UI:
diff --git a/docs/contributing/common-tasks.md b/docs/contributing/common-tasks.md
index 4189d19..7c89e0e 100644
--- a/docs/contributing/common-tasks.md
+++ b/docs/contributing/common-tasks.md
@@ -169,3 +169,22 @@
* There is no way to tie newly added events back to the source events in the
trace which were used to generate them. This is not currently a priority but
something we may add in the future.
+
+
+## Update `TRACE_PROCESSOR_CURRENT_API_VERSION`
+
+Generally you do not have to worry about version skew between the UI
+and the `trace_processor` since they are built together at the same
+commit. However version skew can occur when using the `--httpd` mode
+which allows a native `trace_processor` instance to be used with the UI.
+
+A common case is when the UI is more recent than `trace_processor`
+and depends on a new table definition. With older versions of
+`trace_processor` in `--httpd` mode the UI crashes attempting to query
+a non-existant table. To avoid this we use a version number. If the
+version number `trace_processor` reports is older than the one the UI
+was built with we prompt the user to update.
+
+1. Go to `protos/perfetto/trace_processor/trace_processor.proto`
+2. Increment `TRACE_PROCESSOR_CURRENT_API_VERSION`
+3. Add a comment explaining what has changed.
diff --git a/docs/contributing/perfetto-in-the-press.md b/docs/contributing/perfetto-in-the-press.md
index 108849a..4546bd6 100644
--- a/docs/contributing/perfetto-in-the-press.md
+++ b/docs/contributing/perfetto-in-the-press.md
@@ -2,6 +2,8 @@
This a partial collection of the talks, blogposts, presentations, and articles that mention Perfetto.
+- [Google IO 2023 - What's new in Dart and Flutter](https://youtu.be/yRlwOdCK7Ho?t=798)
+- [Google IO 2023 - Debugging Jetpack Compose](https://youtu.be/Kp-aiSU8qCU?t=1092)
- [Performance: Perfetto Traceviewer - MAD Skills](https://www.youtube.com/watch?v=phhLFicMacY)
"On this episode of the MAD Skills series on Performance, Android Performance Engineer Carmen Jackson discusses the Perfetto traceviewer, an alternative to Android Studio for viewing system traces."
- [Performance and optimisation on the Meta Quest Platform](https://m.facebook.com/RealityLabs/videos/performance-and-optimization-on-meta-quest-platform/488126049869673/)
diff --git a/docs/images/enable-profile-flame-graph.png b/docs/images/enable-profile-flame-graph.png
deleted file mode 100644
index 642ce73..0000000
--- a/docs/images/enable-profile-flame-graph.png
+++ /dev/null
Binary files differ
diff --git a/docs/quickstart/callstack-sampling.md b/docs/quickstart/callstack-sampling.md
index e5da5b2..cb47edc 100644
--- a/docs/quickstart/callstack-sampling.md
+++ b/docs/quickstart/callstack-sampling.md
@@ -119,11 +119,6 @@
## View profile
-Visualizing callstacks in the Perfetto UI is currently disabled behind a
-flag. Please enable it before proceeding further:
-
-
-
Upload the `raw-trace` or `symbolized-trace` file from the output directory to
the [Perfetto UI](https://ui.perfetto.dev) and click and drag over one or more
of the diamond markers in the UI track named "Perf Samples" for the processes
diff --git a/docs/quickstart/chrome-tracing.md b/docs/quickstart/chrome-tracing.md
index 4e17172..6786b4d 100644
--- a/docs/quickstart/chrome-tracing.md
+++ b/docs/quickstart/chrome-tracing.md
@@ -4,6 +4,8 @@
> To record traces from Chrome on Android, follow the [instructions for recording Android system traces](/docs/quickstart/android-tracing.md) and enable the Chrome probe.
+>> If you are using [user build of Android](https://source.android.com/docs/setup/build/building#lunch), you'll have to enable integration with system Perfetto by switching chrome://flags#enable-perfetto-system-tracing to "Enabled" and restarting Chrome.
+
## Recording a trace
1. Navigate to [ui.perfetto.dev](https://ui.perfetto.dev/) and select **"Record new trace"** from the left menu.
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index b778d88..579fc0f 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -296,8 +296,9 @@
enable_perfetto_trace_processor &&
(perfetto_build_standalone || perfetto_build_with_android)
- # Enables Zlib support. This is used both by the "perfetto" cmdline client
- # (for compressing traces) and by trace processor (for compressed traces).
+ # Enables Zlib support. This is used to compress traces (by the tracing
+ # service and by the "perfetto" cmdline client) and to decompress traces (by
+ # trace_processor).
enable_perfetto_zlib =
(enable_perfetto_trace_processor && !build_with_chromium) ||
enable_perfetto_platform_services
diff --git a/include/perfetto/base/status.h b/include/perfetto/base/status.h
index 2939357..f059184 100644
--- a/include/perfetto/base/status.h
+++ b/include/perfetto/base/status.h
@@ -17,7 +17,10 @@
#ifndef INCLUDE_PERFETTO_BASE_STATUS_H_
#define INCLUDE_PERFETTO_BASE_STATUS_H_
+#include <optional>
#include <string>
+#include <string_view>
+#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
@@ -30,6 +33,10 @@
// This can used as the return type of functions which would usually return an
// bool for success or int for errno but also wants to add some string context
// (ususally for logging).
+//
+// Similar to absl::Status, an optional "payload" can also be included with more
+// context about the error. This allows passing additional metadata about the
+// error (e.g. location of errors, potential mitigations etc).
class PERFETTO_EXPORT_COMPONENT Status {
public:
Status() : ok_(true) {}
@@ -52,9 +59,49 @@
const std::string& message() const { return message_; }
const char* c_message() const { return message_.c_str(); }
+ //////////////////////////////////////////////////////////////////////////////
+ // Payload Management APIs
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Payloads can be attached to error statuses to provide additional context.
+ //
+ // Payloads are (key, value) pairs, where the key is a string acting as a
+ // unique "type URL" and the value is an opaque string. The "type URL" should
+ // be unique, follow the format of a URL and, ideally, documentation on how to
+ // interpret its associated data should be available.
+ //
+ // To attach a payload to a status object, call `Status::SetPayload()`.
+ // Similarly, to extract the payload from a status, call
+ // `Status::GetPayload()`.
+ //
+ // Note: the payload APIs are only meaningful to call when the status is an
+ // error. Otherwise, all methods are noops.
+
+ // Gets the payload for the given |type_url| if one exists.
+ //
+ // Will always return std::nullopt if |ok()|.
+ std::optional<std::string_view> GetPayload(std::string_view type_url);
+
+ // Sets the payload for the given key. The key should
+ //
+ // Will always do nothing if |ok()|.
+ void SetPayload(std::string_view type_url, std::string value);
+
+ // Erases the payload for the given string and returns true if the payload
+ // existed and was erased.
+ //
+ // Will always do nothing if |ok()|.
+ bool ErasePayload(std::string_view type_url);
+
private:
+ struct Payload {
+ std::string type_url;
+ std::string payload;
+ };
+
bool ok_ = false;
std::string message_;
+ std::vector<Payload> payloads_;
};
// Returns a status object which represents the Ok status.
diff --git a/include/perfetto/ext/tracing/core/consumer.h b/include/perfetto/ext/tracing/core/consumer.h
index 7a7d81a..f55b810 100644
--- a/include/perfetto/ext/tracing/core/consumer.h
+++ b/include/perfetto/ext/tracing/core/consumer.h
@@ -20,6 +20,7 @@
#include <vector>
#include "perfetto/base/export.h"
+#include "perfetto/ext/base/uuid.h"
#include "perfetto/ext/tracing/core/basic_types.h"
#include "perfetto/ext/tracing/core/observable_events.h"
#include "perfetto/tracing/core/forward_decls.h"
@@ -81,7 +82,12 @@
// Called back by the Service (or transport layer) after invoking
// TracingService::ConsumerEndpoint::CloneSession().
// TODO(primiano): make pure virtual after various 3way patches.
- virtual void OnSessionCloned(bool success, const std::string& error);
+ struct OnSessionClonedArgs {
+ bool success;
+ std::string error;
+ base::Uuid uuid; // UUID of the cloned session.
+ };
+ virtual void OnSessionCloned(const OnSessionClonedArgs&);
};
} // namespace perfetto
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 82e53f7..b82a96a 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -28,6 +28,7 @@
#include "perfetto/ext/base/sys_types.h"
#include "perfetto/ext/tracing/core/basic_types.h"
#include "perfetto/ext/tracing/core/shared_memory.h"
+#include "perfetto/ext/tracing/core/trace_packet.h"
#include "perfetto/tracing/buffer_exhausted_policy.h"
#include "perfetto/tracing/core/forward_decls.h"
@@ -253,6 +254,14 @@
virtual void SaveTraceForBugreport(SaveTraceForBugreportCallback) = 0;
}; // class ConsumerEndpoint.
+struct PERFETTO_EXPORT_COMPONENT TracingServiceInitOpts {
+ // Function used by tracing service to compress packets. Takes a pointer to
+ // a vector of TracePackets and replaces the packets in the vector with
+ // compressed ones.
+ using CompressorFn = void (*)(std::vector<TracePacket>*);
+ CompressorFn compressor_fn = nullptr;
+};
+
// The public API of the tracing Service business logic.
//
// Exposed to:
@@ -267,6 +276,7 @@
public:
using ProducerEndpoint = perfetto::ProducerEndpoint;
using ConsumerEndpoint = perfetto::ConsumerEndpoint;
+ using InitOpts = TracingServiceInitOpts;
// Default sizes used by the service implementation and client library.
static constexpr size_t kDefaultShmPageSize = 4096ul;
@@ -286,10 +296,12 @@
kDisabled
};
- // Implemented in src/core/tracing_service_impl.cc .
+ // Implemented in src/core/tracing_service_impl.cc . CompressorFn can be
+ // nullptr, in which case TracingService will not support compression.
static std::unique_ptr<TracingService> CreateInstance(
std::unique_ptr<SharedMemory::Factory>,
- base::TaskRunner*);
+ base::TaskRunner*,
+ InitOpts init_opts = {});
virtual ~TracingService();
diff --git a/include/perfetto/ext/tracing/ipc/service_ipc_host.h b/include/perfetto/ext/tracing/ipc/service_ipc_host.h
index 5c51c4a..b24ccb5 100644
--- a/include/perfetto/ext/tracing/ipc/service_ipc_host.h
+++ b/include/perfetto/ext/tracing/ipc/service_ipc_host.h
@@ -23,6 +23,7 @@
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/tracing/core/basic_types.h"
+#include "perfetto/ext/tracing/core/tracing_service.h"
namespace perfetto {
namespace base {
@@ -33,8 +34,6 @@
class Host;
} // namespace ipc
-class TracingService;
-
// Creates an instance of the service (business logic + UNIX socket transport).
// Exposed to:
// The code in the tracing client that will host the service e.g., traced.
@@ -42,7 +41,9 @@
// src/tracing/ipc/service/service_ipc_host_impl.cc
class PERFETTO_EXPORT_COMPONENT ServiceIPCHost {
public:
- static std::unique_ptr<ServiceIPCHost> CreateInstance(base::TaskRunner*);
+ static std::unique_ptr<ServiceIPCHost> CreateInstance(
+ base::TaskRunner*,
+ TracingService::InitOpts = {});
virtual ~ServiceIPCHost();
// Start listening on the Producer & Consumer ports. Returns false in case of
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index 2210532..c27fcad 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -340,7 +340,8 @@
uint32_t field_id,
bool* parse_error_location) const {
const Field& field = Get(field_id);
- if (field.valid()) {
+ if (field.valid() &&
+ field.type() == proto_utils::ProtoWireType::kLengthDelimited) {
return PackedRepeatedFieldIterator<wire_type, cpp_type>(
field.data(), field.size(), parse_error_location);
}
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index 3e2a830..e9807ab 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -25,4 +25,16 @@
#include "protos/perfetto/config/trace_config.gen.h"
+namespace perfetto {
+
+inline TraceConfig::TriggerConfig::TriggerMode GetTriggerMode(
+ const TraceConfig& cfg) {
+ auto mode = cfg.trigger_config().trigger_mode();
+ if (cfg.trigger_config().use_clone_snapshot_if_available())
+ mode = TraceConfig::TriggerConfig::CLONE_SNAPSHOT;
+ return mode;
+}
+
+} // namespace perfetto
+
#endif // INCLUDE_PERFETTO_TRACING_CORE_TRACE_CONFIG_H_
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index 95036d1..26525fa 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -105,7 +105,7 @@
};
virtual void OnStart(const StartArgs&);
- class StopArgs {
+ class PERFETTO_EXPORT_COMPONENT StopArgs {
public:
virtual ~StopArgs();
@@ -190,6 +190,22 @@
}
};
+// Holds the type for a DataSource. Accessed by the static Trace() method
+// fastpaths. This allows redefinitions under a component where a component
+// specific export macro is used.
+// Due to C2086 (redefinition) error on MSVC/clang-cl, internal::DataSourceType
+// can't be a static data member. To avoid explicit specialization after
+// instantiation error, type() needs to be in a template helper class that's
+// instantiated independently from DataSource. See b/280777748.
+template <typename DerivedDataSource,
+ typename DataSourceTraits = DefaultDataSourceTraits>
+struct DataSourceHelper {
+ static internal::DataSourceType& type() {
+ static perfetto::internal::DataSourceType type_;
+ return type_;
+ }
+};
+
// Templated base class meant to be derived by embedders to create a custom data
// source. DerivedDataSource must be the type of the derived class itself, e.g.:
// class MyDataSource : public DataSource<MyDataSource> {...}.
@@ -200,6 +216,7 @@
typename DataSourceTraits = DefaultDataSourceTraits>
class DataSource : public DataSourceBase {
struct DefaultTracePointTraits;
+ using Helper = DataSourceHelper<DerivedDataSource, DataSourceTraits>;
public:
// The BufferExhaustedPolicy to use for TraceWriters of this DataSource.
@@ -286,7 +303,8 @@
// validity before using it. After checking, the handle is guaranteed to
// remain valid until the handle goes out of scope.
LockedHandle<DerivedDataSource> GetDataSourceLocked() const {
- auto* internal_state = type_.static_state()->TryGet(instance_index_);
+ auto* internal_state =
+ Helper::type().static_state()->TryGet(instance_index_);
if (!internal_state)
return LockedHandle<DerivedDataSource>();
std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
@@ -304,7 +322,7 @@
typename DataSourceTraits::IncrementalStateType* GetIncrementalState() {
return static_cast<typename DataSourceTraits::IncrementalStateType*>(
- type_.GetIncrementalState(tls_inst_, instance_index_));
+ Helper::type().GetIncrementalState(tls_inst_, instance_index_));
}
private:
@@ -382,20 +400,20 @@
typename Traits::TracePointData trace_point_data = {}) {
PERFETTO_DCHECK(cached_instances);
- if (!type_.TracePrologue<DataSourceTraits, Traits>(
+ if (!Helper::type().template TracePrologue<DataSourceTraits, Traits>(
&tls_state_, &cached_instances, trace_point_data)) {
return;
}
for (internal::DataSourceType::InstancesIterator it =
- type_.BeginIteration<Traits>(cached_instances, tls_state_,
- trace_point_data);
- it.instance;
- type_.NextIteration<Traits>(&it, tls_state_, trace_point_data)) {
+ Helper::type().template BeginIteration<Traits>(
+ cached_instances, tls_state_, trace_point_data);
+ it.instance; Helper::type().template NextIteration<Traits>(
+ &it, tls_state_, trace_point_data)) {
tracing_fn(TraceContext(it.instance, it.i));
}
- type_.TraceEpilogue(tls_state_);
+ Helper::type().TraceEpilogue(tls_state_);
}
// Registers the data source on all tracing backends, including ones that
@@ -413,7 +431,6 @@
const Args&... constructor_args) {
// Silences -Wunused-variable warning in case the trace method is not used
// by the translation unit that declares the data source.
- (void)type_;
(void)tls_state_;
auto factory = [constructor_args...]() {
@@ -423,7 +440,7 @@
internal::DataSourceParams params{
DerivedDataSource::kSupportsMultipleInstances,
DerivedDataSource::kRequiresCallbacksUnderLock};
- return type_.Register(
+ return Helper::type().Register(
descriptor, factory, params, DerivedDataSource::kBufferExhaustedPolicy,
GetCreateTlsFn(
static_cast<typename DataSourceTraits::TlsStateType*>(nullptr)),
@@ -435,7 +452,7 @@
// Updates the data source descriptor.
static void UpdateDescriptor(const DataSourceDescriptor& descriptor) {
- type_.UpdateDescriptor(descriptor);
+ Helper::type().UpdateDescriptor(descriptor);
}
private:
@@ -456,7 +473,7 @@
// implement per-category enabled states.
struct TracePointData {};
static constexpr std::atomic<uint32_t>* GetActiveInstances(TracePointData) {
- return type_.valid_instances();
+ return Helper::type().valid_instances();
}
};
@@ -506,10 +523,6 @@
return nullptr;
}
- // The type of this data source. Accessed by the static Trace() method
- // fastpaths.
- static internal::DataSourceType type_;
-
// This TLS object is a cached raw pointer and has deliberately no destructor.
// The Platform implementation is supposed to create and manage the lifetime
// of the Platform::ThreadLocalObject and take care of destroying it.
@@ -522,9 +535,6 @@
// static
template <typename T, typename D>
-internal::DataSourceType DataSource<T, D>::type_;
-// static
-template <typename T, typename D>
PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState*
DataSource<T, D>::tls_state_;
@@ -547,16 +557,11 @@
// where a component specific export macro is used.
#define PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
template <> \
- attrs perfetto::internal::DataSourceType \
- perfetto::DataSource<__VA_ARGS__>::type_
+ attrs perfetto::internal::DataSourceType& \
+ perfetto::DataSourceHelper<__VA_ARGS__>::type()
// This macro must be used once for each data source in one source file to
// allocate static storage for the data source's static state.
-//
-// Note: if MSVC fails with a C2086 (redefinition) error here, use the
-// permissive- flag to enable standards-compliant mode. See
-// https://developercommunity.visualstudio.com/content/problem/319447/
-// explicit-specialization-of-static-data-member-inco.html.
#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(...) \
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS( \
PERFETTO_COMPONENT_EXPORT, __VA_ARGS__)
@@ -566,7 +571,11 @@
// where a component specific export macro is used.
#define PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(attrs, ...) \
template <> \
- attrs perfetto::internal::DataSourceType \
- perfetto::DataSource<__VA_ARGS__>::type_ {}
+ perfetto::internal::DataSourceType& \
+ perfetto::DataSourceHelper<__VA_ARGS__>::type() { \
+ static perfetto::internal::DataSourceType type_; \
+ return type_; \
+ } \
+ PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
#endif // INCLUDE_PERFETTO_TRACING_DATA_SOURCE_H_
diff --git a/include/perfetto/tracing/interceptor.h b/include/perfetto/tracing/interceptor.h
index b800cb3..637c8bf 100644
--- a/include/perfetto/tracing/interceptor.h
+++ b/include/perfetto/tracing/interceptor.h
@@ -187,7 +187,7 @@
// To define your own state, subclass this with the same name in the
// interceptor class. A reference to the state can then be looked up through
// context.GetThreadLocalState() in the trace packet interceptor function.
- class ThreadLocalState {
+ class PERFETTO_EXPORT_COMPONENT ThreadLocalState {
public:
virtual ~ThreadLocalState();
};
diff --git a/include/perfetto/tracing/internal/data_source_type.h b/include/perfetto/tracing/internal/data_source_type.h
index aee830a..73c84be 100644
--- a/include/perfetto/tracing/internal/data_source_type.h
+++ b/include/perfetto/tracing/internal/data_source_type.h
@@ -167,8 +167,7 @@
InstancesIterator BeginIteration(
uint32_t cached_instances,
DataSourceThreadLocalState* tls_state,
- typename TracePointTraits::TracePointData trace_point_data)
- PERFETTO_ALWAYS_INLINE {
+ typename TracePointTraits::TracePointData trace_point_data) {
InstancesIterator it{};
it.cached_instances = cached_instances;
FirstActiveInstance<TracePointTraits>(&it, tls_state, trace_point_data);
@@ -185,8 +184,7 @@
template <typename TracePointTraits>
void NextIteration(InstancesIterator* iterator,
DataSourceThreadLocalState* tls_state,
- typename TracePointTraits::TracePointData trace_point_data)
- PERFETTO_ALWAYS_INLINE {
+ typename TracePointTraits::TracePointData trace_point_data) {
iterator->i++;
FirstActiveInstance<TracePointTraits>(iterator, tls_state,
trace_point_data);
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index d1e17a6..2ec348e 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -308,276 +308,25 @@
{category_index});
}
- // Once we've determined tracing to be enabled for this category, actually
- // write a trace event onto this thread's default track. Outlined to avoid
- // bloating code (mostly stack depth) at the actual trace point.
- //
- // The following combination of parameters is supported (in the given order):
- // - Zero or one track,
- // - Zero or one custom timestamp,
- // - Arbitrary number of debug annotations.
- // - Zero or one lambda.
-
- // Trace point which does not take a track or timestamp.
- template <typename CategoryType,
- typename EventNameType,
- typename... Arguments>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- TrackEventInternal::kDefaultTrack,
- TrackEventInternal::GetTraceTime(),
- std::forward<Arguments>(args)...);
+ // The following methods forward all arguments to TraceForCategoryBody
+ // while casting string constants to const char*.
+ template <typename... Arguments>
+ static void TraceForCategory(Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryBody(DecayStrType(args)...);
}
- // Trace point which takes a track, but not timestamp.
- // NOTE: Here track should be captured using universal reference (TrackType&&)
- // instead of const TrackType& to ensure that the proper overload is selected
- // (otherwise the compiler will fail to disambiguate between adding const& and
- // parsing track as a part of Arguments...).
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, std::forward<TrackType>(track),
- TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
+ template <typename... Arguments>
+ static void TraceForCategoryLegacy(Arguments&&... args)
+ PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyBody(DecayStrType(args)...);
}
- // Trace point which takes a timestamp, but not track.
- template <typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- TrackEventInternal::kDefaultTrack,
- std::forward<TimestampType>(timestamp),
- std::forward<Arguments>(args)...);
+ template <typename... Arguments>
+ static void TraceForCategoryLegacyWithId(Arguments&&... args)
+ PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyWithIdBody(DecayStrType(args)...);
}
- // Trace point which takes a timestamp and a track.
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type,
- std::forward<TrackType>(track),
- std::forward<TimestampType>(timestamp),
- std::forward<Arguments>(args)...);
- }
-
- // Trace point with with a counter sample.
- template <typename CategoryType, typename EventNameType, typename ValueType>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType&,
- perfetto::protos::pbzero::TrackEvent::Type type,
- CounterTrack track,
- ValueType value) PERFETTO_ALWAYS_INLINE {
- PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
- TraceForCategory(instances, category, /*name=*/nullptr, type, track,
- TrackEventInternal::GetTraceTime(), value);
- }
-
- // Trace point with with a timestamp and a counter sample.
- template <typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type,
- typename ValueType>
- static void TraceForCategory(uint32_t instances,
- const CategoryType& category,
- const EventNameType&,
- perfetto::protos::pbzero::TrackEvent::Type type,
- CounterTrack track,
- TimestampType timestamp,
- ValueType value) PERFETTO_ALWAYS_INLINE {
- PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
- TraceForCategoryImpl(
- instances, category, /*name=*/nullptr, type, track, timestamp,
- [&](EventContext event_ctx) {
- if (std::is_integral<ValueType>::value) {
- int64_t value_int64 = static_cast<int64_t>(value);
- if (track.is_incremental()) {
- TrackEventIncrementalState* incr_state =
- event_ctx.GetIncrementalState();
- PERFETTO_DCHECK(incr_state != nullptr);
- auto prv_value =
- incr_state->last_counter_value_per_track[track.uuid];
- event_ctx.event()->set_counter_value(value_int64 - prv_value);
- prv_value = value_int64;
- incr_state->last_counter_value_per_track[track.uuid] = prv_value;
- } else {
- event_ctx.event()->set_counter_value(value_int64);
- }
- } else {
- event_ctx.event()->set_double_counter_value(
- static_cast<double>(value));
- }
- });
- }
-
-// Additional trace points used in legacy macros.
-// It's possible to implement legacy macros using a common TraceForCategory,
-// by supplying a lambda that sets all necessary legacy fields. But this
-// results in a binary size bloat because every trace point generates its own
-// template instantiation with its own lambda. ICF can't eliminate those as
-// each lambda captures different variables and so the code is not completely
-// identical.
-// What we do instead is define additional TraceForCategoryLegacy templates
-// that take legacy arguments directly. Their instantiations can have the same
-// binary code for at least some macro invocations and so can be successfully
-// folded by the linker.
-#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategoryLegacy(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(instances, category, event_name, type, track,
- TrackEventInternal::GetTraceTime(),
- [&](perfetto::EventContext ctx)
- PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- TrackEventLegacy::WriteLegacyEvent(
- std::move(ctx), phase, flags, args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategoryLegacy(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track, timestamp,
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags,
- args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename ThreadIdType,
- typename LegacyIdType,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type>
- static void TraceForCategoryLegacyWithId(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- ThreadIdType thread_id,
- LegacyIdType legacy_id,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track,
- TrackEventInternal::GetTraceTime(),
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
- TrackEventLegacy::WriteLegacyEventWithIdAndTid(
- std::move(ctx), phase, flags, trace_id, thread_id, args...);
- });
- }
-
- template <typename TrackType,
- typename CategoryType,
- typename EventNameType,
- typename ThreadIdType,
- typename LegacyIdType,
- typename TimestampType = uint64_t,
- typename... Arguments,
- typename TrackTypeCheck = typename std::enable_if<
- std::is_convertible<TrackType, Track>::value>::type,
- typename TimestampTypeCheck = typename std::enable_if<
- IsValidTimestamp<TimestampType>()>::type>
- static void TraceForCategoryLegacyWithId(
- uint32_t instances,
- const CategoryType& category,
- const EventNameType& event_name,
- perfetto::protos::pbzero::TrackEvent::Type type,
- TrackType&& track,
- char phase,
- uint32_t flags,
- ThreadIdType thread_id,
- LegacyIdType legacy_id,
- TimestampType&& timestamp,
- Arguments&&... args) PERFETTO_NO_INLINE {
- TraceForCategoryImpl(
- instances, category, event_name, type, track, timestamp,
- [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
- using ::perfetto::internal::TrackEventLegacy;
- ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
- TrackEventLegacy::WriteLegacyEventWithIdAndTid(
- std::move(ctx), phase, flags, trace_id, thread_id, args...);
- });
- }
-#endif // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
-
// Initialize the track event library. Should be called before tracing is
// enabled.
static bool Register() {
@@ -638,6 +387,295 @@
const protos::gen::TrackEventConfig& GetConfig() const { return config_; }
private:
+ // The DecayStrType method is used to avoid unnecessary instantiations of
+ // templates on string constants of different sizes. Without it, strings
+ // of different lengths have different types: char[10], char[15] etc.
+ // DecayStrType forwards all types of arguments as is, with the exception
+ // of string constants which are all cast to const char*. This allows to
+ // avoid extra instantiations of TraceForCategory templates.
+ template <typename T>
+ static T&& DecayStrType(T&& t) {
+ return std::forward<T>(t);
+ }
+
+ static const char* DecayStrType(const char* t) { return t; }
+
+ // Once we've determined tracing to be enabled for this category, actually
+ // write a trace event onto this thread's default track. Outlined to avoid
+ // bloating code (mostly stack depth) at the actual trace point.
+ //
+ // The following combination of parameters is supported (in the given order):
+ // - Zero or one track,
+ // - Zero or one custom timestamp,
+ // - Arbitrary number of debug annotations.
+ // - Zero or one lambda.
+
+ // Trace point which does not take a track or timestamp.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename... Arguments>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ TrackEventInternal::kDefaultTrack,
+ TrackEventInternal::GetTraceTime(),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a track, but not timestamp.
+ // NOTE: Here track should be captured using universal reference (TrackType&&)
+ // instead of const TrackType& to ensure that the proper overload is selected
+ // (otherwise the compiler will fail to disambiguate between adding const& and
+ // parsing track as a part of Arguments...).
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, std::forward<TrackType>(track),
+ TrackEventInternal::GetTraceTime(), std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a timestamp, but not track.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ TrackEventInternal::kDefaultTrack,
+ std::forward<TimestampType>(timestamp),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point which takes a timestamp and a track.
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type,
+ std::forward<TrackType>(track),
+ std::forward<TimestampType>(timestamp),
+ std::forward<Arguments>(args)...);
+ }
+
+ // Trace point with with a counter sample.
+ template <typename CategoryType, typename EventNameType, typename ValueType>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType&,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ CounterTrack track,
+ ValueType value) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
+ TraceForCategory(instances, category, /*name=*/nullptr, type, track,
+ TrackEventInternal::GetTraceTime(), value);
+ }
+
+ // Trace point with with a timestamp and a counter sample.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type,
+ typename ValueType>
+ static void TraceForCategoryBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType&,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ CounterTrack track,
+ TimestampType timestamp,
+ ValueType value) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(type == perfetto::protos::pbzero::TrackEvent::TYPE_COUNTER);
+ TraceForCategoryImpl(
+ instances, category, /*name=*/nullptr, type, track, timestamp,
+ [&](EventContext event_ctx) {
+ if (std::is_integral<ValueType>::value) {
+ int64_t value_int64 = static_cast<int64_t>(value);
+ if (track.is_incremental()) {
+ TrackEventIncrementalState* incr_state =
+ event_ctx.GetIncrementalState();
+ PERFETTO_DCHECK(incr_state != nullptr);
+ auto prv_value =
+ incr_state->last_counter_value_per_track[track.uuid];
+ event_ctx.event()->set_counter_value(value_int64 - prv_value);
+ prv_value = value_int64;
+ incr_state->last_counter_value_per_track[track.uuid] = prv_value;
+ } else {
+ event_ctx.event()->set_counter_value(value_int64);
+ }
+ } else {
+ event_ctx.event()->set_double_counter_value(
+ static_cast<double>(value));
+ }
+ });
+ }
+
+// Additional trace points used in legacy macros.
+// It's possible to implement legacy macros using a common TraceForCategory,
+// by supplying a lambda that sets all necessary legacy fields. But this
+// results in a binary size bloat because every trace point generates its own
+// template instantiation with its own lambda. ICF can't eliminate those as
+// each lambda captures different variables and so the code is not completely
+// identical.
+// What we do instead is define additional TraceForCategoryLegacy templates
+// that take legacy arguments directly. Their instantiations can have the same
+// binary code for at least some macro invocations and so can be successfully
+// folded by the linker.
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacyBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(instances, category, event_name, type, track,
+ TrackEventInternal::GetTraceTime(),
+ [&](perfetto::EventContext ctx)
+ PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ TrackEventLegacy::WriteLegacyEvent(
+ std::move(ctx), phase, flags, args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacyBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track, timestamp,
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ TrackEventLegacy::WriteLegacyEvent(std::move(ctx), phase, flags,
+ args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacyWithIdBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track,
+ TrackEventInternal::GetTraceTime(),
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
+ TrackEventLegacy::WriteLegacyEventWithIdAndTid(
+ std::move(ctx), phase, flags, trace_id, thread_id, args...);
+ });
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacyWithIdBody(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_NO_INLINE {
+ TraceForCategoryImpl(
+ instances, category, event_name, type, track, timestamp,
+ [&](perfetto::EventContext ctx) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {
+ using ::perfetto::internal::TrackEventLegacy;
+ ::perfetto::internal::LegacyTraceId trace_id{legacy_id};
+ TrackEventLegacy::WriteLegacyEventWithIdAndTid(
+ std::move(ctx), phase, flags, trace_id, thread_id, args...);
+ });
+ }
+#endif // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+
// Each category has its own enabled/disabled state, stored in the category
// registry.
struct CategoryTracePointTraits {
diff --git a/infra/perfetto.dev/src/gen_stdlib_docs_md.py b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
index efb52a2..e52dea0 100644
--- a/infra/perfetto.dev/src/gen_stdlib_docs_md.py
+++ b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
@@ -20,14 +20,14 @@
import argparse
import sys
import json
-from typing import List, Dict
+from typing import Any, List, Dict
# Responsible for module level markdown generation.
class ModuleMd:
- def __init__(self, module_name: str,
- module_files: List[Dict[str, any]]) -> None:
+ def __init__(self, module_name: str, module_files: List[Dict[str,
+ Any]]) -> None:
self.module_name = module_name
self.files_md = [
FileMd(module_name, file_dict) for file_dict in module_files
diff --git a/protos/perfetto/common/observable_events.proto b/protos/perfetto/common/observable_events.proto
index 0bde227..85767a6 100644
--- a/protos/perfetto/common/observable_events.proto
+++ b/protos/perfetto/common/observable_events.proto
@@ -35,6 +35,11 @@
// Introduced in Android 11 (R).
TYPE_ALL_DATA_SOURCES_STARTED = 2;
+ // When a tracing session has one or more triggers of type CLONE_SNAPSHOT
+ // and a matching trigger is hit, the service will send this notification to
+ // the consumer after |stop_delay_ms|.
+ TYPE_CLONE_TRIGGER_HIT = 4;
+
// Note: internally these are used as OR flags. Next values: 4, 8, 16, ...
// TODO(eseckler): Extend this for producer & data source registrations.
@@ -52,6 +57,15 @@
optional DataSourceInstanceState state = 3;
}
+ message CloneTriggerHit {
+ // The TracingSessionID of the original tracing session which had a
+ // CLONE_SNAPSHOT trigger defined. This is necessary just because the
+ // consumer has no idea of what is the TSID of its own tracing session and
+ // there is no other good way to plumb it.
+ optional int64 tracing_session_id = 1;
+ }
+
repeated DataSourceInstanceStateChange instance_state_changes = 1;
optional bool all_data_sources_started = 2;
+ optional CloneTriggerHit clone_trigger_hit = 3;
}
diff --git a/protos/perfetto/common/trace_stats.proto b/protos/perfetto/common/trace_stats.proto
index 4e6d455..bbee8f9 100644
--- a/protos/perfetto/common/trace_stats.proto
+++ b/protos/perfetto/common/trace_stats.proto
@@ -20,7 +20,7 @@
// Statistics for the internals of the tracing service.
//
-// Next id: 17.
+// Next id: 19.
message TraceStats {
// From TraceBuffer::Stats.
//
diff --git a/protos/perfetto/common/tracing_service_capabilities.proto b/protos/perfetto/common/tracing_service_capabilities.proto
index 123f9bf..308f18d 100644
--- a/protos/perfetto/common/tracing_service_capabilities.proto
+++ b/protos/perfetto/common/tracing_service_capabilities.proto
@@ -35,4 +35,7 @@
// Whether the service supports TraceConfig.output_path (for asking traced to
// create the output file instead of passing a file descriptor).
optional bool has_trace_config_output_path = 3;
+
+ // Whether the service supports CloneSession and CLONE_SNAPSHOT triggers.
+ optional bool has_clone_session = 4;
}
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 7b1171f..85f1de7 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -833,6 +833,7 @@
// If > 0 samples counters (see process_stats.proto) from
// /proc/pid/status and oom_score_adj every X ms.
+ // It will also sample /proc/pid/smaps_rollup if scan_smaps_rollup = true.
// This is required to be > 100ms to avoid excessive CPU usage.
// TODO(primiano): add CPU cost for change this value.
optional uint32 proc_stats_poll_ms = 4;
@@ -861,6 +862,10 @@
// new fds opened after initially scanning a process will not be
// recognized.
optional bool resolve_process_fds = 9;
+
+ // If enabled memory stats from /proc/pid/smaps_rollup will be included
+ // in process stats.
+ optional bool scan_smaps_rollup = 10;
}
// End of protos/perfetto/config/process_stats/process_stats_config.proto
@@ -2700,7 +2705,7 @@
// It contains the general config for the logging buffer(s) and the configs for
// all the data source being enabled.
//
-// Next id: 37.
+// Next id: 38.
message TraceConfig {
message BufferConfig {
optional uint32 size_kb = 1;
@@ -2968,12 +2973,35 @@
// consumer.
STOP_TRACING = 2;
- // NOTE: do not add new enum values here because of a subtle backward
- // compat bug which might cause indefinite tracing on older versions of
- // the service. See b/274931668 .
+ // When this mode is chosen, this causes a snapshot of the current tracing
+ // session to be created after |stop_delay_ms| while the current tracing
+ // session continues undisturbed (% an extra flush). This mode can be
+ // used only when the tracing session is handled by the "perfetto" cmdline
+ // client (which is true in 90% of cases). Part of the business logic
+ // necessary for this behavior, and ensuing file handling, lives in
+ // perfetto_cmd.cc . On other consumers, this causes only a notification
+ // of the trigger through a CloneTriggerHit ObservableEvent. The custom
+ // consumer is supposed to call CloneSession() itself after the event.
+ // Use use_clone_snapshot_if_available=true when targeting older versions
+ // of perfetto.
+ CLONE_SNAPSHOT = 3;
+
+ // NOTE: CLONE_SNAPSHOT should be used only when we targeting Android U+
+ // (14+) / Perfetto v34+. A bug in older versions of the tracing service
+ // might cause indefinitely long tracing sessions (see b/274931668).
}
optional TriggerMode trigger_mode = 1;
+ // This flag is really a workaround for b/274931668. This is needed only
+ // when deploying configs to different versions of the tracing service.
+ // When this is set to true this has the same effect of setting trigger_mode
+ // to CLONE_SNAPSHOT on newer versions of the service. This boolean has been
+ // introduced to allow to have configs that use CLONE_SNAPSHOT on newer
+ // versions of Android and fall back to STOP_TRACING on older versions where
+ // CLONE_SNAPSHOT did not exist.
+ // When using this flag, trigger_mode must be set to STOP_TRACING.
+ optional bool use_clone_snapshot_if_available = 4;
+
message Trigger {
// The producer must specify this name to activate the trigger.
optional string name = 1;
@@ -2985,6 +3013,8 @@
// After a trigger is received either in START_TRACING or STOP_TRACING
// mode then the trace will end |stop_delay_ms| after triggering.
+ // In CLONE_SNAPSHOT mode, this is the delay between the trigger and the
+ // snapshot.
// If |prefer_suspend_clock_for_duration| is set, the duration will be
// based on wall-clock, counting also time in suspend.
optional uint32 stop_delay_ms = 3;
@@ -3064,6 +3094,11 @@
}
optional CompressionType compression_type = 24;
+ // Use the legacy codepath that compresses from perfetto_cmd.cc instead of
+ // using the new codepath that compresses from tracing_service_impl.cc. This
+ // will be removed in the future.
+ optional bool compress_from_cli = 37;
+
// Android-only. Not for general use. If set, saves the trace into an
// incident. This field is read by perfetto_cmd, rather than the tracing
// service. This field must be set when passing the --upload flag to
diff --git a/protos/perfetto/config/process_stats/process_stats_config.proto b/protos/perfetto/config/process_stats/process_stats_config.proto
index d71e7d3..239513f 100644
--- a/protos/perfetto/config/process_stats/process_stats_config.proto
+++ b/protos/perfetto/config/process_stats/process_stats_config.proto
@@ -41,6 +41,7 @@
// If > 0 samples counters (see process_stats.proto) from
// /proc/pid/status and oom_score_adj every X ms.
+ // It will also sample /proc/pid/smaps_rollup if scan_smaps_rollup = true.
// This is required to be > 100ms to avoid excessive CPU usage.
// TODO(primiano): add CPU cost for change this value.
optional uint32 proc_stats_poll_ms = 4;
@@ -69,4 +70,8 @@
// new fds opened after initially scanning a process will not be
// recognized.
optional bool resolve_process_fds = 9;
+
+ // If enabled memory stats from /proc/pid/smaps_rollup will be included
+ // in process stats.
+ optional bool scan_smaps_rollup = 10;
}
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index 04e637c..4fe3ea9 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -26,7 +26,7 @@
// It contains the general config for the logging buffer(s) and the configs for
// all the data source being enabled.
//
-// Next id: 37.
+// Next id: 38.
message TraceConfig {
message BufferConfig {
optional uint32 size_kb = 1;
@@ -294,12 +294,35 @@
// consumer.
STOP_TRACING = 2;
- // NOTE: do not add new enum values here because of a subtle backward
- // compat bug which might cause indefinite tracing on older versions of
- // the service. See b/274931668 .
+ // When this mode is chosen, this causes a snapshot of the current tracing
+ // session to be created after |stop_delay_ms| while the current tracing
+ // session continues undisturbed (% an extra flush). This mode can be
+ // used only when the tracing session is handled by the "perfetto" cmdline
+ // client (which is true in 90% of cases). Part of the business logic
+ // necessary for this behavior, and ensuing file handling, lives in
+ // perfetto_cmd.cc . On other consumers, this causes only a notification
+ // of the trigger through a CloneTriggerHit ObservableEvent. The custom
+ // consumer is supposed to call CloneSession() itself after the event.
+ // Use use_clone_snapshot_if_available=true when targeting older versions
+ // of perfetto.
+ CLONE_SNAPSHOT = 3;
+
+ // NOTE: CLONE_SNAPSHOT should be used only when we targeting Android U+
+ // (14+) / Perfetto v34+. A bug in older versions of the tracing service
+ // might cause indefinitely long tracing sessions (see b/274931668).
}
optional TriggerMode trigger_mode = 1;
+ // This flag is really a workaround for b/274931668. This is needed only
+ // when deploying configs to different versions of the tracing service.
+ // When this is set to true this has the same effect of setting trigger_mode
+ // to CLONE_SNAPSHOT on newer versions of the service. This boolean has been
+ // introduced to allow to have configs that use CLONE_SNAPSHOT on newer
+ // versions of Android and fall back to STOP_TRACING on older versions where
+ // CLONE_SNAPSHOT did not exist.
+ // When using this flag, trigger_mode must be set to STOP_TRACING.
+ optional bool use_clone_snapshot_if_available = 4;
+
message Trigger {
// The producer must specify this name to activate the trigger.
optional string name = 1;
@@ -311,6 +334,8 @@
// After a trigger is received either in START_TRACING or STOP_TRACING
// mode then the trace will end |stop_delay_ms| after triggering.
+ // In CLONE_SNAPSHOT mode, this is the delay between the trigger and the
+ // snapshot.
// If |prefer_suspend_clock_for_duration| is set, the duration will be
// based on wall-clock, counting also time in suspend.
optional uint32 stop_delay_ms = 3;
@@ -390,6 +415,11 @@
}
optional CompressionType compression_type = 24;
+ // Use the legacy codepath that compresses from perfetto_cmd.cc instead of
+ // using the new codepath that compresses from tracing_service_impl.cc. This
+ // will be removed in the future.
+ optional bool compress_from_cli = 37;
+
// Android-only. Not for general use. If set, saves the trace into an
// incident. This field is read by perfetto_cmd, rather than the tracing
// service. This field must be set when passing the --upload flag to
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index d5a4025..2fdc919 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -289,4 +289,8 @@
// the details about the failure.
optional bool success = 1;
optional string error = 2;
+
+ // The UUID of the cloned session.
+ optional int64 uuid_msb = 3;
+ optional int64 uuid_lsb = 4;
}
diff --git a/protos/perfetto/metrics/android/binder_metric.proto b/protos/perfetto/metrics/android/binder_metric.proto
index 5c4e66f..36c1abb 100644
--- a/protos/perfetto/metrics/android/binder_metric.proto
+++ b/protos/perfetto/metrics/android/binder_metric.proto
@@ -56,6 +56,9 @@
optional uint32 client_tid = 15;
optional uint32 server_tid = 16;
+
+ optional uint32 client_pid = 17;
+ optional uint32 server_pid = 18;
}
message ThreadStateBreakdown {
diff --git a/protos/perfetto/metrics/android/java_heap_stats.proto b/protos/perfetto/metrics/android/java_heap_stats.proto
index 2579888..10a0b8b 100644
--- a/protos/perfetto/metrics/android/java_heap_stats.proto
+++ b/protos/perfetto/metrics/android/java_heap_stats.proto
@@ -26,7 +26,7 @@
optional int64 obj_count = 3;
}
- // Next id: 10
+ // Next id: 11
message Sample {
optional int64 ts = 1;
// Size of the Java heap in bytes
@@ -45,6 +45,8 @@
// ART root objects
repeated HeapRoots roots = 7;
+ // OOM adjustment score
+ optional int64 oom_score_adj = 10;
}
// Heap stats per process. One sample per dump (can be > 1 if continuous
diff --git a/protos/perfetto/metrics/android/monitor_contention_metric.proto b/protos/perfetto/metrics/android/monitor_contention_metric.proto
index 91122eb..00585d8 100644
--- a/protos/perfetto/metrics/android/monitor_contention_metric.proto
+++ b/protos/perfetto/metrics/android/monitor_contention_metric.proto
@@ -21,28 +21,38 @@
// This metric provides information about the monitor contention graph in a
// trace
message AndroidMonitorContentionMetric {
+ // Next field id: 24
message Node {
+ // Global context
optional int64 node_parent_id = 1;
optional int64 node_id = 2;
optional int64 ts = 3;
optional int64 dur = 4;
- optional string blocking_method = 5;
- optional string blocked_method = 6;
- optional string short_blocking_method = 7;
- optional string short_blocked_method = 8;
- optional string blocking_src = 9;
- optional string blocked_src = 10;
- optional uint32 waiter_count = 11;
- optional string blocked_thread_name = 12;
- optional string blocking_thread_name = 13;
optional string process_name = 14;
- optional bool is_blocked_thread_main = 15;
- optional bool is_blocking_thread_main = 16;
- optional int64 binder_reply_ts = 17;
- optional uint32 binder_reply_tid = 18;
-
+ optional uint32 pid = 23;
+ optional uint32 waiter_count = 11;
repeated ThreadStateBreakdown thread_states = 19;
repeated BlockedFunctionBreakdown blocked_functions = 20;
+
+ // Blocking context
+ optional string blocking_method = 5;
+ optional string short_blocking_method = 7;
+ optional string blocking_src = 9;
+ optional string blocking_thread_name = 13;
+ optional bool is_blocking_thread_main = 16;
+ optional uint32 blocking_thread_tid = 22;
+
+ // Blocked context
+ optional string blocked_method = 6;
+ optional string short_blocked_method = 8;
+ optional string blocked_src = 10;
+ optional string blocked_thread_name = 12;
+ optional bool is_blocked_thread_main = 15;
+ optional uint32 blocked_thread_tid = 21;
+
+ // Binder context
+ optional int64 binder_reply_ts = 17;
+ optional uint32 binder_reply_tid = 18;
}
message ThreadStateBreakdown {
diff --git a/protos/perfetto/metrics/android/process_metadata.proto b/protos/perfetto/metrics/android/process_metadata.proto
index fd7fe7b..fa766be 100644
--- a/protos/perfetto/metrics/android/process_metadata.proto
+++ b/protos/perfetto/metrics/android/process_metadata.proto
@@ -44,5 +44,8 @@
// https://developer.android.com/guide/topics/manifest/manifest-element#uid
repeated Package packages_for_uid = 8;
+ // Pid of the process name.
+ optional int64 pid = 9;
+
reserved 3, 4, 5, 6;
}
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index 95a2ff0..67c9044 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -47,7 +47,7 @@
// Timing information spanning the intent received by the
// activity manager to the first frame drawn.
- // Next id: 34.
+ // Next id: 35.
message ToFirstFrame {
// The duration between the intent received and first frame.
optional int64 dur_ns = 1;
@@ -121,6 +121,10 @@
// being started up.
optional Slice time_dex_open_thread_main = 33;
+ // Time spent in dlopening .so files on the main thread of the process
+ // being started up.
+ optional Slice time_dlopen_thread_main = 34;
+
// Removed: was other_process_to_activity_cpu_ratio.
reserved 12;
@@ -220,7 +224,7 @@
optional int64 dex2oat_dur_ns = 7;
}
- // Next id: 20
+ // Next id: 21
message Startup {
// Random id uniquely identifying an app startup in this trace.
optional uint32 startup_id = 1;
@@ -276,6 +280,9 @@
// Contains information about the class verification.
repeated VerifyClass verify_class = 19;
+ // Contains the dlopen file names.
+ repeated string dlopen_file = 20;
+
// Package name of startups running concurrent to the launch.
repeated string startup_concurrent_to_launch = 18;
diff --git a/protos/perfetto/metrics/chrome/scroll_jank_v2.proto b/protos/perfetto/metrics/chrome/scroll_jank_v2.proto
index 9464b78..9b7919d 100644
--- a/protos/perfetto/metrics/chrome/scroll_jank_v2.proto
+++ b/protos/perfetto/metrics/chrome/scroll_jank_v2.proto
@@ -29,4 +29,14 @@
optional double scroll_jank_processing_ms = 2 [(unit) = "ms_smallerIsBetter"];
// Computed as: `100 * scroll_jank_processing_ms / scroll_processing_ms`.
optional double scroll_jank_percentage = 3 [(unit) = "n%_smallerIsBetter"];
+ // The number of scroll janks. Similar to above, this excludes jank caused
+ // due to `RendererCompositorQueueingDelay`, which can add nontrivial noise.
+ optional int64 num_scroll_janks = 4 [(unit) = "count_smallerIsBetter"];
+ // The primary cause and duration for scroll jank, one for each jank.
+ // There are exactly `num_scroll_janks` items in this field.
+ message ScrollJankCauseAndDuration {
+ optional string cause = 1;
+ optional double duration_ms = 2 [(unit) = "ms_smallerIsBetter"];
+ }
+ repeated ScrollJankCauseAndDuration scroll_jank_causes_and_durations = 5;
}
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 60ffcd4..393095c 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -42,6 +42,9 @@
// https://developer.android.com/guide/topics/manifest/manifest-element#uid
repeated Package packages_for_uid = 8;
+ // Pid of the process name.
+ optional int64 pid = 9;
+
reserved 3, 4, 5, 6;
}
@@ -222,6 +225,9 @@
optional uint32 client_tid = 15;
optional uint32 server_tid = 16;
+
+ optional uint32 client_pid = 17;
+ optional uint32 server_pid = 18;
}
message ThreadStateBreakdown {
@@ -1019,7 +1025,7 @@
optional int64 obj_count = 3;
}
- // Next id: 10
+ // Next id: 11
message Sample {
optional int64 ts = 1;
// Size of the Java heap in bytes
@@ -1038,6 +1044,8 @@
// ART root objects
repeated HeapRoots roots = 7;
+ // OOM adjustment score
+ optional int64 oom_score_adj = 10;
}
// Heap stats per process. One sample per dump (can be > 1 if continuous
@@ -1185,28 +1193,38 @@
// This metric provides information about the monitor contention graph in a
// trace
message AndroidMonitorContentionMetric {
+ // Next field id: 24
message Node {
+ // Global context
optional int64 node_parent_id = 1;
optional int64 node_id = 2;
optional int64 ts = 3;
optional int64 dur = 4;
- optional string blocking_method = 5;
- optional string blocked_method = 6;
- optional string short_blocking_method = 7;
- optional string short_blocked_method = 8;
- optional string blocking_src = 9;
- optional string blocked_src = 10;
- optional uint32 waiter_count = 11;
- optional string blocked_thread_name = 12;
- optional string blocking_thread_name = 13;
optional string process_name = 14;
- optional bool is_blocked_thread_main = 15;
- optional bool is_blocking_thread_main = 16;
- optional int64 binder_reply_ts = 17;
- optional uint32 binder_reply_tid = 18;
-
+ optional uint32 pid = 23;
+ optional uint32 waiter_count = 11;
repeated ThreadStateBreakdown thread_states = 19;
repeated BlockedFunctionBreakdown blocked_functions = 20;
+
+ // Blocking context
+ optional string blocking_method = 5;
+ optional string short_blocking_method = 7;
+ optional string blocking_src = 9;
+ optional string blocking_thread_name = 13;
+ optional bool is_blocking_thread_main = 16;
+ optional uint32 blocking_thread_tid = 22;
+
+ // Blocked context
+ optional string blocked_method = 6;
+ optional string short_blocked_method = 8;
+ optional string blocked_src = 10;
+ optional string blocked_thread_name = 12;
+ optional bool is_blocked_thread_main = 15;
+ optional uint32 blocked_thread_tid = 21;
+
+ // Binder context
+ optional int64 binder_reply_ts = 17;
+ optional uint32 binder_reply_tid = 18;
}
message ThreadStateBreakdown {
@@ -1591,7 +1609,7 @@
// Timing information spanning the intent received by the
// activity manager to the first frame drawn.
- // Next id: 34.
+ // Next id: 35.
message ToFirstFrame {
// The duration between the intent received and first frame.
optional int64 dur_ns = 1;
@@ -1665,6 +1683,10 @@
// being started up.
optional Slice time_dex_open_thread_main = 33;
+ // Time spent in dlopening .so files on the main thread of the process
+ // being started up.
+ optional Slice time_dlopen_thread_main = 34;
+
// Removed: was other_process_to_activity_cpu_ratio.
reserved 12;
@@ -1764,7 +1786,7 @@
optional int64 dex2oat_dur_ns = 7;
}
- // Next id: 20
+ // Next id: 21
message Startup {
// Random id uniquely identifying an app startup in this trace.
optional uint32 startup_id = 1;
@@ -1820,6 +1842,9 @@
// Contains information about the class verification.
repeated VerifyClass verify_class = 19;
+ // Contains the dlopen file names.
+ repeated string dlopen_file = 20;
+
// Package name of startups running concurrent to the launch.
repeated string startup_concurrent_to_launch = 18;
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index d208b94..1681cd9 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -833,6 +833,7 @@
// If > 0 samples counters (see process_stats.proto) from
// /proc/pid/status and oom_score_adj every X ms.
+ // It will also sample /proc/pid/smaps_rollup if scan_smaps_rollup = true.
// This is required to be > 100ms to avoid excessive CPU usage.
// TODO(primiano): add CPU cost for change this value.
optional uint32 proc_stats_poll_ms = 4;
@@ -861,6 +862,10 @@
// new fds opened after initially scanning a process will not be
// recognized.
optional bool resolve_process_fds = 9;
+
+ // If enabled memory stats from /proc/pid/smaps_rollup will be included
+ // in process stats.
+ optional bool scan_smaps_rollup = 10;
}
// End of protos/perfetto/config/process_stats/process_stats_config.proto
@@ -2700,7 +2705,7 @@
// It contains the general config for the logging buffer(s) and the configs for
// all the data source being enabled.
//
-// Next id: 37.
+// Next id: 38.
message TraceConfig {
message BufferConfig {
optional uint32 size_kb = 1;
@@ -2968,12 +2973,35 @@
// consumer.
STOP_TRACING = 2;
- // NOTE: do not add new enum values here because of a subtle backward
- // compat bug which might cause indefinite tracing on older versions of
- // the service. See b/274931668 .
+ // When this mode is chosen, this causes a snapshot of the current tracing
+ // session to be created after |stop_delay_ms| while the current tracing
+ // session continues undisturbed (% an extra flush). This mode can be
+ // used only when the tracing session is handled by the "perfetto" cmdline
+ // client (which is true in 90% of cases). Part of the business logic
+ // necessary for this behavior, and ensuing file handling, lives in
+ // perfetto_cmd.cc . On other consumers, this causes only a notification
+ // of the trigger through a CloneTriggerHit ObservableEvent. The custom
+ // consumer is supposed to call CloneSession() itself after the event.
+ // Use use_clone_snapshot_if_available=true when targeting older versions
+ // of perfetto.
+ CLONE_SNAPSHOT = 3;
+
+ // NOTE: CLONE_SNAPSHOT should be used only when we targeting Android U+
+ // (14+) / Perfetto v34+. A bug in older versions of the tracing service
+ // might cause indefinitely long tracing sessions (see b/274931668).
}
optional TriggerMode trigger_mode = 1;
+ // This flag is really a workaround for b/274931668. This is needed only
+ // when deploying configs to different versions of the tracing service.
+ // When this is set to true this has the same effect of setting trigger_mode
+ // to CLONE_SNAPSHOT on newer versions of the service. This boolean has been
+ // introduced to allow to have configs that use CLONE_SNAPSHOT on newer
+ // versions of Android and fall back to STOP_TRACING on older versions where
+ // CLONE_SNAPSHOT did not exist.
+ // When using this flag, trigger_mode must be set to STOP_TRACING.
+ optional bool use_clone_snapshot_if_available = 4;
+
message Trigger {
// The producer must specify this name to activate the trigger.
optional string name = 1;
@@ -2985,6 +3013,8 @@
// After a trigger is received either in START_TRACING or STOP_TRACING
// mode then the trace will end |stop_delay_ms| after triggering.
+ // In CLONE_SNAPSHOT mode, this is the delay between the trigger and the
+ // snapshot.
// If |prefer_suspend_clock_for_duration| is set, the duration will be
// based on wall-clock, counting also time in suspend.
optional uint32 stop_delay_ms = 3;
@@ -3064,6 +3094,11 @@
}
optional CompressionType compression_type = 24;
+ // Use the legacy codepath that compresses from perfetto_cmd.cc instead of
+ // using the new codepath that compresses from tracing_service_impl.cc. This
+ // will be removed in the future.
+ optional bool compress_from_cli = 37;
+
// Android-only. Not for general use. If set, saves the trace into an
// incident. This field is read by perfetto_cmd, rather than the tracing
// service. This field must be set when passing the --upload flag to
@@ -3205,7 +3240,7 @@
// Statistics for the internals of the tracing service.
//
-// Next id: 17.
+// Next id: 19.
message TraceStats {
// From TraceBuffer::Stats.
//
@@ -11216,6 +11251,13 @@
optional uint32 chrome_peak_resident_set_kb = 14;
repeated FDInfo fds = 15;
+
+ // These fields are set only when scan_smaps_rollup=true
+ optional uint64 smr_rss_kb = 16;
+ optional uint64 smr_pss_kb = 17;
+ optional uint64 smr_pss_anon_kb = 18;
+ optional uint64 smr_pss_file_kb = 19;
+ optional uint64 smr_pss_shmem_kb = 20;
}
repeated Process processes = 1;
@@ -11302,7 +11344,7 @@
// Deliberate empty message. See comment on StatsdAtom#atom below.
message Atom {}
-// One or more statsd atoms. Ideally this should continue to match:
+// One or more statsd atoms. This must continue to match:
// perfetto/protos/third_party/statsd/shell_data.proto
// So that we can efficiently add data from statsd directly to the
// trace.
diff --git a/protos/perfetto/trace/ps/process_stats.proto b/protos/perfetto/trace/ps/process_stats.proto
index 4ac0c40..03759b4 100644
--- a/protos/perfetto/trace/ps/process_stats.proto
+++ b/protos/perfetto/trace/ps/process_stats.proto
@@ -76,6 +76,13 @@
optional uint32 chrome_peak_resident_set_kb = 14;
repeated FDInfo fds = 15;
+
+ // These fields are set only when scan_smaps_rollup=true
+ optional uint64 smr_rss_kb = 16;
+ optional uint64 smr_pss_kb = 17;
+ optional uint64 smr_pss_anon_kb = 18;
+ optional uint64 smr_pss_file_kb = 19;
+ optional uint64 smr_pss_shmem_kb = 20;
}
repeated Process processes = 1;
diff --git a/protos/perfetto/trace/statsd/statsd_atom.proto b/protos/perfetto/trace/statsd/statsd_atom.proto
index 026766d..62ca960 100644
--- a/protos/perfetto/trace/statsd/statsd_atom.proto
+++ b/protos/perfetto/trace/statsd/statsd_atom.proto
@@ -20,7 +20,7 @@
// Deliberate empty message. See comment on StatsdAtom#atom below.
message Atom {}
-// One or more statsd atoms. Ideally this should continue to match:
+// One or more statsd atoms. This must continue to match:
// perfetto/protos/third_party/statsd/shell_data.proto
// So that we can efficiently add data from statsd directly to the
// trace.
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index 667cab0..6e4dad0 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -42,7 +42,9 @@
// every time a new feature that the UI depends on is being introduced (e.g.
// new tables, new SQL operators, metrics that are required by the UI).
// See also StatusResult.api_version (below).
- TRACE_PROCESSOR_CURRENT_API_VERSION = 6;
+ // Changes:
+ // 7. Introduce GUESS_CPU_SIZE
+ TRACE_PROCESSOR_CURRENT_API_VERSION = 7;
}
// At lowest level, the wire-format of the RPC procol is a linear sequence of
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 1f5a211..cce5e76 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -513,7 +513,10 @@
optional int32 browsing_instance_id = 1;
// The ID of the CoopRelatedGroup that the BrowsingContextState belongs to.
- optional int32 coop_related_group_id = 2;
+ optional int32 coop_related_group_id = 2 [deprecated = true];
+
+ // The token of the CoopRelatedGroup that the BrowsingContextState belongs to.
+ optional string coop_related_group_token = 3;
// Additional untyped debug information associated with this
// FrameTreeNode, populated via TracedProto::AddDebugAnnotations API.
diff --git a/protos/third_party/statsd/shell_data.proto b/protos/third_party/statsd/shell_data.proto
index e4c31bd..64551ea 100644
--- a/protos/third_party/statsd/shell_data.proto
+++ b/protos/third_party/statsd/shell_data.proto
@@ -20,8 +20,8 @@
// This is a manual import of ShellData:
// https://cs.android.com/android/platform/superproject/+/master:packages/modules/StatsD/statsd/src/shell/shell_data.proto;l=27;drc=d2e51ecdf08753688fb889b657dcba60adb994f3
-
+// This must exactly match perfetto.protos.StatsdAtom.
message ShellData {
repeated bytes atom = 1;
- repeated int64 timestamp_nanos = 2 [packed = true];
+ repeated int64 timestamp_nanos = 2;
}
diff --git a/python/BUILD.gn b/python/BUILD.gn
index 06ebd9c..17ad6f3 100644
--- a/python/BUILD.gn
+++ b/python/BUILD.gn
@@ -24,10 +24,10 @@
perfetto_py_library("trace_processor_stdlib_docs") {
sources = [
+ "generators/stdlib_docs/extractor.py",
"generators/stdlib_docs/parse.py",
- "generators/stdlib_docs/stdlib.py",
+ "generators/stdlib_docs/types.py",
"generators/stdlib_docs/utils.py",
- "generators/stdlib_docs/validate.py",
]
}
diff --git a/python/generators/stdlib_docs/extractor.py b/python/generators/stdlib_docs/extractor.py
new file mode 100644
index 0000000..94db4d3
--- /dev/null
+++ b/python/generators/stdlib_docs/extractor.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+# 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.
+
+from dataclasses import dataclass
+from re import Match
+from typing import List, Optional, Tuple
+
+from python.generators.stdlib_docs.types import ObjKind
+from python.generators.stdlib_docs.utils import extract_comment
+from python.generators.stdlib_docs.utils import match_pattern
+from python.generators.stdlib_docs.utils import PATTERN_BY_KIND
+
+
+class DocsExtractor:
+ """Extracts documentation for views/tables/functions from SQL."""
+ path: str
+ module_name: str
+ sql: str
+
+ @dataclass
+ class Annotation:
+ key: str
+ value: str
+
+ @dataclass
+ class Extract:
+ """Extracted documentation for a single view/table/function."""
+ obj_kind: ObjKind
+ obj_match: Match
+
+ description: str
+ annotations: List['DocsExtractor.Annotation']
+
+ def __init__(self, path: str, module_name: str, sql: str):
+ self.path = path
+ self.module_name = module_name
+ self.sql = sql
+
+ self.sql_lines = sql.split("\n")
+ self.errors = []
+
+ def extract(self) -> List[Extract]:
+ extracted = []
+ extracted += self._extract_for_kind(ObjKind.table_view)
+ extracted += self._extract_for_kind(ObjKind.function)
+ extracted += self._extract_for_kind(ObjKind.view_function)
+ return extracted
+
+ def _extract_for_kind(self, kind: ObjKind) -> List[Extract]:
+ line_number_to_matches = match_pattern(PATTERN_BY_KIND[kind], self.sql)
+ extracts = []
+ for line_number, match in sorted(list(line_number_to_matches.items())):
+ comment_lines = extract_comment(self.sql_lines, line_number)
+ e = self._extract_from_comment(kind, match, comment_lines)
+ if e:
+ extracts.append(e)
+ return extracts
+
+ def _extract_from_comment(self, kind: ObjKind, match: Match,
+ comment_lines: List[str]) -> Optional[Extract]:
+ extract = DocsExtractor.Extract(kind, match, '', [])
+ for line in comment_lines:
+ assert line.startswith('--')
+
+ # Remove the comment.
+ stripped = line.lstrip('--').lstrip()
+
+ # Ignore lines which only contain '--'.
+ if not stripped:
+ continue
+
+ # Check if the line is an annotation.
+ if not stripped.startswith('@'):
+ # We are not in annotation: if we haven't seen an annotation yet, we
+ # must be still be parsing the description. Just add to that
+ if not extract.annotations:
+ extract.description += stripped + " "
+ continue
+
+ # Otherwise, add to the latest annotation.
+ extract.annotations[-1].value += " " + stripped
+ continue
+
+ # This line is an annotation: find its name and add a new entry
+ annotation, rest = stripped.split(' ', 1)
+ extract.annotations.append(DocsExtractor.Annotation(annotation, rest))
+ return extract
diff --git a/python/generators/stdlib_docs/parse.py b/python/generators/stdlib_docs/parse.py
index bdef717..a78bcca 100644
--- a/python/generators/stdlib_docs/parse.py
+++ b/python/generators/stdlib_docs/parse.py
@@ -13,102 +13,261 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from abc import ABC
+from dataclasses import dataclass
import re
-from typing import Union, List, Tuple
+import sys
+from typing import Any, Dict, List, Optional, Set, Tuple, Union
-from python.generators.stdlib_docs import stdlib
-from python.generators.stdlib_docs.utils import Errors, Pattern, get_text, fetch_comment, match_pattern
+from python.generators.stdlib_docs.extractor import DocsExtractor
+from python.generators.stdlib_docs.types import ObjKind
+from python.generators.stdlib_docs.utils import ARG_ANNOTATION_PATTERN
+from python.generators.stdlib_docs.utils import NAME_AND_TYPE_PATTERN
+from python.generators.stdlib_docs.utils import FUNCTION_RETURN_PATTERN
+from python.generators.stdlib_docs.utils import COLUMN_ANNOTATION_PATTERN
-def parse_desc(docs: 'stdlib.AnyDocs') -> str:
- desc_lines = [get_text(line, False) for line in docs.desc]
- return ' '.join(desc_lines).strip('\n').strip()
+class AbstractDocParser(ABC):
+ @dataclass
+ class Column:
+ pass
-# Whether comment segment about columns contain proper schema. Can be matched
-# against parsed SQL data by setting `use_data_from_sql`.
-def parse_columns(docs: Union['stdlib.TableViewDocs', 'stdlib.ViewFunctionDocs']
- ) -> dict:
- cols = {}
- last_col = None
- last_desc = []
- for line in docs.columns:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @column"):
- last_desc.append(get_text(line))
- continue
+ def __init__(self, path: str, module: str):
+ self.path = path
+ self.module = module
+ self.name = None
+ self.errors = []
- # Look for '-- @column' line as a column description
- m = re.match(Pattern['column'], line)
- if last_col:
- cols[last_col] = ' '.join(last_desc)
+ def _parse_name(self, upper: bool = False):
+ assert self.name
+ assert isinstance(self.name, str)
+ module_pattern = f"^{self.module}_.*"
+ if upper:
+ module_pattern = module_pattern.upper()
+ starts_with_module_name = re.match(module_pattern, self.name)
+ if self.module == "common":
+ if starts_with_module_name:
+ self._error('Names of tables/views/functions in the "common" module '
+ f'should not start with {module_pattern}')
+ return self.name
+ if not starts_with_module_name:
+ self._error('Names of tables/views/functions should be prefixed with the '
+ f'module name (i.e. should start with {module_pattern})')
+ return self.name.strip()
+
+ def _parse_desc_not_empty(self, desc: str):
+ if not desc:
+ self._error('Description of the table/view/function is missing')
+ return desc.strip()
+
+ def _validate_only_contains_annotations(self,
+ ans: List[DocsExtractor.Annotation],
+ ans_types: Set[str]):
+ used_ans_types = set(a.key for a in ans)
+ for type in used_ans_types.difference(ans_types):
+ self._error(f'Unknown documentation annotation {type}')
+
+ def _parse_columns(self, ans: List[DocsExtractor.Annotation],
+ sql_cols_str: str) -> Dict[str, str]:
+ cols = {}
+ for t in ans:
+ if t.key != '@column':
+ continue
+ m = re.match(COLUMN_ANNOTATION_PATTERN, t.value)
+ if not m:
+ self._error(f'@column annotation value {t.value} does not match '
+ f'pattern {COLUMN_ANNOTATION_PATTERN}')
+ continue
+ cols[m.group(1)] = m.group(2).strip()
+
+ sql_cols = self._parse_name_and_types_str(sql_cols_str)
+ if sql_cols:
+ for col in set(cols.keys()).difference(sql_cols.keys()):
+ self._error(f'@column "{col}" documented but does not exist in '
+ 'function definition')
+ for col in set(sql_cols.keys()).difference(cols):
+ self._error(f'Column "{col}" defined in SQL but is not documented with '
+ '@column')
+ return cols
+
+ def _parse_args(self, ans: List[DocsExtractor.Annotation],
+ sql_args_str: str) -> Dict[str, Any]:
+ args = {}
+ for an in ans:
+ if an.key != '@arg':
+ continue
+ m = re.match(ARG_ANNOTATION_PATTERN, an.value)
+ if m is None:
+ self._error(f'Expected arg documentation "{an.value}" to match pattern '
+ f'{ARG_ANNOTATION_PATTERN}')
+ continue
+ args[m.group(1)] = {'type': m.group(2), 'desc': m.group(3).strip()}
+
+ sql_args = self._parse_name_and_types_str(sql_args_str)
+ if sql_args:
+ for col in set(args.keys()).difference(sql_args.keys()):
+ self._error(f'Arg "{col}" documented with @arg but does not exist '
+ 'in function definition')
+ for arg in set(sql_args.keys()).difference(args.keys()):
+ self._error(f'Arg "{arg}" defined in SQL but is not documented with '
+ '@arg')
+ return args
+
+ def _parse_ret(self, ans: List[DocsExtractor.Annotation],
+ sql_ret_type: str) -> Tuple[str, str]:
+ rets = [a.value for a in ans if a.key == '@ret']
+ if len(rets) != 1:
+ self._error('Return value is not documentated with @ret')
+ return '', ''
+
+ ret = rets[0]
+ m = re.match(FUNCTION_RETURN_PATTERN, ret)
if not m:
- print(f'Expected line {line} to match @column format', file=sys.stderr)
- last_col, last_desc = m.group(1), [m.group(2)]
+ self._error(
+ f'@ret {ret} does not match pattern {FUNCTION_RETURN_PATTERN}')
+ return '', ''
- cols[last_col] = ' '.join(last_desc)
- return cols
+ ret_type, ret_desc = m.group(1), m.group(2)
+ if ret_type != sql_ret_type:
+ self._error(
+ f'@ret {ret_type} does not match SQL return type {sql_ret_type}')
+ return '', ''
+ return ret_type, ret_desc.strip()
+
+ def _parse_name_and_types_str(self, args_str: str) -> Dict[str, str]:
+ if not args_str:
+ return {}
+
+ args = {}
+ for arg_str in args_str.split(","):
+ m = re.match(NAME_AND_TYPE_PATTERN, arg_str)
+ if m is None:
+ self._error(f'Expected "{arg_str}" to match pattern '
+ f'{NAME_AND_TYPE_PATTERN}')
+ continue
+ args[m.group(1)] = m.group(2).strip()
+ return args
+
+ def _error(self, error: str):
+ self.errors.append(
+ f'Error while parsing documentation for {self.name} in {self.path}: '
+ f'{error}')
-def parse_args(docs: "stdlib.FunctionDocs") -> dict:
- if not docs.args:
- return {}
+class TableViewDocParser(AbstractDocParser):
+ """Parses documentation for CREATE TABLE and CREATE VIEW statements."""
- args = {}
- last_arg, last_desc, last_type = None, [], None
- for line in docs.args:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @arg"):
- last_desc.append(get_text(line))
- continue
+ def __init__(self, path: str, module: str):
+ super().__init__(path, module)
- m = re.match(Pattern['args'], line)
- if last_arg:
- args[last_arg] = {'type': last_type, 'desc': ' '.join(last_desc)}
- last_arg, last_type, last_desc = m.group(1), m.group(2), [m.group(3)]
+ def parse(self, doc: DocsExtractor.Extract) -> Optional[Dict[str, Any]]:
+ assert doc.obj_kind == ObjKind.table_view
- args[last_arg] = {'type': last_type, 'desc': ' '.join(last_desc)}
- return args
+ # Ignore internal tables and views.
+ self.name = doc.obj_match[1]
+ if re.match(r'^internal_.*', self.name):
+ return None
+
+ self._validate_only_contains_annotations(doc.annotations, {'@column'})
+ return {
+ 'name': self._parse_name(),
+ 'type': doc.obj_match[0],
+ 'desc': self._parse_desc_not_empty(doc.description),
+ 'cols': self._parse_columns(doc.annotations, ''),
+ }
-# Whether comment segment about return contain proper schema. Matches against
-# parsed SQL data.
-def parse_ret(docs: "stdlib.FunctionDocs") -> Tuple[str, str]:
- desc = []
- for line in docs.ret:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @ret"):
- desc.append(get_text(line))
+class FunctionDocParser(AbstractDocParser):
+ """Parses documentation for CREATE_FUNCTION statements."""
- m = re.match(Pattern['return_arg'], line)
- if not m:
- print(f'Expected line {line} to match @ret format', file=sys.stderr)
- ret_type, desc = m.group(1), [m.group(2)]
- return (ret_type, ' '.join(desc))
+ def __init__(self, path: str, module: str):
+ super().__init__(path, module)
+
+ def parse(self, doc: DocsExtractor.Extract) -> Optional[Dict[str, Any]]:
+ self.name, args, ret, _ = doc.obj_match
+
+ # Ignore internal functions.
+ if re.match(r'^INTERNAL_.*', self.name):
+ return None
+
+ self._validate_only_contains_annotations(doc.annotations, {'@arg', '@ret'})
+
+ ret_type, ret_desc = self._parse_ret(doc.annotations, ret)
+ return {
+ 'name': self._parse_name(upper=True),
+ 'desc': self._parse_desc_not_empty(doc.description),
+ 'args': self._parse_args(doc.annotations, args),
+ 'return_type': ret_type,
+ 'return_desc': ret_desc,
+ }
-# After matching file to Pattern, fetches and validates related documentation.
-def parse_typed_docs(path: str, module: str, sql: str, Pattern: str,
- docs_object: type
- ) -> Tuple[List['stdlib.AnyDocs'], Errors]:
+class ViewFunctionDocParser(AbstractDocParser):
+ """Parses documentation for CREATE_VIEW_FUNCTION statements."""
+
+ def __init__(self, path: str, module: str):
+ super().__init__(path, module)
+
+ def parse(self, doc: DocsExtractor.Extract) -> Optional[Dict[str, Any]]:
+ self.name, args, columns, _ = doc.obj_match
+
+ # Ignore internal functions.
+ if re.match(r'^INTERNAL_.*', self.name):
+ return None
+
+ self._validate_only_contains_annotations(doc.annotations,
+ {'@arg', '@column'})
+ return {
+ 'name': self._parse_name(upper=True),
+ 'desc': self._parse_desc_not_empty(doc.description),
+ 'cols': self._parse_columns(doc.annotations, columns),
+ 'args': self._parse_args(doc.annotations, args),
+ }
+
+
+# Reads the provided SQL and, if possible, generates a dictionary with data
+# from documentation together with errors from validation of the schema.
+def parse_file_to_dict(path: str, sql: str) -> Union[Dict[str, Any], List[str]]:
+ if sys.platform.startswith('win'):
+ path = path.replace('\\', '/')
+
+ # Get module name
+ module_name = path.split('/stdlib/')[-1].split('/')[0]
+
+ # Extract all the docs from the SQL.
+ extractor = DocsExtractor(path, module_name, sql)
+ docs = extractor.extract()
+ if extractor.errors:
+ return extractor.errors
+
+ # Parse the extracted docs.
errors = []
- line_id_to_match = match_pattern(Pattern, sql)
- lines = sql.split("\n")
- all_typed_docs = []
- for line_id, matches in line_id_to_match.items():
- # Fetch comment by looking at lines over beginning of match in reverse
- # order.
- comment = fetch_comment(lines[line_id - 1::-1])
- typed_docs, obj_errors = docs_object.create_from_comment(
- path, comment, module, matches)
- errors += obj_errors
+ table_views = []
+ functions = []
+ view_functions = []
+ for doc in docs:
+ if doc.obj_kind == ObjKind.table_view:
+ parser = TableViewDocParser(path, module_name)
+ res = parser.parse(doc)
+ if res:
+ table_views.append(res)
+ errors += parser.errors
+ if doc.obj_kind == ObjKind.function:
+ parser = FunctionDocParser(path, module_name)
+ res = parser.parse(doc)
+ if res:
+ functions.append(res)
+ errors += parser.errors
+ if doc.obj_kind == ObjKind.view_function:
+ parser = ViewFunctionDocParser(path, module_name)
+ res = parser.parse(doc)
+ if res:
+ view_functions.append(res)
+ errors += parser.errors
- if not typed_docs:
- continue
-
- errors += typed_docs.check_comment()
-
- if not errors:
- all_typed_docs.append(typed_docs)
-
- return all_typed_docs, errors
+ return errors if errors else {
+ 'imports': table_views,
+ 'functions': functions,
+ 'view_functions': view_functions
+ }
diff --git a/python/generators/stdlib_docs/stdlib.py b/python/generators/stdlib_docs/stdlib.py
deleted file mode 100644
index bf35d55..0000000
--- a/python/generators/stdlib_docs/stdlib.py
+++ /dev/null
@@ -1,343 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-# This tool checks that every SQL object created without prefix
-# 'internal_' is documented with proper schema.
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os
-import re
-import sys
-from typing import Union, List, Tuple, Dict
-from dataclasses import dataclass
-
-from python.generators.stdlib_docs.utils import *
-from python.generators.stdlib_docs.validate import *
-from python.generators.stdlib_docs.parse import *
-
-CommentLines = List[str]
-AnyDocs = Union['TableViewDocs', 'FunctionDocs', 'ViewFunctionDocs']
-
-
-# Stores documentation for CREATE {TABLE|VIEW} with comment split into
-# segments.
-@dataclass
-class TableViewDocs:
- name: str
- obj_type: str
- desc: CommentLines
- columns: CommentLines
- path: str
-
- # Contructs new TableViewDocs from the entire comment, by splitting it on
- # typed lines. Returns None for improperly structured schemas.
- @staticmethod
- def create_from_comment(path: str, comment: CommentLines, module: str,
- matches: Tuple) -> Tuple['TableViewDocs', Errors]:
- obj_type, name = matches[:2]
-
- # Ignore internal tables and views.
- if re.match(r"^internal_.*", name):
- return None, []
-
- errors = validate_name(name, module)
- col_start = None
- has_desc = False
-
- # Splits code into segments by finding beginning of column segment.
- for i, line in enumerate(comment):
- # Ignore only '--' line.
- if line == "--":
- continue
-
- m = re.match(Pattern['typed_line'], line)
-
- # Ignore untyped lines
- if not m:
- if not col_start:
- has_desc = True
- continue
-
- line_type = m.group(1)
- if line_type == "column" and not col_start:
- col_start = i
- continue
-
- if not has_desc:
- errors.append(f"No description for {obj_type}: '{name}' in {path}'\n")
- return None, errors
-
- if not col_start:
- errors.append(f"No columns for {obj_type}: '{name}' in {path}'\n")
- return None, errors
-
- return (
- TableViewDocs(name, obj_type, comment[:col_start], comment[col_start:],
- path),
- errors,
- )
-
- def check_comment(self) -> Errors:
- return validate_columns(self)
-
- def parse_comment(self) -> dict:
- return {
- 'name': self.name,
- 'type': self.obj_type,
- 'desc': parse_desc(self),
- 'cols': parse_columns(self)
- }
-
-
-# Stores documentation for create_function with comment split into segments.
-class FunctionDocs:
-
- def __init__(
- self,
- path: str,
- data_from_sql: dict,
- module: str,
- name: str,
- desc: str,
- args: CommentLines,
- ret: CommentLines,
- ):
- self.path = path
- self.data_from_sql = data_from_sql
- self.module = module
- self.name = name
- self.desc = desc
- self.args = args
- self.ret = ret
-
- # Contructs new FunctionDocs from whole comment, by splitting it on typed
- # lines. Returns None for improperly structured schemas.
- @staticmethod
- def create_from_comment(path: str, comment: CommentLines, module: str,
- matches: Tuple) -> Tuple['FunctionDocs', Errors]:
- name, args, ret, sql = matches
-
- # Ignore internal functions.
- if re.match(r"^INTERNAL_.*", name):
- return None, []
-
- errors = validate_name(name, module, upper=True)
- has_desc, start_args, start_ret = False, None, None
-
- args_dict, parse_errors = parse_args_str(args)
- errors += parse_errors
-
- # Splits code into segments by finding beginning of args and ret segments.
- for i, line in enumerate(comment):
- # Ignore only '--' line.
- if line == "--":
- continue
-
- m = re.match(Pattern['typed_line'], line)
-
- # Ignore untyped lines
- if not m:
- if not start_args:
- has_desc = True
- continue
-
- line_type = m.group(1)
- if line_type == "arg" and not start_args:
- start_args = i
- continue
-
- if line_type == "ret" and not start_ret:
- start_ret = i
- continue
-
- if not has_desc:
- errors.append(f"No description for '{name}' in {path}'\n")
- return None, errors
-
- if not start_ret or (args_dict and not start_args):
- errors.append(f"Function requires 'arg' and 'ret' comments.\n"
- f"'{name}' in {path}\n")
- return None, errors
-
- if not args_dict:
- start_args = start_ret
-
- data_from_sql = {'name': name, 'args': args_dict, 'ret': ret, 'sql': sql}
- return (
- FunctionDocs(
- path,
- data_from_sql,
- module,
- name,
- comment[:start_args],
- comment[start_args:start_ret] if args_dict else None,
- comment[start_ret:],
- ),
- errors,
- )
-
- def check_comment(self) -> Errors:
- errors = validate_args(self)
- errors += validate_ret(self)
- return errors
-
- def parse_comment(self) -> dict:
- ret_type, ret_desc = parse_ret(self)
- return {
- 'name': self.name,
- 'desc': parse_desc(self),
- 'args': parse_args(self),
- 'return_type': ret_type,
- 'return_desc': ret_desc
- }
-
-
-# Stores documentation for create_view_function with comment split into
-# segments.
-class ViewFunctionDocs:
-
- def __init__(
- self,
- path: str,
- data_from_sql: str,
- module: str,
- name: str,
- desc: CommentLines,
- args: CommentLines,
- columns: CommentLines,
- ):
- self.path = path
- self.data_from_sql = data_from_sql
- self.module = module
- self.name = name
- self.desc = desc
- self.args = args
- self.columns = columns
-
- # Contructs new ViewFunctionDocs from whole comment, by splitting it on typed
- # lines. Returns None for improperly structured schemas.
- @staticmethod
- def create_from_comment(path: str, comment: CommentLines, module: str,
- matches: Tuple) -> Tuple['ViewFunctionDocs', Errors]:
- name, args, columns, sql = matches
-
- # Ignore internal functions.
- if re.match(r"^INTERNAL_.*", name):
- return None, []
-
- errors = validate_name(name, module, upper=True)
- args_dict, parse_errors = parse_args_str(args)
- errors += parse_errors
- has_desc, start_args, start_cols = False, None, None
-
- # Splits code into segments by finding beginning of args and cols segments.
- for i, line in enumerate(comment):
- # Ignore only '--' line.
- if line == "--":
- continue
-
- m = re.match(Pattern['typed_line'], line)
-
- # Ignore untyped lines
- if not m:
- if not start_args:
- has_desc = True
- continue
-
- line_type = m.group(1)
- if line_type == "arg" and not start_args:
- start_args = i
- continue
-
- if line_type == "column" and not start_cols:
- start_cols = i
- continue
-
- if not has_desc:
- errors.append(f"No description for '{name}' in {path}'\n")
- return None, errors
-
- if not start_cols or (args_dict and not start_args):
- errors.append(f"Function requires 'arg' and 'column' comments.\n"
- f"'{name}' in {path}\n")
- return None, errors
-
- if not args_dict:
- start_args = start_cols
-
- cols_dict, parse_errors = parse_args_str(columns)
- errors += parse_errors
-
- data_from_sql = dict(name=name, args=args_dict, columns=cols_dict, sql=sql)
- return (
- ViewFunctionDocs(
- path,
- data_from_sql,
- module,
- name,
- comment[:start_args],
- comment[start_args:start_cols] if args_dict else None,
- comment[start_cols:],
- ),
- errors,
- )
-
- def check_comment(self) -> Errors:
- errors = validate_args(self)
- errors += validate_columns(self, use_data_from_sql=True)
- return errors
-
- def parse_comment(self) -> dict:
- return {
- 'name': self.name,
- 'desc': parse_desc(self),
- 'args': parse_args(self),
- 'cols': parse_columns(self)
- }
-
-
-# Reads the provided SQL and, if possible, generates a dictionary with data
-# from documentation together with errors from validation of the schema.
-def parse_file_to_dict(path: str, sql: str) -> Tuple[Dict[str, any], Errors]:
- if sys.platform.startswith('win'):
- path = path.replace("\\", "/")
-
- # Get module name
- module_name = path.split("/stdlib/")[-1].split("/")[0]
-
- imports, import_errors = parse_typed_docs(path, module_name, sql,
- Pattern['create_table_view'],
- TableViewDocs)
- functions, function_errors = parse_typed_docs(path, module_name, sql,
- Pattern['create_function'],
- FunctionDocs)
- view_functions, view_function_errors = parse_typed_docs(
- path, module_name, sql, Pattern['create_view_function'], ViewFunctionDocs)
-
- errors = import_errors + function_errors + view_function_errors
-
- if errors:
- sys.stderr.write("\n\n".join(errors))
-
- return ({
- 'imports': [imp.parse_comment() for imp in imports if imp],
- 'functions': [fun.parse_comment() for fun in functions if fun],
- 'view_functions': [
- view_fun.parse_comment() for view_fun in view_functions if view_fun
- ]
- }, errors)
diff --git a/python/generators/stdlib_docs/types.py b/python/generators/stdlib_docs/types.py
new file mode 100644
index 0000000..a9baad3
--- /dev/null
+++ b/python/generators/stdlib_docs/types.py
@@ -0,0 +1,21 @@
+# 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.
+
+from enum import Enum
+
+
+class ObjKind(str, Enum):
+ table_view = 'table_view'
+ function = 'function'
+ view_function = 'view_function'
diff --git a/python/generators/stdlib_docs/utils.py b/python/generators/stdlib_docs/utils.py
index d23b414..eeccc01 100644
--- a/python/generators/stdlib_docs/utils.py
+++ b/python/generators/stdlib_docs/utils.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
# Copyright (C) 2022 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,116 +12,82 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import re
-from typing import List, Tuple
+from typing import Dict, List
-Errors = List[str]
-CommentLines = List[str]
+from python.generators.stdlib_docs.types import ObjKind
-LOWER_NAME = r'[a-z_\d]*'
-UPPER_NAME = r'[A-Z_\d]*'
-ANY_WORDS = r'[A-Za-z_\d, \n]*'
-TYPE = r'[A-Z]*'
+LOWER_NAME = r'[a-z_\d]+'
+UPPER_NAME = r'[A-Z_\d]+'
+ANY_WORDS = r'[^\s].*'
+TYPE = r'[A-Z]+'
SQL = r'[\s\S]*?'
-Pattern = {
- 'create_table_view': (
- # Match create table/view and catch type
- r'CREATE (?:VIRTUAL )?(TABLE|VIEW)?(?:IF NOT EXISTS)?\s*'
- # Catch the name
- fr'({LOWER_NAME})\s*(?:AS|USING)?.*'),
- 'create_function': (
- r"SELECT\s*CREATE_FUNCTION\(\s*"
- # Function name: we are matching everything [A-Z]* between ' and ).
- fr"'\s*({UPPER_NAME})\s*\("
- # Args: anything before closing bracket with '.
- fr"({ANY_WORDS})\)',\s*"
- # Type: [A-Z]* between two '.
- fr"'({TYPE})',\s*"
- # Sql: Anything between ' and ');. We are catching \'.
- fr"'({SQL})'\s*\);"),
- 'create_view_function': (
- r"SELECT\s*CREATE_VIEW_FUNCTION\(\s*"
- # Function name: we are matching everything [A-Z]* between ' and ).
- fr"'({UPPER_NAME})\s*\("
- # Args: anything before closing bracket with '.
- fr"({ANY_WORDS})\)',\s*"
- # Return columns: anything between two '.
- fr"'\s*({ANY_WORDS})',\s*"
- # Sql: Anything between ' and ');. We are catching \'.
- fr"'({SQL})'\s*\);"),
- 'column': fr'^-- @column\s*({LOWER_NAME})\s*({ANY_WORDS})',
- 'arg_str': fr"\s*({LOWER_NAME})\s*({TYPE})\s*",
- 'args': fr'^-- @arg\s*({LOWER_NAME})\s*({TYPE})\s*(.*)',
- 'return_arg': fr"^-- @ret ({TYPE})\s*(.*)",
- 'typed_line': fr'^-- @([a-z]*)'
+CREATE_TABLE_VIEW_PATTERN = (
+ # Match create table/view and catch type
+ r'CREATE (?:VIRTUAL )?(TABLE|VIEW)?(?:IF NOT EXISTS)?\s*'
+ # Catch the name
+ fr'({LOWER_NAME})\s*(?:AS|USING)?.*')
+
+CREATE_FUNCTION_PATTERN = (
+ r"SELECT\s*CREATE_FUNCTION\(\s*"
+ # Function name: we are matching everything [A-Z]* between ' and ).
+ fr"'\s*({UPPER_NAME})\s*\("
+ # Args: anything before closing bracket with '.
+ fr"({ANY_WORDS})\)',\s*"
+ # Type: [A-Z]* between two '.
+ fr"'({TYPE})',\s*"
+ # Sql: Anything between ' and ');. We are catching \'.
+ fr"'({SQL})'\s*\);")
+
+CREATE_VIEW_FUNCTION_PATTERN = (
+ r"SELECT\s*CREATE_VIEW_FUNCTION\(\s*"
+ # Function name: we are matching everything [A-Z]* between ' and ).
+ fr"'({UPPER_NAME})\s*\("
+ # Args: anything before closing bracket with '.
+ fr"({ANY_WORDS})\)',\s*"
+ # Return columns: anything between two '.
+ fr"'\s*({ANY_WORDS})',\s*"
+ # Sql: Anything between ' and ');. We are catching \'.
+ fr"'({SQL})'\s*\);")
+
+PATTERN_BY_KIND = {
+ ObjKind.table_view: CREATE_TABLE_VIEW_PATTERN,
+ ObjKind.function: CREATE_FUNCTION_PATTERN,
+ ObjKind.view_function: CREATE_VIEW_FUNCTION_PATTERN,
}
+COLUMN_ANNOTATION_PATTERN = fr'^\s*({LOWER_NAME})\s*({ANY_WORDS})'
-def fetch_comment(lines_reversed: CommentLines) -> CommentLines:
- comment_reversed = []
- for line in lines_reversed:
+NAME_AND_TYPE_PATTERN = fr'\s*({LOWER_NAME})\s+({TYPE})\s*'
+
+ARG_ANNOTATION_PATTERN = fr'\s*{NAME_AND_TYPE_PATTERN}\s+({ANY_WORDS})'
+
+FUNCTION_RETURN_PATTERN = fr'^\s*({TYPE})\s+({ANY_WORDS})'
+
+
+# Given a list of lines in a text and the line number, scans backwards to find
+# all the comments.
+def extract_comment(lines: List[str], line_number: int) -> List[str]:
+ comments = []
+ for line in lines[line_number - 1::-1]:
# Break on empty line, as that suggests it is no longer a part of
# this comment.
if not line or not line.startswith('--'):
break
+ comments.append(line)
- # The only option left is a description, but it has to be after
- # schema columns.
- comment_reversed.append(line)
-
- comment_reversed.reverse()
- return comment_reversed
+ # Reverse as the above was reversed
+ comments.reverse()
+ return comments
-def match_pattern(pattern: str, file_str: str) -> dict:
- objects = {}
+# Given a regex pattern and a string to match against, returns all the
+# matching positions. Specifically, it returns a dictionary from the line
+# number of the match to the regex match object.
+def match_pattern(pattern: str, file_str: str) -> Dict[int, re.Match]:
+ line_number_to_matches = {}
for match in re.finditer(pattern, file_str):
line_id = file_str[:match.start()].count('\n')
- objects[line_id] = match.groups()
- return dict(sorted(objects.items()))
-
-
-# Whether the name starts with module_name.
-def validate_name(name: str, module: str, upper: bool = False) -> Errors:
- module_pattern = f"^{module}_.*"
- if upper:
- module_pattern = module_pattern.upper()
- starts_with_module_name = re.match(module_pattern, name)
- if module == "common":
- if starts_with_module_name:
- return [(f"Invalid name in module {name}. "
- f"In module 'common' the name shouldn't "
- f"start with '{module_pattern}'.\n")]
- else:
- if not starts_with_module_name:
- return [(f"Invalid name in module {name}. "
- f"Name has to begin with '{module_pattern}'.\n")]
- return []
-
-
-# Parses string with multiple arguments with type separated by comma into dict.
-def parse_args_str(args_str: str) -> Tuple[dict, Errors]:
- if not args_str.strip():
- return None, []
-
- errors = []
- args = {}
- for arg_str in args_str.split(","):
- m = re.match(Pattern['arg_str'], arg_str)
- if m is None:
- errors.append(f"Wrong arguments formatting for '{arg_str}'\n")
- continue
- args[m.group(1)] = m.group(2)
- return args, errors
-
-
-def get_text(line: str, no_break_line: bool = True) -> str:
- line = line.lstrip('--').strip()
- if not line:
- return '' if no_break_line else '\n'
- return line
+ line_number_to_matches[line_id] = match.groups()
+ return line_number_to_matches
diff --git a/python/generators/stdlib_docs/validate.py b/python/generators/stdlib_docs/validate.py
deleted file mode 100644
index fb419e2..0000000
--- a/python/generators/stdlib_docs/validate.py
+++ /dev/null
@@ -1,175 +0,0 @@
-#!/usr/bin/env python3
-# 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 re
-from typing import Union, List
-
-from python.generators.stdlib_docs import stdlib
-from python.generators.stdlib_docs.utils import Pattern, Errors
-
-
-# Whether the only typed comment in provided comment segment is of type
-# `comment_type`.
-def validate_typed_comment(
- comment_segment: str,
- comment_type: str,
- docs: 'stdlib.AnyDocs',
-) -> Errors:
- for line in comment_segment:
- # Ignore only '--' line.
- if line == "--":
- continue
-
- m = re.match(Pattern['typed_line'], line)
-
- # Ignore untyped lines
- if not m:
- continue
-
- line_type = m.group(1)
-
- if line_type != comment_type:
- return [(
- f"Wrong comment type. Expected '{comment_type}', got '{line_type}'.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
- return []
-
-
-# Whether comment segment about columns contain proper schema. Can be matched
-# against parsed SQL data by setting `use_data_from_sql`.
-def validate_columns(
- docs: Union['stdlib.TableViewDocs', 'stdlib.ViewFunctionDocs'],
- use_data_from_sql=False) -> Errors:
- errors = validate_typed_comment(docs.columns, "column", docs)
-
- if errors:
- return errors
-
- if use_data_from_sql:
- cols_from_sql = docs.data_from_sql["columns"]
-
- for line in docs.columns:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @column"):
- continue
-
- # Look for '-- @column' line as a column description
- m = re.match(Pattern['column'], line)
- if not m:
- errors.append(f"Wrong column description.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
- continue
-
- if not m.group(2).strip():
- errors.append(f"No description for column '{m.group(1)}'.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
- continue
-
- if not use_data_from_sql:
- return errors
-
- col_name = m.group(1)
- if col_name not in cols_from_sql:
- errors.append(f"There is no argument '{col_name}' specified in code.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
- continue
-
- cols_from_sql.pop(col_name)
-
- if not use_data_from_sql:
- errors.append(f"Missing columns for {docs.name}\n{docs.path}\n")
- return errors
-
- if not cols_from_sql:
- return errors
-
- errors.append(
- f"Missing documentation of columns: {list(cols_from_sql.keys())}.\n"
- f"'{docs.name}' in {docs.path}:\n")
- return errors
-
-
-# Whether comment segment about columns contain proper schema. Matches against
-# parsed SQL data.
-def validate_args(docs: Union['stdlib.FunctionDocs', 'stdlib.ViewFunctionDocs']
- ) -> Errors:
- if not docs.args:
- return []
-
- errors = validate_typed_comment(docs.args, "arg", docs)
-
- if errors:
- return errors
-
- args_from_sql = docs.data_from_sql["args"]
- for line in docs.args:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @"):
- continue
-
- m = re.match(Pattern['args'], line)
- if m is None:
- errors.append("The arg docs formatting is wrong. It should be:\n"
- "-- @arg [a-z_]* [A-Z]* {desc}\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
- return errors
-
- arg_name, arg_type = m.group(1), m.group(2)
- if arg_name not in args_from_sql:
- errors.append(f"There is not argument '{arg_name}' specified in code.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
- continue
-
- arg_type_from_sql = args_from_sql.pop(arg_name)
- if arg_type != arg_type_from_sql:
- errors.append(f"In the code, the type of '{arg_name}' is "
- f"'{arg_type_from_sql}', but according to the docs "
- f"it is '{arg_type}'.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")
-
- if not args_from_sql:
- return errors
-
- errors.append(
- f"Missing documentation of args: {list(args_from_sql.keys())}.\n"
- f"'{docs.name}' in {docs.path}\n")
- return errors
-
-
-# Whether comment segment about return contain proper schema. Matches against
-# parsed SQL data.
-def validate_ret(docs: "stdlib.FunctionDocs") -> Errors:
- errors = validate_typed_comment(docs.ret, "ret", docs)
- if errors:
- return errors
-
- ret_type_from_sql = docs.data_from_sql["ret"]
-
- for line in docs.ret:
- # Ignore only '--' line.
- if line == "--" or not line.startswith("-- @ret"):
- continue
-
- m = re.match(Pattern['return_arg'], line)
- if m is None:
- return [("The return docs formatting is wrong. It should be:\n"
- "-- @ret [A-Z]* {desc}\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
- docs_ret_type = m.group(1)
- if ret_type_from_sql != docs_ret_type:
- return [(f"The return type in docs is '{docs_ret_type}', "
- f"but it is {ret_type_from_sql} in code.\n"
- f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
- return []
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index ffd39d1..536dff9 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor b/python/perfetto/trace_processor/trace_processor.descriptor
index 20b87cf..2e0a2c6 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor
+++ b/python/perfetto/trace_processor/trace_processor.descriptor
Binary files differ
diff --git a/python/run_tests.py b/python/run_tests.py
index 245b7f6..b0a03c9 100755
--- a/python/run_tests.py
+++ b/python/run_tests.py
@@ -24,6 +24,7 @@
from test import stdlib_unittest
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(ROOT_DIR))
def main():
diff --git a/python/setup.py b/python/setup.py
index db49cac..a484eda 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -8,7 +8,7 @@
],
package_data={'perfetto.trace_processor': ['*.descriptor']},
include_package_data=True,
- version='0.6.0',
+ version='0.7.0',
license='apache-2.0',
description='Python API for Perfetto\'s Trace Processor',
author='Perfetto',
diff --git a/python/test/stdlib_unittest.py b/python/test/stdlib_unittest.py
index 3d64be7..1f609d0 100644
--- a/python/test/stdlib_unittest.py
+++ b/python/test/stdlib_unittest.py
@@ -12,15 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import unittest
import os
import sys
+import unittest
ROOT_DIR = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.append(os.path.join(ROOT_DIR))
-from generators.stdlib_docs.stdlib import FunctionDocs, ViewFunctionDocs, TableViewDocs
+from python.generators.stdlib_docs.parse import parse_file_to_dict
DESC = """--
-- First line.
@@ -34,14 +34,12 @@
ARGS_STR = """--
-- @arg utid INT Utid of thread.
--- @arg name STRING String name.
-"""
+-- @arg name STRING String name."""
ARGS_SQL_STR = "utid INT, name STRING"
RET_STR = """--
--- @ret BOOL Exists.
-"""
+-- @ret BOOL Exists."""
RET_SQL_STR = "BOOL"
@@ -50,162 +48,236 @@
class TestStdlib(unittest.TestCase):
- # Valid schemas
-
def test_valid_table(self):
- valid_table_comment = f"{DESC}\n{COLS_STR}".split('\n')
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+{COLS_STR}
+CREATE TABLE foo_table AS
+{SQL_STR};
+ '''.strip())
+ assert isinstance(res, dict)
- docs, create_errors = TableViewDocs.create_from_comment(
- "", valid_table_comment, 'common', ('table', 'tab_name', 'to_ignore'))
- self.assertFalse(create_errors)
-
- validation_errors = docs.check_comment()
- self.assertFalse(validation_errors)
+ table = res['imports'][0]
+ self.assertEqual(table['name'], 'foo_table')
+ self.assertEqual(table['desc'], 'First line. Second line.')
+ self.assertEqual(table['type'], 'TABLE')
+ self.assertEqual(table['cols'], {
+ 'slice_id': 'Id of slice.',
+ 'slice_name': 'Name of slice.'
+ })
def test_valid_function(self):
- valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
- docs, create_errors = FunctionDocs.create_from_comment(
- "", valid_function, 'common', valid_regex_matches)
- self.assertFalse(create_errors)
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+{ARGS_STR}
+{RET_STR}
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, dict)
- validation_errors = docs.check_comment()
- self.assertFalse(validation_errors)
+ fn = res['functions'][0]
+ self.assertEqual(fn['name'], 'FOO_FN')
+ self.assertEqual(fn['desc'], 'First line. Second line.')
+ self.assertEqual(
+ fn['args'], {
+ 'utid': {
+ 'type': 'INT',
+ 'desc': 'Utid of thread.',
+ },
+ 'name': {
+ 'type': 'STRING',
+ 'desc': 'String name.',
+ },
+ })
+ self.assertEqual(fn['return_type'], 'BOOL')
+ self.assertEqual(fn['return_desc'], 'Exists.')
def test_valid_view_function(self):
- valid_view_function = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
- docs, create_errors = ViewFunctionDocs.create_from_comment(
- "", valid_view_function, 'common', valid_regex_matches)
- self.assertFalse(create_errors)
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+{ARGS_STR}
+{COLS_STR}
+SELECT CREATE_VIEW_FUNCTION(
+ 'FOO_VIEW_FN({ARGS_SQL_STR})',
+ '{COLS_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, dict)
- validation_errors = docs.check_comment()
- self.assertFalse(validation_errors)
+ fn = res['view_functions'][0]
+ self.assertEqual(fn['name'], 'FOO_VIEW_FN')
+ self.assertEqual(fn['desc'], 'First line. Second line.')
+ self.assertEqual(
+ fn['args'], {
+ 'utid': {
+ 'type': 'INT',
+ 'desc': 'Utid of thread.',
+ },
+ 'name': {
+ 'type': 'STRING',
+ 'desc': 'String name.',
+ },
+ })
+ self.assertEqual(fn['cols'], {
+ 'slice_id': 'Id of slice.',
+ 'slice_name': 'Name of slice.'
+ })
- # Missing modules in names
+ def test_missing_module_name(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+{COLS_STR}
+CREATE TABLE bar_table AS
+{SQL_STR};
+ '''.strip())
+ assert isinstance(res, list)
- def test_missing_module_in_table_name(self):
- valid_table_comment = f"{DESC}\n{COLS_STR}".split('\n')
+ def test_common_does_not_include_module_name(self):
+ res = parse_file_to_dict(
+ 'common/bar.sql', f'''
+{DESC}
+{COLS_STR}
+CREATE TABLE common_table AS
+{SQL_STR};
+ '''.strip())
+ assert isinstance(res, list)
- _, create_errors = TableViewDocs.create_from_comment(
- "", valid_table_comment, 'android', ('table', 'tab_name', 'to_ignore'))
- self.assertTrue(create_errors)
+ def test_cols_typo(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+--
+-- @column slice_id2 Foo.
+-- @column slice_name Bar.
+CREATE TABLE bar_table AS
+{SQL_STR};
+ '''.strip())
+ assert isinstance(res, list)
- def test_missing_module_in_function_name(self):
- valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
- _, create_errors = FunctionDocs.create_from_comment("", valid_function,
- 'android',
- valid_regex_matches)
- self.assertTrue(create_errors)
+ def test_cols_no_desc(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+--
+-- @column slice_id
+-- @column slice_name Bar.
+CREATE TABLE bar_table AS
+{SQL_STR};
+ '''.strip())
+ assert isinstance(res, list)
- def test_missing_module_in_view_function_name(self):
- valid_view_function = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
- _, create_errors = ViewFunctionDocs.create_from_comment(
- "", valid_view_function, 'android', valid_regex_matches)
- self.assertTrue(create_errors)
+ def test_args_typo(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+--
+-- @arg utid2 INT Uint.
+-- @arg name STRING String name.
+{RET_STR}
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, list)
- # Missing part of schemas
+ def test_args_no_desc(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+--
+-- @arg utid INT
+-- @arg name STRING String name.
+{RET_STR}
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, list)
- def test_missing_desc_in_table_name(self):
- comment = f"{COLS_STR}".split('\n')
+ def test_ret_no_desc(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+{ARGS_STR}
+--
+-- @ret BOOL
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, list)
- _, create_errors = TableViewDocs.create_from_comment(
- "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
- self.assertTrue(create_errors)
+ def test_multiline_desc(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+-- This
+-- is
+--
+-- a
+-- very
+--
+-- long
+--
+-- description.
+{ARGS_STR}
+{RET_STR}
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, dict)
- def test_missing_cols_in_table(self):
- comment = f"{DESC}".split('\n')
+ fn = res['functions'][0]
+ self.assertEqual(fn['desc'], 'This is a very long description.')
- _, create_errors = TableViewDocs.create_from_comment(
- "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
- self.assertTrue(create_errors)
+ def test_multiline_arg_desc(self):
+ res = parse_file_to_dict(
+ 'foo/bar.sql', f'''
+{DESC}
+--
+-- @arg utid INT Uint
+-- spread
+--
+-- across lines.
+-- @arg name STRING String name
+-- which spans across multiple lines
+-- inconsistently.
+{RET_STR}
+SELECT CREATE_FUNCTION(
+ 'FOO_FN({ARGS_SQL_STR})',
+ '{RET_SQL_STR}',
+ '{SQL_STR}'
+);
+ '''.strip())
+ assert isinstance(res, dict)
- def test_missing_desc_in_function(self):
- comment = f"{ARGS_STR}\n{RET_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
- _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
- valid_regex_matches)
- self.assertTrue(create_errors)
-
- def test_missing_args_in_function(self):
- comment = f"{DESC}\n{RET_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
- _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
- valid_regex_matches)
- self.assertTrue(create_errors)
-
- def test_missing_ret_in_function(self):
- comment = f"{DESC}\n{ARGS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
- _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
- valid_regex_matches)
- self.assertTrue(create_errors)
-
- def test_missing_desc_in_view_function(self):
- comment = f"{ARGS_STR}\n{COLS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
- _, create_errors = ViewFunctionDocs.create_from_comment(
- "", comment, 'common', valid_regex_matches)
- self.assertTrue(create_errors)
-
- def test_missing_args_in_view_function(self):
- comment = f"{DESC}\n{COLS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
- _, create_errors = ViewFunctionDocs.create_from_comment(
- "", comment, 'common', valid_regex_matches)
- self.assertTrue(create_errors)
-
- def test_missing_cols_in_view_function(self):
- comment = f"{DESC}\n{ARGS_STR}".split('\n')
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
- _, create_errors = ViewFunctionDocs.create_from_comment(
- "", comment, 'common', valid_regex_matches)
- self.assertTrue(create_errors)
-
- # Validate elements
-
- def test_invalid_table_columns(self):
- invalid_cols = "-- @column slice_id"
- comment = f"{DESC}\n{invalid_cols}".split('\n')
-
- docs, create_errors = TableViewDocs.create_from_comment(
- "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
- self.assertFalse(create_errors)
-
- validation_errors = docs.check_comment()
- self.assertTrue(validation_errors)
-
- def test_invalid_view_function_columns(self):
- comment = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
- cols_sql_str = "slice_id INT"
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, cols_sql_str, SQL_STR)
- docs, create_errors = ViewFunctionDocs.create_from_comment(
- "", comment, 'common', valid_regex_matches)
- self.assertFalse(create_errors)
-
- validation_errors = docs.check_comment()
- self.assertTrue(validation_errors)
-
- def test_invalid_arguments(self):
- valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
- args_sql_str = "utid BOOL"
- valid_regex_matches = ('fun_name', args_sql_str, RET_SQL_STR, SQL_STR)
- docs, create_errors = FunctionDocs.create_from_comment(
- "", valid_function, 'common', valid_regex_matches)
- self.assertFalse(create_errors)
-
- validation_errors = docs.check_comment()
- self.assertTrue(validation_errors)
-
- def test_invalid_ret(self):
- valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
- ret_sql_str = "utid BOOL"
- valid_regex_matches = ('fun_name', ARGS_SQL_STR, ret_sql_str, SQL_STR)
- docs, create_errors = FunctionDocs.create_from_comment(
- "", valid_function, 'common', valid_regex_matches)
- self.assertFalse(create_errors)
-
- validation_errors = docs.check_comment()
- self.assertTrue(validation_errors)
+ fn = res['functions'][0]
+ self.assertEqual(
+ fn['args'], {
+ 'utid': {
+ 'type': 'INT',
+ 'desc': 'Uint spread across lines.',
+ },
+ 'name': {
+ 'type': 'STRING',
+ 'desc': 'String name which spans across multiple lines '
+ 'inconsistently.',
+ },
+ })
diff --git a/python/tools/record_android_trace.py b/python/tools/record_android_trace.py
index 9256bd8..1b1e659 100644
--- a/python/tools/record_android_trace.py
+++ b/python/tools/record_android_trace.py
@@ -64,12 +64,17 @@
class HttpHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
- self.send_header('Access-Control-Allow-Origin', '*')
- return super().end_headers()
+ self.send_header('Access-Control-Allow-Origin', self.server.allow_origin)
+ self.send_header('Cache-Control', 'no-cache')
+ super().end_headers()
def do_GET(self):
- self.server.last_request = self.path
- return super().do_GET()
+ if self.path != '/' + self.server.expected_fname:
+ self.send_error(404, "File not found")
+ return
+
+ self.server.fname_get_completed = True
+ super().do_GET()
def do_POST(self):
self.send_error(404, "File not found")
@@ -95,9 +100,15 @@
help = 'Output file or directory (default: %s)' % default_out_dir_str
parser.add_argument('-o', '--out', default=default_out_dir, help=help)
- help = 'Don\'t open in the browser'
+ help = 'Don\'t open or serve the trace'
parser.add_argument('-n', '--no-open', action='store_true', help=help)
+ help = 'Don\'t open in browser, but still serve trace (good for remote use)'
+ parser.add_argument('--no-open-browser', action='store_true', help=help)
+
+ help = 'The web address used to open trace files'
+ parser.add_argument('--origin', default='https://ui.perfetto.dev', help=help)
+
help = 'Force the use of the sideloaded binaries rather than system daemons'
parser.add_argument('--sideload', action='store_true', help=help)
@@ -340,7 +351,8 @@
if not args.no_open:
prt('\n')
prt('Opening the trace (%s) in the browser' % host_file)
- open_trace_in_browser(host_file)
+ open_browser = not args.no_open_browser
+ open_trace_in_browser(host_file, open_browser, args.origin)
def prt(msg, colors=ANSI.END):
@@ -368,17 +380,24 @@
sys.exit(1)
-def open_trace_in_browser(path):
+def open_trace_in_browser(path, open_browser, origin):
# We reuse the HTTP+RPC port because it's the only one allowed by the CSP.
PORT = 9001
+ path = os.path.abspath(path)
os.chdir(os.path.dirname(path))
fname = os.path.basename(path)
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
- webbrowser.open_new_tab(
- 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
- (PORT, fname))
- while httpd.__dict__.get('last_request') != '/' + fname:
+ address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}'
+ if open_browser:
+ webbrowser.open_new_tab(address)
+ else:
+ print(f'Open URL in browser: {address}')
+
+ httpd.expected_fname = fname
+ httpd.fname_get_completed = None
+ httpd.allow_origin = origin
+ while httpd.fname_get_completed is None:
httpd.handle_request()
diff --git a/src/android_internal/BUILD.gn b/src/android_internal/BUILD.gn
index 8435eab..a175063 100644
--- a/src/android_internal/BUILD.gn
+++ b/src/android_internal/BUILD.gn
@@ -32,6 +32,7 @@
"health_hal.cc",
"incident_service.cc",
"power_stats.cc",
+ "statsd.cc",
"statsd_logging.cc",
"tracing_service_proxy.cc",
]
@@ -53,7 +54,7 @@
"services",
"tracingproxy",
"utils",
- "libstatspull",
+ "statspull",
]
# This target should never depend on any other perfetto target to avoid ODR
diff --git a/src/android_internal/statsd.cc b/src/android_internal/statsd.cc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/android_internal/statsd.cc
diff --git a/src/android_stats/perfetto_atoms.h b/src/android_stats/perfetto_atoms.h
index e9ef08e..122dfad 100644
--- a/src/android_stats/perfetto_atoms.h
+++ b/src/android_stats/perfetto_atoms.h
@@ -47,6 +47,7 @@
// they log the trigger name.
kTracedTriggerStartTracing = 41,
kTracedTriggerStopTracing = 42,
+ kTracedTriggerCloneSnapshot = 53,
// Guardrails inside traced.
kTracedEnableTracingExistingTraceSession = 18,
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 8d48bf0..7dfce6b 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -199,6 +199,7 @@
"scoped_file_unittest.cc",
"small_vector_unittest.cc",
"status_or_unittest.cc",
+ "status_unittest.cc",
"string_splitter_unittest.cc",
"string_utils_unittest.cc",
"string_view_unittest.cc",
diff --git a/src/base/http/BUILD.gn b/src/base/http/BUILD.gn
index e086134..dda19a7 100644
--- a/src/base/http/BUILD.gn
+++ b/src/base/http/BUILD.gn
@@ -40,8 +40,11 @@
"../../../gn:default_deps",
"../../../gn:gtest_and_gmock",
]
- sources = [
- "http_server_unittest.cc",
- "sha1_unittest.cc",
- ]
+ sources = [ "sha1_unittest.cc" ]
+
+ # The HTTP server unittests cannot be run in parallel. Chromium runs tests
+ # in parallel on some bots so exclude all of these ones.
+ if (!build_with_chromium) {
+ sources += [ "http_server_unittest.cc" ]
+ }
}
diff --git a/src/base/status.cc b/src/base/status.cc
index 30ccc47..d3c13e8 100644
--- a/src/base/status.cc
+++ b/src/base/status.cc
@@ -17,6 +17,7 @@
#include "perfetto/base/status.h"
#include <stdarg.h>
+#include <algorithm>
namespace perfetto {
namespace base {
@@ -31,5 +32,42 @@
return status;
}
+std::optional<std::string_view> Status::GetPayload(std::string_view type_url) {
+ if (ok()) {
+ return std::nullopt;
+ }
+ for (const auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ return kv.payload;
+ }
+ }
+ return std::nullopt;
+}
+
+void Status::SetPayload(std::string_view type_url, std::string value) {
+ if (ok()) {
+ return;
+ }
+ for (auto& kv : payloads_) {
+ if (kv.type_url == type_url) {
+ kv.payload = value;
+ return;
+ }
+ }
+ payloads_.push_back(Payload{std::string(type_url), std::move(value)});
+}
+
+bool Status::ErasePayload(std::string_view type_url) {
+ if (ok()) {
+ return false;
+ }
+ auto it = std::remove_if(
+ payloads_.begin(), payloads_.end(),
+ [type_url](const Payload& p) { return p.type_url == type_url; });
+ bool erased = it != payloads_.end();
+ payloads_.erase(it, payloads_.end());
+ return erased;
+}
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/status_unittest.cc b/src/base/status_unittest.cc
new file mode 100644
index 0000000..df42b31
--- /dev/null
+++ b/src/base/status_unittest.cc
@@ -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.
+ */
+
+#include "perfetto/base/status.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+
+TEST(StatusTest, GetMissingPayload) {
+ base::Status status = base::ErrStatus("Error");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetThenGetPayload) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "payload_value");
+}
+
+TEST(StatusTest, SetEraseGetPayload) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_TRUE(status.ErasePayload("test.foo.com/bar"));
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetOverride) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ status.SetPayload("test.foo.com/bar", "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "other_value");
+}
+
+TEST(StatusTest, SetGetOk) {
+ base::Status status = base::OkStatus();
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), std::nullopt);
+}
+
+TEST(StatusTest, SetMultipleAndDuplicate) {
+ base::Status status = base::ErrStatus("Error");
+ status.SetPayload("test.foo.com/bar", "payload_value");
+ status.SetPayload("test.foo.com/bar1", "1");
+ status.SetPayload("test.foo.com/bar2", "2");
+ status.SetPayload("test.foo.com/bar", "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar"), "other_value");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar1"), "1");
+ ASSERT_EQ(status.GetPayload("test.foo.com/bar2"), "2");
+}
+
+} // namespace base
+} // namespace perfetto
diff --git a/src/base/utils.cc b/src/base/utils.cc
index 198daa9..82386fa 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -55,9 +55,16 @@
#define PERFETTO_M_PURGE -101
#endif // M_PURGE
+#ifdef M_PURGE_ALL
+#define PERFETTO_M_PURGE_ALL M_PURGE_ALL
+#else
+// Only available in in-tree builds and on newer SDKs.
+#define PERFETTO_M_PURGE_ALL -104
+#endif // M_PURGE
+
namespace {
extern "C" {
-using MalloptType = void (*)(int, int);
+using MalloptType = int (*)(int, int);
}
} // namespace
#endif // OS_ANDROID
@@ -140,7 +147,9 @@
reinterpret_cast<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
if (!mallopt_fn)
return;
- mallopt_fn(PERFETTO_M_PURGE, 0);
+ if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) {
+ mallopt_fn(PERFETTO_M_PURGE, 0);
+ }
#endif
}
diff --git a/src/cloud_trace_processor/worker_impl.cc b/src/cloud_trace_processor/worker_impl.cc
index 2f124a0..6f11560 100644
--- a/src/cloud_trace_processor/worker_impl.cc
+++ b/src/cloud_trace_processor/worker_impl.cc
@@ -72,8 +72,6 @@
// pools.
auto tp = std::make_unique<TraceProcessorWrapper>(
trace, thread_pool_, TraceProcessorWrapper::Statefulness::kStateless);
- shard->tps.emplace_back(std::move(tp));
-
auto load_trace_future =
tp->LoadTrace(environment_->ReadFile(trace))
.ContinueWith(
@@ -84,6 +82,7 @@
return resp;
});
streams.emplace_back(base::StreamFromFuture(std::move(load_trace_future)));
+ shard->tps.emplace_back(std::move(tp));
}
return base::FlattenStreams(std::move(streams));
}
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 4a2b081..3052341 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -19,6 +19,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/proc_utils.h"
#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_splitter.h"
#include <fcntl.h>
#include <stdio.h>
@@ -201,15 +202,17 @@
const char* kStateDir = "/data/misc/perfetto-traces";
PerfettoCmd::PerfettoCmd() {
- PERFETTO_DCHECK(!g_perfetto_cmd);
- g_perfetto_cmd = this;
+ // Only the main thread instance on the main thread will receive ctrl-c.
+ if (!g_perfetto_cmd)
+ g_perfetto_cmd = this;
}
PerfettoCmd::~PerfettoCmd() {
- PERFETTO_DCHECK(g_perfetto_cmd == this);
- g_perfetto_cmd = nullptr;
- if (ctrl_c_handler_installed_) {
- task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
+ if (g_perfetto_cmd == this) {
+ g_perfetto_cmd = nullptr;
+ if (ctrl_c_handler_installed_) {
+ task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
+ }
}
}
@@ -228,6 +231,9 @@
session, identified by its ID (see --query).
--config -c : /path/to/trace/config/file or - for stdin
--out -o : /path/to/out/trace/file or - for stdout
+ If using CLONE_SNAPSHOT triggers, each snapshot
+ will be saved in a new file with a counter suffix
+ (e.g., file.0, file.1, file.2).
--txt : Parse config as pbtxt. Not for production use.
Not a stable API.
--query : Queries the service state and prints it as
@@ -339,6 +345,7 @@
return 1;
}
+ optind = 1; // Reset getopt state. It's reused by the snapshot thread.
for (;;) {
int option =
getopt_long(argc, argv, "hc:o:dDt:b:s:a:", long_options, nullptr);
@@ -365,6 +372,14 @@
opts.categories.emplace_back("power/gpu_frequency");
PERFETTO_CHECK(CreateConfigFromOptions(opts, &test_config));
trace_config_raw = test_config.SerializeAsString();
+ } else if (strcmp(optarg, ":mem") == 0) {
+ // This is used by OnCloneSnapshotTriggerReceived(), which passes the
+ // original trace config as a member field. This is needed because, in
+ // the new PerfettoCmd instance, we need to know upfront trace config
+ // fields that affect the behaviour of perfetto_cmd, e.g., the guardrail
+ // overrides, the unique_session_name, the reporter API package etc.
+ PERFETTO_CHECK(!snapshot_config_.empty());
+ trace_config_raw = snapshot_config_;
} else {
if (!base::ReadFile(optarg, &trace_config_raw)) {
PERFETTO_PLOG("Could not open %s", optarg);
@@ -573,7 +588,7 @@
bool parsed = false;
const bool will_trace_or_trigger = !is_attach() && !query_service_;
- if (!will_trace_or_trigger || clone_tsid_) {
+ if (!will_trace_or_trigger) {
if ((!trace_config_raw.empty() || has_config_options)) {
PERFETTO_ELOG("Cannot specify a trace config with this option");
return 1;
@@ -587,7 +602,7 @@
}
parsed = CreateConfigFromOptions(config_options, trace_config_.get());
} else {
- if (trace_config_raw.empty()) {
+ if (trace_config_raw.empty() && !clone_tsid_) {
PERFETTO_ELOG("The TraceConfig is empty");
return 1;
}
@@ -784,8 +799,12 @@
packet_writer_ = CreateFilePacketWriter(trace_out_stream_.get());
}
- if (trace_config_->compression_type() ==
- TraceConfig::COMPRESSION_TYPE_DEFLATE) {
+ // TODO(b/281043457): this code path will go away after Android U. Compression
+ // has been moved to the service. This code is here only as a fallback in case
+ // of bugs in the U timeframe.
+ if (trace_config_->compress_from_cli() &&
+ trace_config_->compression_type() ==
+ TraceConfig::COMPRESSION_TYPE_DEFLATE) {
if (packet_writer_) {
#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
packet_writer_ = CreateZipPacketWriter(std::move(packet_writer_));
@@ -819,6 +838,7 @@
#endif
}
+ PERFETTO_CHECK(!snapshot_thread_); // No threads before demonization.
base::Daemonize([this]() -> int {
background_wait_pipe_.wr.reset();
@@ -992,9 +1012,16 @@
connected_ = true;
LogUploadEvent(PerfettoStatsdAtom::kOnConnect);
+ uint32_t events_mask = 0;
+ if (GetTriggerMode(*trace_config_) ==
+ TraceConfig::TriggerConfig::CLONE_SNAPSHOT) {
+ events_mask |= ObservableEvents::TYPE_CLONE_TRIGGER_HIT;
+ }
if (background_wait_) {
- consumer_endpoint_->ObserveEvents(
- perfetto::ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+ events_mask |= ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED;
+ }
+ if (events_mask) {
+ consumer_endpoint_->ObserveEvents(events_mask);
}
if (query_service_) {
@@ -1119,6 +1146,14 @@
// be marked as "E" in the event log. Hence why LOG and not ELOG here.
PERFETTO_LOG("Service error: %s", error.c_str());
+ // In case of errors don't leave a partial file around. This happens
+ // frequently in the case of --save-for-bugreport if there is no eligible
+ // trace. See also b/279753347 .
+ if (bytes_written_ == 0 && !trace_out_path_.empty() &&
+ trace_out_path_ != "-") {
+ remove(trace_out_path_.c_str());
+ }
+
// Update guardrail state even if we failed. This is for two
// reasons:
// 1. Keeps compatibility with pre-stats code which used to
@@ -1214,6 +1249,9 @@
}
void PerfettoCmd::SetupCtrlCSignalHandler() {
+ // Only the main thread instance should handle CTRL+C.
+ if (g_perfetto_cmd != this)
+ return;
ctrl_c_handler_installed_ = true;
base::InstallCtrlCHandler([] {
if (!g_perfetto_cmd)
@@ -1282,17 +1320,18 @@
// TODO(eseckler): Support GetTraceStats().
}
-void PerfettoCmd::OnSessionCloned(bool success, const std::string& error) {
+void PerfettoCmd::OnSessionCloned(const OnSessionClonedArgs& args) {
PERFETTO_DLOG("Cloned tracing session %" PRIu64 ", success=%d",
- clone_tsid_.value_or(0), success);
+ clone_tsid_.value_or(0), args.success);
std::string full_error;
- if (!success) {
+ if (!args.success) {
full_error = "Failed to clone tracing session " +
- std::to_string(clone_tsid_.value_or(0)) + ": " + error;
+ std::to_string(clone_tsid_.value_or(0)) + ": " + args.error;
}
// Kick off the readback and file finalization (as if we started tracing and
// reached the duration_ms timeout).
+ uuid_ = args.uuid.ToString();
ReadbackTraceDataAndQuit(full_error);
}
@@ -1413,6 +1452,69 @@
if (observable_events.all_data_sources_started()) {
NotifyBgProcessPipe(kBackgroundOk);
}
+ if (observable_events.has_clone_trigger_hit()) {
+ int64_t tsid = observable_events.clone_trigger_hit().tracing_session_id();
+ OnCloneSnapshotTriggerReceived(static_cast<TracingSessionID>(tsid));
+ }
+}
+
+void PerfettoCmd::OnCloneSnapshotTriggerReceived(TracingSessionID tsid) {
+ PERFETTO_DLOG("Creating snapshot for tracing session %" PRIu64, tsid);
+
+ // Only the main thread instance should be handling snapshots.
+ // We should never end up in a state where each secondary PerfettoCmd
+ // instance handles other snapshots and creates other threads.
+ PERFETTO_CHECK(g_perfetto_cmd == this);
+
+ std::string cmdline;
+ auto add_argv = [&cmdline](const std::string& str) {
+ cmdline.append(str);
+ cmdline.append("\0", 1);
+ };
+ add_argv("perfetto");
+ add_argv("--config");
+ add_argv(":mem"); // Use the copied config from `snapshot_config_`.
+ add_argv("--clone");
+ add_argv(std::to_string(tsid));
+ if (upload_flag_) {
+ add_argv("--upload");
+ } else if (!trace_out_path_.empty()) {
+ add_argv("--out");
+ add_argv(trace_out_path_ + "." + std::to_string(snapshot_count_++));
+ } else {
+ PERFETTO_FATAL("Cannot use CLONE_SNAPSHOT with the current cmdline args");
+ }
+
+ if (!snapshot_thread_) {
+ // The destructor of the main-thread's PerfettoCmdMain will destroy and
+ // join the secondary thread that we are crating here.
+ snapshot_thread_.reset(new base::ThreadTaskRunner(
+ base::ThreadTaskRunner::CreateAndStart("snapshot")));
+ }
+
+ // We need to pass a copy of the trace config to the new PerfettoCmd instance
+ // because the trace config defines a bunch of properties that are used by the
+ // cmdline client (reporter API package, guardrails, etc).
+ std::string trace_config_copy = trace_config_->SerializeAsString();
+
+ snapshot_thread_->PostTask([tsid, cmdline, trace_config_copy] {
+ int argc = 0;
+ char* argv[32];
+ // `splitter` needs to live on the stack for the whole scope as it owns the
+ // underlying string storage (that gets std::moved) passed PerfettoCmd.
+ base::StringSplitter splitter(std::move(cmdline), '\0');
+ while (splitter.Next()) {
+ argv[argc++] = splitter.cur_token();
+ PERFETTO_CHECK(static_cast<size_t>(argc) < base::ArraySize(argv));
+ }
+ perfetto::PerfettoCmd cmd;
+ cmd.snapshot_config_ = std::move(trace_config_copy);
+ auto cmdline_res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
+ PERFETTO_CHECK(!cmdline_res.has_value()); // No daemonization expected.
+ int res = cmd.ConnectToServiceRunAndMaybeNotify();
+ if (res)
+ PERFETTO_ELOG("Cloning session %" PRIu64 " failed (%d)", tsid, res);
+ });
}
void PerfettoCmd::LogUploadEvent(PerfettoStatsdAtom atom) {
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 9504ca5..b55cbc8 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -28,6 +28,7 @@
#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/thread_task_runner.h"
#include "perfetto/ext/base/unix_task_runner.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/tracing/core/consumer.h"
@@ -67,7 +68,7 @@
void OnAttach(bool, const TraceConfig&) override;
void OnTraceStats(bool, const TraceStats&) override;
void OnObservableEvents(const ObservableEvents&) override;
- void OnSessionCloned(bool, const std::string&) override;
+ void OnSessionCloned(const OnSessionClonedArgs&) override;
void SignalCtrlC() { ctrl_c_evt_.Notify(); }
@@ -116,6 +117,8 @@
// will have no effect.
void NotifyBgProcessPipe(BgProcessStatus status);
+ void OnCloneSnapshotTriggerReceived(TracingSessionID);
+
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
static base::ScopedFile CreateUnlinkedTmpFile();
void SaveTraceIntoIncidentOrCrash();
@@ -162,6 +165,14 @@
// How long we expect to trace for or 0 if the trace is indefinite.
uint32_t expected_duration_ms_ = 0;
bool trace_data_timeout_armed_ = false;
+
+ // The aux thread that is used to invoke secondary instances of PerfettoCmd
+ // to create snapshots. This is used only when the trace config involves a
+ // CLONE_SNAPSHOT trigger.
+ std::unique_ptr<base::ThreadTaskRunner> snapshot_thread_;
+ int snapshot_count_ = 0;
+ std::string snapshot_config_;
+
base::WeakPtrFactory<PerfettoCmd> weak_factory_{this};
};
diff --git a/src/profiling/common/producer_support.h b/src/profiling/common/producer_support.h
index 6f54045..c1cd696 100644
--- a/src/profiling/common/producer_support.h
+++ b/src/profiling/common/producer_support.h
@@ -19,6 +19,7 @@
#include <cinttypes>
#include <string>
+#include <vector>
#include "perfetto/tracing/core/forward_decls.h"
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index 3e03f5e..9263d0c 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -19,6 +19,9 @@
#include <sys/types.h>
#include <unistd.h>
+#include <condition_variable>
+#include <mutex>
+
#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineMips.h>
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 0fb7be3..fec5b6d 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -16,8 +16,10 @@
#include "src/profiling/perf/perf_producer.h"
+#include <optional>
#include <random>
#include <utility>
+#include <vector>
#include <unistd.h>
@@ -26,7 +28,9 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/tracing/core/basic_types.h"
@@ -80,6 +84,35 @@
return static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF));
}
+std::vector<uint32_t> GetOnlineCpus() {
+ size_t cpu_count = NumberOfCpus();
+ if (cpu_count == 0) {
+ return {};
+ }
+
+ static constexpr char kOnlineValue[] = "1\n";
+ std::vector<uint32_t> online_cpus;
+ online_cpus.reserve(cpu_count);
+ for (uint32_t cpu = 0; cpu < cpu_count; ++cpu) {
+ std::string res;
+ base::StackString<1024> path("/sys/devices/system/cpu/cpu%u/online", cpu);
+ if (!base::ReadFile(path.c_str(), &res)) {
+ // Always consider CPU 0 to be online if the "online" file does not exist
+ // for it. There seem to be several assumptions in the kernel which make
+ // CPU 0 special so this is a pretty safe bet.
+ if (cpu != 0) {
+ return {};
+ }
+ res = kOnlineValue;
+ }
+ if (res != kOnlineValue) {
+ continue;
+ }
+ online_cpus.push_back(cpu);
+ }
+ return online_cpus;
+}
+
int32_t ToBuiltinClock(int32_t clockid) {
switch (clockid) {
case CLOCK_REALTIME:
@@ -394,9 +427,14 @@
return;
}
- size_t num_cpus = NumberOfCpus();
+ std::vector<uint32_t> online_cpus = GetOnlineCpus();
+ if (online_cpus.empty()) {
+ PERFETTO_ELOG("No online CPUs found.");
+ return;
+ }
+
std::vector<EventReader> per_cpu_readers;
- for (uint32_t cpu = 0; cpu < num_cpus; cpu++) {
+ for (uint32_t cpu : online_cpus) {
std::optional<EventReader> event_reader =
EventReader::ConfigureEvents(cpu, event_config.value());
if (!event_reader.has_value()) {
diff --git a/src/protozero/proto_decoder_unittest.cc b/src/protozero/proto_decoder_unittest.cc
index 0991f88..1e962d7 100644
--- a/src/protozero/proto_decoder_unittest.cc
+++ b/src/protozero/proto_decoder_unittest.cc
@@ -592,5 +592,26 @@
ASSERT_FALSE(field.valid());
}
+// Check what happens when trying to parse packed repeated field and finding a
+// mismatching wire type instead. A compliant protobuf decoder should accept it,
+// but protozero doesn't handle that. At least it shouldn't crash.
+TEST(ProtoDecoderTest, PacketRepeatedWireTypeMismatch) {
+ protozero::HeapBuffered<pbtest::PackedRepeatedFields> message;
+ // A proper packed encoding should have a length delimited wire type. Use a
+ // var int wire type instead.
+ constexpr int kFieldId = pbtest::PackedRepeatedFields::kFieldInt32FieldNumber;
+ message->AppendTinyVarInt(kFieldId, 5);
+ auto data = message.SerializeAsArray();
+
+ pbtest::PackedRepeatedFields::Decoder decoder(data.data(), data.size());
+ bool parse_error = false;
+ auto it = decoder.field_int32(&parse_error);
+ // The decoder doesn't return a parse error (maybe it should, but that has
+ // been the behavior since the beginning).
+ ASSERT_FALSE(parse_error);
+ // But the iterator returns 0 elements.
+ EXPECT_FALSE(it);
+}
+
} // namespace
} // namespace protozero
diff --git a/src/protozero/protoc_plugin/protozero_plugin.cc b/src/protozero/protoc_plugin/protozero_plugin.cc
index bcfbf28..55c3c9d 100644
--- a/src/protozero/protoc_plugin/protozero_plugin.cc
+++ b/src/protozero/protoc_plugin/protozero_plugin.cc
@@ -874,8 +874,9 @@
for (int j = 0; j < nested_enum->value_count(); ++j) {
const EnumValueDescriptor* value = nested_enum->value(j);
- stub_h_->Print("static const $class$ $name$ = $class$::$name$;\n",
- "class", nested_enum->name(), "name", value->name());
+ stub_h_->Print(
+ "static inline const $class$ $name$ = $class$::$name$;\n", "class",
+ nested_enum->name(), "name", value->name());
}
}
diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
index f692373..712e3d1 100644
--- a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
@@ -191,6 +191,11 @@
// configurations)
if (group == "ftrace" && proto.event_name == "print" && field->name == "ip")
continue;
+ // Ignore the "nid" field. On new kernels, this field has a type that we
+ // don't know how to parse. See b/281660544
+ if (group == "f2fs" && proto.event_name == "f2fs_truncate_partial_nodes" &&
+ field->name == "nid")
+ continue;
s += "{";
s += "kUnsetOffset, ";
s += "kUnsetSize, ";
diff --git a/src/tools/proto_merger/proto_file_serializer.cc b/src/tools/proto_merger/proto_file_serializer.cc
index dca5a07..c94a101 100644
--- a/src/tools/proto_merger/proto_file_serializer.cc
+++ b/src/tools/proto_merger/proto_file_serializer.cc
@@ -70,8 +70,11 @@
std::string output;
output += " [";
- for (const auto& option : options) {
- output += option.key + " = " + option.value;
+ size_t n = options.size();
+ for (size_t i = 0; i < n; i++) {
+ output += options[i].key + " = " + options[i].value;
+ if (i != n - 1)
+ output += ", ";
}
output += "]";
return output;
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6f0660c..04814a6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -268,6 +268,8 @@
":top_level_unittests",
"containers:unittests",
"db:unittests",
+ "db/overlays:unittests",
+ "db/storage:unittests",
"importers/android_bugreport:unittests",
"importers/common:unittests",
"importers/ftrace:unittests",
diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h
index c1cdc33..a9a5fbd 100644
--- a/src/trace_processor/containers/bit_vector.h
+++ b/src/trace_processor/containers/bit_vector.h
@@ -89,17 +89,10 @@
std::vector<uint32_t> counts(no_blocks);
// Calculate counts only for full blocks.
- for (uint32_t i = 1; i < no_blocks - 1; ++i) {
- counts[i] +=
- counts[i - 1] +
- ConstBlock(&words_[Block::kWords * (i - 1)]).CountSetBits();
+ for (uint32_t i = 1; i < no_blocks; ++i) {
+ counts[i] = counts[i - 1] +
+ ConstBlock(&words_[Block::kWords * (i - 1)]).CountSetBits();
}
- if (size_ % Block::kBits == 0) {
- counts[no_blocks - 1] +=
- counts[no_blocks - 2] +
- ConstBlock(&words_[Block::kWords * (no_blocks - 2)]).CountSetBits();
- }
-
return BitVector{std::move(words_), std::move(counts), size_};
}
diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc
index 3db984c..dd8c951 100644
--- a/src/trace_processor/containers/bit_vector_unittest.cc
+++ b/src/trace_processor/containers/bit_vector_unittest.cc
@@ -572,6 +572,22 @@
ASSERT_FALSE(bv.IsSet(2));
}
+TEST(BitVectorUnittest, BuilderCountSetBits) {
+ // 16 words and 1 bit
+ BitVector::Builder builder(1025);
+
+ // 100100011010001010110011110001001 as a hex literal, with 15 set bits.
+ uint64_t word = 0x123456789;
+ for (uint32_t i = 0; i < 16; ++i) {
+ builder.AppendWord(word);
+ }
+ builder.Append(1);
+ BitVector bv = std::move(builder).Build();
+
+ ASSERT_EQ(bv.CountSetBits(500), 120u);
+ ASSERT_EQ(bv.CountSetBits(), 16 * 15 + 1u);
+}
+
TEST(BitVectorUnittest, BuilderStressTest) {
// Space for 128 words and 1 bit
uint32_t size = 8 * 1024 + 1;
@@ -590,7 +606,7 @@
ASSERT_EQ(builder.BitsUntilFull(), size - 1024);
ASSERT_EQ(builder.BitsUntilWordBoundaryOrFull(), 0u);
- // 100100011010001010110011110001001 as a hex literal.
+ // 100100011010001010110011110001001 as a hex literal, with 15 set bits.
uint64_t word = 0x123456789;
// Add all of the remaining words.
@@ -607,6 +623,8 @@
builder.Append(1);
BitVector bv = std::move(builder).Build();
+
+ ASSERT_EQ(bv.CountSetBits(), 2681u);
ASSERT_EQ(bv.size(), 8u * 1024u + 1u);
ASSERT_TRUE(bv.IsSet(0));
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index bcde78f..1ef081a 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -20,19 +20,12 @@
"base_id.h",
"column.cc",
"column.h",
- "column_overlay.h",
"column_storage.cc",
"column_storage.h",
"column_storage_overlay.h",
"compare.h",
+ "null_overlay.cc",
"null_overlay.h",
- "numeric_storage.cc",
- "numeric_storage.h",
- "sorting_overlay.h",
- "storage.cc",
- "storage.h",
- "storage_overlay.h",
- "storage_variants.h",
"table.cc",
"table.h",
"typed_column.h",
@@ -47,6 +40,8 @@
"../../../include/perfetto/trace_processor",
"../containers",
"../util:glob",
+ "overlays",
+ "storage",
]
}
@@ -59,7 +54,6 @@
sources = [
"column_storage_overlay_unittest.cc",
"compare_unittest.cc",
- "storage_unittest.cc",
"view_unittest.cc",
]
deps = [
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index f1adb96..66b273d 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -27,55 +27,12 @@
#include "src/trace_processor/db/column_storage.h"
#include "src/trace_processor/db/column_storage_overlay.h"
#include "src/trace_processor/db/compare.h"
+#include "src/trace_processor/db/storage/types.h"
#include "src/trace_processor/db/typed_column_internal.h"
namespace perfetto {
namespace trace_processor {
-// Represents the possible filter operations on a column.
-enum class FilterOp {
- kEq,
- kNe,
- kGt,
- kLt,
- kGe,
- kLe,
- kIsNull,
- kIsNotNull,
- kGlob,
-};
-
-// Represents a constraint on a column.
-struct Constraint {
- uint32_t col_idx;
- FilterOp op;
- SqlValue value;
-};
-
-// Represents an order by operation on a column.
-struct Order {
- uint32_t col_idx;
- bool desc;
-};
-
-// The enum type of the column.
-// Public only to stop GCC complaining about templates being defined in a
-// non-namespace scope (see ColumnTypeHelper below).
-enum class ColumnType {
- // Standard primitive types.
- kInt32,
- kUint32,
- kInt64,
- kDouble,
- kString,
-
- // Types generated on the fly.
- kId,
-
- // Types which don't have any data backing them.
- kDummy,
-};
-
// Helper class for converting a type to a ColumnType.
template <typename T>
struct ColumnTypeHelper;
diff --git a/src/trace_processor/db/column_overlay.h b/src/trace_processor/db/column_overlay.h
deleted file mode 100644
index 7dc15f9..0000000
--- a/src/trace_processor/db/column_overlay.h
+++ /dev/null
@@ -1,49 +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_COLUMN_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_COLUMN_OVERLAY_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-// Column overlay introduce separation between column storage (vector of data)
-// and state (nullability, sorting) and actions (filtering, expanding, joining)
-// done on the storage. This is a composable design - one ColumnOverlay
-// subclass might hold another subclass, and each of them implements all of the
-// functions in it's own specific way.
-class ColumnOverlay {
- public:
- virtual ~ColumnOverlay();
-
- // Clears the rows of RowMap, on which data don't match the FilterOp operation
- // with SqlValue. Efficient.
- virtual void Filter(FilterOp, SqlValue, RowMap&) = 0;
-
- // Sorts (ascending) provided vector of indices based on storage.
- virtual void Sort(std::vector<uint32_t>&) = 0;
-};
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_COLUMN_OVERLAY_H_
diff --git a/src/trace_processor/db/null_overlay.cc b/src/trace_processor/db/null_overlay.cc
new file mode 100644
index 0000000..71b38f4
--- /dev/null
+++ b/src/trace_processor/db/null_overlay.cc
@@ -0,0 +1,85 @@
+/*
+ * 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/null_overlay.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+void NullOverlay::Filter(FilterOp op, SqlValue, RowMap& rm) const {
+ if (op == FilterOp::kIsNull) {
+ rm.Intersect(RowMap(null_bv_->Not()));
+ return;
+ }
+ if (op == FilterOp::kIsNotNull) {
+ rm.Intersect(RowMap(null_bv_->Copy()));
+ return;
+ }
+
+ // Row map for filtered data, not the size of whole column.
+ RowMap filtered_data_rm(0, null_bv_->CountSetBits());
+ // inner_->Filter(op, sql_val, filtered_data_rm);
+
+ // Select only rows that were not filtered out from null BitVector and
+ // intersect it with RowMap&.
+ rm.Intersect(RowMap(null_bv_->Copy()).SelectRows(filtered_data_rm));
+}
+
+void NullOverlay::StableSort(uint32_t* rows, uint32_t rows_size) const {
+ uint32_t count_set_bits = null_bv_->CountSetBits();
+
+ std::vector<uint32_t> non_null_rows(count_set_bits);
+ std::vector<uint32_t> storage_to_rows(count_set_bits);
+
+ // Saving the map from `out` index to `storage` index gives us free `IsSet()`
+ // function, which would be very expensive otherwise.
+ for (auto it = null_bv_->IterateSetBits(); it; it.Next()) {
+ storage_to_rows[it.ordinal()] = it.index();
+ }
+
+ uint32_t cur_non_null_id = 0;
+ uint32_t cur_null_id = 0;
+
+ // Sort elements into null and non null.
+ for (uint32_t i = 0; i < rows_size; ++i) {
+ uint32_t row_idx = rows[i];
+ auto it = std::lower_bound(storage_to_rows.begin(), storage_to_rows.end(),
+ row_idx);
+
+ // This condition holds if the row is null.
+ if (it == storage_to_rows.end() || *it != row_idx) {
+ // We can override the out because we already used this data.
+ rows[cur_null_id++] = row_idx;
+ continue;
+ }
+
+ uint32_t non_null_idx =
+ static_cast<uint32_t>(std::distance(storage_to_rows.begin(), it));
+ non_null_rows[cur_non_null_id++] = non_null_idx;
+ }
+
+ // Sort storage and translate them into `rows` indices.
+ // inner_->StableSort(non_null_rows.data(), count_set_bits);
+ uint32_t set_rows_offset = null_bv_->size() - count_set_bits;
+ for (uint32_t i = 0; i < count_set_bits; ++i) {
+ rows[set_rows_offset + i] = storage_to_rows[non_null_rows[i]];
+ }
+}
+
+} // namespace overlays
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/null_overlay.h b/src/trace_processor/db/null_overlay.h
index 52cf85c..dc38f38 100644
--- a/src/trace_processor/db/null_overlay.h
+++ b/src/trace_processor/db/null_overlay.h
@@ -17,31 +17,26 @@
#ifndef SRC_TRACE_PROCESSOR_DB_NULL_OVERLAY_H_
#define SRC_TRACE_PROCESSOR_DB_NULL_OVERLAY_H_
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/column_overlay.h"
-#include "src/trace_processor/db/storage.h"
+#include "src/trace_processor/db/storage/storage.h"
namespace perfetto {
namespace trace_processor {
-namespace column {
+namespace overlays {
// Overlay responsible for operations related to column nullability.
-class NullOverlay : public ColumnOverlay {
+class NullOverlay {
public:
- explicit NullOverlay(std::unique_ptr<ColumnOverlay>);
- void Filter(FilterOp, SqlValue, RowMap&) override;
- void Sort(std::vector<uint32_t>&) override;
+ explicit NullOverlay(const BitVector* null_bv) : null_bv_(null_bv) {}
+
+ void Filter(FilterOp, SqlValue, RowMap&) const;
+ void StableSort(uint32_t* rows, uint32_t rows_size) const;
private:
- std::unique_ptr<ColumnOverlay> inner_;
-
// Vector of data nullability.
const BitVector* null_bv_;
};
-} // namespace column
+} // namespace overlays
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/db/numeric_storage.cc b/src/trace_processor/db/numeric_storage.cc
deleted file mode 100644
index a1984a8..0000000
--- a/src/trace_processor/db/numeric_storage.cc
+++ /dev/null
@@ -1,230 +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 <variant>
-
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/numeric_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-namespace {
-// As we don't template those functions, we need to use std::visitor to type
-// `start`, hence this wrapping.
-inline uint32_t UpperBoundIndex(NumericValue val,
- const void* start,
- uint32_t num_elements) {
- return std::visit(
- [start, num_elements](auto val_data) {
- using T = decltype(val_data);
- const T* typed_start = static_cast<const T*>(start);
- auto upper =
- std::upper_bound(typed_start, typed_start + num_elements, val_data);
- return static_cast<uint32_t>(std::distance(typed_start, upper));
- },
- val);
-}
-
-// As we don't template those functions, we need to use std::visitor to type
-// `start`, hence this wrapping.
-inline uint32_t LowerBoundIndex(NumericValue val,
- const void* start,
- uint32_t num_elements) {
- return std::visit(
- [start, num_elements](auto val_data) {
- using T = decltype(val_data);
- const T* typed_start = static_cast<const T*>(start);
- auto upper =
- std::lower_bound(typed_start, typed_start + num_elements, val_data);
- return static_cast<uint32_t>(std::distance(typed_start, upper));
- },
- val);
-}
-
-// Templated part of FastPathComparison.
-template <typename T>
-inline void TypedFastPathComparison(std::optional<NumericValue> val,
- FilterOp op,
- const T* start,
- uint32_t num_elements,
- BitVector::Builder& builder) {
- if (!val) {
- builder.Skip(num_elements);
- return;
- }
- std::visit(
- [val, start, num_elements, &builder](auto comparator) {
- T typed_val = std::get<T>(*val);
- for (uint32_t i = 0; i < num_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) {
- bool comp_result = comparator(start[i + k], typed_val);
- word |= static_cast<uint64_t>(comp_result) << k;
- }
- builder.AppendWord(word);
- }
- },
- GetFilterOpVariant<T>(op));
-}
-
-// Templated part of SlowPathComparison.
-template <typename T>
-inline void TypedSlowPathComparison(std::optional<NumericValue> val,
- FilterOp op,
- const T* start,
- uint32_t num_elements,
- BitVector::Builder& builder) {
- if (!val) {
- builder.Skip(num_elements);
- return;
- }
- std::visit(
- [val, start, num_elements, &builder](auto comparator) {
- T typed_val = std::get<T>(*val);
- for (uint32_t i = 0; i < num_elements; ++i) {
- builder.Append(comparator(start[i], typed_val));
- }
- },
- GetFilterOpVariant<T>(op));
-}
-
-} // namespace
-
-void NumericStorage::StableSort(std::vector<uint32_t>& out) const {
- NumericValue val = *GetNumericTypeVariant(type_, SqlValue::Long(0));
- std::visit(
- [this, &out](auto val_data) {
- using T = decltype(val_data);
- const T* typed_start = static_cast<const T*>(data_);
- std::stable_sort(out.begin(), out.end(),
- [typed_start](uint32_t a_idx, uint32_t b_idx) {
- T first_val = typed_start[a_idx];
- T second_val = typed_start[b_idx];
- return first_val < second_val;
- });
- },
- val);
-}
-
-// Responsible for invoking templated version of FastPathComparison.
-void NumericStorage::CompareFast(FilterOp op,
- SqlValue sql_val,
- const void* start,
- uint32_t num_elements,
- BitVector::Builder& builder) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-
- // If the value is invalid we should just ignore those elements.
- if (!val.has_value() || op == FilterOp::kIsNotNull ||
- op == FilterOp::kIsNull || op == FilterOp::kGlob) {
- builder.Skip(num_elements);
- return;
- }
- std::visit(
- [op, start, num_elements, &builder](auto num_val) {
- using T = decltype(num_val);
- auto* typed_start = static_cast<const T*>(start);
- TypedFastPathComparison(num_val, op, typed_start, num_elements,
- builder);
- },
- *val);
-}
-
-// Responsible for invoking templated version of SlowPathComparison.
-void NumericStorage::CompareSlow(FilterOp op,
- SqlValue sql_val,
- const void* start,
- uint32_t num_elements,
- BitVector::Builder& builder) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-
- // If the value is invalid we should just ignore those elements.
- if (!val.has_value() || op == FilterOp::kIsNotNull ||
- op == FilterOp::kIsNull || op == FilterOp::kGlob) {
- builder.Skip(num_elements);
- return;
- }
-
- std::visit(
- [op, start, num_elements, &builder](auto val) {
- using T = decltype(val);
- auto* typed_start = static_cast<const T*>(start);
- TypedSlowPathComparison(val, op, typed_start, num_elements, builder);
- },
- *val);
-}
-
-void NumericStorage::CompareSorted(FilterOp op,
- SqlValue sql_val,
- const void* start,
- uint32_t num_elements,
- RowMap& rm) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (!val.has_value() || op == FilterOp::kIsNotNull ||
- op == FilterOp::kIsNull || op == FilterOp::kGlob) {
- rm.Clear();
- return;
- }
-
- switch (op) {
- case FilterOp::kEq: {
- uint32_t beg = LowerBoundIndex(*val, start, num_elements);
- uint32_t end = UpperBoundIndex(*val, start, num_elements);
- RowMap sec(beg, end);
- rm.Intersect(sec);
- return;
- }
- case FilterOp::kLe: {
- uint32_t end = UpperBoundIndex(*val, start, num_elements);
- RowMap sec(0, end);
- rm.Intersect(sec);
- return;
- }
- case FilterOp::kLt: {
- uint32_t end = LowerBoundIndex(*val, start, num_elements);
- RowMap sec(0, end);
- rm.Intersect(sec);
- return;
- }
- case FilterOp::kGe: {
- uint32_t beg = LowerBoundIndex(*val, start, num_elements);
- RowMap sec(beg, num_elements);
- rm.Intersect(sec);
- return;
- }
- case FilterOp::kGt: {
- uint32_t beg = UpperBoundIndex(*val, start, num_elements);
- RowMap sec(beg, num_elements);
- rm.Intersect(sec);
- return;
- }
- case FilterOp::kNe:
- case FilterOp::kIsNull:
- case FilterOp::kIsNotNull:
- case FilterOp::kGlob:
- rm.Clear();
- }
- return;
-}
-
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/numeric_storage.h b/src/trace_processor/db/numeric_storage.h
deleted file mode 100644
index 7677f82..0000000
--- a/src/trace_processor/db/numeric_storage.h
+++ /dev/null
@@ -1,67 +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_NUMERIC_STORAGE_H_
-#define SRC_TRACE_PROCESSOR_DB_NUMERIC_STORAGE_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/storage.h"
-#include "src/trace_processor/db/storage_variants.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-class NumericStorage : public Storage {
- public:
- NumericStorage(const void* data, ColumnType type)
- : type_(type), data_(data) {}
-
- void StableSort(std::vector<uint32_t>&) const override;
-
- void CompareFast(FilterOp op,
- SqlValue val,
- const void* start,
- uint32_t num_elements,
- BitVector::Builder& builder) const override;
-
- // Inefficiently compares series of |num_elements| of data from |data_start|
- // to comparator value and appends results to BitVector::Builder. Should be
- // avoided if possible, with `FastSeriesComparison` used instead.
- void CompareSlow(FilterOp op,
- SqlValue val,
- const void* start,
- uint32_t num_elements,
- BitVector::Builder& builder) const override;
-
- // Compares sorted (asc) series of |num_elements| of data from |data_start| to
- // comparator value. Should be used where possible.
- void CompareSorted(FilterOp op,
- SqlValue val,
- const void* data_start,
- uint32_t num_elements,
- RowMap&) const override;
-
- private:
- const ColumnType type_;
- const void* data_;
-};
-
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_DB_NUMERIC_STORAGE_H_
diff --git a/src/trace_processor/db/overlays/BUILD.gn b/src/trace_processor/db/overlays/BUILD.gn
new file mode 100644
index 0000000..c35891f
--- /dev/null
+++ b/src/trace_processor/db/overlays/BUILD.gn
@@ -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.
+
+import("../../../../gn/perfetto_tp_tables.gni")
+import("../../../../gn/test.gni")
+
+source_set("overlays") {
+ sources = [
+ "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",
+ ]
+}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ sources = [
+ "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/null_overlay.cc b/src/trace_processor/db/overlays/null_overlay.cc
new file mode 100644
index 0000000..c5f5f66
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.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/overlays/null_overlay.h"
+#include "perfetto/ext/base/flat_hash_map.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({Range(start, end)});
+}
+
+TableBitVector NullOverlay::MapToTableBitVector(StorageBitVector s_bv) const {
+ BitVector res = non_null_->Copy();
+ res.UpdateSetBits(s_bv.bv);
+ 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();
+
+ 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();
+
+ 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
new file mode 100644
index 0000000..93da2c0
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.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_DB_OVERLAYS_NULL_OVERLAY_H_
+#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
+
+#include "src/trace_processor/db/overlays/storage_overlay.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(BitVector* null) : non_null_(std::move(null)) {}
+
+ StorageRange MapToStorageRange(TableRange) const override;
+
+ TableBitVector MapToTableBitVector(StorageBitVector) 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.
+ 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
new file mode 100644
index 0000000..6f80eba
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay_unittest.cc
@@ -0,0 +1,129 @@
+/*
+ * 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({RowMap::Range(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({RowMap::Range(3, 8)});
+
+ ASSERT_EQ(r.range.start, 1u);
+ ASSERT_EQ(r.range.end, 4u);
+}
+
+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)});
+
+ ASSERT_EQ(table_bv.bv.CountSetBits(), 2u);
+ ASSERT_TRUE(table_bv.bv.IsSet(2));
+ ASSERT_TRUE(table_bv.bv.IsSet(6));
+}
+
+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.size(), 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.size(), 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
new file mode 100644
index 0000000..e501ddf
--- /dev/null
+++ b/src/trace_processor/db/overlays/selector_overlay.cc
@@ -0,0 +1,90 @@
+/*
+ * 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"
+
+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)};
+}
+
+TableBitVector SelectorOverlay::MapToTableBitVector(
+ StorageBitVector s_bv) 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.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 {
+ // 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
new file mode 100644
index 0000000..70be983
--- /dev/null
+++ b/src/trace_processor/db/overlays/selector_overlay.h
@@ -0,0 +1,52 @@
+/*
+ * 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"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+// Overlay responsible for selecting specific rows from Storage.
+class SelectorOverlay : public StorageOverlay {
+ public:
+ explicit SelectorOverlay(BitVector* selected) : selected_(selected) {}
+
+ StorageRange MapToStorageRange(TableRange) const override;
+
+ TableBitVector MapToTableBitVector(StorageBitVector) 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:
+ 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
new file mode 100644
index 0000000..6e14597
--- /dev/null
+++ b/src/trace_processor/db/overlays/selector_overlay_unittest.cc
@@ -0,0 +1,82 @@
+/*
+ * 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 "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({RowMap::Range(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({RowMap::Range(1, 3)});
+
+ ASSERT_EQ(r.range.start, 4u);
+ ASSERT_EQ(r.range.end, 7u);
+}
+
+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)});
+
+ 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/storage.cc b/src/trace_processor/db/overlays/storage_overlay.cc
similarity index 82%
copy from src/trace_processor/db/storage.cc
copy to src/trace_processor/db/overlays/storage_overlay.cc
index 4799d04..56897f2 100644
--- a/src/trace_processor/db/storage.cc
+++ b/src/trace_processor/db/overlays/storage_overlay.cc
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "src/trace_processor/db/storage.h"
+#include "src/trace_processor/db/overlays/storage_overlay.h"
namespace perfetto {
namespace trace_processor {
-namespace column {
+namespace overlays {
-Storage::~Storage() = default;
+StorageOverlay::~StorageOverlay() = default;
-} // namespace column
+} // 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
new file mode 100644
index 0000000..58dbf50
--- /dev/null
+++ b/src/trace_processor/db/overlays/storage_overlay.h
@@ -0,0 +1,89 @@
+/*
+ * 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".
+class StorageOverlay {
+ public:
+ // 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.
+
+ 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;
+
+ // Maps a BitVector of indices in storage space to an equivalent range of
+ // indices in the table space.
+ virtual TableBitVector MapToTableBitVector(StorageBitVector) 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
new file mode 100644
index 0000000..4e72a41
--- /dev/null
+++ b/src/trace_processor/db/overlays/types.h
@@ -0,0 +1,77 @@
+/*
+ * 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 "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/containers/row_map.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+// A range of indices in the table space.
+struct TableRange {
+ RowMap::Range range;
+};
+
+// A range of indices in the storage space.
+struct StorageRange {
+ RowMap::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;
+};
+
+// Represents a vector of indices in the table space.
+struct TableIndexVector {
+ std::vector<uint32_t> indices;
+};
+
+// Represents a vector of indices in the storage space.
+struct StorageIndexVector {
+ std::vector<uint32_t> indices;
+};
+
+// A subset of FilterOp containing operations which can be handled by
+// overlays.
+enum class OverlayOp {
+ kIsNull,
+ kIsNotNull,
+ 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/sorting_overlay.h b/src/trace_processor/db/sorting_overlay.h
deleted file mode 100644
index 86a5676..0000000
--- a/src/trace_processor/db/sorting_overlay.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_DB_SORTING_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_SORTING_OVERLAY_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/column_overlay.h"
-#include "src/trace_processor/db/storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-// Overlay responsible for operations related to column sorted state.
-class SortingOverlay : public ColumnOverlay {
- public:
- explicit SortingOverlay(ColumnOverlay* ancestor);
- void Filter(FilterOp, SqlValue, RowMap&) override;
- void Sort(std::vector<uint32_t>&) override;
-
- private:
- std::unique_ptr<ColumnOverlay> inner_;
-
- // Index vector of data sorted in ascending order.
- const std::vector<uint32_t>* sorted_state_;
-};
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_SORTING_OVERLAY_H_
diff --git a/src/trace_processor/db/storage.h b/src/trace_processor/db/storage.h
deleted file mode 100644
index dbf037d..0000000
--- a/src/trace_processor/db/storage.h
+++ /dev/null
@@ -1,66 +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_STORAGE_H_
-#define SRC_TRACE_PROCESSOR_DB_STORAGE_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-// Most base column interpreting layer - responsible for implementing operations
-// that require looking at the data, such as comparison or sorting.
-class Storage {
- public:
- virtual ~Storage();
-
- // Changes the vector of indices to represent the sorted state of the column.
- virtual void StableSort(std::vector<uint32_t>&) const = 0;
-
- // Efficiently compares series of |num_elements| of data from |data_start| to
- // comparator value and appends results to BitVector::Builder. Should be used
- // on as much data as possible.
- virtual void CompareFast(FilterOp op,
- SqlValue value,
- const void* start,
- uint32_t compare_elements_count,
- BitVector::Builder&) const = 0;
-
- // Inefficiently compares series of |num_elements| of data from |data_start|
- // to comparator value and appends results to BitVector::Builder. Should be
- // avoided if possible, with `FastSeriesComparison` used instead.
- virtual void CompareSlow(FilterOp op,
- SqlValue value,
- const void* data_start,
- uint32_t compare_elements_count,
- BitVector::Builder&) const = 0;
-
- // Compares sorted (asc) series of |num_elements| of data from |data_start| to
- // comparator value. Should be used where possible.
- virtual void CompareSorted(FilterOp op,
- SqlValue value,
- const void* data_start,
- uint32_t compare_elements_count,
- RowMap&) const = 0;
-};
-
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_H_
diff --git a/src/trace_processor/db/storage/BUILD.gn b/src/trace_processor/db/storage/BUILD.gn
new file mode 100644
index 0000000..81d3b5f
--- /dev/null
+++ b/src/trace_processor/db/storage/BUILD.gn
@@ -0,0 +1,40 @@
+# 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/test.gni")
+
+source_set("storage") {
+ sources = [
+ "numeric_storage.cc",
+ "numeric_storage.h",
+ "storage.cc",
+ "storage.h",
+ "types.h",
+ ]
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../../include/perfetto/trace_processor:basic_types",
+ "../../containers",
+ ]
+}
+
+perfetto_unittest_source_set("unittests") {
+ testonly = true
+ sources = [ "storage_unittest.cc" ]
+ deps = [
+ ":storage",
+ "../../../../gn:default_deps",
+ "../../../../gn:gtest_and_gmock",
+ ]
+}
diff --git a/src/trace_processor/db/storage/numeric_storage.cc b/src/trace_processor/db/storage/numeric_storage.cc
new file mode 100644
index 0000000..eb1dd08
--- /dev/null
+++ b/src/trace_processor/db/storage/numeric_storage.cc
@@ -0,0 +1,380 @@
+
+/*
+ * 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/numeric_storage.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 trace_processor {
+namespace storage {
+namespace {
+
+// All viable numeric values for ColumnTypes.
+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
+// can wrap those classes in variants and use them for std::visit in
+// SerialComparators. This helps prevent excess templating and switches.
+template <typename T>
+using FilterOpVariant = std::variant<std::greater<T>,
+ std::greater_equal<T>,
+ std::less<T>,
+ std::less_equal<T>,
+ 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;
+
+ 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("For GCC");
+}
+
+// Fetch std binary comparator class based on FilterOp. Can be used in
+// std::visit for comparison.
+template <typename T>
+inline FilterOpVariant<T> GetFilterOpVariant(FilterOp op) {
+ switch (op) {
+ case FilterOp::kEq:
+ return FilterOpVariant<T>(std::equal_to<T>());
+ case FilterOp::kNe:
+ return FilterOpVariant<T>(std::not_equal_to<T>());
+ case FilterOp::kGe:
+ return FilterOpVariant<T>(std::greater_equal<T>());
+ case FilterOp::kGt:
+ return FilterOpVariant<T>(std::greater<T>());
+ case FilterOp::kLe:
+ return FilterOpVariant<T>(std::less_equal<T>());
+ case FilterOp::kLt:
+ return FilterOpVariant<T>(std::less<T>());
+ case FilterOp::kGlob:
+ case FilterOp::kIsNotNull:
+ case FilterOp::kIsNull:
+ PERFETTO_FATAL("Not a valid operation on numeric type.");
+ }
+ PERFETTO_FATAL("For GCC");
+}
+
+uint32_t LowerBoundIntrinsic(const void* data,
+ NumericValue val,
+ RowMap::Range search_range) {
+ return std::visit(
+ [data, search_range](auto val_data) {
+ using T = decltype(val_data);
+ const T* typed_start = static_cast<const T*>(data);
+ auto lower = std::lower_bound(typed_start + search_range.start,
+ typed_start + search_range.end, val_data);
+ return static_cast<uint32_t>(std::distance(typed_start, lower));
+ },
+ val);
+}
+
+uint32_t UpperBoundIntrinsic(const void* data,
+ NumericValue val,
+ RowMap::Range search_range) {
+ return std::visit(
+ [data, search_range](auto val_data) {
+ using T = decltype(val_data);
+ const T* typed_start = static_cast<const T*>(data);
+ auto upper = std::upper_bound(typed_start + search_range.start,
+ typed_start + search_range.end, val_data);
+ return static_cast<uint32_t>(std::distance(typed_start, upper));
+ },
+ val);
+}
+
+uint32_t LowerBoundExtrinsic(const void* data,
+ NumericValue val,
+ uint32_t* indices,
+ uint32_t indices_count) {
+ return std::visit(
+ [data, indices, indices_count](auto val_data) {
+ using T = decltype(val_data);
+ const T* typed_start = static_cast<const T*>(data);
+ auto lower =
+ std::lower_bound(indices, indices + indices_count, val_data,
+ [typed_start](uint32_t index, T val) {
+ return typed_start[index] < val;
+ });
+ return static_cast<uint32_t>(std::distance(indices, lower));
+ },
+ val);
+}
+
+uint32_t UpperBoundExtrinsic(const void* data,
+ NumericValue val,
+ uint32_t* indices,
+ uint32_t indices_count) {
+ return std::visit(
+ [data, indices, indices_count](auto val_data) {
+ using T = decltype(val_data);
+ const T* typed_start = static_cast<const T*>(data);
+ auto upper =
+ std::upper_bound(indices, indices + indices_count, val_data,
+ [typed_start](T val, uint32_t index) {
+ return val < typed_start[index];
+ });
+ return static_cast<uint32_t>(std::distance(indices, upper));
+ },
+ val);
+}
+
+template <typename T, typename Comparator>
+void TypedLinearSearch(T typed_val,
+ const T* start,
+ Comparator comparator,
+ BitVector::Builder& builder) {
+ // Slow path: we compare <64 elements and append to get us to a word
+ // boundary.
+ const T* ptr = start;
+ uint32_t front_elements = builder.BitsUntilWordBoundaryOrFull();
+ for (uint32_t i = 0; i < front_elements; ++i) {
+ builder.Append(comparator(ptr[i], typed_val));
+ }
+ ptr += front_elements;
+
+ // 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) {
+ bool comp_result = comparator(start[i + k], typed_val);
+ word |= static_cast<uint64_t>(comp_result) << k;
+ }
+ builder.AppendWord(word);
+ }
+ ptr += fast_path_elements;
+
+ // 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) {
+ builder.Append(comparator(ptr[i], typed_val));
+ }
+}
+
+template <typename T, typename Comparator>
+void TypedIndexSearch(T typed_val,
+ const T* start,
+ uint32_t* indices,
+ Comparator comparator,
+ BitVector::Builder& builder) {
+ // Slow path: we compare <64 elements and append to get us to a word
+ // boundary.
+ const T* ptr = start;
+ uint32_t front_elements = builder.BitsUntilWordBoundaryOrFull();
+ for (uint32_t i = 0; i < front_elements; ++i) {
+ builder.Append(comparator(ptr[indices[i]], typed_val));
+ }
+ ptr += front_elements;
+
+ // 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) {
+ bool comp_result = comparator(start[indices[i + k]], typed_val);
+ word |= static_cast<uint64_t>(comp_result) << k;
+ }
+ builder.AppendWord(word);
+ }
+ ptr += fast_path_elements;
+
+ // 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) {
+ builder.Append(comparator(ptr[indices[i]], typed_val));
+ }
+}
+
+} // namespace
+
+BitVector NumericStorage::LinearSearch(FilterOp op,
+ SqlValue sql_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();
+
+ BitVector::Builder builder(range.end);
+ builder.Skip(range.start);
+ std::visit(
+ [this, range, op, &builder](auto val) {
+ using T = decltype(val);
+ auto* start = static_cast<const T*>(data_) + range.start;
+ std::visit(
+ [start, val, &builder](auto comparator) {
+ TypedLinearSearch(val, start, comparator, builder);
+ },
+ GetFilterOpVariant<T>(op));
+ },
+ *val);
+ return std::move(builder).Build();
+}
+
+BitVector NumericStorage::IndexSearch(FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_count) 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();
+
+ BitVector::Builder builder(indices_count);
+ std::visit(
+ [this, indices, op, &builder](auto val) {
+ using T = decltype(val);
+ auto* start = static_cast<const T*>(data_);
+ std::visit(
+ [start, indices, val, &builder](auto comparator) {
+ TypedIndexSearch(val, start, indices, comparator, builder);
+ },
+ GetFilterOpVariant<T>(op));
+ },
+ *val);
+ return std::move(builder).Build();
+}
+
+RowMap::Range NumericStorage::BinarySearchIntrinsic(
+ FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range search_range) 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(LowerBoundIntrinsic(data_, *val, search_range),
+ UpperBoundIntrinsic(data_, *val, search_range));
+ case FilterOp::kLe:
+ return RowMap::Range(0, UpperBoundIntrinsic(data_, *val, search_range));
+ case FilterOp::kLt:
+ return RowMap::Range(0, LowerBoundIntrinsic(data_, *val, search_range));
+ case FilterOp::kGe:
+ return RowMap::Range(LowerBoundIntrinsic(data_, *val, search_range),
+ size_);
+ case FilterOp::kGt:
+ return RowMap::Range(UpperBoundIntrinsic(data_, *val, search_range),
+ size_);
+ case FilterOp::kNe:
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ case FilterOp::kGlob:
+ return RowMap::Range();
+ }
+ return RowMap::Range();
+}
+
+RowMap::Range NumericStorage::BinarySearchExtrinsic(
+ FilterOp op,
+ SqlValue sql_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));
+ case FilterOp::kLe:
+ return RowMap::Range(
+ 0, UpperBoundExtrinsic(data_, *val, indices, indices_count));
+ case FilterOp::kLt:
+ return RowMap::Range(
+ 0, LowerBoundExtrinsic(data_, *val, indices, indices_count));
+ case FilterOp::kGe:
+ return RowMap::Range(
+ LowerBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ case FilterOp::kGt:
+ return RowMap::Range(
+ UpperBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ case FilterOp::kNe:
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ case FilterOp::kGlob:
+ return RowMap::Range();
+ }
+ return RowMap::Range();
+}
+
+void NumericStorage::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);
+ const T* typed_start = static_cast<const T*>(data_);
+ std::stable_sort(rows, rows + rows_size,
+ [typed_start](uint32_t a_idx, uint32_t b_idx) {
+ T first_val = typed_start[a_idx];
+ T second_val = typed_start[b_idx];
+ return first_val < second_val;
+ });
+ },
+ val);
+}
+
+void NumericStorage::Sort(uint32_t*, uint32_t) const {}
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/numeric_storage.h b/src/trace_processor/db/storage/numeric_storage.h
new file mode 100644
index 0000000..ab802de
--- /dev/null
+++ b/src/trace_processor/db/storage/numeric_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_NUMERIC_STORAGE_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_NUMERIC_STORAGE_H_
+
+#include <variant>
+
+#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+
+// Storage for all numeric type data (i.e. doubles, int32, int64, uint32).
+class NumericStorage : public Storage {
+ public:
+ NumericStorage(void* data, uint32_t size, ColumnType type)
+ : type_(type), data_(data), size_(size) {}
+
+ void StableSort(uint32_t* rows, uint32_t rows_size) const override;
+
+ void Sort(uint32_t* rows, uint32_t rows_size) const override;
+
+ BitVector LinearSearch(FilterOp op,
+ SqlValue val,
+ RowMap::Range) const override;
+
+ BitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count) const override;
+
+ RowMap::Range BinarySearchIntrinsic(
+ FilterOp op,
+ SqlValue val,
+ RowMap::Range search_range) const override;
+
+ RowMap::Range BinarySearchExtrinsic(FilterOp op,
+ SqlValue val,
+ uint32_t* indices,
+ uint32_t indices_count) const override;
+
+ uint32_t size() const override { return size_; }
+
+ private:
+ const ColumnType type_ = ColumnType::kDummy;
+ const void* data_ = nullptr;
+ const uint32_t size_ = 0;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_NUMERIC_STORAGE_H_
diff --git a/src/trace_processor/db/storage.cc b/src/trace_processor/db/storage/storage.cc
similarity index 88%
rename from src/trace_processor/db/storage.cc
rename to src/trace_processor/db/storage/storage.cc
index 4799d04..78ed335 100644
--- a/src/trace_processor/db/storage.cc
+++ b/src/trace_processor/db/storage/storage.cc
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#include "src/trace_processor/db/storage.h"
+#include "src/trace_processor/db/storage/storage.h"
namespace perfetto {
namespace trace_processor {
-namespace column {
+namespace storage {
Storage::~Storage() = default;
-} // namespace column
+} // namespace storage
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/db/storage/storage.h b/src/trace_processor/db/storage/storage.h
new file mode 100644
index 0000000..1021723
--- /dev/null
+++ b/src/trace_processor/db/storage/storage.h
@@ -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.
+ */
+#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 trace_processor {
+namespace storage {
+
+// Backing storage for columnar tables.
+class Storage {
+ public:
+ virtual ~Storage();
+
+ // Searches for elements which match |op| and |value| between |range.start|
+ // and |range.end|.
+ //
+ // Returns a BitVector of size |range.end| with the position of the 1s
+ // representing the positions which matched and 0s otherwise. The first
+ // |range.start| number of elements will be zero.
+ virtual BitVector LinearSearch(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const = 0;
+
+ // Searches for elements which match |op| and |value| at the positions given
+ // by |indices| array.
+ //
+ // Returns a BitVector of size |indices_count| with the position of the 1s
+ // representing the positions which matched and 0s otherwise.
+ virtual BitVector IndexSearch(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count) const = 0;
+
+ // Binary searches for elements which match |op| and |value| between
+ // |range.start_index| and |range.end_index|.
+ //
+ // Returns a range, indexing the storage, where all elements in that range
+ // match the constraint.
+ //
+ // Note: the caller *must* know that the elements in this storage are sorted;
+ // it is an error to call this method otherwise.
+ virtual RowMap::Range BinarySearchIntrinsic(FilterOp op,
+ SqlValue value,
+ RowMap::Range range) const = 0;
+
+ // Binary searches for elements which match |op| and |value| only considering
+ // the elements in |indices|.
+ //
+ // Returns a sub-Range of Range[0, indices_count) which indicates the
+ // positions of elements in |indices| which match.
+ //
+ // Note: the caller *must* known that the elements in storage will be sorted
+ // by the elements in |indices|; it is undefined behaviour to call this method
+ // otherwise.
+ virtual RowMap::Range BinarySearchExtrinsic(FilterOp op,
+ SqlValue value,
+ uint32_t* indices,
+ uint32_t indices_count) const = 0;
+
+ // Sorts |rows| in ascending order with the comparator:
+ // data[rows[a]] < data[rows[b]].
+ virtual void Sort(uint32_t* rows, uint32_t rows_size) const = 0;
+
+ // Stable sorts |rows| in ascending order with the comparator:
+ // data[rows[a]] < data[rows[b]].
+ virtual void StableSort(uint32_t* rows, uint32_t rows_size) const = 0;
+
+ // Number of elements in stored data.
+ virtual uint32_t size() const = 0;
+};
+
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_STORAGE_H_
diff --git a/src/trace_processor/db/storage/storage_unittest.cc b/src/trace_processor/db/storage/storage_unittest.cc
new file mode 100644
index 0000000..6d737b9
--- /dev/null
+++ b/src/trace_processor/db/storage/storage_unittest.cc
@@ -0,0 +1,122 @@
+/*
+ * 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 <numeric>
+
+#include "src/trace_processor/db/storage/numeric_storage.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace storage {
+namespace {
+
+using Range = RowMap::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};
+
+ NumericStorage storage(data_vec.data(), 9, ColumnType::kUint32);
+ RowMap rm(0, 9);
+ storage.StableSort(out.data(), 9);
+
+ std::vector<uint32_t> stable_out{0, 3, 6, 1, 4, 7, 2, 5, 8};
+ ASSERT_EQ(out, stable_out);
+}
+
+TEST(NumericStorageUnittest, StableSort) {
+ std::vector<uint32_t> data_vec{0, 1, 2, 0, 1, 2, 0, 1, 2};
+ std::vector<uint32_t> out = {1, 7, 4, 0, 6, 3, 2, 5, 8};
+
+ NumericStorage storage(data_vec.data(), 9, ColumnType::kUint32);
+ RowMap rm(0, 9);
+ storage.StableSort(out.data(), 9);
+
+ std::vector<uint32_t> stable_out{0, 6, 3, 1, 7, 4, 2, 5, 8};
+ ASSERT_EQ(out, stable_out);
+}
+
+TEST(NumericStorageUnittest, CompareFast) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage storage(data_vec.data(), 128, ColumnType::kUint32);
+ BitVector bv =
+ storage.LinearSearch(FilterOp::kGe, SqlValue::Long(100), Range(0, 128));
+
+ ASSERT_EQ(bv.CountSetBits(), 28u);
+ ASSERT_EQ(bv.IndexOfNthSet(0), 100u);
+}
+
+TEST(NumericStorageUnittest, CompareSorted) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage storage(data_vec.data(), 128, ColumnType::kUint32);
+ Range range = storage.BinarySearchIntrinsic(
+ FilterOp::kGe, SqlValue::Long(100), Range(0, 128));
+
+ ASSERT_EQ(range.size(), 28u);
+ ASSERT_EQ(range.start, 100u);
+ ASSERT_EQ(range.end, 128u);
+}
+
+TEST(NumericStorageUnittest, CompareSortedIndexesGreaterEqual) {
+ std::vector<uint32_t> data_vec{30, 40, 50, 60, 90, 80, 70, 0, 10, 20};
+ std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
+
+ NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
+
+ std::optional<Range> range = storage.BinarySearchExtrinsic(
+ FilterOp::kGe, SqlValue::Long(60), sorted_order.data(), 10);
+
+ ASSERT_EQ(range->size(), 4u);
+ ASSERT_EQ(range->start, 6u);
+ ASSERT_EQ(range->end, 10u);
+}
+
+TEST(NumericStorageUnittest, CompareSortedIndexesLess) {
+ std::vector<uint32_t> data_vec{30, 40, 50, 60, 90, 80, 70, 0, 10, 20};
+ std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
+
+ NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
+
+ std::optional<Range> range = storage.BinarySearchExtrinsic(
+ FilterOp::kLt, SqlValue::Long(60), sorted_order.data(), 10);
+
+ ASSERT_EQ(range->size(), 6u);
+ ASSERT_EQ(range->start, 0u);
+ ASSERT_EQ(range->end, 6u);
+}
+
+TEST(NumericStorageUnittest, CompareSortedIndexesEqual) {
+ std::vector<uint32_t> data_vec{30, 40, 50, 60, 90, 80, 70, 0, 10, 20};
+ std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
+
+ NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
+
+ std::optional<Range> range = storage.BinarySearchExtrinsic(
+ FilterOp::kEq, SqlValue::Long(60), sorted_order.data(), 10);
+
+ ASSERT_EQ(range->size(), 1u);
+ ASSERT_EQ(range->start, 6u);
+ ASSERT_EQ(range->end, 7u);
+}
+
+} // namespace
+} // namespace storage
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage/types.h b/src/trace_processor/db/storage/types.h
new file mode 100644
index 0000000..12ee297
--- /dev/null
+++ b/src/trace_processor/db/storage/types.h
@@ -0,0 +1,71 @@
+/*
+ * 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_TYPES_H_
+#define SRC_TRACE_PROCESSOR_DB_STORAGE_TYPES_H_
+
+#include "perfetto/trace_processor/basic_types.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Represents the possible filter operations on a column.
+enum class FilterOp {
+ kEq,
+ kNe,
+ kGt,
+ kLt,
+ kGe,
+ kLe,
+ kIsNull,
+ kIsNotNull,
+ kGlob,
+};
+
+// Represents a constraint on a column.
+struct Constraint {
+ uint32_t col_idx;
+ FilterOp op;
+ SqlValue value;
+};
+
+// Represents an order by operation on a column.
+struct Order {
+ uint32_t col_idx;
+ bool desc;
+};
+
+// The enum type of the column.
+// Public only to stop GCC complaining about templates being defined in a
+// non-namespace scope (see ColumnTypeHelper below).
+enum class ColumnType {
+ // Standard primitive types.
+ kInt32,
+ kUint32,
+ kInt64,
+ kDouble,
+ kString,
+
+ // Types generated on the fly.
+ kId,
+
+ // Types which don't have any data backing them.
+ kDummy,
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_TYPES_H_
diff --git a/src/trace_processor/db/storage_overlay.h b/src/trace_processor/db/storage_overlay.h
deleted file mode 100644
index 2690919..0000000
--- a/src/trace_processor/db/storage_overlay.h
+++ /dev/null
@@ -1,44 +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_STORAGE_OVERLAY_H_
-#define SRC_TRACE_PROCESSOR_DB_STORAGE_OVERLAY_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/column_overlay.h"
-#include "src/trace_processor/db/storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-// Overlay responsible for doing operations on storage.
-class StorageOverlay : public ColumnOverlay {
- public:
- explicit StorageOverlay(const Storage*);
- void Filter(FilterOp, SqlValue, RowMap&) override;
- void Sort(std::vector<uint32_t>&) override;
-
- private:
- const Storage* storage_;
-};
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_OVERLAY_H_
diff --git a/src/trace_processor/db/storage_unittest.cc b/src/trace_processor/db/storage_unittest.cc
deleted file mode 100644
index 3b8f7c4..0000000
--- a/src/trace_processor/db/storage_unittest.cc
+++ /dev/null
@@ -1,95 +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.
- */
-
-#include "src/trace_processor/db/numeric_storage.h"
-
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-namespace {
-
-TEST(StorageUnittest, 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};
-
- NumericStorage storage(data_vec.data(), ColumnType::kUint32);
- RowMap rm(0, 9);
- storage.StableSort(out);
-
- std::vector<uint32_t> stable_out{0, 3, 6, 1, 4, 7, 2, 5, 8};
- ASSERT_EQ(out, stable_out);
-}
-
-TEST(StorageUnittest, StableSort) {
- std::vector<uint32_t> data_vec{0, 1, 2, 0, 1, 2, 0, 1, 2};
- std::vector<uint32_t> out = {1, 7, 4, 0, 6, 3, 2, 5, 8};
-
- NumericStorage storage(data_vec.data(), ColumnType::kUint32);
- RowMap rm(0, 9);
- storage.StableSort(out);
-
- std::vector<uint32_t> stable_out{0, 6, 3, 1, 7, 4, 2, 5, 8};
- ASSERT_EQ(out, stable_out);
-}
-
-TEST(StorageUnittest, CompareSlow) {
- std::vector<uint32_t> data_vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- NumericStorage storage(data_vec.data(), ColumnType::kUint32);
- BitVector::Builder builder(10);
- storage.CompareSlow(FilterOp::kGe, SqlValue::Long(5), data_vec.data(), 10,
- builder);
- BitVector bv = std::move(builder).Build();
-
- ASSERT_EQ(bv.CountSetBits(), 5u);
- ASSERT_EQ(bv.IndexOfNthSet(0), 5u);
-}
-
-TEST(StorageUnittest, CompareFast) {
- std::vector<uint32_t> data_vec;
- for (uint32_t i = 0; i < 128; ++i) {
- data_vec.push_back(i);
- }
- NumericStorage storage(data_vec.data(), ColumnType::kUint32);
- BitVector::Builder builder(128);
- storage.CompareFast(FilterOp::kGe, SqlValue::Long(100), data_vec.data(), 128,
- builder);
- BitVector bv = std::move(builder).Build();
-
- ASSERT_EQ(bv.CountSetBits(), 28u);
- ASSERT_EQ(bv.IndexOfNthSet(0), 100u);
-}
-
-TEST(StorageUnittest, CompareSorted) {
- std::vector<uint32_t> data_vec;
- for (uint32_t i = 0; i < 128; ++i) {
- data_vec.push_back(i);
- }
- NumericStorage storage(data_vec.data(), ColumnType::kUint32);
- RowMap rm(0, 128);
- storage.CompareSorted(FilterOp::kGe, SqlValue::Long(100), data_vec.data(),
- 128, rm);
-
- ASSERT_EQ(rm.size(), 28u);
- ASSERT_EQ(rm.Get(0), 100u);
-}
-
-} // namespace
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
diff --git a/src/trace_processor/db/storage_variants.h b/src/trace_processor/db/storage_variants.h
deleted file mode 100644
index bc169b9..0000000
--- a/src/trace_processor/db/storage_variants.h
+++ /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.
- */
-#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_VARIANTS_H_
-#define SRC_TRACE_PROCESSOR_DB_STORAGE_VARIANTS_H_
-
-#include <variant>
-#include "perfetto/ext/base/status_or.h"
-#include "src/trace_processor/db/column.h"
-#include "src/trace_processor/db/storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace column {
-
-// All viable numeric values for ColumnTypes.
-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
-// can wrap those classes in variants and use them for std::visit in
-// SerialComparators. This helps prevent excess templating and switches.
-template <typename T>
-using FilterOpVariant = std::variant<std::greater<T>,
- std::greater_equal<T>,
- std::less<T>,
- std::less_equal<T>,
- 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;
-
- 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("For GCC");
-}
-
-// 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) {
- return GetNumericTypeVariant(type, SqlValue::Long(0));
-}
-
-// Fetch std binary comparator class based on FilterOp. Can be used in
-// std::visit for comparison.
-template <typename T>
-inline FilterOpVariant<T> GetFilterOpVariant(FilterOp op) {
- switch (op) {
- case FilterOp::kEq:
- return FilterOpVariant<T>(std::equal_to<T>());
- case FilterOp::kNe:
- return FilterOpVariant<T>(std::not_equal_to<T>());
- case FilterOp::kGe:
- return FilterOpVariant<T>(std::greater_equal<T>());
- case FilterOp::kGt:
- return FilterOpVariant<T>(std::greater<T>());
- case FilterOp::kLe:
- return FilterOpVariant<T>(std::less_equal<T>());
- case FilterOp::kLt:
- return FilterOpVariant<T>(std::less<T>());
- case FilterOp::kGlob:
- case FilterOp::kIsNotNull:
- case FilterOp::kIsNull:
- PERFETTO_FATAL("Not a valid operation on numeric type.");
- }
- PERFETTO_FATAL("For GCC");
-}
-
-} // namespace column
-} // namespace trace_processor
-} // namespace perfetto
-#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_VARIANTS_H_
diff --git a/src/trace_processor/importers/common/parser_types.h b/src/trace_processor/importers/common/parser_types.h
index 2b5648a..7290626 100644
--- a/src/trace_processor/importers/common/parser_types.h
+++ b/src/trace_processor/importers/common/parser_types.h
@@ -35,10 +35,12 @@
struct alignas(8) InlineSchedWaking {
int32_t pid;
- int32_t target_cpu;
- int32_t prio;
+ uint16_t target_cpu;
+ uint16_t prio;
StringPool::Id comm;
+ uint16_t common_flags;
};
+static_assert(sizeof(InlineSchedWaking) == 16);
struct alignas(8) JsonEvent {
std::string value;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index d0d58ba..6ccf70d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -71,6 +71,7 @@
#include "protos/perfetto/trace/ftrace/signal.pbzero.h"
#include "protos/perfetto/trace/ftrace/skb.pbzero.h"
#include "protos/perfetto/trace/ftrace/sock.pbzero.h"
+#include "protos/perfetto/trace/ftrace/synthetic.pbzero.h"
#include "protos/perfetto/trace/ftrace/systrace.pbzero.h"
#include "protos/perfetto/trace/ftrace/task.pbzero.h"
#include "protos/perfetto/trace/ftrace/tcp.pbzero.h"
@@ -232,6 +233,10 @@
cpu_idle_name_id_(context->storage->InternString("cpuidle")),
suspend_resume_name_id_(
context->storage->InternString("Suspend/Resume Latency")),
+ suspend_resume_minimal_name_id_(
+ context->storage->InternString("Suspend/Resume Minimal")),
+ suspend_resume_minimal_slice_name_id_(
+ context->storage->InternString("Suspended")),
kfree_skb_name_id_(context->storage->InternString("Kfree Skb IP Prot")),
ion_total_id_(context->storage->InternString("mem.ion")),
ion_change_id_(context->storage->InternString("mem.ion_change")),
@@ -870,6 +875,10 @@
ParseSuspendResume(ts, fld_bytes);
break;
}
+ case FtraceEvent::kSuspendResumeMinimalFieldNumber: {
+ ParseSuspendResumeMinimal(ts, fld_bytes);
+ break;
+ }
case FtraceEvent::kDrmVblankEventFieldNumber:
case FtraceEvent::kDrmVblankEventDeliveredFieldNumber:
case FtraceEvent::kDrmSchedJobFieldNumber:
@@ -1060,9 +1069,9 @@
}
using protos::pbzero::FtraceEvent;
SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
- sched_tracker->PushSchedWakingCompact(cpu, ts,
- static_cast<uint32_t>(data.pid),
- data.target_cpu, data.prio, data.comm);
+ sched_tracker->PushSchedWakingCompact(
+ cpu, ts, static_cast<uint32_t>(data.pid), data.target_cpu, data.prio,
+ data.comm, data.common_flags);
return util::OkStatus();
}
@@ -2924,6 +2933,26 @@
ongoing_suspend_resume_actions[current_action] = true;
}
+void FtraceParser::ParseSuspendResumeMinimal(int64_t timestamp,
+ protozero::ConstBytes blob) {
+ protos::pbzero::SuspendResumeMinimalFtraceEvent::Decoder evt(blob.data,
+ blob.size);
+ auto async_track = context_->async_track_set_tracker->InternGlobalTrackSet(
+ suspend_resume_minimal_name_id_);
+
+ if (evt.start()) {
+ TrackId start_id = context_->async_track_set_tracker->Begin(
+ async_track, static_cast<int64_t>(0));
+ context_->slice_tracker->Begin(timestamp, start_id,
+ suspend_resume_minimal_name_id_,
+ suspend_resume_minimal_slice_name_id_);
+ } else {
+ TrackId end_id = context_->async_track_set_tracker->End(
+ async_track, static_cast<int64_t>(0));
+ context_->slice_tracker->End(timestamp, end_id);
+ }
+}
+
void FtraceParser::ParseSchedCpuUtilCfs(int64_t timestamp,
protozero::ConstBytes blob) {
protos::pbzero::SchedCpuUtilCfsFtraceEvent::Decoder evt(blob.data, blob.size);
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index abfb0e3..ccb40f8 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -216,6 +216,7 @@
void ParseWakeSourceActivate(int64_t timestamp, protozero::ConstBytes);
void ParseWakeSourceDeactivate(int64_t timestamp, protozero::ConstBytes);
void ParseSuspendResume(int64_t timestamp, protozero::ConstBytes);
+ void ParseSuspendResumeMinimal(int64_t timestamp, protozero::ConstBytes);
void ParseSchedCpuUtilCfs(int64_t timestap, protozero::ConstBytes);
void ParseFuncgraphEntry(int64_t timestamp,
@@ -288,6 +289,8 @@
const StringId gpu_freq_name_id_;
const StringId cpu_idle_name_id_;
const StringId suspend_resume_name_id_;
+ const StringId suspend_resume_minimal_name_id_;
+ const StringId suspend_resume_minimal_slice_name_id_;
const StringId kfree_skb_name_id_;
const StringId ion_total_id_;
const StringId ion_change_id_;
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 67fccfe..c699ab9 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -247,6 +247,7 @@
auto tcpu_it = compact.waking_target_cpu(&parse_error);
auto prio_it = compact.waking_prio(&parse_error);
auto comm_it = compact.waking_comm_index(&parse_error);
+ auto common_flags_it = compact.waking_common_flags(&parse_error);
for (; timestamp_it && pid_it && tcpu_it && prio_it && comm_it;
++timestamp_it, ++pid_it, ++tcpu_it, ++prio_it, ++comm_it) {
@@ -261,8 +262,13 @@
event.comm = string_table[*comm_it];
event.pid = *pid_it;
- event.target_cpu = *tcpu_it;
- event.prio = *prio_it;
+ event.target_cpu = static_cast<uint16_t>(*tcpu_it);
+ event.prio = static_cast<uint16_t>(*prio_it);
+
+ if (common_flags_it) {
+ event.common_flags = static_cast<uint16_t>(*common_flags_it);
+ common_flags_it++;
+ }
base::StatusOr<int64_t> timestamp =
ResolveTraceTime(context_, clock_id, event_timestamp);
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 68fe3b7..09459c4 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -212,9 +212,10 @@
void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
int64_t ts,
uint32_t wakee_pid,
- int32_t target_cpu,
- int32_t prio,
- StringId comm_id) {
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags) {
// At this stage all events should be globally timestamp ordered.
if (ts < context_->event_tracker->max_timestamp()) {
PERFETTO_ELOG(
@@ -239,10 +240,15 @@
auto curr_utid = pending_sched->last_utid;
if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
+ tables::FtraceEventTable::Row row;
+ row.ts = ts;
+ row.name = sched_waking_id_;
+ row.cpu = cpu;
+ row.utid = curr_utid;
+ row.common_flags = common_flags;
+
// Add an entry to the raw table.
- RawId id = context_->storage->mutable_ftrace_event_table()
- ->Insert({ts, sched_waking_id_, cpu, curr_utid})
- .id;
+ RawId id = context_->storage->mutable_ftrace_event_table()->Insert(row).id;
using SW = protos::pbzero::SchedWakingFtraceEvent;
auto inserter = context_->args_tracker->AddArgsTo(id);
@@ -258,8 +264,8 @@
// Add a waking entry to the ThreadState table.
auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
- ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(ts, wakee_utid,
- curr_utid);
+ ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(
+ ts, wakee_utid, curr_utid, common_flags);
}
PERFETTO_ALWAYS_INLINE
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.h b/src/trace_processor/importers/ftrace/sched_event_tracker.h
index beebcfe..745b7c3 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.h
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.h
@@ -73,9 +73,10 @@
void PushSchedWakingCompact(uint32_t cpu,
int64_t ts,
uint32_t wakee_pid,
- int32_t target_cpu,
- int32_t prio,
- StringId comm_id);
+ uint16_t target_cpu,
+ uint16_t prio,
+ StringId comm_id,
+ uint16_t common_flags);
private:
// Information retained from the preceding sched_switch seen on a given cpu.
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.cc b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
index 3c6b123..a80d6a4 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.cc
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
@@ -50,7 +50,8 @@
void ThreadStateTracker::PushWakingEvent(int64_t event_ts,
UniqueTid utid,
- UniqueTid waker_utid) {
+ UniqueTid waker_utid,
+ std::optional<uint16_t> common_flags) {
// Only open new runnable state if thread already had a sched switch event.
if (!HasPreviousRowNumbersForUtid(utid)) {
return;
@@ -62,12 +63,20 @@
// is running), we just ignore the waking event. See b/186509316 for details
// and an example on when this happens. Only blocked events can be waken up.
if (!IsBlocked(last_row_ref.state())) {
+ // If we receive a waking event while we are not blocked, we ignore this
+ // in the |thread_state| table but we track in the |sched_wakeup| table.
+ // The |thread_state_id| in |sched_wakeup| is the current running/runnable
+ // event.
+ storage_->mutable_spurious_sched_wakeup_table()->Insert(
+ {event_ts, prev_row_numbers_for_thread_[utid]->last_row.row_number(),
+ CommonFlagsToIrqContext(*common_flags), utid, waker_utid});
return;
}
// Close the sleeping state and open runnable state.
ClosePendingState(event_ts, utid, false);
- AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
+ AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid,
+ common_flags);
}
void ThreadStateTracker::PushNewTaskEvent(int64_t event_ts,
@@ -102,8 +111,9 @@
void ThreadStateTracker::AddOpenState(int64_t ts,
UniqueTid utid,
StringId state,
- std::optional<uint32_t> cpu,
- std::optional<UniqueTid> waker_utid) {
+ std::optional<uint16_t> cpu,
+ std::optional<UniqueTid> waker_utid,
+ std::optional<uint16_t> common_flags) {
// Ignore utid 0 because it corresponds to the swapper thread which doesn't
// make sense to insert.
if (utid == 0)
@@ -117,6 +127,10 @@
row.dur = -1;
row.utid = utid;
row.state = state;
+ if (common_flags.has_value()) {
+ row.irq_context = CommonFlagsToIrqContext(*common_flags);
+ }
+
auto row_num = storage_->mutable_thread_state_table()->Insert(row).row_number;
if (utid >= prev_row_numbers_for_thread_.size()) {
@@ -136,6 +150,14 @@
}
}
+uint32_t ThreadStateTracker::CommonFlagsToIrqContext(uint32_t common_flags) {
+ // If common_flags contains TRACE_FLAG_HARDIRQ | TRACE_FLAG_SOFTIRQ, wakeup
+ // was emitted in interrupt context.
+ // See:
+ // https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/include/trace/trace_events.h
+ return common_flags & (0x08 | 0x10) ? 1 : 0;
+}
+
void ThreadStateTracker::ClosePendingState(int64_t end_ts,
UniqueTid utid,
bool data_loss) {
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.h b/src/trace_processor/importers/ftrace/thread_state_tracker.h
index 7faae9c..839ad31 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.h
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.h
@@ -49,7 +49,10 @@
UniqueTid next_utid);
// Will add a runnable state for utid and close the previously blocked one.
- void PushWakingEvent(int64_t event_ts, UniqueTid utid, UniqueTid waker_utid);
+ void PushWakingEvent(int64_t event_ts,
+ UniqueTid utid,
+ UniqueTid waker_utid,
+ std::optional<uint16_t> common_flags = std::nullopt);
// Will add a runnable state for utid. For a new task there are no previous
// states to close.
@@ -64,10 +67,13 @@
void AddOpenState(int64_t ts,
UniqueTid utid,
StringId state,
- std::optional<uint32_t> cpu = std::nullopt,
- std::optional<UniqueTid> waker_utid = std::nullopt);
+ std::optional<uint16_t> cpu = std::nullopt,
+ std::optional<UniqueTid> waker_utid = std::nullopt,
+ std::optional<uint16_t> common_flags = std::nullopt);
void ClosePendingState(int64_t end_ts, UniqueTid utid, bool data_loss);
+ uint32_t CommonFlagsToIrqContext(uint32_t common_flags);
+
bool IsRunning(StringId state);
bool IsBlocked(StringId state);
bool IsRunnable(StringId state);
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.h b/src/trace_processor/importers/proto/android_camera_event_module.h
index 1d55081..e9df6c46 100644
--- a/src/trace_processor/importers/proto/android_camera_event_module.h
+++ b/src/trace_processor/importers/proto/android_camera_event_module.h
@@ -24,6 +24,7 @@
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 94a9cb5..fece007 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -181,7 +181,9 @@
: packet_timestamp;
protozero::HeapBuffered<protos::pbzero::TracePacket> data_packet;
- data_packet->set_timestamp(static_cast<uint64_t>(actual_ts));
+ // Keep the original timestamp to later extract as an arg; the sorter does
+ // not read this.
+ data_packet->set_timestamp(static_cast<uint64_t>(packet_timestamp));
auto* energy = data_packet->set_power_rails()->add_energy_data();
energy->set_energy(data.energy());
@@ -207,7 +209,7 @@
parser_.ParseBatteryCounters(ts, decoder.battery());
return;
case TracePacket::kPowerRailsFieldNumber:
- parser_.ParsePowerRails(ts, decoder.power_rails());
+ parser_.ParsePowerRails(ts, decoder.timestamp(), decoder.power_rails());
return;
case TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber:
parser_.ParseEnergyBreakdown(
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 7521061..b165a2b 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -62,7 +62,8 @@
screen_state_id_(context->storage->InternString("ScreenState")),
device_state_id_(context->storage->InternString("DeviceStateChanged")),
battery_status_id_(context->storage->InternString("BatteryStatus")),
- plug_type_id_(context->storage->InternString("PlugType")) {}
+ plug_type_id_(context->storage->InternString("PlugType")),
+ rail_packet_timestamp_id_(context->storage->InternString("packet_ts")) {}
void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) {
protos::pbzero::BatteryCounters::Decoder evt(blob.data, blob.size);
@@ -118,7 +119,9 @@
}
}
-void AndroidProbesParser::ParsePowerRails(int64_t ts, ConstBytes blob) {
+void AndroidProbesParser::ParsePowerRails(int64_t ts,
+ uint64_t trace_packet_ts,
+ ConstBytes blob) {
protos::pbzero::PowerRails::Decoder evt(blob.data, blob.size);
// Descriptors should have been processed at tokenization time.
@@ -134,12 +137,16 @@
auto opt_track = tracker->GetPowerRailTrack(desc.index());
if (opt_track.has_value()) {
// The tokenization makes sure that this field is always present and
- // is equal to the packet's timestamp (as the packet was forged in
- // the tokenizer).
+ // is equal to the packet's timestamp that was passed to us via the sorter.
PERFETTO_DCHECK(desc.has_timestamp_ms());
PERFETTO_DCHECK(ts / 1000000 == static_cast<int64_t>(desc.timestamp_ms()));
- context_->event_tracker->PushCounter(ts, static_cast<double>(desc.energy()),
- *opt_track);
+ auto maybe_counter_id = context_->event_tracker->PushCounter(
+ ts, static_cast<double>(desc.energy()), *opt_track);
+ if (maybe_counter_id) {
+ context_->args_tracker->AddArgsTo(*maybe_counter_id)
+ .AddArg(rail_packet_timestamp_id_,
+ Variadic::UnsignedInteger(trace_packet_ts));
+ }
} else {
context_->storage->IncrementStats(stats::power_rail_unknown_index);
}
diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h
index 7ea91a1..06b1019 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.h
+++ b/src/trace_processor/importers/proto/android_probes_parser.h
@@ -34,7 +34,7 @@
explicit AndroidProbesParser(TraceProcessorContext*);
void ParseBatteryCounters(int64_t ts, ConstBytes);
- void ParsePowerRails(int64_t ts, ConstBytes);
+ void ParsePowerRails(int64_t ts, uint64_t trace_packet_ts, ConstBytes);
void ParseEnergyBreakdown(int64_t ts, ConstBytes);
void ParseEntityStateResidency(int64_t ts, ConstBytes);
void ParseAndroidLogPacket(ConstBytes);
@@ -56,6 +56,7 @@
const StringId device_state_id_;
const StringId battery_status_id_;
const StringId plug_type_id_;
+ const StringId rail_packet_timestamp_id_;
};
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_parser.h b/src/trace_processor/importers/proto/chrome_system_probes_parser.h
index 8c65fd4..b011fe5 100644
--- a/src/trace_processor/importers/proto/chrome_system_probes_parser.h
+++ b/src/trace_processor/importers/proto/chrome_system_probes_parser.h
@@ -43,7 +43,7 @@
// Maps a proto field number for memcounters in ProcessStats::Process to
// their StringId. Keep kProcStatsProcessSize equal to 1 + max proto field
// id of ProcessStats::Process. Also update SystemProbesParser.
- static constexpr size_t kProcStatsProcessSize = 15;
+ static constexpr size_t kProcStatsProcessSize = 21;
std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
};
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index fa1ffd6..b18bdea 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -357,7 +357,7 @@
// args in arrays.
std::stable_sort(interned.begin(), interned.end(),
[](const Arg& a, const Arg& b) {
- return a.first.raw_id() < b.second.raw_id();
+ return a.first.raw_id() < b.first.raw_id();
});
// Compute the correct key for each arg, possibly adding an index to
@@ -373,20 +373,16 @@
inserter->AddArg(key, Variadic::String(it->second));
} else {
constexpr size_t kMaxIndexSize = 20;
- base::StringView key_str = context_->storage->GetString(key);
+ NullTermStringView key_str = context_->storage->GetString(key);
if (key_str.size() >= sizeof(buffer) - kMaxIndexSize) {
PERFETTO_DLOG("Ignoring arg with unreasonbly large size");
continue;
}
- base::StringWriter writer(buffer, sizeof(buffer));
- writer.AppendString(key_str);
- writer.AppendChar('[');
- writer.AppendUnsignedInt(current_idx);
- writer.AppendChar(']');
-
+ base::StackString<2048> array_key("%s[%u]", key_str.c_str(),
+ current_idx);
StringId new_key =
- context_->storage->InternString(writer.GetStringView());
+ context_->storage->InternString(array_key.string_view());
inserter->AddArg(key, new_key, Variadic::String(it->second));
current_idx = key == next_key ? current_idx + 1 : 0;
diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h
index 430b7a2..653c1eb 100644
--- a/src/trace_processor/importers/proto/statsd_module.h
+++ b/src/trace_processor/importers/proto/statsd_module.h
@@ -26,6 +26,7 @@
#include "src/trace_processor/importers/common/trace_parser.h"
#include "src/trace_processor/importers/proto/proto_importer_module.h"
#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 999d365..da6a2d2 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -153,6 +153,16 @@
context->storage->InternString("mem.rss.watermark");
proc_stats_process_names_[ProcessStats::Process::kOomScoreAdjFieldNumber] =
oom_score_adj_id_;
+ proc_stats_process_names_[ProcessStats::Process::kSmrRssKbFieldNumber] =
+ context->storage->InternString("mem.smaps.rss");
+ proc_stats_process_names_[ProcessStats::Process::kSmrPssKbFieldNumber] =
+ context->storage->InternString("mem.smaps.pss");
+ proc_stats_process_names_[ProcessStats::Process::kSmrPssAnonKbFieldNumber] =
+ context->storage->InternString("mem.smaps.pss.anon");
+ proc_stats_process_names_[ProcessStats::Process::kSmrPssFileKbFieldNumber] =
+ context->storage->InternString("mem.smaps.pss.file");
+ proc_stats_process_names_[ProcessStats::Process::kSmrPssShmemKbFieldNumber] =
+ context->storage->InternString("mem.smaps.pss.shmem");
}
void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) {
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index 77721d5..c1576a5 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -72,7 +72,7 @@
// their StringId. Keep kProcStatsProcessSize equal to 1 + max proto field
// id of ProcessStats::Process. Also update the value in
// ChromeSystemProbesParser.
- static constexpr size_t kProcStatsProcessSize = 15;
+ static constexpr size_t kProcStatsProcessSize = 21;
std::array<StringId, kProcStatsProcessSize> proc_stats_process_names_{};
uint64_t ms_per_tick_ = 0;
diff --git a/src/trace_processor/iterator_impl.cc b/src/trace_processor/iterator_impl.cc
index 5ee1e1d..c21c27e 100644
--- a/src/trace_processor/iterator_impl.cc
+++ b/src/trace_processor/iterator_impl.cc
@@ -18,6 +18,7 @@
#include "perfetto/base/time.h"
#include "perfetto/trace_processor/trace_processor_storage.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/trace_processor_impl.h"
@@ -25,17 +26,12 @@
namespace perfetto {
namespace trace_processor {
-IteratorImpl::IteratorImpl(TraceProcessorImpl* trace_processor,
- sqlite3* db,
- base::Status status,
- ScopedStmt stmt,
- StmtMetadata metadata,
- uint32_t sql_stats_row)
+IteratorImpl::IteratorImpl(
+ TraceProcessorImpl* trace_processor,
+ base::StatusOr<PerfettoSqlEngine::ExecutionResult> result,
+ uint32_t sql_stats_row)
: trace_processor_(trace_processor),
- db_(db),
- status_(std::move(status)),
- stmt_(std::move(stmt)),
- stmt_metadata_(std::move(metadata)),
+ result_(std::move(result)),
sql_stats_row_(sql_stats_row) {}
IteratorImpl::~IteratorImpl() {
diff --git a/src/trace_processor/iterator_impl.h b/src/trace_processor/iterator_impl.h
index 0c8b372..ec587f2 100644
--- a/src/trace_processor/iterator_impl.h
+++ b/src/trace_processor/iterator_impl.h
@@ -28,6 +28,7 @@
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/iterator.h"
#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
@@ -38,17 +39,8 @@
class IteratorImpl {
public:
- struct StmtMetadata {
- uint32_t column_count = 0;
- uint32_t statement_count = 0;
- uint32_t statement_count_with_output = 0;
- };
-
IteratorImpl(TraceProcessorImpl* impl,
- sqlite3* db,
- base::Status,
- ScopedStmt,
- StmtMetadata,
+ base::StatusOr<PerfettoSqlEngine::ExecutionResult>,
uint32_t sql_stats_row);
~IteratorImpl();
@@ -60,8 +52,6 @@
// Methods called by the base Iterator class.
bool Next() {
- PERFETTO_DCHECK(stmt_ || !status_.ok());
-
if (!called_next_) {
// Delegate to the cc file to prevent trace_storage.h include in this
// file.
@@ -78,46 +68,49 @@
// (i.e. implement operator bool, make Next return nothing similar to C++
// iterators); however, too many clients depend on the current behavior so
// we have to keep the API as is.
- return status_.ok() && !sqlite_utils::IsStmtDone(*stmt_);
+ return result_.ok() && !sqlite_utils::IsStmtDone(*result_->stmt);
}
- if (!status_.ok())
+ if (!result_.ok())
return false;
- int ret = sqlite3_step(*stmt_);
+ int ret = sqlite3_step(*result_->stmt);
if (PERFETTO_UNLIKELY(ret != SQLITE_ROW && ret != SQLITE_DONE)) {
- status_ = base::ErrStatus("%s", sqlite_utils::FormatErrorMessage(
- stmt_.get(), std::nullopt, db_, ret)
- .c_message());
- stmt_.reset();
+ result_ =
+ base::ErrStatus("%s", sqlite_utils::FormatErrorMessage(
+ result_->stmt.get(), std::nullopt,
+ sqlite3_db_handle(result_->stmt.get()), ret)
+ .c_message());
return false;
}
return ret == SQLITE_ROW;
}
- SqlValue Get(uint32_t col) {
+ SqlValue Get(uint32_t col) const {
+ PERFETTO_DCHECK(result_.ok());
+
auto column = static_cast<int>(col);
- auto col_type = sqlite3_column_type(*stmt_, column);
+ auto col_type = sqlite3_column_type(*result_->stmt, column);
SqlValue value;
switch (col_type) {
case SQLITE_INTEGER:
value.type = SqlValue::kLong;
- value.long_value = sqlite3_column_int64(*stmt_, column);
+ value.long_value = sqlite3_column_int64(*result_->stmt, column);
break;
case SQLITE_TEXT:
value.type = SqlValue::kString;
- value.string_value =
- reinterpret_cast<const char*>(sqlite3_column_text(*stmt_, column));
+ value.string_value = reinterpret_cast<const char*>(
+ sqlite3_column_text(*result_->stmt, column));
break;
case SQLITE_FLOAT:
value.type = SqlValue::kDouble;
- value.double_value = sqlite3_column_double(*stmt_, column);
+ value.double_value = sqlite3_column_double(*result_->stmt, column);
break;
case SQLITE_BLOB:
value.type = SqlValue::kBytes;
- value.bytes_value = sqlite3_column_blob(*stmt_, column);
+ value.bytes_value = sqlite3_column_blob(*result_->stmt, column);
value.bytes_count =
- static_cast<size_t>(sqlite3_column_bytes(*stmt_, column));
+ static_cast<size_t>(sqlite3_column_bytes(*result_->stmt, column));
break;
case SQLITE_NULL:
value.type = SqlValue::kNull;
@@ -126,18 +119,24 @@
return value;
}
- std::string GetColumnName(uint32_t col) {
- return stmt_ ? sqlite3_column_name(*stmt_, static_cast<int>(col)) : "";
+ std::string GetColumnName(uint32_t col) const {
+ return result_.ok() && result_->stmt
+ ? sqlite3_column_name(*result_->stmt, static_cast<int>(col))
+ : "";
}
- base::Status Status() { return status_; }
+ base::Status Status() const { return result_.status(); }
- uint32_t ColumnCount() { return stmt_metadata_.column_count; }
+ uint32_t ColumnCount() const {
+ return result_.ok() ? result_->column_count : 0;
+ }
- uint32_t StatementCount() { return stmt_metadata_.statement_count; }
+ uint32_t StatementCount() const {
+ return result_.ok() ? result_->statement_count : 0;
+ }
- uint32_t StatementCountWithOutput() {
- return stmt_metadata_.statement_count_with_output;
+ uint32_t StatementCountWithOutput() const {
+ return result_.ok() ? result_->statement_count_with_output : 0;
}
private:
@@ -156,11 +155,7 @@
void RecordFirstNextInSqlStats();
ScopedTraceProcessor trace_processor_;
- sqlite3* db_ = nullptr;
- base::Status status_;
-
- ScopedStmt stmt_;
- StmtMetadata stmt_metadata_;
+ base::StatusOr<PerfettoSqlEngine::ExecutionResult> result_;
uint32_t sql_stats_row_ = 0;
bool called_next_ = false;
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index 16e004c..2691e91 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -27,7 +27,7 @@
#include "perfetto/protozero/message.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/util/descriptors.h"
#include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
index bf54d44..a566661 100644
--- a/src/trace_processor/metrics/sql/android/BUILD.gn
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -82,6 +82,7 @@
"java_heap_histogram.sql",
"java_heap_stats.sql",
"mem_stats_priority_breakdown.sql",
+ "network_activity_template.sql",
"p_state.sql",
"power_drain_in_watts.sql",
"power_profile_data.sql",
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index 7dde099..a5cfd5a 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -14,6 +14,7 @@
-- limitations under the License.
--
SELECT IMPORT('android.battery');
+SELECT IMPORT('android.battery_stats');
DROP VIEW IF EXISTS battery_view;
CREATE VIEW battery_view AS
@@ -49,8 +50,16 @@
)
GROUP BY group_id;
+DROP VIEW IF EXISTS suspend_slice_from_minimal;
+CREATE VIEW suspend_slice_from_minimal AS
+SELECT ts, dur
+FROM track t JOIN slice s ON s.track_id = t.id
+WHERE t.name = 'Suspend/Resume Minimal';
+
DROP TABLE IF EXISTS suspend_slice_;
CREATE TABLE suspend_slice_ AS
+SELECT ts, dur FROM suspend_slice_from_minimal
+UNION ALL
SELECT
ts,
dur
@@ -62,7 +71,10 @@
WHERE
track.name = 'Suspend/Resume Latency'
AND (slice.name = 'syscore_resume(0)' OR slice.name = 'timekeeping_freeze(0)')
- AND dur != -1;
+ AND dur != -1
+ AND NOT EXISTS(SELECT * FROM suspend_slice_from_minimal);
+
+DROP VIEW suspend_slice_from_minimal;
SELECT RUN_METRIC('android/counter_span_view_merged.sql',
'table_name', 'screen_state',
@@ -171,7 +183,26 @@
END AS slice_name,
'Plug type' AS track_name,
'slice' AS track_type
-FROM plug_type_span;
+FROM plug_type_span
+UNION ALL
+SELECT *
+FROM (
+ SELECT ts,
+ dur,
+ value_name AS slice_name,
+ CASE track_name
+ WHEN 'battery_stats.mobile_radio' THEN 'Cellular radio'
+ WHEN 'battery_stats.data_conn' THEN 'Cellular connection'
+ WHEN 'battery_stats.phone_signal_strength' THEN 'Cellular strength'
+ WHEN 'battery_stats.wifi_radio' THEN 'WiFi radio'
+ WHEN 'battery_stats.wifi_suppl' THEN 'Wifi supplicant state'
+ WHEN 'battery_stats.wifi_signal_strength' THEN 'WiFi strength'
+ ELSE NULL
+ END AS track_name,
+ 'slice' AS track_type
+ FROM android_battery_stats_state
+)
+WHERE track_name IS NOT NULL;
DROP VIEW IF EXISTS android_batt_output;
CREATE VIEW android_batt_output AS
diff --git a/src/trace_processor/metrics/sql/android/android_binder.sql b/src/trace_processor/metrics/sql/android/android_binder.sql
index 1d15050..f59f5d9 100644
--- a/src/trace_processor/metrics/sql/android/android_binder.sql
+++ b/src/trace_processor/metrics/sql/android/android_binder.sql
@@ -45,11 +45,13 @@
'client_ts', client_ts,
'client_dur', client_dur,
'client_tid', client_tid,
+ 'client_pid', client_pid,
'server_process', server_process,
'server_thread', server_thread,
'server_ts', server_ts,
'server_dur', server_dur,
'server_tid', server_tid,
+ 'server_pid', server_pid,
'thread_states', (
SELECT RepeatedField(
AndroidBinderMetric_ThreadStateBreakdown(
diff --git a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
index 7c56db2..0724ba7 100644
--- a/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
+++ b/src/trace_processor/metrics/sql/android/android_blocking_calls_cuj_metric.sql
@@ -20,6 +20,7 @@
SELECT RUN_METRIC('android/android_jank_cuj.sql');
SELECT IMPORT('android.slices');
+SELECT IMPORT('android.binder');
-- Jank "J<*>" and latency "L<*>" cujs are put together in android_cujs table.
-- They are computed separately as latency ones are slightly different, don't
@@ -75,6 +76,24 @@
FROM all_cujs;
+DROP TABLE IF EXISTS relevant_binder_calls_with_names;
+CREATE TABLE relevant_binder_calls_with_names AS
+SELECT DISTINCT
+ tx.aidl_name AS name,
+ tx.client_ts AS ts,
+ s.track_id,
+ tx.client_dur AS dur,
+ s.id,
+ tx.client_process as process_name,
+ tx.client_utid as utid,
+ tx.client_upid as upid
+FROM android_sync_binder_metrics_by_txn AS tx
+ JOIN slice AS s ON s.id = tx.binder_txn_id
+ -- Keeps only slices in cuj processes.
+ JOIN android_cujs ON tx.client_upid = android_cujs.upid
+WHERE is_main_thread AND aidl_name IS NOT NULL;
+
+
DROP TABLE IF EXISTS android_blocking_calls_cuj_calls;
CREATE TABLE android_blocking_calls_cuj_calls AS
WITH all_main_thread_relevant_slices AS (
@@ -107,7 +126,20 @@
OR s.name GLOB '*CancellableContinuationImpl*'
OR s.name GLOB 'relayoutWindow*'
OR s.name GLOB 'ImageDecoder#decode*'
+ OR s.name GLOB 'NotificationStackScrollLayout#onMeasure'
+ OR s.name GLOB 'ExpNotRow#*'
)
+ UNION ALL
+ SELECT
+ name,
+ ts,
+ track_id,
+ dur,
+ id,
+ process_name,
+ utid,
+ upid
+ FROM relevant_binder_calls_with_names
),
-- Now we have:
-- (1) a list of slices from the main thread of each process
diff --git a/src/trace_processor/metrics/sql/android/android_monitor_contention.sql b/src/trace_processor/metrics/sql/android/android_monitor_contention.sql
index 7cfaeb8..52e1693 100644
--- a/src/trace_processor/metrics/sql/android/android_monitor_contention.sql
+++ b/src/trace_processor/metrics/sql/android/android_monitor_contention.sql
@@ -35,7 +35,10 @@
'waiter_count', waiter_count,
'blocking_thread_name', blocking_thread_name,
'blocked_thread_name', blocked_thread_name,
+ 'blocked_thread_tid', blocked_thread_tid,
+ 'blocking_thread_tid', blocking_thread_tid,
'process_name', process_name,
+ 'pid', pid,
'is_blocked_thread_main', is_blocked_thread_main,
'is_blocking_thread_main', is_blocking_thread_main,
'binder_reply_ts', binder_reply_ts,
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index ab76b6c..a9823af 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -82,6 +82,19 @@
'
);
+-- Given a launch id and GLOB for a slice name, returns the N longest slice name and duration.
+SELECT CREATE_VIEW_FUNCTION(
+ 'GET_LONG_SLICES_FOR_LAUNCH(startup_id INT, slice_name STRING, top_n INT)',
+ 'slice_name STRING, slice_dur INT',
+ '
+ SELECT slice_name, slice_dur
+ FROM android_thread_slices_for_all_startups s
+ WHERE s.startup_id = $startup_id AND s.slice_name GLOB $slice_name
+ ORDER BY slice_dur DESC
+ LIMIT $top_n
+ '
+);
+
-- Define the view
DROP VIEW IF EXISTS startup_view;
CREATE VIEW startup_view AS
@@ -213,6 +226,10 @@
DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
launches.startup_id,
'OpenDexFilesFromOat*'),
+ 'time_dlopen_thread_main',
+ DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
+ launches.startup_id,
+ 'dlopen:*.so'),
'time_lock_contention_thread_main',
DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
launches.startup_id,
@@ -287,10 +304,7 @@
SELECT RepeatedField(AndroidStartupMetric_VerifyClass(
'name', STR_SPLIT(slice_name, "VerifyClass ", 1),
'dur_ns', slice_dur))
- FROM android_thread_slices_for_all_startups
- WHERE startup_id = launches.startup_id AND slice_name GLOB "VerifyClass *"
- ORDER BY slice_dur DESC
- LIMIT 5
+ FROM GET_LONG_SLICES_FOR_LAUNCH(launches.startup_id, "VerifyClass *", 5)
),
'startup_concurrent_to_launch', (
SELECT RepeatedField(package)
@@ -298,6 +312,11 @@
WHERE l.startup_id != launches.startup_id
AND IS_SPANS_OVERLAPPING(l.ts, l.ts_end, launches.ts, launches.ts_end)
),
+ 'dlopen_file', (
+ SELECT RepeatedField(STR_SPLIT(slice_name, "dlopen: ", 1))
+ FROM android_thread_slices_for_all_startups s
+ WHERE startup_id = launches.startup_id AND slice_name GLOB "dlopen: *.so"
+ ),
'system_state', AndroidStartupMetric_SystemState(
'dex2oat_running',
DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 0,
diff --git a/src/trace_processor/metrics/sql/android/java_heap_stats.sql b/src/trace_processor/metrics/sql/android/java_heap_stats.sql
index 15627b4..32c43f4 100644
--- a/src/trace_processor/metrics/sql/android/java_heap_stats.sql
+++ b/src/trace_processor/metrics/sql/android/java_heap_stats.sql
@@ -65,7 +65,7 @@
SELECT * FROM base_stat_counts JOIN heap_roots_proto USING (upid, graph_sample_ts)
),
-- Find closest value
-closest_anon_swap AS (
+closest_anon_swap_oom AS (
SELECT
upid,
graph_sample_ts,
@@ -84,7 +84,23 @@
-- accept it if close (500ms)
OR (graph_sample_ts < ts AND diff <= 500 * 1e6)
ORDER BY diff LIMIT 1
- ) AS val
+ ) AS anon_swap_val,
+ (
+ SELECT oom_score_val
+ FROM (
+ SELECT
+ ts, dur,
+ oom_score_val,
+ ABS(ts - base_stats.graph_sample_ts) AS diff
+ FROM oom_score_span
+ WHERE upid = base_stats.upid)
+ WHERE
+ (graph_sample_ts >= ts AND graph_sample_ts < ts + dur)
+ -- If the first memory sample for the UPID comes *after* the heap profile
+ -- accept it if close (500ms)
+ OR (graph_sample_ts < ts AND diff <= 500 * 1e6)
+ ORDER BY diff LIMIT 1
+ ) AS oom_score_val
FROM base_stats
),
-- Group by upid
@@ -100,10 +116,11 @@
'reachable_heap_native_size', reachable_native_size,
'reachable_obj_count', reachable_obj_count,
'roots', roots,
- 'anon_rss_and_swap_size', closest_anon_swap.val
+ 'anon_rss_and_swap_size', closest_anon_swap_oom.anon_swap_val,
+ 'oom_score_adj', closest_anon_swap_oom.oom_score_val
)) AS sample_protos
FROM base_stats
- LEFT JOIN closest_anon_swap USING (upid, graph_sample_ts)
+ LEFT JOIN closest_anon_swap_oom USING (upid, graph_sample_ts)
GROUP BY 1
)
SELECT JavaHeapStats(
diff --git a/src/trace_processor/metrics/sql/android/network_activity_template.sql b/src/trace_processor/metrics/sql/android/network_activity_template.sql
new file mode 100644
index 0000000..3e83f87
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/network_activity_template.sql
@@ -0,0 +1,75 @@
+--
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+SELECT IMPORT('android.network_packets');
+
+-- Creates a view of aggregated network activity. It is common among networking
+-- to have the interface active for some time after network use. For example, in
+-- mobile networking, it is common to have the cellular interface active for 10
+-- or more seconds after the last packet was sent or received. This view takes
+-- raw packet timing and aggregates it into something that approximates the
+-- activity of the underlying interface.
+--
+-- @arg view_name The name of the output view.
+-- @arg group_by Expression to group by (set to 'null' for no grouping).
+-- @arg filter Expression on `android_network_packets` to filter by.
+-- @arg idle_ns The amount of time before considering the network idle.
+-- @arg quant_ns Quantization value, to group rows before the heavy
+-- part of the query. This should be smaller than idle_ns.
+--
+-- @column group_by The group_by columns are all present in the output.
+-- @column ts The timestamp indicating the start of the segment.
+-- @column dur The duration of the current segment.
+-- @column packet_count The total number of packets in this segment.
+-- @column packet_length The total number of bytes for packets in this segment.
+DROP VIEW IF EXISTS {{view_name}};
+CREATE VIEW {{view_name}} AS
+WITH quantized AS (
+ SELECT
+ {{group_by}},
+ MIN(ts) AS ts,
+ MAX(ts+dur)-MIN(ts) AS dur,
+ SUM(packet_count) AS packet_count,
+ SUM(packet_length) AS packet_length
+ FROM android_network_packets
+ WHERE {{filter}}
+ GROUP BY CAST(ts / {{quant_ns}} AS INT64), {{group_by}}
+),
+with_last AS (
+ SELECT
+ *,
+ LAG(ts+dur) OVER (
+ PARTITION BY {{group_by}}
+ ORDER BY ts
+ ) AS last_ts
+ FROM quantized
+),
+with_group AS (
+ SELECT
+ *,
+ COUNT(IIF(ts-last_ts>{{idle_ns}}, 1, null)) OVER (
+ PARTITION BY {{group_by}}
+ ORDER BY ts
+ ) AS group_id
+ FROM with_last
+)
+SELECT
+ {{group_by}},
+ MIN(ts) AS ts,
+ MAX(ts+dur)-MIN(ts)+{{idle_ns}} AS dur,
+ SUM(packet_count) AS packet_count,
+ SUM(packet_length) AS packet_length
+FROM with_group
+GROUP BY group_id, {{group_by}}
diff --git a/src/trace_processor/metrics/sql/android/process_metadata.sql b/src/trace_processor/metrics/sql/android/process_metadata.sql
index acaceb7..5ccf2b1 100644
--- a/src/trace_processor/metrics/sql/android/process_metadata.sql
+++ b/src/trace_processor/metrics/sql/android/process_metadata.sql
@@ -18,7 +18,8 @@
DROP VIEW IF EXISTS process_metadata_table;
CREATE VIEW process_metadata_table AS
-SELECT * FROM android_process_metadata;
+SELECT android_process_metadata.*, pid FROM android_process_metadata
+JOIN process USING(upid);
DROP VIEW IF EXISTS uid_package_count;
CREATE VIEW uid_package_count AS
@@ -43,6 +44,7 @@
NULL_IF_EMPTY(AndroidProcessMetadata(
'name', process_name,
'uid', uid,
+ 'pid', pid,
'package', NULL_IF_EMPTY(AndroidProcessMetadata_Package(
'package_name', package_name,
'apk_version_code', version_code,
diff --git a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
index b3939bb..4ef1fc4 100644
--- a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
+++ b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
@@ -38,8 +38,10 @@
CREATE TABLE launch_thread_state_io_wait_dur_sum AS
SELECT startup_id, state, is_main_thread, thread_name, io_wait, SUM(dur) AS dur
FROM launch_threads_by_thread_state l
+JOIN android_startup_processes p USING (startup_id)
WHERE
- is_main_thread
+ -- If it is a main thread, only add it if it is the lauching thread.
+ (is_main_thread AND p.startup_type NOT NULL)
-- Allowlist specific threads which need this. Do not add to this list
-- without careful consideration as every thread added here can cause
-- memory usage to balloon.
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
index 9461d2c..30f6e09 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql
@@ -18,6 +18,59 @@
SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+DROP VIEW IF EXISTS __chrome_scroll_jank_v2_scroll_processing;
+
+CREATE VIEW __chrome_scroll_jank_v2_scroll_processing
+AS
+SELECT COALESCE(SUM(jank.dur), 0) / 1.0e6 AS scroll_processing_ms
+FROM
+ scroll_event_latency_jank AS jank
+LEFT JOIN
+ event_latency_scroll_jank_cause AS cause
+ ON
+ jank.id = cause.slice_id;
+
+DROP VIEW IF EXISTS __chrome_scroll_jank_v2_causes_and_durations;
+
+CREATE VIEW __chrome_scroll_jank_v2_causes_and_durations
+AS
+SELECT
+ COALESCE(SUM(jank.dur), 0) / 1.0e6 AS scroll_jank_processing_ms,
+ COUNT(*) AS num_scroll_janks,
+ RepeatedField(
+ ChromeScrollJankV2_ScrollJankCauseAndDuration(
+ 'cause', cause.cause_of_jank, 'duration_ms', jank.dur / 1.0e6))
+ AS scroll_jank_causes_and_durations
+FROM
+ scroll_event_latency_jank AS jank
+LEFT JOIN
+ event_latency_scroll_jank_cause AS cause
+ ON
+ jank.id = cause.slice_id
+WHERE
+ jank.jank AND cause.cause_of_jank != 'RendererCompositorQueueingDelay';
+
+DROP VIEW IF EXISTS __chrome_scroll_jank_v2;
+
+CREATE VIEW __chrome_scroll_jank_v2
+AS
+SELECT
+ 100.0 * scroll_jank_processing_ms / scroll_processing_ms
+ AS scroll_jank_percentage,
+ *
+FROM
+ (
+ SELECT
+ total_scroll_processing.scroll_processing_ms,
+ causes_and_durations.scroll_jank_processing_ms,
+ causes_and_durations.num_scroll_janks,
+ causes_and_durations.scroll_jank_causes_and_durations
+ FROM
+ __chrome_scroll_jank_v2_scroll_processing
+ AS total_scroll_processing,
+ __chrome_scroll_jank_v2_causes_and_durations AS causes_and_durations
+ );
+
DROP VIEW IF EXISTS chrome_scroll_jank_v2_output;
CREATE VIEW chrome_scroll_jank_v2_output
@@ -29,33 +82,10 @@
'scroll_jank_processing_ms',
scroll_jank_processing_ms,
'scroll_jank_percentage',
- scroll_jank_percentage)
+ scroll_jank_percentage,
+ 'num_scroll_janks',
+ num_scroll_janks,
+ 'scroll_jank_causes_and_durations',
+ scroll_jank_causes_and_durations)
FROM
- (
- SELECT
- 100.0 * scroll_jank_processing_ms / scroll_processing_ms
- AS scroll_jank_percentage,
- *
- FROM
- (
- SELECT
- COALESCE(SUM(jank.dur), 0) / 1.0e6 AS scroll_processing_ms,
- COALESCE(
- SUM(
- CASE
- WHEN
- jank.jank
- AND cause.cause_of_jank != 'RendererCompositorQueueingDelay'
- THEN jank.dur
- ELSE 0
- END),
- 0)
- / 1.0e6 AS scroll_jank_processing_ms
- FROM
- scroll_event_latency_jank AS jank
- LEFT JOIN
- event_latency_scroll_jank_cause AS cause
- ON
- jank.id = cause.slice_id
- )
- );
+ __chrome_scroll_jank_v2;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql
index d2886f1..b5464cf 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql
@@ -427,6 +427,7 @@
WHERE
s1.posted_from IN (
"mojo/public/cpp/system/simple_watcher.cc:Notify",
+ "mojo/public/cpp/system/simple_watcher.cc:ArmOrNotify",
"mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe",
"ipc/ipc_mojo_bootstrap.cc:Accept")
),
diff --git a/src/trace_processor/prelude/functions/BUILD.gn b/src/trace_processor/prelude/functions/BUILD.gn
index f7f2cd7..ddbf904 100644
--- a/src/trace_processor/prelude/functions/BUILD.gn
+++ b/src/trace_processor/prelude/functions/BUILD.gn
@@ -29,10 +29,10 @@
"import.h",
"layout_functions.cc",
"layout_functions.h",
+ "math.cc",
+ "math.h",
"pprof_functions.cc",
"pprof_functions.h",
- "register_function.cc",
- "register_function.h",
"sqlite3_str_split.cc",
"sqlite3_str_split.h",
"stack_functions.cc",
@@ -59,7 +59,7 @@
"../../importers/common",
"../../importers/ftrace:ftrace_descriptors",
"../../prelude/table_functions",
- "../../sqlite:sqlite_minimal",
+ "../../sqlite",
"../../storage",
"../../types",
"../../util",
@@ -67,6 +67,20 @@
"../../util:sql_argument",
"../../util:stdlib",
]
+ public_deps = [ ":interface" ]
+}
+
+source_set("interface") {
+ sources = [
+ "sql_function.cc",
+ "sql_function.h",
+ ]
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../../gn:sqlite",
+ "../../../../include/perfetto/trace_processor:basic_types",
+ "../../../base",
+ ]
}
perfetto_unittest_source_set("unittests") {
@@ -78,6 +92,6 @@
"../../../../gn:gtest_and_gmock",
"../../../../gn:sqlite",
"../../../base",
- "../../sqlite:sqlite_minimal",
+ "../../sqlite",
]
}
diff --git a/src/trace_processor/prelude/functions/clock_functions.h b/src/trace_processor/prelude/functions/clock_functions.h
index 34f23f2..aac6280 100644
--- a/src/trace_processor/prelude/functions/clock_functions.h
+++ b/src/trace_processor/prelude/functions/clock_functions.h
@@ -25,7 +25,7 @@
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/create_function.cc b/src/trace_processor/prelude/functions/create_function.cc
index ee2e733..9ca6ba8 100644
--- a/src/trace_processor/prelude/functions/create_function.cc
+++ b/src/trace_processor/prelude/functions/create_function.cc
@@ -19,7 +19,9 @@
#include "perfetto/base/status.h"
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/tp_metatrace.h"
#include "src/trace_processor/util/status_macros.h"
@@ -31,11 +33,11 @@
struct CreatedFunction : public SqlFunction {
struct Context {
- sqlite3* db;
+ PerfettoSqlEngine* engine;
Prototype prototype;
sql_argument::Type return_type;
std::string sql;
- sqlite3_stmt* stmt;
+ ScopedStmt stmt;
};
static base::Status Run(Context* ctx,
@@ -88,20 +90,21 @@
// Bind all the arguments to the appropriate places in the function.
for (size_t i = 0; i < argc; ++i) {
- RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt, ctx->prototype.function_name,
+ RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt.get(),
+ ctx->prototype.function_name,
ctx->prototype.arguments[i], argv[i]));
}
- int ret = sqlite3_step(ctx->stmt);
- RETURN_IF_ERROR(
- SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+ int ret = sqlite3_step(ctx->stmt.get());
+ RETURN_IF_ERROR(SqliteRetToStatus(ctx->engine->sqlite_engine()->db(),
+ ctx->prototype.function_name, ret));
if (ret == SQLITE_DONE) {
// No return value means we just return don't set |out|.
return base::OkStatus();
}
PERFETTO_DCHECK(ret == SQLITE_ROW);
- size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
+ size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt.get()));
if (col_count != 1) {
return base::ErrStatus(
"%s: SQL definition should only return one column: returned %zu "
@@ -109,7 +112,8 @@
ctx->prototype.function_name.c_str(), col_count);
}
- out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
+ out = sqlite_utils::SqliteValueToSqlValue(
+ sqlite3_column_value(ctx->stmt.get(), 0));
// If we return a bytes type but have a null pointer, SQLite will convert this
// to an SQL null. However, for proto build functions, we actively want to
@@ -123,11 +127,11 @@
}
base::Status CreatedFunction::VerifyPostConditions(Context* ctx) {
- int ret = sqlite3_step(ctx->stmt);
- RETURN_IF_ERROR(
- SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+ int ret = sqlite3_step(ctx->stmt.get());
+ RETURN_IF_ERROR(SqliteRetToStatus(ctx->engine->sqlite_engine()->db(),
+ ctx->prototype.function_name, ret));
if (ret == SQLITE_ROW) {
- auto expanded_sql = sqlite_utils::ExpandedSqlForStmt(ctx->stmt);
+ auto expanded_sql = sqlite_utils::ExpandedSqlForStmt(ctx->stmt.get());
return base::ErrStatus(
"%s: multiple values were returned when executing function body. "
"Executed SQL was %s",
@@ -138,21 +142,13 @@
}
void CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
- sqlite3_reset(ctx->stmt);
- sqlite3_clear_bindings(ctx->stmt);
+ sqlite3_reset(ctx->stmt.get());
+ sqlite3_clear_bindings(ctx->stmt.get());
}
} // namespace
-size_t CreateFunction::NameAndArgc::Hasher::operator()(
- const NameAndArgc& s) const noexcept {
- base::Hasher hash;
- hash.Update(s.name.data(), s.name.size());
- hash.Update(s.argc);
- return static_cast<size_t>(hash.digest());
-}
-
-base::Status CreateFunction::Run(CreateFunction::Context* ctx,
+base::Status CreateFunction::Run(PerfettoSqlEngine* engine,
size_t argc,
sqlite3_value** argv,
SqlValue&,
@@ -216,16 +212,14 @@
}
int created_argc = static_cast<int>(prototype.arguments.size());
- NameAndArgc key{prototype.function_name, created_argc};
- auto it = ctx->state->find(key);
- if (it != ctx->state->end()) {
+ auto* fn_ctx = engine->sqlite_engine()->GetFunctionContext(
+ prototype.function_name, created_argc);
+ if (fn_ctx) {
// If the function already exists, just verify that the prototype, return
// type and SQL matches exactly with what we already had registered. By
// doing this, we can avoid the problem plaguing C++ macros where macro
// ordering determines which one gets run.
- auto* created_ctx = static_cast<CreatedFunction::Context*>(
- it->second.created_functon_context);
-
+ auto* created_ctx = static_cast<CreatedFunction::Context*>(fn_ctx);
if (created_ctx->prototype != prototype) {
return base::ErrStatus(
"CREATE_FUNCTION[prototype=%s]: function prototype changed",
@@ -252,28 +246,28 @@
// Prepare the SQL definition as a statement using SQLite.
ScopedStmt stmt;
sqlite3_stmt* stmt_raw = nullptr;
- int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
- static_cast<int>(sql_defn_str.size()), &stmt_raw,
- nullptr);
+ int ret = sqlite3_prepare_v2(
+ engine->sqlite_engine()->db(), sql_defn_str.data(),
+ static_cast<int>(sql_defn_str.size()), &stmt_raw, nullptr);
if (ret != SQLITE_OK) {
return base::ErrStatus(
"CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
"statement %s",
prototype_str.ToStdString().c_str(),
- sqlite_utils::FormatErrorMessage(
- stmt_raw, base::StringView(sql_defn_str), ctx->db, ret)
+ sqlite_utils::FormatErrorMessage(stmt_raw,
+ base::StringView(sql_defn_str),
+ engine->sqlite_engine()->db(), ret)
.c_message());
}
stmt.reset(stmt_raw);
- std::unique_ptr<CreatedFunction::Context> created(
- new CreatedFunction::Context{ctx->db, std::move(prototype),
+ std::string function_name = prototype.function_name;
+ std::unique_ptr<CreatedFunction::Context> created_fn_ctx(
+ new CreatedFunction::Context{engine, std::move(prototype),
*opt_return_type, std::move(sql_defn_str),
- stmt.get()});
- CreatedFunction::Context* created_ptr = created.get();
- RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
- ctx->db, key.name.c_str(), created_argc, std::move(created)));
- ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
+ std::move(stmt)});
+ RETURN_IF_ERROR(engine->RegisterSqlFunction<CreatedFunction>(
+ function_name.c_str(), created_argc, std::move(created_fn_ctx)));
// CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
return base::OkStatus();
diff --git a/src/trace_processor/prelude/functions/create_function.h b/src/trace_processor/prelude/functions/create_function.h
index 258c4bb..ed8c06c 100644
--- a/src/trace_processor/prelude/functions/create_function.h
+++ b/src/trace_processor/prelude/functions/create_function.h
@@ -20,39 +20,20 @@
#include <sqlite3.h>
#include <unordered_map>
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
namespace perfetto {
namespace trace_processor {
+class PerfettoSqlEngine;
+
// Implementation of CREATE_FUNCTION SQL function.
// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
// usage of this function.
struct CreateFunction : public SqlFunction {
- struct PerFunctionState {
- ScopedStmt stmt;
- // void* to avoid leaking state.
- void* created_functon_context;
- };
- struct NameAndArgc {
- std::string name;
- int argc;
-
- struct Hasher {
- std::size_t operator()(const NameAndArgc& s) const noexcept;
- };
- bool operator==(const NameAndArgc& other) const {
- return name == other.name && argc == other.argc;
- }
- };
- using State = std::unordered_map<NameAndArgc,
- CreateFunction::PerFunctionState,
- NameAndArgc::Hasher>;
-
- struct Context {
- sqlite3* db;
- State* state;
- };
+ using Context = PerfettoSqlEngine;
static constexpr bool kVoidReturn = true;
diff --git a/src/trace_processor/prelude/functions/create_view_function.cc b/src/trace_processor/prelude/functions/create_view_function.cc
index 9b7dab9..952924f 100644
--- a/src/trace_processor/prelude/functions/create_view_function.cc
+++ b/src/trace_processor/prelude/functions/create_view_function.cc
@@ -24,6 +24,7 @@
#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "src/trace_processor/sqlite/sqlite_table.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/tp_metatrace.h"
@@ -34,19 +35,20 @@
namespace {
-class CreatedViewFunction : public SqliteTable {
+class CreatedViewFunction final
+ : public TypedSqliteTable<CreatedViewFunction, void*> {
public:
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
explicit Cursor(CreatedViewFunction* table);
- ~Cursor() override;
+ ~Cursor() final;
base::Status Filter(const QueryConstraints& qc,
sqlite3_value**,
- FilterHistory) override;
- base::Status Next() override;
- bool Eof() override;
- base::Status Column(sqlite3_context* context, int N) override;
+ FilterHistory);
+ base::Status Next();
+ bool Eof();
+ base::Status Column(sqlite3_context* context, int N);
private:
ScopedStmt scoped_stmt_;
@@ -57,19 +59,11 @@
};
CreatedViewFunction(sqlite3*, void*);
- ~CreatedViewFunction() override;
+ ~CreatedViewFunction() final;
- base::Status Init(int argc, const char* const* argv, Schema*) override;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
-
- static void Register(sqlite3* db) {
- RegistrationFlags flags;
- flags.type = RegistrationFlags::kExplicitCreateStateless;
-
- SqliteTable::Register<CreatedViewFunction>(
- db, nullptr, "internal_view_function_impl", flags);
- }
+ base::Status Init(int argc, const char* const* argv, Schema*) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) final;
private:
Schema CreateSchema();
@@ -249,7 +243,7 @@
return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
}
-std::unique_ptr<SqliteTable::Cursor> CreatedViewFunction::CreateCursor() {
+std::unique_ptr<SqliteTable::BaseCursor> CreatedViewFunction::CreateCursor() {
return std::unique_ptr<Cursor>(new Cursor(this));
}
@@ -275,7 +269,7 @@
}
CreatedViewFunction::Cursor::Cursor(CreatedViewFunction* table)
- : SqliteTable::Cursor(table), table_(table) {}
+ : SqliteTable::BaseCursor(table), table_(table) {}
CreatedViewFunction::Cursor::~Cursor() = default;
@@ -488,8 +482,10 @@
return base::OkStatus();
}
-void CreateViewFunction::RegisterTable(sqlite3* db) {
- CreatedViewFunction::Register(db);
+void RegisterCreateViewFunctionModule(SqliteEngine* engine) {
+ engine->RegisterVirtualTableModule<CreatedViewFunction>(
+ "internal_view_function_impl", nullptr,
+ SqliteTable::TableType::kExplicitCreate, false);
}
} // namespace trace_processor
diff --git a/src/trace_processor/prelude/functions/create_view_function.h b/src/trace_processor/prelude/functions/create_view_function.h
index c91817a..509ea5e 100644
--- a/src/trace_processor/prelude/functions/create_view_function.h
+++ b/src/trace_processor/prelude/functions/create_view_function.h
@@ -20,11 +20,13 @@
#include <sqlite3.h>
#include <unordered_map>
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
+class SqliteEngine;
+
// Implementation of CREATE_VIEW_FUNCTION SQL function.
// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
// usage of this function.
@@ -40,10 +42,10 @@
sqlite3_value** argv,
SqlValue& out,
Destructors&);
-
- static void RegisterTable(sqlite3* db);
};
+void RegisterCreateViewFunctionModule(SqliteEngine*);
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/import.h b/src/trace_processor/prelude/functions/import.h
index 9773b5f..da40f5f 100644
--- a/src/trace_processor/prelude/functions/import.h
+++ b/src/trace_processor/prelude/functions/import.h
@@ -23,7 +23,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/trace_processor/trace_processor.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/util/sql_modules.h"
namespace perfetto {
diff --git a/src/trace_processor/prelude/functions/math.cc b/src/trace_processor/prelude/functions/math.cc
new file mode 100644
index 0000000..14fc253
--- /dev/null
+++ b/src/trace_processor/prelude/functions/math.cc
@@ -0,0 +1,86 @@
+// 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/prelude/functions/math.h"
+
+#include <cmath>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto::trace_processor {
+
+namespace {
+
+struct Ln : public SqlFunction {
+ static base::Status Run(Context*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ PERFETTO_CHECK(argc == 1);
+ switch (sqlite3_value_numeric_type(argv[0])) {
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ double value = sqlite3_value_double(argv[0]);
+ if (value > 0.0) {
+ out = SqlValue::Double(std::log(value));
+ }
+ break;
+ }
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ case SqlValue::kBytes:
+ break;
+ }
+
+ return base::OkStatus();
+ }
+};
+
+struct Exp : public SqlFunction {
+ static base::Status Run(Context*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ PERFETTO_CHECK(argc == 1);
+ switch (sqlite3_value_numeric_type(argv[0])) {
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ out = SqlValue::Double(std::exp(sqlite3_value_double(argv[0])));
+ break;
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ case SqlValue::kBytes:
+ break;
+ }
+
+ return base::OkStatus();
+ }
+};
+
+} // namespace
+
+base::Status RegisterMathFunctions(PerfettoSqlEngine& engine) {
+ RETURN_IF_ERROR(engine.RegisterSqlFunction<Ln>("ln", 1, nullptr, true));
+ return engine.RegisterSqlFunction<Exp>("exp", 1, nullptr, true);
+}
+
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/prelude/functions/math.h b/src/trace_processor/prelude/functions/math.h
new file mode 100644
index 0000000..746ab44
--- /dev/null
+++ b/src/trace_processor/prelude/functions/math.h
@@ -0,0 +1,31 @@
+// 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_PRELUDE_FUNCTIONS_MATH_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_MATH_H_
+
+#include "perfetto/base/status.h"
+
+namespace perfetto::trace_processor {
+
+class PerfettoSqlEngine;
+
+// Registers LN and EXP.
+// We do not compile the SQLite library with -DSQLITE_ENABLE_MATH_FUNCTIONS so
+// these functions are not provided by default.
+base::Status RegisterMathFunctions(PerfettoSqlEngine& engine);
+
+} // namespace perfetto::trace_processor
+
+#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_MATH_H_
diff --git a/src/trace_processor/prelude/functions/register_function.h b/src/trace_processor/prelude/functions/register_function.h
deleted file mode 100644
index acca3e6..0000000
--- a/src/trace_processor/prelude/functions/register_function.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 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_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
-
-#include <sqlite3.h>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Prototype for a C++ function which can be registered with SQLite.
-//
-// Usage
-//
-// Define a subclass of this struct as follows:
-// struct YourFunction : public SqlFunction {
-// // Optional if you want a custom context object (i.e. an object
-// // passed in at registration time which will be passed to Run on
-// // every invocation)
-// struct YourContext { /* define context fields here */ };
-//
-// static base::Status Run(/* see parameters below */) {
-// /* function body here */
-// }
-//
-// static base::Status Cleanup(/* see parameters below */) {
-// /* function body here */
-// }
-// }
-//
-// Then, register this function with SQLite using RegisterFunction (see below);
-// you'll likely want to do this in TraceProcessorImpl:
-// RegisterFunction<YourFunction>(/* see arguments below */)
-struct SqlFunction {
- // The type of the context object which will be passed to the function.
- // Can be redefined in any sub-classes to override the context.
- using Context = void;
-
- // Indicates whether this function is "void" (i.e. doesn't actually want
- // to return a value). While the function will still return null in SQL
- // (because SQLite does not actually allow null functions), for accounting
- // purposes, this null will be ignored when verifying whether this statement
- // has any output.
- // Can be redefined in any sub-classes to override it.
- // If this is set to true, subclasses must not modify |out| or |destructors|.
- static constexpr bool kVoidReturn = false;
-
- // Struct which holds destructors for strings/bytes returned from the
- // function. Passed as an argument to |Run| to allow implementations to
- // override the destructors.
- struct Destructors {
- sqlite3_destructor_type string_destructor = sqlite_utils::kSqliteTransient;
- sqlite3_destructor_type bytes_destructor = sqlite_utils::kSqliteTransient;
- };
-
- // The function which will be exectued with the arguments from SQL.
- //
- // Implementations MUST define this function themselves; this function is
- // declared but *not* defined so linker errors will be thrown if not defined.
- //
- // |ctx|: the context object passed at registration time.
- // |argc|: number of arguments.
- // |argv|: arguments to the function.
- // |out|: the return value of the function.
- // |destructors|: destructors for string/bytes return values.
- static base::Status Run(Context* ctx,
- size_t argc,
- sqlite3_value** argv,
- SqlValue& out,
- Destructors& destructors);
-
- // Executed after the result from |Run| is reported to SQLite.
- // Allows implementations to verify post-conditions without needing to worry
- // about overwriting return types.
- //
- // Implementations do not need to define this function; a default no-op
- // implementation will be used in this case.
- static base::Status VerifyPostConditions(Context*);
-
- // Executed after the result from |Run| is reported to SQLite.
- // Allows any pending state to be cleaned up post-copy of results by SQLite:
- // this function will be called even if |Run| or |PostRun| returned errors.
- //
- // Implementations do not need to define this function; a default no-op
- // implementation will be used in this case.
- static void Cleanup(Context*);
-};
-
-// Registers a C++ function to be runnable from SQL.
-// The format of the function is given by the |SqlFunction|; see the
-// documentaion above.
-//
-// |db|: sqlite3 database object
-// |name|: name of the function in SQL
-// |argc|: number of arguments for this function, -1 if variable
-// |ctx|: context object for the function (see SqlFunction::Run above);
-// this object *must* outlive the function so should likely be
-// either static or scoped to the lifetime of TraceProcessor.
-// |determistic|: whether this function has deterministic output given the
-// same set of arguments.
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
- const char* name,
- int argc,
- typename Function::Context* ctx,
- bool deterministic = true);
-
-// Same as above except allows a unique_ptr to be passed for the context; this
-// allows for SQLite to manage the lifetime of this pointer instead of the
-// essentially static requirement of the context pointer above.
-template <typename Function>
-base::Status RegisterSqlFunction(
- sqlite3* db,
- const char* name,
- int argc,
- std::unique_ptr<typename Function::Context> ctx,
- bool deterministic = true);
-
-} // namespace trace_processor
-} // namespace perfetto
-
-// The rest of this file is just implementation details which we need
-// in the header file because it is templated code. We separate it out
-// like this to keep the API people actually care about easy to read.
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace sqlite_internal {
-
-// RAII type to call Function::Cleanup when destroyed.
-template <typename Function>
-struct ScopedCleanup {
- typename Function::Context* ctx;
- ~ScopedCleanup() { Function::Cleanup(ctx); }
-};
-
-template <typename Function>
-void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
- using Context = typename Function::Context;
- Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
-
- ScopedCleanup<Function> scoped_cleanup{ud};
- SqlValue value{};
- SqlFunction::Destructors destructors{};
- base::Status status =
- Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
- if (!status.ok()) {
- sqlite3_result_error(ctx, status.c_message(), -1);
- return;
- }
-
- if (Function::kVoidReturn) {
- if (!value.is_null()) {
- sqlite3_result_error(ctx, "void SQL function returned value", -1);
- return;
- }
-
- // If the function doesn't want to return anything, set the "VOID"
- // pointer type to a non-null value. Note that because of the weird
- // way |sqlite3_value_pointer| works, we need to set some value even
- // if we don't actually read it - just set it to a pointer to an empty
- // string for this reason.
- static char kVoidValue[] = "";
- sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
- } else {
- sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
- destructors.bytes_destructor);
- }
-
- status = Function::VerifyPostConditions(ud);
- if (!status.ok()) {
- sqlite3_result_error(ctx, status.c_message(), -1);
- return;
- }
-}
-} // namespace sqlite_internal
-
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
- const char* name,
- int argc,
- typename Function::Context* ctx,
- bool deterministic) {
- int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
- int ret = sqlite3_create_function_v2(
- db, name, static_cast<int>(argc), flags, ctx,
- sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
- if (ret != SQLITE_OK) {
- return base::ErrStatus("Unable to register function with name %s", name);
- }
- return base::OkStatus();
-}
-
-template <typename Function>
-base::Status RegisterSqlFunction(
- sqlite3* db,
- const char* name,
- int argc,
- std::unique_ptr<typename Function::Context> user_data,
- bool deterministic) {
- int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
- int ret = sqlite3_create_function_v2(
- db, name, static_cast<int>(argc), flags, user_data.release(),
- sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
- [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
- if (ret != SQLITE_OK) {
- return base::ErrStatus("Unable to register function with name %s", name);
- }
- return base::OkStatus();
-}
-
-} // namespace trace_processor
-} // namespace perfetto
-
-#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/register_function.cc b/src/trace_processor/prelude/functions/sql_function.cc
similarity index 80%
rename from src/trace_processor/prelude/functions/register_function.cc
rename to src/trace_processor/prelude/functions/sql_function.cc
index ef41f1d..26bac5e 100644
--- a/src/trace_processor/prelude/functions/register_function.cc
+++ b/src/trace_processor/prelude/functions/sql_function.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,9 +14,7 @@
* limitations under the License.
*/
-#include "src/trace_processor/prelude/functions/register_function.h"
-#include "sqlite3.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/sql_function.h b/src/trace_processor/prelude/functions/sql_function.h
new file mode 100644
index 0000000..ba7fab7
--- /dev/null
+++ b/src/trace_processor/prelude/functions/sql_function.h
@@ -0,0 +1,114 @@
+/*
+ * 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_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <memory>
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Prototype for a C++ function which can be registered with SQLite.
+//
+// Usage
+//
+// Define a subclass of this struct as follows:
+// struct YourFunction : public SqlFunction {
+// // Optional if you want a custom context object (i.e. an object
+// // passed in at registration time which will be passed to Run on
+// // every invocation)
+// struct YourContext { /* define context fields here */ };
+//
+// static base::Status Run(/* see parameters below */) {
+// /* function body here */
+// }
+//
+// static base::Status Cleanup(/* see parameters below */) {
+// /* function body here */
+// }
+// }
+//
+// Then, register this function with SQLite using RegisterFunction (see below);
+// you'll likely want to do this in TraceProcessorImpl:
+// RegisterFunction<YourFunction>(/* see arguments below */)
+struct SqlFunction {
+ // The type of the context object which will be passed to the function.
+ // Can be redefined in any sub-classes to override the context.
+ using Context = void;
+
+ // Indicates whether this function is "void" (i.e. doesn't actually want
+ // to return a value). While the function will still return null in SQL
+ // (because SQLite does not actually allow null functions), for accounting
+ // purposes, this null will be ignored when verifying whether this statement
+ // has any output.
+ // Can be redefined in any sub-classes to override it.
+ // If this is set to true, subclasses must not modify |out| or |destructors|.
+ static constexpr bool kVoidReturn = false;
+
+ // Struct which holds destructors for strings/bytes returned from the
+ // function. Passed as an argument to |Run| to allow implementations to
+ // override the destructors.
+ struct Destructors {
+ // This matches SQLITE_TRANSIENT constant which we cannot use because it
+ // expands to a C-style cast, causing compiler warnings.
+ sqlite3_destructor_type string_destructor =
+ reinterpret_cast<sqlite3_destructor_type>(-1);
+ sqlite3_destructor_type bytes_destructor =
+ reinterpret_cast<sqlite3_destructor_type>(-1);
+ };
+
+ // The function which will be executed with the arguments from SQL.
+ //
+ // Implementations MUST define this function themselves; this function is
+ // declared but *not* defined so linker errors will be thrown if not defined.
+ //
+ // |ctx|: the context object passed at registration time.
+ // |argc|: number of arguments.
+ // |argv|: arguments to the function.
+ // |out|: the return value of the function.
+ // |destructors|: destructors for string/bytes return values.
+ static base::Status Run(Context* ctx,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors& destructors);
+
+ // Executed after the result from |Run| is reported to SQLite.
+ // Allows implementations to verify post-conditions without needing to worry
+ // about overwriting return types.
+ //
+ // Implementations do not need to define this function; a default no-op
+ // implementation will be used in this case.
+ static base::Status VerifyPostConditions(Context*);
+
+ // Executed after the result from |Run| is reported to SQLite.
+ // Allows any pending state to be cleaned up post-copy of results by SQLite:
+ // this function will be called even if |Run| or |PostRun| returned errors.
+ //
+ // Implementations do not need to define this function; a default no-op
+ // implementation will be used in this case.
+ static void Cleanup(Context*);
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQL_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/stack_functions.cc b/src/trace_processor/prelude/functions/stack_functions.cc
index 2f8730b..b73afa0 100644
--- a/src/trace_processor/prelude/functions/stack_functions.cc
+++ b/src/trace_processor/prelude/functions/stack_functions.cc
@@ -31,7 +31,8 @@
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/status.h"
#include "protos/perfetto/trace_processor/stack.pbzero.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
@@ -243,15 +244,16 @@
} // namespace
-base::Status RegisterStackFunctions(sqlite3* db,
+base::Status RegisterStackFunctions(PerfettoSqlEngine* engine,
TraceProcessorContext* context) {
- RETURN_IF_ERROR(RegisterSqlFunction<CatStacksFunction>(
- db, CatStacksFunction::kFunctionName, -1, context->storage.get()));
- RETURN_IF_ERROR(RegisterSqlFunction<StackFromStackProfileFrameFunction>(
- db, StackFromStackProfileFrameFunction::kFunctionName, 1,
- context->storage.get()));
- return RegisterSqlFunction<StackFromStackProfileCallsiteFunction>(
- db, StackFromStackProfileCallsiteFunction::kFunctionName, -1,
+ RETURN_IF_ERROR(engine->RegisterSqlFunction<CatStacksFunction>(
+ CatStacksFunction::kFunctionName, -1, context->storage.get()));
+ RETURN_IF_ERROR(
+ engine->RegisterSqlFunction<StackFromStackProfileFrameFunction>(
+ StackFromStackProfileFrameFunction::kFunctionName, 1,
+ context->storage.get()));
+ return engine->RegisterSqlFunction<StackFromStackProfileCallsiteFunction>(
+ StackFromStackProfileCallsiteFunction::kFunctionName, -1,
context->storage.get());
}
diff --git a/src/trace_processor/prelude/functions/stack_functions.h b/src/trace_processor/prelude/functions/stack_functions.h
index 5acb046..011361d 100644
--- a/src/trace_processor/prelude/functions/stack_functions.h
+++ b/src/trace_processor/prelude/functions/stack_functions.h
@@ -26,6 +26,7 @@
namespace perfetto {
namespace trace_processor {
+class PerfettoSqlEngine;
class TraceProcessorContext;
// Registers the stack manipulation related functions:
@@ -49,7 +50,7 @@
// it generates a fake Frame
//
// See protos/perfetto/trace_processor/stack.proto
-base::Status RegisterStackFunctions(sqlite3* db,
+base::Status RegisterStackFunctions(PerfettoSqlEngine* engine,
TraceProcessorContext* context);
} // namespace trace_processor
diff --git a/src/trace_processor/prelude/functions/to_ftrace.h b/src/trace_processor/prelude/functions/to_ftrace.h
index c32ed79..9c55067 100644
--- a/src/trace_processor/prelude/functions/to_ftrace.h
+++ b/src/trace_processor/prelude/functions/to_ftrace.h
@@ -19,7 +19,7 @@
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/string_writer.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/prelude/functions/utils.h b/src/trace_processor/prelude/functions/utils.h
index 82b293f..ac848d0 100644
--- a/src/trace_processor/prelude/functions/utils.h
+++ b/src/trace_processor/prelude/functions/utils.h
@@ -19,6 +19,7 @@
#include <sqlite3.h>
#include <unordered_map>
+
#include "perfetto/ext/base/base64.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/trace_processor/demangle.h"
@@ -26,10 +27,10 @@
#include "src/trace_processor/export_json.h"
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
-
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/functions/window_functions.h b/src/trace_processor/prelude/functions/window_functions.h
index 3a76fce..a3fbeaf 100644
--- a/src/trace_processor/prelude/functions/window_functions.h
+++ b/src/trace_processor/prelude/functions/window_functions.h
@@ -28,7 +28,7 @@
#include "src/trace_processor/prelude/functions/create_function_internal.h"
#include "src/trace_processor/util/status_macros.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
namespace perfetto {
namespace trace_processor {
diff --git a/src/trace_processor/prelude/operators/BUILD.gn b/src/trace_processor/prelude/operators/BUILD.gn
index 2a3daa6..3d994a7 100644
--- a/src/trace_processor/prelude/operators/BUILD.gn
+++ b/src/trace_processor/prelude/operators/BUILD.gn
@@ -42,5 +42,6 @@
"../../../../gn:default_deps",
"../../../../gn:gtest_and_gmock",
"../../../../gn:sqlite",
+ "../../sqlite",
]
}
diff --git a/src/trace_processor/prelude/operators/span_join_operator.cc b/src/trace_processor/prelude/operators/span_join_operator.cc
index 54e8999..2ee2839 100644
--- a/src/trace_processor/prelude/operators/span_join_operator.cc
+++ b/src/trace_processor/prelude/operators/span_join_operator.cc
@@ -106,20 +106,9 @@
} // namespace
-SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const TraceStorage*)
+SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const void*)
: db_(db) {}
-
-void SpanJoinOperatorTable::RegisterTable(sqlite3* db,
- const TraceStorage* storage) {
- RegistrationFlags flags;
- flags.type = RegistrationFlags::kExplicitCreateStateless;
-
- SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_join", flags);
- SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_left_join",
- flags);
- SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_outer_join",
- flags);
-}
+SpanJoinOperatorTable::~SpanJoinOperatorTable() = default;
util::Status SpanJoinOperatorTable::Init(int argc,
const char* const* argv,
@@ -239,7 +228,7 @@
}
}
-std::unique_ptr<SqliteTable::Cursor> SpanJoinOperatorTable::CreateCursor() {
+std::unique_ptr<SqliteTable::BaseCursor> SpanJoinOperatorTable::CreateCursor() {
return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_));
}
@@ -404,10 +393,11 @@
}
SpanJoinOperatorTable::Cursor::Cursor(SpanJoinOperatorTable* table, sqlite3* db)
- : SqliteTable::Cursor(table),
+ : SqliteTable::BaseCursor(table),
t1_(table, &table->t1_defn_, db),
t2_(table, &table->t2_defn_, db),
table_(table) {}
+SpanJoinOperatorTable::Cursor::~Cursor() = default;
base::Status SpanJoinOperatorTable::Cursor::Filter(const QueryConstraints& qc,
sqlite3_value** argv,
diff --git a/src/trace_processor/prelude/operators/span_join_operator.h b/src/trace_processor/prelude/operators/span_join_operator.h
index e92aac2..3f7a006 100644
--- a/src/trace_processor/prelude/operators/span_join_operator.h
+++ b/src/trace_processor/prelude/operators/span_join_operator.h
@@ -69,7 +69,8 @@
//
// All other columns apart from timestamp (ts), duration (dur) and the join key
// are passed through unchanged.
-class SpanJoinOperatorTable : public SqliteTable {
+class SpanJoinOperatorTable final
+ : public TypedSqliteTable<SpanJoinOperatorTable, const void*> {
public:
// Enum indicating whether the queries on the two inner tables should
// emit shadows.
@@ -316,17 +317,17 @@
};
// Base class for a cursor on the span table.
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
Cursor(SpanJoinOperatorTable*, sqlite3* db);
- ~Cursor() override = default;
+ ~Cursor() final;
base::Status Filter(const QueryConstraints& qc,
sqlite3_value** argv,
- FilterHistory) override;
- base::Status Next() override;
- base::Status Column(sqlite3_context* context, int N) override;
- bool Eof() override;
+ FilterHistory);
+ base::Status Next();
+ base::Status Column(sqlite3_context* context, int N);
+ bool Eof();
private:
Cursor(Cursor&) = delete;
@@ -350,15 +351,14 @@
SpanJoinOperatorTable* table_;
};
- SpanJoinOperatorTable(sqlite3*, const TraceStorage*);
-
- static void RegisterTable(sqlite3* db, const TraceStorage* storage);
+ SpanJoinOperatorTable(sqlite3*, const void*);
+ ~SpanJoinOperatorTable() final;
// Table implementation.
- util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
- int FindFunction(const char* name, FindFunctionFn* fn, void** args) override;
+ util::Status Init(int, const char* const*, SqliteTable::Schema*) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) final;
+ int FindFunction(const char* name, FindFunctionFn* fn, void** args) final;
private:
// Columns of the span operator table.
diff --git a/src/trace_processor/prelude/operators/span_join_operator_unittest.cc b/src/trace_processor/prelude/operators/span_join_operator_unittest.cc
index 737e828..661a7f1 100644
--- a/src/trace_processor/prelude/operators/span_join_operator_unittest.cc
+++ b/src/trace_processor/prelude/operators/span_join_operator_unittest.cc
@@ -16,6 +16,7 @@
#include "src/trace_processor/prelude/operators/span_join_operator.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
@@ -25,19 +26,19 @@
class SpanJoinOperatorTableTest : public ::testing::Test {
public:
SpanJoinOperatorTableTest() {
- sqlite3* db = nullptr;
- PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
- PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
- db_.reset(db);
-
- SpanJoinOperatorTable::RegisterTable(db_.get(), nullptr);
+ engine_.RegisterVirtualTableModule<SpanJoinOperatorTable>(
+ "span_join", nullptr, SqliteTable::TableType::kExplicitCreate, false);
+ engine_.RegisterVirtualTableModule<SpanJoinOperatorTable>(
+ "span_left_join", nullptr, SqliteTable::TableType::kExplicitCreate,
+ false);
}
void PrepareValidStatement(const std::string& sql) {
int size = static_cast<int>(sql.size());
sqlite3_stmt* stmt;
- ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
- SQLITE_OK);
+ ASSERT_EQ(
+ sqlite3_prepare_v2(engine_.db(), sql.c_str(), size, &stmt, nullptr),
+ SQLITE_OK);
stmt_.reset(stmt);
}
@@ -55,7 +56,7 @@
}
protected:
- ScopedDb db_;
+ SqliteEngine engine_;
ScopedStmt stmt_;
};
diff --git a/src/trace_processor/prelude/operators/window_operator.cc b/src/trace_processor/prelude/operators/window_operator.cc
index ddaff51..68f2ff4 100644
--- a/src/trace_processor/prelude/operators/window_operator.cc
+++ b/src/trace_processor/prelude/operators/window_operator.cc
@@ -27,15 +27,7 @@
} // namespace
WindowOperatorTable::WindowOperatorTable(sqlite3*, const TraceStorage*) {}
-
-void WindowOperatorTable::RegisterTable(sqlite3* db,
- const TraceStorage* storage) {
- RegistrationFlags flags;
- flags.writable = true;
- flags.type = RegistrationFlags::kEponymous;
-
- SqliteTable::Register<WindowOperatorTable>(db, storage, "window", flags);
-}
+WindowOperatorTable::~WindowOperatorTable() = default;
base::Status WindowOperatorTable::Init(int,
const char* const*,
@@ -62,8 +54,8 @@
return base::OkStatus();
}
-std::unique_ptr<SqliteTable::Cursor> WindowOperatorTable::CreateCursor() {
- return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
+std::unique_ptr<SqliteTable::BaseCursor> WindowOperatorTable::CreateCursor() {
+ return std::unique_ptr<SqliteTable::BaseCursor>(new Cursor(this));
}
int WindowOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
@@ -105,7 +97,8 @@
}
WindowOperatorTable::Cursor::Cursor(WindowOperatorTable* table)
- : SqliteTable::Cursor(table), table_(table) {}
+ : SqliteTable::BaseCursor(table), table_(table) {}
+WindowOperatorTable::Cursor::~Cursor() = default;
base::Status WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc,
sqlite3_value** argv,
diff --git a/src/trace_processor/prelude/operators/window_operator.h b/src/trace_processor/prelude/operators/window_operator.h
index c29fa4f..5f4af16 100644
--- a/src/trace_processor/prelude/operators/window_operator.h
+++ b/src/trace_processor/prelude/operators/window_operator.h
@@ -28,7 +28,8 @@
class TraceStorage;
-class WindowOperatorTable : public SqliteTable {
+class WindowOperatorTable final
+ : public TypedSqliteTable<WindowOperatorTable, const TraceStorage*> {
public:
enum Column {
kRowId = 0,
@@ -39,17 +40,21 @@
kDuration = 5,
kQuantumTs = 6
};
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
explicit Cursor(WindowOperatorTable*);
+ ~Cursor() final;
+
+ Cursor(Cursor&&) = default;
+ Cursor& operator=(Cursor&&) = default;
// Implementation of SqliteTable::Cursor.
base::Status Filter(const QueryConstraints& qc,
sqlite3_value**,
- FilterHistory) override;
- base::Status Next() override;
- bool Eof() override;
- base::Status Column(sqlite3_context*, int N) override;
+ FilterHistory);
+ base::Status Next();
+ bool Eof();
+ base::Status Column(sqlite3_context*, int N);
private:
// Defines the data to be generated by the table.
@@ -73,16 +78,15 @@
WindowOperatorTable* table_ = nullptr;
};
- static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
WindowOperatorTable(sqlite3*, const TraceStorage*);
+ ~WindowOperatorTable() final;
// Table implementation.
- base::Status Init(int, const char* const*, Schema* schema) override;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
- base::Status ModifyConstraints(QueryConstraints* qc) override;
- base::Status Update(int, sqlite3_value**, sqlite3_int64*) override;
+ base::Status Init(int, const char* const*, Schema* schema) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ int BestIndex(const QueryConstraints&, BestIndexInfo*) final;
+ base::Status ModifyConstraints(QueryConstraints* qc) final;
+ base::Status Update(int, sqlite3_value**, sqlite3_int64*) final;
private:
int64_t quantum_ = 0;
@@ -92,6 +96,7 @@
// uint64s.
int64_t window_dur_ = std::numeric_limits<int64_t>::max();
};
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/prelude/table_functions/BUILD.gn b/src/trace_processor/prelude/table_functions/BUILD.gn
index 1125c22..2a97bca 100644
--- a/src/trace_processor/prelude/table_functions/BUILD.gn
+++ b/src/trace_processor/prelude/table_functions/BUILD.gn
@@ -39,8 +39,6 @@
"experimental_slice_layout.h",
"flamegraph_construction_algorithms.cc",
"flamegraph_construction_algorithms.h",
- "table_function.cc",
- "table_function.h",
"view.cc",
"view.h",
]
@@ -53,12 +51,26 @@
"../../db",
"../../importers/proto:full",
"../../importers/proto:minimal",
- "../../sqlite:sqlite_minimal",
+ "../../sqlite",
"../../storage",
"../../tables",
"../../types",
"../../util",
]
+ public_deps = [ ":interface" ]
+}
+
+source_set("interface") {
+ sources = [
+ "table_function.cc",
+ "table_function.h",
+ ]
+ deps = [
+ "../../../../gn:default_deps",
+ "../../../base",
+ "../../db",
+ "../../sqlite:query_constraints",
+ ]
}
perfetto_tp_tables("tables") {
diff --git a/src/trace_processor/prelude/table_functions/tables.py b/src/trace_processor/prelude/table_functions/tables.py
index 4ffc526..32e4550 100644
--- a/src/trace_processor/prelude/table_functions/tables.py
+++ b/src/trace_processor/prelude/table_functions/tables.py
@@ -28,7 +28,7 @@
from src.trace_processor.tables.metadata_tables import PROCESS_TABLE
from src.trace_processor.tables.profiler_tables import STACK_PROFILE_CALLSITE_TABLE
from src.trace_processor.tables.slice_tables import SLICE_TABLE
-from src.trace_processor.tables.slice_tables import SCHED_SLICE_TABLE
+from src.trace_processor.tables.sched_tables import SCHED_SLICE_TABLE
ANCESTOR_SLICE_TABLE = Table(
python_module=__file__,
diff --git a/src/trace_processor/prelude/tables_views/tables.sql b/src/trace_processor/prelude/tables_views/tables.sql
index 1b7d016..28ea0f5 100644
--- a/src/trace_processor/prelude/tables_views/tables.sql
+++ b/src/trace_processor/prelude/tables_views/tables.sql
@@ -20,4 +20,6 @@
ts BIGINT,
dur BIGINT,
depth BIGINT
-);
\ No newline at end of file
+);
+
+CREATE VIRTUAL TABLE window USING window();
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 25024b1..5ba1283 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -20,49 +20,55 @@
sources = [
"db_sqlite_table.cc",
"db_sqlite_table.h",
+ "perfetto_sql_engine.cc",
+ "perfetto_sql_engine.h",
+ "perfetto_sql_parser.cc",
+ "perfetto_sql_parser.h",
"query_cache.h",
+ "scoped_db.h",
"sql_stats_table.cc",
"sql_stats_table.h",
"sqlite_engine.cc",
"sqlite_engine.h",
+ "sqlite_table.cc",
+ "sqlite_table.h",
+ "sqlite_tokenizer.cc",
+ "sqlite_tokenizer.h",
"sqlite_utils.cc",
"sqlite_utils.h",
+ "sqlite_utils.h",
"stats_table.cc",
"stats_table.h",
]
deps = [
+ ":query_constraints",
"..:metatrace",
"../../../gn:default_deps",
"../../../gn:sqlite",
+ "../../../include/perfetto/trace_processor",
"../../../protos/perfetto/trace/ftrace:zero",
"../../base",
"../containers",
"../db",
"../importers/common",
"../importers/ftrace:ftrace_descriptors",
- "../prelude/table_functions",
+ "../prelude/functions:interface",
+ "../prelude/table_functions:interface",
"../storage",
"../types",
"../util",
"../util:profile_builder",
]
- public_deps = [ ":sqlite_minimal" ]
}
-source_set("sqlite_minimal") {
+source_set("query_constraints") {
sources = [
"query_constraints.cc",
"query_constraints.h",
- "scoped_db.h",
- "sqlite_table.cc",
- "sqlite_table.h",
- "sqlite_utils.h",
]
deps = [
- "..:metatrace",
"../../../gn:default_deps",
"../../../gn:sqlite",
- "../../../include/perfetto/trace_processor",
"../../base",
]
}
@@ -71,12 +77,14 @@
testonly = true
sources = [
"db_sqlite_table_unittest.cc",
+ "perfetto_sql_parser_unittest.cc",
"query_constraints_unittest.cc",
+ "sqlite_tokenizer_unittest.cc",
"sqlite_utils_unittest.cc",
]
deps = [
+ ":query_constraints",
":sqlite",
- ":sqlite_minimal",
"../../../gn:default_deps",
"../../../gn:gtest_and_gmock",
"../../../gn:sqlite",
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 8f2d066..f73c5c1 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -135,32 +135,6 @@
generator_(std::move(context.generator)) {}
DbSqliteTable::~DbSqliteTable() = default;
-void DbSqliteTable::RegisterTable(sqlite3* db,
- QueryCache* cache,
- const Table* table,
- const std::string& name) {
- Context context{cache, TableComputation::kStatic, table, nullptr};
- SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name,
- RegistrationFlags{});
-}
-
-void DbSqliteTable::RegisterTable(sqlite3* db,
- QueryCache* cache,
- std::unique_ptr<TableFunction> generator) {
- // Figure out if the table needs explicit args (in the form of constraints
- // on hidden columns) passed to it in order to make the query valid.
- base::Status status = generator->ValidateConstraints(
- QueryConstraints(std::numeric_limits<uint64_t>::max()));
-
- std::string table_name = generator->TableName();
- Context context{cache, TableComputation::kDynamic, nullptr,
- std::move(generator)};
- RegistrationFlags flags;
- flags.requires_hidden_constraints = !status.ok();
- SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context),
- table_name, flags);
-}
-
base::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
switch (computation_) {
case TableComputation::kStatic:
@@ -395,14 +369,15 @@
return QueryCost{final_cost, current_row_count};
}
-std::unique_ptr<SqliteTable::Cursor> DbSqliteTable::CreateCursor() {
+std::unique_ptr<SqliteTable::BaseCursor> DbSqliteTable::CreateCursor() {
return std::unique_ptr<Cursor>(new Cursor(this, cache_));
}
DbSqliteTable::Cursor::Cursor(DbSqliteTable* sqlite_table, QueryCache* cache)
- : SqliteTable::Cursor(sqlite_table),
+ : SqliteTable::BaseCursor(sqlite_table),
db_sqlite_table_(sqlite_table),
cache_(cache) {}
+DbSqliteTable::Cursor::~Cursor() = default;
void DbSqliteTable::Cursor::TryCacheCreateSortedTable(
const QueryConstraints& qc,
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 337385e..96a63e5 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -27,21 +27,37 @@
namespace perfetto {
namespace trace_processor {
+enum class DbSqliteTableComputation {
+ // Mode when the table is static (i.e. passed in at construction
+ // time).
+ kStatic,
+
+ // Mode when table is dynamically computed at filter time.
+ kDynamic,
+};
+
+struct DbSqliteTableContext {
+ QueryCache* cache;
+ DbSqliteTableComputation computation;
+
+ // Only valid when computation == TableComputation::kStatic.
+ const Table* static_table;
+
+ // Only valid when computation == TableComputation::kDynamic.
+ std::unique_ptr<TableFunction> generator;
+};
+
// Implements the SQLite table interface for db tables.
-class DbSqliteTable : public SqliteTable {
+class DbSqliteTable final
+ : public TypedSqliteTable<DbSqliteTable, DbSqliteTableContext> {
public:
- enum class TableComputation {
- // Mode when the table is static (i.e. passed in at construction
- // time).
- kStatic,
+ using TableComputation = DbSqliteTableComputation;
+ using Context = DbSqliteTableContext;
- // Mode when table is dynamically computed at filter time.
- kDynamic,
- };
-
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
Cursor(DbSqliteTable*, QueryCache*);
+ ~Cursor() final;
Cursor(Cursor&&) noexcept = default;
Cursor& operator=(Cursor&&) = default;
@@ -49,10 +65,10 @@
// Implementation of SqliteTable::Cursor.
base::Status Filter(const QueryConstraints& qc,
sqlite3_value** argv,
- FilterHistory) override;
- base::Status Next() override;
- bool Eof() override;
- base::Status Column(sqlite3_context*, int N) override;
+ FilterHistory);
+ base::Status Next();
+ bool Eof();
+ base::Status Column(sqlite3_context*, int N);
private:
enum class Mode {
@@ -109,36 +125,15 @@
double cost;
uint32_t rows;
};
- struct Context {
- QueryCache* cache;
- TableComputation computation;
-
- // Only valid when computation == TableComputation::kStatic.
- const Table* static_table;
-
- // Only valid when computation == TableComputation::kDynamic.
- std::unique_ptr<TableFunction> generator;
- };
-
- static void RegisterTable(sqlite3* db,
- QueryCache* cache,
- const Table* table,
- const std::string& name);
-
- static void RegisterTable(sqlite3* db,
- QueryCache* cache,
- std::unique_ptr<TableFunction> generator);
DbSqliteTable(sqlite3*, Context context);
- virtual ~DbSqliteTable() override;
+ virtual ~DbSqliteTable() final;
// Table implementation.
- base::Status Init(int,
- const char* const*,
- SqliteTable::Schema*) override final;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- base::Status ModifyConstraints(QueryConstraints*) override final;
- int BestIndex(const QueryConstraints&, BestIndexInfo*) override final;
+ base::Status Init(int, const char* const*, SqliteTable::Schema*) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ base::Status ModifyConstraints(QueryConstraints*) final;
+ int BestIndex(const QueryConstraints&, BestIndexInfo*) final;
// These static functions are useful to allow other callers to make use
// of them.
diff --git a/src/trace_processor/sqlite/perfetto_sql_engine.cc b/src/trace_processor/sqlite/perfetto_sql_engine.cc
new file mode 100644
index 0000000..ea06188
--- /dev/null
+++ b/src/trace_processor/sqlite/perfetto_sql_engine.cc
@@ -0,0 +1,194 @@
+/*
+ * 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/sqlite/perfetto_sql_engine.h"
+
+#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+void IncrementCountForStmt(sqlite3_stmt* stmt,
+ PerfettoSqlEngine::ExecutionResult* res) {
+ res->statement_count++;
+
+ // If the stmt is already done, it clearly didn't have any output.
+ if (sqlite_utils::IsStmtDone(stmt))
+ return;
+
+ if (sqlite3_column_count(stmt) == 1) {
+ sqlite3_value* value = sqlite3_column_value(stmt, 0);
+
+ // If the "VOID" pointer associated to the return value is not null,
+ // that means this is a function which is forced to return a value
+ // (because all functions in SQLite have to) but doesn't actually
+ // wait to (i.e. it wants to be treated like CREATE TABLE or similar).
+ // Because of this, ignore the return value of this function.
+ // See |WrapSqlFunction| for where this is set.
+ if (sqlite3_value_pointer(value, "VOID") != nullptr) {
+ return;
+ }
+
+ // If the statement only has a single column and that column is named
+ // "suppress_query_output", treat it as a statement without output for
+ // accounting purposes. This allows an escape hatch for cases where the
+ // user explicitly wants to ignore functions as having output.
+ if (strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
+ return;
+ }
+ }
+
+ // Otherwise, the statement has output and so increment the count.
+ res->statement_count_with_output++;
+}
+
+} // namespace
+
+PerfettoSqlEngine::PerfettoSqlEngine() : query_cache_(new QueryCache()) {}
+
+void PerfettoSqlEngine::RegisterTable(const Table& table,
+ const std::string& table_name) {
+ DbSqliteTable::Context context{query_cache_.get(),
+ DbSqliteTable::TableComputation::kStatic,
+ &table, nullptr};
+ engine_.RegisterVirtualTableModule<DbSqliteTable>(
+ table_name, std::move(context), SqliteTable::kEponymousOnly, false);
+
+ // Register virtual tables into an internal 'perfetto_tables' table.
+ // This is used for iterating through all the tables during a database
+ // export.
+ char* insert_sql = sqlite3_mprintf(
+ "INSERT INTO perfetto_tables(name) VALUES('%q')", table_name.c_str());
+ char* error = nullptr;
+ sqlite3_exec(engine_.db(), insert_sql, nullptr, nullptr, &error);
+ sqlite3_free(insert_sql);
+ if (error) {
+ PERFETTO_ELOG("Error adding table to perfetto_tables: %s", error);
+ sqlite3_free(error);
+ }
+}
+
+void PerfettoSqlEngine::RegisterTableFunction(
+ std::unique_ptr<TableFunction> fn) {
+ std::string table_name = fn->TableName();
+ DbSqliteTable::Context context{query_cache_.get(),
+ DbSqliteTable::TableComputation::kDynamic,
+ nullptr, std::move(fn)};
+ engine_.RegisterVirtualTableModule<DbSqliteTable>(
+ table_name, std::move(context), SqliteTable::kEponymousOnly, false);
+}
+
+base::StatusOr<PerfettoSqlEngine::ExecutionResult>
+PerfettoSqlEngine::ExecuteUntilLastStatement(const std::string& sql) {
+ ExecutionResult res;
+
+ // A sql string can contain several statements. Some of them might be comment
+ // only, e.g. "SELECT 1; /* comment */; SELECT 2;". Here we process one
+ // statement on each iteration. SQLite's sqlite_prepare_v2 (wrapped by
+ // PrepareStmt) returns on each iteration a pointer to the unprocessed string.
+ //
+ // Unfortunately we cannot call PrepareStmt and tokenize all statements
+ // upfront because sqlite_prepare_v2 also semantically checks the statement
+ // against the schema. In some cases statements might depend on the execution
+ // of previous ones (e.e. CREATE VIEW x; SELECT FROM x; DELETE VIEW x;).
+ //
+ // Also, unfortunately, we need to PrepareStmt to find out if a statement is a
+ // comment or a real statement.
+ //
+ // The logic here is the following:
+ // - We invoke PrepareStmt on each statement.
+ // - If the statement is a comment we simply skip it.
+ // - If the statement is valid, we step once to make sure side effects take
+ // effect.
+ // - If we encounter a valid statement afterwards, we step internally through
+ // all rows of the previous one. This ensures that any further side effects
+ // take hold *before* we step into the next statement.
+ // - Once no further non-comment statements are encountered, we return an
+ // iterator to the last valid statement.
+ for (const char* rem_sql = sql.c_str(); rem_sql && rem_sql[0];) {
+ ScopedStmt cur_stmt;
+ {
+ PERFETTO_TP_TRACE(metatrace::Category::QUERY, "QUERY_PREPARE");
+ const char* tail = nullptr;
+ RETURN_IF_ERROR(
+ sqlite_utils::PrepareStmt(engine_.db(), rem_sql, &cur_stmt, &tail));
+ rem_sql = tail;
+ }
+
+ // The only situation where we'd have an ok status but also no prepared
+ // statement is if the statement of SQL we parsed was a pure comment. In
+ // this case, just continue to the next statement.
+ if (!cur_stmt)
+ continue;
+
+ // Before stepping into |cur_stmt|, we need to finish iterating through
+ // the previous statement so we don't have two clashing statements (e.g.
+ // SELECT * FROM v and DROP VIEW v) partially stepped into.
+ if (res.stmt) {
+ PERFETTO_TP_TRACE(metatrace::Category::QUERY, "STMT_STEP_UNTIL_DONE",
+ [&res](metatrace::Record* record) {
+ auto expanded_sql =
+ sqlite_utils::ExpandedSqlForStmt(res.stmt.get());
+ record->AddArg("SQL", expanded_sql.get());
+ });
+ RETURN_IF_ERROR(sqlite_utils::StepStmtUntilDone(res.stmt.get()));
+ res.stmt.reset();
+ }
+
+ PERFETTO_DLOG("Executing statement: %s", sqlite3_sql(*cur_stmt));
+
+ {
+ PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "STMT_FIRST_STEP",
+ [&cur_stmt](metatrace::Record* record) {
+ auto expanded_sql =
+ sqlite_utils::ExpandedSqlForStmt(*cur_stmt);
+ record->AddArg("SQL", expanded_sql.get());
+ });
+
+ // Now step once into |cur_stmt| so that when we prepare the next statment
+ // we will have executed any dependent bytecode in this one.
+ int err = sqlite3_step(*cur_stmt);
+ if (err != SQLITE_ROW && err != SQLITE_DONE) {
+ return base::ErrStatus(
+ "%s", sqlite_utils::FormatErrorMessage(
+ cur_stmt.get(), base::StringView(sql), engine_.db(), err)
+ .c_message());
+ }
+ }
+
+ // Increment the neecessary counts for the statement.
+ IncrementCountForStmt(cur_stmt.get(), &res);
+
+ // Propogate the current statement to the next iteration.
+ res.stmt = std::move(cur_stmt);
+ }
+
+ // If we didn't manage to prepare a single statment, that means everything
+ // in the SQL was treated as a comment.
+ if (!res.stmt)
+ return base::ErrStatus("No valid SQL to run");
+
+ // Update the output statment and column count.
+ res.column_count =
+ static_cast<uint32_t>(sqlite3_column_count(res.stmt.get()));
+ return std::move(res);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/perfetto_sql_engine.h b/src/trace_processor/sqlite/perfetto_sql_engine.h
new file mode 100644
index 0000000..f06b159
--- /dev/null
+++ b/src/trace_processor/sqlite/perfetto_sql_engine.h
@@ -0,0 +1,184 @@
+/*
+ * 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_SQLITE_PERFETTO_SQL_ENGINE_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_PERFETTO_SQL_ENGINE_H_
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Intermediary class which translates high-level concepts and algorithms used
+// in trace processor into lower-level concepts and functions can be understood
+// by and executed against SQLite.
+class PerfettoSqlEngine {
+ public:
+ struct ExecutionResult {
+ ScopedStmt stmt;
+ uint32_t column_count = 0;
+ uint32_t statement_count = 0;
+ uint32_t statement_count_with_output = 0;
+ };
+
+ PerfettoSqlEngine();
+
+ // Executes all the statements in |sql| until the last one and returns a
+ // |ExecutionResult| object containing a |ScopedStmt| for the final statement
+ // and metadata about all statements executed.
+ //
+ // Returns an error if the execution of any statement failed or if there was
+ // no valid SQL to run.
+ base::StatusOr<ExecutionResult> ExecuteUntilLastStatement(
+ const std::string& sql);
+
+ // Registers a trace processor C++ function to be runnable from SQL.
+ //
+ // The format of the function is given by the |SqlFunction|.
+ //
+ // |name|: name of the function in SQL
+ // |argc|: number of arguments for this function. This can be -1 if
+ // the number of arguments is variable.
+ // |ctx|: context object for the function (see SqlFunction::Run);
+ // this object *must* outlive the function so should likely be
+ // either static or scoped to the lifetime of TraceProcessor.
+ // |determistic|: whether this function has deterministic output given the
+ // same set of arguments.
+ template <typename Function = SqlFunction>
+ base::Status RegisterSqlFunction(const char* name,
+ int argc,
+ typename Function::Context* ctx,
+ bool deterministic = true);
+
+ // Registers a trace processor C++ function to be runnable from SQL.
+ //
+ // This function is the same as the above except allows a unique_ptr to be
+ // passed for the context; this allows for SQLite to manage the lifetime of
+ // this pointer instead of the essentially static requirement of the context
+ // pointer above.
+ template <typename Function>
+ base::Status RegisterSqlFunction(
+ const char* name,
+ int argc,
+ std::unique_ptr<typename Function::Context> ctx,
+ bool deterministic = true);
+
+ // Registers a trace processor C++ table with SQLite with an SQL name of
+ // |name|.
+ void RegisterTable(const Table& table, const std::string& name);
+
+ // Registers a trace processor C++ table function with SQLite.
+ void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
+
+ SqliteEngine* sqlite_engine() { return &engine_; }
+
+ private:
+ std::unique_ptr<QueryCache> query_cache_;
+ SqliteEngine engine_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+// The rest of this file is just implementation details which we need
+// in the header file because it is templated code. We separate it out
+// like this to keep the API people actually care about easy to read.
+
+namespace perfetto {
+namespace trace_processor {
+namespace perfetto_sql_internal {
+
+// RAII type to call Function::Cleanup when destroyed.
+template <typename Function>
+struct ScopedCleanup {
+ typename Function::Context* ctx;
+ ~ScopedCleanup() { Function::Cleanup(ctx); }
+};
+
+template <typename Function>
+void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ using Context = typename Function::Context;
+ Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
+
+ ScopedCleanup<Function> scoped_cleanup{ud};
+ SqlValue value{};
+ SqlFunction::Destructors destructors{};
+ base::Status status =
+ Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
+ if (!status.ok()) {
+ sqlite3_result_error(ctx, status.c_message(), -1);
+ return;
+ }
+
+ if (Function::kVoidReturn) {
+ if (!value.is_null()) {
+ sqlite3_result_error(ctx, "void SQL function returned value", -1);
+ return;
+ }
+
+ // If the function doesn't want to return anything, set the "VOID"
+ // pointer type to a non-null value. Note that because of the weird
+ // way |sqlite3_value_pointer| works, we need to set some value even
+ // if we don't actually read it - just set it to a pointer to an empty
+ // string for this reason.
+ static char kVoidValue[] = "";
+ sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
+ } else {
+ sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
+ destructors.bytes_destructor);
+ }
+
+ status = Function::VerifyPostConditions(ud);
+ if (!status.ok()) {
+ sqlite3_result_error(ctx, status.c_message(), -1);
+ return;
+ }
+}
+
+} // namespace perfetto_sql_internal
+
+template <typename Function>
+base::Status PerfettoSqlEngine::RegisterSqlFunction(
+ const char* name,
+ int argc,
+ typename Function::Context* ctx,
+ bool deterministic) {
+ return engine_.RegisterFunction(
+ name, argc, perfetto_sql_internal::WrapSqlFunction<Function>, ctx,
+ nullptr, deterministic);
+}
+
+template <typename Function>
+base::Status PerfettoSqlEngine::RegisterSqlFunction(
+ const char* name,
+ int argc,
+ std::unique_ptr<typename Function::Context> user_data,
+ bool deterministic) {
+ auto ctx_destructor = [](void* ptr) {
+ delete static_cast<typename Function::Context*>(ptr);
+ };
+ return engine_.RegisterFunction(
+ name, argc, perfetto_sql_internal::WrapSqlFunction<Function>,
+ user_data.release(), ctx_destructor, deterministic);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_SQLITE_PERFETTO_SQL_ENGINE_H_
diff --git a/src/trace_processor/sqlite/perfetto_sql_parser.cc b/src/trace_processor/sqlite/perfetto_sql_parser.cc
new file mode 100644
index 0000000..fb14368
--- /dev/null
+++ b/src/trace_processor/sqlite/perfetto_sql_parser.cc
@@ -0,0 +1,79 @@
+/*
+ * 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/sqlite/perfetto_sql_parser.h"
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/sqlite/sqlite_tokenizer.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using Token = SqliteTokenizer::Token;
+using Statement = PerfettoSqlParser::Statement;
+
+bool TokenIsTerminal(Token t) {
+ return t.token_type == SqliteTokenType::TK_SEMI || t.str.empty();
+}
+
+} // namespace
+
+PerfettoSqlParser::PerfettoSqlParser(const char* sql)
+ : tokenizer_(sql), start_(sql) {}
+
+bool PerfettoSqlParser::Next() {
+ PERFETTO_DCHECK(status_.ok());
+
+ const char* non_space_ptr = nullptr;
+ for (Token token = tokenizer_.Next();; token = tokenizer_.Next()) {
+ // Space should always be completely ignored by any logic below as it will
+ // never change the current state in the state machine.
+ if (token.token_type == SqliteTokenType::TK_SPACE) {
+ continue;
+ }
+
+ if (TokenIsTerminal(token)) {
+ // If we have a non-space character we've seen, just return all the stuff
+ // we've seen between that and the current token.
+ if (non_space_ptr) {
+ uint32_t offset_of_non_space =
+ static_cast<uint32_t>(non_space_ptr - start_);
+ uint32_t chars_since_non_space =
+ static_cast<uint32_t>(tokenizer_.ptr() - non_space_ptr);
+ statement_ = Statement(
+ SqliteSql{std::string_view(non_space_ptr, chars_since_non_space),
+ offset_of_non_space});
+ return true;
+ }
+ // This means we've seen a semi-colon without any non-space content. Just
+ // try and find the next statement as this "statement" is a noop.
+ if (token.token_type == SqliteTokenType::TK_SEMI) {
+ continue;
+ }
+ // This means we've reached the end of the SQL.
+ PERFETTO_DCHECK(token.str.empty());
+ return false;
+ }
+
+ // If we've not seen a space character, keep track of the current position.
+ if (!non_space_ptr) {
+ non_space_ptr = token.str.data();
+ }
+ }
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/perfetto_sql_parser.h b/src/trace_processor/sqlite/perfetto_sql_parser.h
new file mode 100644
index 0000000..85622bb
--- /dev/null
+++ b/src/trace_processor/sqlite/perfetto_sql_parser.h
@@ -0,0 +1,86 @@
+/*
+ * 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_SQLITE_PERFETTO_SQL_PARSER_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_PERFETTO_SQL_PARSER_H_
+
+#include <string_view>
+#include <variant>
+
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/sqlite/sqlite_tokenizer.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Parser for PerfettoSQL statements. This class provides an iterator-style
+// interface for reading all PerfettoSQL statements from a block of SQL.
+//
+// Usage:
+// PerfettoSqlParser parser(my_sql_string.c_str());
+// while (parser.Next()) {
+// auto& stmt = parser.statement();
+// // Handle |stmt| here
+// }
+// RETURN_IF_ERROR(r.status());
+class PerfettoSqlParser {
+ public:
+ // Indicates that the specified SQLite SQL was extracted directly from a
+ // PerfettoSQL statement and should be directly executed with SQLite.
+ struct SqliteSql {
+ std::string_view sql;
+ uint32_t global_pos;
+
+ bool operator==(const SqliteSql& o) const {
+ return sql == o.sql && global_pos == o.global_pos;
+ }
+ };
+ using Statement = std::variant<SqliteSql>;
+
+ // Creates a new SQL parser with the a block of PerfettoSQL statements.
+ // Concretely, the passed string can contain >1 statement.
+ explicit PerfettoSqlParser(const char* sql);
+
+ // Attempts to parse to the next statement in the SQL. Returns true if
+ // a statement was successfully parsed and false if EOF was reached or the
+ // statement was not parsed correctly.
+ //
+ // Note: if this function returns false, callers *must* call |status()|: it
+ // is undefined behaviour to not do so.
+ bool Next();
+
+ // Returns the current statement which was parsed. This function *must not* be
+ // called unless |Next()| returned true.
+ Statement& statement() {
+ PERFETTO_DCHECK(statement_.has_value());
+ return statement_.value();
+ }
+
+ // Returns the error status for the parser. This will be |base::OkStatus()|
+ // until
+ const base::Status& status() const { return status_; }
+
+ private:
+ SqliteTokenizer tokenizer_;
+ const char* start_ = nullptr;
+ base::Status status_;
+ std::optional<Statement> statement_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_SQLITE_PERFETTO_SQL_PARSER_H_
diff --git a/src/trace_processor/sqlite/perfetto_sql_parser_unittest.cc b/src/trace_processor/sqlite/perfetto_sql_parser_unittest.cc
new file mode 100644
index 0000000..014b5b2
--- /dev/null
+++ b/src/trace_processor/sqlite/perfetto_sql_parser_unittest.cc
@@ -0,0 +1,73 @@
+/*
+ * 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/sqlite/perfetto_sql_parser.h"
+
+#include <variant>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/status_or.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using Result = PerfettoSqlParser::Statement;
+using SqliteSql = PerfettoSqlParser::SqliteSql;
+
+class PerfettoSqlParserTest : public ::testing::Test {
+ protected:
+ base::StatusOr<std::vector<PerfettoSqlParser::Statement>> Parse(
+ const char* sql) {
+ PerfettoSqlParser parser(sql);
+ std::vector<PerfettoSqlParser::Statement> results;
+ while (parser.Next()) {
+ results.push_back(std::move(parser.statement()));
+ }
+ if (!parser.status().ok()) {
+ return parser.status();
+ }
+ return results;
+ }
+};
+
+TEST_F(PerfettoSqlParserTest, Empty) {
+ ASSERT_THAT(*Parse(""), testing::IsEmpty());
+}
+
+TEST_F(PerfettoSqlParserTest, SemiColonTerminatedStatement) {
+ static constexpr char kSql[] = "SELECT * FROM slice;";
+ ASSERT_THAT(*Parse(kSql), testing::ElementsAre(SqliteSql{kSql, 0}));
+}
+
+TEST_F(PerfettoSqlParserTest, MultipleStmts) {
+ static constexpr char kSql[] = "SELECT * FROM slice; SELECT * FROM s";
+ ASSERT_THAT(*Parse(kSql),
+ testing::ElementsAre(SqliteSql{"SELECT * FROM slice;", 0},
+ SqliteSql{"SELECT * FROM s", 21}));
+}
+
+TEST_F(PerfettoSqlParserTest, IgnoreOnlySpace) {
+ static constexpr char kSql[] = " ; SELECT * FROM s; ; ;";
+ ASSERT_THAT(*Parse(kSql),
+ testing::ElementsAre(SqliteSql{"SELECT * FROM s;", 3}));
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/query_cache.h b/src/trace_processor/sqlite/query_cache.h
index f3b8223..1bb9d69 100644
--- a/src/trace_processor/sqlite/query_cache.h
+++ b/src/trace_processor/sqlite/query_cache.h
@@ -16,6 +16,7 @@
#ifndef SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
#define SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
+
#include <optional>
#include "src/trace_processor/db/table.h"
diff --git a/src/trace_processor/sqlite/sql_stats_table.cc b/src/trace_processor/sqlite/sql_stats_table.cc
index ad41857..e6cdf87 100644
--- a/src/trace_processor/sqlite/sql_stats_table.cc
+++ b/src/trace_processor/sqlite/sql_stats_table.cc
@@ -31,11 +31,7 @@
SqlStatsTable::SqlStatsTable(sqlite3*, const TraceStorage* storage)
: storage_(storage) {}
-
-void SqlStatsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
- SqliteTable::Register<SqlStatsTable>(db, storage, "sqlstats",
- RegistrationFlags{});
-}
+SqlStatsTable::~SqlStatsTable() = default;
base::Status SqlStatsTable::Init(int, const char* const*, Schema* schema) {
*schema = Schema(
@@ -52,8 +48,8 @@
return util::OkStatus();
}
-std::unique_ptr<SqliteTable::Cursor> SqlStatsTable::CreateCursor() {
- return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
+std::unique_ptr<SqliteTable::BaseCursor> SqlStatsTable::CreateCursor() {
+ return std::unique_ptr<SqliteTable::BaseCursor>(new Cursor(this));
}
int SqlStatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
@@ -61,8 +57,9 @@
}
SqlStatsTable::Cursor::Cursor(SqlStatsTable* table)
- : SqliteTable::Cursor(table), storage_(table->storage_), table_(table) {}
-
+ : SqliteTable::BaseCursor(table),
+ storage_(table->storage_),
+ table_(table) {}
SqlStatsTable::Cursor::~Cursor() = default;
base::Status SqlStatsTable::Cursor::Filter(const QueryConstraints&,
diff --git a/src/trace_processor/sqlite/sql_stats_table.h b/src/trace_processor/sqlite/sql_stats_table.h
index ad43234..d78224c 100644
--- a/src/trace_processor/sqlite/sql_stats_table.h
+++ b/src/trace_processor/sqlite/sql_stats_table.h
@@ -31,7 +31,8 @@
// A virtual table that allows to introspect performances of the SQL engine
// for the kMaxLogEntries queries.
-class SqlStatsTable : public SqliteTable {
+class SqlStatsTable final
+ : public TypedSqliteTable<SqlStatsTable, const TraceStorage*> {
public:
enum Column {
kQuery = 0,
@@ -41,18 +42,18 @@
};
// Implementation of the SQLite cursor interface.
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
explicit Cursor(SqlStatsTable* storage);
- ~Cursor() override;
+ ~Cursor() final;
// Implementation of SqliteTable::Cursor.
base::Status Filter(const QueryConstraints&,
sqlite3_value**,
- FilterHistory) override;
- base::Status Next() override;
- bool Eof() override;
- base::Status Column(sqlite3_context*, int N) override;
+ FilterHistory);
+ base::Status Next();
+ bool Eof();
+ base::Status Column(sqlite3_context*, int N);
private:
Cursor(Cursor&) = delete;
@@ -68,13 +69,12 @@
};
SqlStatsTable(sqlite3*, const TraceStorage* storage);
-
- static void RegisterTable(sqlite3* db, const TraceStorage* storage);
+ ~SqlStatsTable() final;
// Table implementation.
- base::Status Init(int, const char* const*, Schema*) override;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+ base::Status Init(int, const char* const*, Schema*) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ int BestIndex(const QueryConstraints&, BestIndexInfo*) final;
private:
const TraceStorage* const storage_;
diff --git a/src/trace_processor/sqlite/sqlite_engine.cc b/src/trace_processor/sqlite/sqlite_engine.cc
index 429318a..8414193 100644
--- a/src/trace_processor/sqlite/sqlite_engine.cc
+++ b/src/trace_processor/sqlite/sqlite_engine.cc
@@ -15,8 +15,13 @@
*/
#include "src/trace_processor/sqlite/sqlite_engine.h"
+
+#include <utility>
+
+#include "perfetto/base/status.h"
#include "src/trace_processor/sqlite/db_sqlite_table.h"
#include "src/trace_processor/sqlite/query_cache.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
// In Android and Chromium tree builds, we don't have the percentile module.
// Just don't include it.
@@ -58,7 +63,7 @@
} // namespace
-SqliteEngine::SqliteEngine() : query_cache_(new QueryCache()) {
+SqliteEngine::SqliteEngine() {
sqlite3* db = nullptr;
EnsureSqliteInitialized();
PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
@@ -66,12 +71,69 @@
db_.reset(std::move(db));
}
-void SqliteEngine::RegisterTable(const Table& table, const std::string& name) {
- DbSqliteTable::RegisterTable(*db_, query_cache_.get(), &table, name);
+SqliteEngine::~SqliteEngine() {
+ // It is important to unregister any functions that have been registered with
+ // the database before destroying it. This is because functions can hold onto
+ // prepared statements, which must be finalized before database destruction.
+ for (auto it = fn_ctx_.GetIterator(); it; ++it) {
+ int ret = sqlite3_create_function_v2(db_.get(), it.key().first.c_str(),
+ it.key().second, SQLITE_UTF8, nullptr,
+ nullptr, nullptr, nullptr, nullptr);
+ PERFETTO_CHECK(ret == 0);
+ }
+ fn_ctx_.Clear();
}
-void SqliteEngine::RegisterTableFunction(std::unique_ptr<TableFunction> fn) {
- DbSqliteTable::RegisterTable(*db_, query_cache_.get(), std::move(fn));
+base::Status SqliteEngine::RegisterFunction(const char* name,
+ int argc,
+ Fn* fn,
+ void* ctx,
+ FnCtxDestructor* destructor,
+ bool deterministic) {
+ int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
+ int ret =
+ sqlite3_create_function_v2(db_.get(), name, static_cast<int>(argc), flags,
+ ctx, fn, nullptr, nullptr, destructor);
+ if (ret != SQLITE_OK) {
+ return base::ErrStatus("Unable to register function with name %s", name);
+ }
+ *fn_ctx_.Insert(std::make_pair(name, argc), ctx).first = ctx;
+ return base::OkStatus();
+}
+
+base::Status SqliteEngine::DeclareVirtualTable(const std::string& create_stmt) {
+ int res = sqlite3_declare_vtab(db_.get(), create_stmt.c_str());
+ if (res != SQLITE_OK) {
+ return base::ErrStatus("Declare vtab failed: %s",
+ sqlite3_errmsg(db_.get()));
+ }
+ return base::OkStatus();
+}
+
+base::Status SqliteEngine::SaveSqliteTable(const std::string& table_name,
+ std::unique_ptr<SqliteTable> table) {
+ auto res = saved_tables_.Insert(table_name, {});
+ if (!res.second) {
+ return base::ErrStatus("Table with name %s already is saved",
+ table_name.c_str());
+ }
+ *res.first = std::move(table);
+ return base::OkStatus();
+}
+
+base::StatusOr<std::unique_ptr<SqliteTable>> SqliteEngine::RestoreSqliteTable(
+ const std::string& table_name) {
+ auto* res = saved_tables_.Find(table_name);
+ if (!res) {
+ return base::ErrStatus("Table with name %s does not exist in saved state",
+ table_name.c_str());
+ }
+ return std::move(*res);
+}
+
+void* SqliteEngine::GetFunctionContext(const std::string& name, int argc) {
+ auto* res = fn_ctx_.Find(std::make_pair(name, argc));
+ return res ? *res : nullptr;
}
} // namespace trace_processor
diff --git a/src/trace_processor/sqlite/sqlite_engine.h b/src/trace_processor/sqlite/sqlite_engine.h
index f1ebdd3..6c5ad10 100644
--- a/src/trace_processor/sqlite/sqlite_engine.h
+++ b/src/trace_processor/sqlite/sqlite_engine.h
@@ -18,11 +18,21 @@
#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
#include <sqlite3.h>
+#include <stdint.h>
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
#include "src/trace_processor/db/table.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/prelude/table_functions/table_function.h"
#include "src/trace_processor/sqlite/query_cache.h"
#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
namespace perfetto {
namespace trace_processor {
@@ -37,25 +47,87 @@
// what functionality we rely on.
class SqliteEngine {
public:
+ using Fn = void(sqlite3_context* ctx, int argc, sqlite3_value** argv);
+ using FnCtxDestructor = void(void*);
+
SqliteEngine();
+ ~SqliteEngine();
- // Registers a trace processor C++ table with SQLite with an SQL name of
- // |name|.
- void RegisterTable(const Table& table, const std::string& name);
+ // Registers a C++ function to be runnable from SQL.
+ base::Status RegisterFunction(const char* name,
+ int argc,
+ Fn* fn,
+ void* ctx,
+ FnCtxDestructor* ctx_destructor,
+ bool deterministic);
- // Registers a trace processor C++ function with SQLite.
- void RegisterTableFunction(std::unique_ptr<TableFunction> fn);
+ // Registers a SQLite virtual table module with the given name.
+ template <typename Vtab, typename Context>
+ void RegisterVirtualTableModule(const std::string& module_name,
+ Context ctx,
+ SqliteTable::TableType table_type,
+ bool updatable);
+
+ // Declares a virtual table with SQLite.
+ base::Status DeclareVirtualTable(const std::string& create_stmt);
+
+ // Saves a SQLite table across a pair of xDisconnect/xConnect callbacks.
+ base::Status SaveSqliteTable(const std::string& table_name,
+ std::unique_ptr<SqliteTable>);
+
+ // Restores a SQLite table across a pair of xDisconnect/xConnect callbacks.
+ base::StatusOr<std::unique_ptr<SqliteTable>> RestoreSqliteTable(
+ const std::string& table_name);
+
+ // Gets the context for a registered SQL function.
+ void* GetFunctionContext(const std::string& name, int argc);
sqlite3* db() const { return db_.get(); }
private:
- // Keep this first: we need this to be destroyed after we clean up
- // everything else.
+ struct FnHasher {
+ size_t operator()(const std::pair<std::string, int>& x) const {
+ base::Hasher hasher;
+ hasher.Update(x.first);
+ hasher.Update(x.second);
+ return static_cast<size_t>(hasher.digest());
+ }
+ };
+
+ base::FlatHashMap<std::string, std::unique_ptr<SqliteTable>> saved_tables_;
+ base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_;
+
ScopedDb db_;
- std::unique_ptr<QueryCache> query_cache_;
};
} // namespace trace_processor
} // namespace perfetto
+// The rest of this file is just implementation details which we need
+// in the header file because it is templated code. We separate it out
+// like this to keep the API people actually care about easy to read.
+
+namespace perfetto {
+namespace trace_processor {
+
+template <typename Vtab, typename Context>
+void SqliteEngine::RegisterVirtualTableModule(const std::string& module_name,
+ Context ctx,
+ SqliteTable::TableType table_type,
+ bool updatable) {
+ static_assert(std::is_base_of_v<SqliteTable, Vtab>,
+ "Must subclass TypedSqliteTable");
+
+ auto module_arg =
+ Vtab::CreateModuleArg(this, std::move(ctx), table_type, updatable);
+ sqlite3_module* module = &module_arg->module;
+ int res = sqlite3_create_module_v2(
+ db_.get(), module_name.c_str(), module, module_arg.release(),
+ [](void* arg) { delete static_cast<typename Vtab::ModuleArg*>(arg); });
+ PERFETTO_CHECK(res == SQLITE_OK);
+}
+
+} // namespace trace_processor
+} // namespace perfetto
+
#endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_ENGINE_H_
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index 343ae04..2ad925c 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -20,10 +20,16 @@
#include <algorithm>
#include <cinttypes>
#include <map>
+#include <memory>
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/ext/base/string_view.h"
+#include "sqlite3.h"
+#include "src/trace_processor/sqlite/sqlite_engine.h"
#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
namespace perfetto {
namespace trace_processor {
@@ -154,88 +160,6 @@
SqliteTable::SqliteTable() = default;
SqliteTable::~SqliteTable() = default;
-int SqliteTable::OpenInternal(sqlite3_vtab_cursor** ppCursor) {
- // Freed in xClose().
- *ppCursor = static_cast<sqlite3_vtab_cursor*>(CreateCursor().release());
- return SQLITE_OK;
-}
-
-int SqliteTable::BestIndexInternal(sqlite3_index_info* idx) {
- QueryConstraints qc(idx->colUsed);
-
- for (int i = 0; i < idx->nConstraint; i++) {
- const auto& cs = idx->aConstraint[i];
- if (!cs.usable)
- continue;
- qc.AddConstraint(cs.iColumn, cs.op, i);
- }
-
- for (int i = 0; i < idx->nOrderBy; i++) {
- int column = idx->aOrderBy[i].iColumn;
- bool desc = idx->aOrderBy[i].desc;
- qc.AddOrderBy(column, desc);
- }
-
- int ret = SetStatusAndReturn(ModifyConstraints(&qc));
- if (ret != SQLITE_OK)
- return ret;
-
- BestIndexInfo info;
- info.estimated_cost = idx->estimatedCost;
- info.estimated_rows = idx->estimatedRows;
- info.sqlite_omit_constraint.resize(qc.constraints().size());
-
- ret = BestIndex(qc, &info);
-
- if (ret != SQLITE_OK)
- return ret;
-
- idx->orderByConsumed = qc.order_by().empty() || info.sqlite_omit_order_by;
- idx->estimatedCost = info.estimated_cost;
- idx->estimatedRows = info.estimated_rows;
-
- // First pass: mark all constraints as omitted to ensure that any pruned
- // constraints are not checked for by SQLite.
- for (int i = 0; i < idx->nConstraint; ++i) {
- auto& u = idx->aConstraintUsage[i];
- u.omit = true;
- }
-
- // Second pass: actually set the correct omit and index values for all
- // retained constraints.
- for (uint32_t i = 0; i < qc.constraints().size(); ++i) {
- auto& u = idx->aConstraintUsage[qc.constraints()[i].a_constraint_idx];
- u.omit = info.sqlite_omit_constraint[i];
- u.argvIndex = static_cast<int>(i) + 1;
- }
-
- PERFETTO_TP_TRACE(
- metatrace::Category::QUERY, "SQLITE_TABLE_BEST_INDEX",
- [&](metatrace::Record* r) {
- r->AddArg("name", name_);
- WriteQueryConstraintsToMetatrace(r, qc, schema());
- r->AddArg("order_by_consumed", std::to_string(idx->orderByConsumed));
- r->AddArg("estimated_cost", std::to_string(idx->estimatedCost));
- r->AddArg("estimated_rows",
- std::to_string(static_cast<int64_t>(idx->estimatedRows)));
- });
-
- auto out_qc_str = qc.ToNewSqlite3String();
- if (SqliteTable::debug) {
- PERFETTO_LOG(
- "[%s::BestIndex] constraints=%s orderByConsumed=%d estimatedCost=%f "
- "estimatedRows=%" PRId64,
- name_.c_str(), QcDebugStr(qc, schema()).c_str(), idx->orderByConsumed,
- idx->estimatedCost, static_cast<int64_t>(idx->estimatedRows));
- }
-
- idx->idxStr = out_qc_str.release();
- idx->needToFreeIdxStr = true;
- idx->idxNum = ++best_index_num_;
-
- return SQLITE_OK;
-}
-
base::Status SqliteTable::ModifyConstraints(QueryConstraints*) {
return base::OkStatus();
}
@@ -245,7 +169,7 @@
}
base::Status SqliteTable::Update(int, sqlite3_value**, sqlite3_int64*) {
- return base::OkStatus();
+ return base::ErrStatus("Updating not supported");
}
bool SqliteTable::ReadConstraints(int idxNum, const char* idxStr, int argc) {
@@ -275,12 +199,20 @@
return cache_hit;
}
-SqliteTable::Cursor::Cursor(SqliteTable* table) : table_(table) {
+////////////////////////////////////////////////////////////////////////////////
+// SqliteTable::BaseCursor implementation
+////////////////////////////////////////////////////////////////////////////////
+
+SqliteTable::BaseCursor::BaseCursor(SqliteTable* table) : table_(table) {
// This is required to prevent us from leaving this field uninitialised if
// we ever move construct the Cursor.
pVtab = table;
}
-SqliteTable::Cursor::~Cursor() = default;
+SqliteTable::BaseCursor::~BaseCursor() = default;
+
+////////////////////////////////////////////////////////////////////////////////
+// SqliteTable::Column implementation
+////////////////////////////////////////////////////////////////////////////////
SqliteTable::Column::Column(size_t index,
std::string name,
@@ -288,6 +220,12 @@
bool hidden)
: index_(index), name_(name), type_(type), hidden_(hidden) {}
+////////////////////////////////////////////////////////////////////////////////
+// SqliteTable::Schema implementation
+////////////////////////////////////////////////////////////////////////////////
+
+SqliteTable::Schema::Schema() = default;
+
SqliteTable::Schema::Schema(std::vector<Column> columns,
std::vector<size_t> primary_keys)
: columns_(std::move(columns)), primary_keys_(std::move(primary_keys)) {
@@ -299,7 +237,6 @@
}
}
-SqliteTable::Schema::Schema() = default;
SqliteTable::Schema::Schema(const Schema&) = default;
SqliteTable::Schema& SqliteTable::Schema::operator=(const Schema&) = default;
@@ -331,5 +268,170 @@
return stmt;
}
+////////////////////////////////////////////////////////////////////////////////
+// TypedSqliteTableBase implementation
+////////////////////////////////////////////////////////////////////////////////
+
+TypedSqliteTableBase::~TypedSqliteTableBase() = default;
+
+base::Status TypedSqliteTableBase::DeclareAndAssignVtab(
+ std::unique_ptr<SqliteTable> table,
+ sqlite3_vtab** tab) {
+ auto create_stmt = table->schema().ToCreateTableStmt();
+ PERFETTO_DLOG("Create table statement: %s", create_stmt.c_str());
+ RETURN_IF_ERROR(table->engine_->DeclareVirtualTable(create_stmt));
+ *tab = table.release();
+ return base::OkStatus();
+}
+
+int TypedSqliteTableBase::xDestroy(sqlite3_vtab* t) {
+ delete static_cast<SqliteTable*>(t);
+ return SQLITE_OK;
+}
+
+int TypedSqliteTableBase::xDestroyFatal(sqlite3_vtab*) {
+ PERFETTO_FATAL("xDestroy should not be called");
+}
+
+int TypedSqliteTableBase::xConnectRestoreTable(sqlite3*,
+ void* arg,
+ int,
+ const char* const* argv,
+ sqlite3_vtab** tab,
+ char** pzErr) {
+ auto* xArg = static_cast<BaseModuleArg*>(arg);
+
+ // SQLite guarantees that argv[2] contains the name of the table.
+ std::string table_name = argv[2];
+ base::StatusOr<std::unique_ptr<SqliteTable>> table =
+ xArg->engine->RestoreSqliteTable(table_name);
+ if (!table.status().ok()) {
+ *pzErr = sqlite3_mprintf("%s", table.status().c_message());
+ return SQLITE_ERROR;
+ }
+ base::Status status = DeclareAndAssignVtab(std::move(table.value()), tab);
+ if (!status.ok()) {
+ *pzErr = sqlite3_mprintf("%s", status.c_message());
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+int TypedSqliteTableBase::xDisconnectSaveTable(sqlite3_vtab* t) {
+ auto* table = static_cast<TypedSqliteTableBase*>(t);
+ base::Status status = table->engine_->SaveSqliteTable(
+ table->name(), std::unique_ptr<SqliteTable>(table));
+ return table->SetStatusAndReturn(status);
+}
+
+base::Status TypedSqliteTableBase::InitInternal(SqliteEngine* engine,
+ int argc,
+ const char* const* argv) {
+ // Set the engine to allow saving into it later.
+ engine_ = engine;
+
+ // SQLite guarantees that argv[0] will be the "module" name: this is the
+ // same as |table_name| passed to the Register function.
+ module_name_ = argv[0];
+
+ // SQLite guarantees that argv[2] contains the name of the table: for
+ // non-arg taking tables, this will be the same as |table_name| but for
+ // arg-taking tables, this will be the table name as defined by the
+ // user in the CREATE VIRTUAL TABLE call.
+ name_ = argv[2];
+
+ Schema schema;
+ RETURN_IF_ERROR(Init(argc, argv, &schema));
+ schema_ = std::move(schema);
+ return base::OkStatus();
+}
+
+int TypedSqliteTableBase::xOpen(sqlite3_vtab* t,
+ sqlite3_vtab_cursor** ppCursor) {
+ auto* table = static_cast<TypedSqliteTableBase*>(t);
+ *ppCursor =
+ static_cast<sqlite3_vtab_cursor*>(table->CreateCursor().release());
+ return SQLITE_OK;
+}
+
+int TypedSqliteTableBase::xBestIndex(sqlite3_vtab* t, sqlite3_index_info* idx) {
+ auto* table = static_cast<TypedSqliteTableBase*>(t);
+
+ QueryConstraints qc(idx->colUsed);
+
+ for (int i = 0; i < idx->nConstraint; i++) {
+ const auto& cs = idx->aConstraint[i];
+ if (!cs.usable)
+ continue;
+ qc.AddConstraint(cs.iColumn, cs.op, i);
+ }
+
+ for (int i = 0; i < idx->nOrderBy; i++) {
+ int column = idx->aOrderBy[i].iColumn;
+ bool desc = idx->aOrderBy[i].desc;
+ qc.AddOrderBy(column, desc);
+ }
+
+ int ret = table->SetStatusAndReturn(table->ModifyConstraints(&qc));
+ if (ret != SQLITE_OK)
+ return ret;
+
+ BestIndexInfo info;
+ info.estimated_cost = idx->estimatedCost;
+ info.estimated_rows = idx->estimatedRows;
+ info.sqlite_omit_constraint.resize(qc.constraints().size());
+
+ ret = table->BestIndex(qc, &info);
+
+ if (ret != SQLITE_OK)
+ return ret;
+
+ idx->orderByConsumed = qc.order_by().empty() || info.sqlite_omit_order_by;
+ idx->estimatedCost = info.estimated_cost;
+ idx->estimatedRows = info.estimated_rows;
+
+ // First pass: mark all constraints as omitted to ensure that any pruned
+ // constraints are not checked for by SQLite.
+ for (int i = 0; i < idx->nConstraint; ++i) {
+ auto& u = idx->aConstraintUsage[i];
+ u.omit = true;
+ }
+
+ // Second pass: actually set the correct omit and index values for all
+ // retained constraints.
+ for (uint32_t i = 0; i < qc.constraints().size(); ++i) {
+ auto& u = idx->aConstraintUsage[qc.constraints()[i].a_constraint_idx];
+ u.omit = info.sqlite_omit_constraint[i];
+ u.argvIndex = static_cast<int>(i) + 1;
+ }
+
+ PERFETTO_TP_TRACE(
+ metatrace::Category::QUERY, "SQLITE_TABLE_BEST_INDEX",
+ [&](metatrace::Record* r) {
+ r->AddArg("name", table->name());
+ WriteQueryConstraintsToMetatrace(r, qc, table->schema());
+ r->AddArg("order_by_consumed", std::to_string(idx->orderByConsumed));
+ r->AddArg("estimated_cost", std::to_string(idx->estimatedCost));
+ r->AddArg("estimated_rows",
+ std::to_string(static_cast<int64_t>(idx->estimatedRows)));
+ });
+
+ auto out_qc_str = qc.ToNewSqlite3String();
+ if (SqliteTable::debug) {
+ PERFETTO_LOG(
+ "[%s::BestIndex] constraints=%s orderByConsumed=%d estimatedCost=%f "
+ "estimatedRows=%" PRId64,
+ table->name().c_str(), QcDebugStr(qc, table->schema()).c_str(),
+ idx->orderByConsumed, idx->estimatedCost,
+ static_cast<int64_t>(idx->estimatedRows));
+ }
+
+ idx->idxStr = out_qc_str.release();
+ idx->needToFreeIdxStr = true;
+ idx->idxNum = ++table->best_index_num_;
+
+ return SQLITE_OK;
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 2d2599b..2d0cabc 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -27,31 +27,30 @@
#include <vector>
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/db/table.h"
#include "src/trace_processor/sqlite/query_constraints.h"
namespace perfetto {
namespace trace_processor {
-class TraceStorage;
+class SqliteEngine;
+class TypedSqliteTableBase;
// Abstract base class representing a SQLite virtual table. Implements the
// common bookeeping required across all tables and allows subclasses to
// implement a friendlier API than that required by SQLite.
class SqliteTable : public sqlite3_vtab {
public:
- template <typename Context>
- using Factory =
- std::function<std::unique_ptr<SqliteTable>(sqlite3*, Context)>;
-
// Custom opcodes used by subclasses of SqliteTable.
// Stored here as we need a central repository of opcodes to prevent clashes
// between different sub-classes.
enum CustomFilterOpcode {
kSourceGeqOpCode = SQLITE_INDEX_CONSTRAINT_FUNCTION + 1,
};
-
// Describes a column of this table.
class Column {
public:
@@ -74,15 +73,9 @@
bool hidden_ = false;
};
- // When set it logs all BestIndex and Filter actions on the console.
- static bool debug;
-
- // Public for unique_ptr destructor calls.
- virtual ~SqliteTable();
-
// Abstract base class representing an SQLite Cursor. Presents a friendlier
// API for subclasses to implement.
- class Cursor : public sqlite3_vtab_cursor {
+ class BaseCursor : public sqlite3_vtab_cursor {
public:
// Enum for the history of calls to Filter.
enum class FilterHistory : uint32_t {
@@ -97,40 +90,39 @@
kSame = 1,
};
- explicit Cursor(SqliteTable* table);
- virtual ~Cursor();
+ explicit BaseCursor(SqliteTable* table);
+ virtual ~BaseCursor();
// Methods to be implemented by derived table classes.
+ // Note: these methods are intentionally not virtual for performance
+ // reasons. As these methods are not defined, there will be compile errors
+ // thrown if any of these methods are missing.
// Called to intialise the cursor with the constraints of the query.
- virtual base::Status Filter(const QueryConstraints& qc,
- sqlite3_value**,
- FilterHistory) = 0;
+ base::Status Filter(const QueryConstraints& qc,
+ sqlite3_value**,
+ FilterHistory);
// Called to forward the cursor to the next row in the table.
- virtual base::Status Next() = 0;
+ base::Status Next();
// Called to check if the cursor has reached eof. Column will be called iff
// this method returns true.
- virtual bool Eof() = 0;
+ bool Eof();
// Used to extract the value from the column at index |N|.
- virtual base::Status Column(sqlite3_context* context, int N) = 0;
+ base::Status Column(sqlite3_context* context, int N);
+
+ SqliteTable* table() const { return table_; }
protected:
- Cursor(Cursor&) = delete;
- Cursor& operator=(const Cursor&) = delete;
+ BaseCursor(BaseCursor&) = delete;
+ BaseCursor& operator=(const BaseCursor&) = delete;
- Cursor(Cursor&&) noexcept = default;
- Cursor& operator=(Cursor&&) = default;
+ BaseCursor(BaseCursor&&) noexcept = default;
+ BaseCursor& operator=(BaseCursor&&) = default;
private:
- friend class SqliteTable;
-
- int SetStatusAndReturn(base::Status status) {
- return table_->SetStatusAndReturn(status);
- }
-
SqliteTable* table_ = nullptr;
};
@@ -160,6 +152,24 @@
std::vector<size_t> primary_keys_;
};
+ enum TableType {
+ // A table which automatically exists in the main schema and cannot be
+ // created with CREATE VIRTUAL TABLE.
+ // Note: the name value here matches the naming in the vtable docs of
+ // SQLite.
+ kEponymousOnly,
+
+ // A table which must be explicitly created using a CREATE VIRTUAL TABLE
+ // statement (i.e. does exist automatically).
+ kExplicitCreate,
+ };
+
+ // Public for unique_ptr destructor calls.
+ virtual ~SqliteTable();
+
+ // When set it logs all BestIndex and Filter actions on the console.
+ static bool debug;
+
protected:
// Populated by a BestIndex call to allow subclasses to tweak SQLite's
// handling of sets of constraints.
@@ -186,194 +196,11 @@
int64_t estimated_rows = 0;
};
- struct RegistrationFlags {
- // Specifies whether the table can also be written to.
- bool writable = false;
-
- enum TableType {
- // A table which automatically exists in the main schema and cannot be
- // created with CREATE VIRTUAL TABLE.
- // Note: the name value here matches the naming in the vtable docs of
- // SQLite.
- kEponymousOnly,
-
- // A table which automatically exists in the main schema and can also be
- // created with CREATE VIRTUAL TABLE.
- // Note: the name value here matches the naming in the vtable docs of
- // SQLite.
- kEponymous,
-
- // A table which must be explicitly created using a CREATE VIRTUAL TABLE
- // statement (i.e. does exist automatically) but does not have any
- // backing state beyond the arguments passed to it.
- kExplicitCreateStateless,
- };
- TableType type = TableType::kEponymousOnly;
-
- // Whether the table requires some number of hidden constraints to be passed
- // to be able to the queried (i.e. a SELECT * FROM table would not work).
- bool requires_hidden_constraints = false;
- };
-
SqliteTable();
- // Called by derived classes to register themselves with the SQLite db.
- // Note: this function is inlined here because we use the TTable template to
- // devirtualise the function calls.
- template <typename TTable, typename Context = const TraceStorage*>
- static void Register(sqlite3* db,
- Context ctx,
- const std::string& module_name,
- RegistrationFlags flags) {
- using TCursor = typename TTable::Cursor;
-
- struct TableDescriptor {
- SqliteTable::Factory<Context> factory;
- Context context;
- sqlite3_module module = {};
- };
-
- std::unique_ptr<TableDescriptor> desc(new TableDescriptor());
- desc->context = std::move(ctx);
- desc->factory = GetFactory<TTable, Context>();
- sqlite3_module* module = &desc->module;
- memset(module, 0, sizeof(*module));
-
- auto create_fn = [](sqlite3* xdb, void* arg, int argc,
- const char* const* argv, sqlite3_vtab** tab,
- char** pzErr) {
- auto* xdesc = static_cast<TableDescriptor*>(arg);
- auto table = xdesc->factory(xdb, std::move(xdesc->context));
-
- // SQLite guarantees that argv[0] will be the "module" name: this is the
- // same as |table_name| passed to the Register function.
- table->module_name_ = argv[0];
-
- // SQLite guarantees that argv[2] contains the name of the table: for
- // non-arg taking tables, this will be the same as |table_name| but for
- // arg-taking tables, this will be the table name as defined by the user
- // in the CREATE VIRTUAL TABLE call.
- table->name_ = argv[2];
-
- Schema schema;
- base::Status status = table->Init(argc, argv, &schema);
- if (!status.ok()) {
- *pzErr = sqlite3_mprintf("%s", status.c_message());
- return SQLITE_ERROR;
- }
-
- auto create_stmt = schema.ToCreateTableStmt();
- PERFETTO_DLOG("Create table statement: %s", create_stmt.c_str());
-
- int res = sqlite3_declare_vtab(xdb, create_stmt.c_str());
- if (res != SQLITE_OK)
- return res;
-
- // Freed in xDisconnect().
- table->schema_ = std::move(schema);
- *tab = table.release();
-
- return SQLITE_OK;
- };
- auto destroy_fn = [](sqlite3_vtab* t) {
- delete static_cast<TTable*>(t);
- return SQLITE_OK;
- };
-
- switch (flags.type) {
- case RegistrationFlags::kEponymousOnly:
- module->xCreate = nullptr;
- break;
- case RegistrationFlags::kEponymous:
- module->xCreate = create_fn;
- break;
- case RegistrationFlags::kExplicitCreateStateless:
- // TODO(lalitm): this is not accurate as we're basically creating an
- // eponymous table. Change this to be a different function once we can
- // do so easily.
- module->xCreate = create_fn;
- break;
- }
- module->xConnect = create_fn;
- module->xDisconnect = destroy_fn;
- module->xDestroy = destroy_fn;
- module->xOpen = [](sqlite3_vtab* t, sqlite3_vtab_cursor** c) {
- return static_cast<SqliteTable*>(t)->OpenInternal(c);
- };
- module->xClose = [](sqlite3_vtab_cursor* c) {
- delete static_cast<TCursor*>(c);
- return SQLITE_OK;
- };
- module->xBestIndex = [](sqlite3_vtab* t, sqlite3_index_info* i) {
- return static_cast<TTable*>(t)->BestIndexInternal(i);
- };
- module->xFilter = [](sqlite3_vtab_cursor* vc, int i, const char* s, int a,
- sqlite3_value** v) {
- bool is_cached =
- static_cast<Cursor*>(vc)->table_->ReadConstraints(i, s, a);
-
- auto history = is_cached ? Cursor::FilterHistory::kSame
- : Cursor::FilterHistory::kDifferent;
- auto* cursor = static_cast<TCursor*>(vc);
- return cursor->SetStatusAndReturn(cursor->Filter(
- static_cast<Cursor*>(vc)->table_->qc_cache_, v, history));
- };
- module->xNext = [](sqlite3_vtab_cursor* c) {
- auto* cursor = static_cast<TCursor*>(c);
- return cursor->SetStatusAndReturn(cursor->Next());
- };
- module->xEof = [](sqlite3_vtab_cursor* c) {
- return static_cast<int>(static_cast<TCursor*>(c)->Eof());
- };
- module->xColumn = [](sqlite3_vtab_cursor* c, sqlite3_context* a, int b) {
- auto* cursor = static_cast<TCursor*>(c);
- return cursor->SetStatusAndReturn(cursor->Column(a, b));
- };
- module->xRowid = [](sqlite3_vtab_cursor*, sqlite3_int64*) {
- return SQLITE_ERROR;
- };
- module->xFindFunction =
- [](sqlite3_vtab* t, int, const char* name,
- void (**fn)(sqlite3_context*, int, sqlite3_value**), void** args) {
- return static_cast<TTable*>(t)->FindFunction(name, fn, args);
- };
-
- if (flags.writable) {
- module->xUpdate = [](sqlite3_vtab* t, int a, sqlite3_value** v,
- sqlite3_int64* r) {
- auto* table = static_cast<TTable*>(t);
- return table->SetStatusAndReturn(table->Update(a, v, r));
- };
- }
-
- int res = sqlite3_create_module_v2(
- db, module_name.c_str(), module, desc.release(),
- [](void* arg) { delete static_cast<TableDescriptor*>(arg); });
- PERFETTO_CHECK(res == SQLITE_OK);
-
- // Register virtual tables into an internal 'perfetto_tables' table. This is
- // used for iterating through all the tables during a database export. Note
- // that virtual tables which requires explicit CREATE statements or require
- // hidden constraints cannot be inserted.
- bool explicit_create =
- flags.type == RegistrationFlags::kExplicitCreateStateless;
- if (!explicit_create && !flags.requires_hidden_constraints) {
- char* insert_sql =
- sqlite3_mprintf("INSERT INTO perfetto_tables(name) VALUES('%q')",
- module_name.c_str());
- char* error = nullptr;
- sqlite3_exec(db, insert_sql, nullptr, nullptr, &error);
- sqlite3_free(insert_sql);
- if (error) {
- PERFETTO_ELOG("Error registering table: %s", error);
- sqlite3_free(error);
- }
- }
- }
-
// Methods to be implemented by derived table classes.
virtual base::Status Init(int argc, const char* const* argv, Schema*) = 0;
- virtual std::unique_ptr<Cursor> CreateCursor() = 0;
+ virtual std::unique_ptr<BaseCursor> CreateCursor() = 0;
virtual int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) = 0;
// Optional metods to implement.
@@ -384,36 +211,24 @@
// At registration time, the function should also pass true for |read_write|.
virtual base::Status Update(int, sqlite3_value**, sqlite3_int64*);
+ bool ReadConstraints(int idxNum, const char* idxStr, int argc);
+
const Schema& schema() const { return schema_; }
const std::string& module_name() const { return module_name_; }
const std::string& name() const { return name_; }
private:
- template <typename TableType, typename Context>
- static Factory<Context> GetFactory() {
- return [](sqlite3* db, Context ctx) {
- return std::unique_ptr<SqliteTable>(new TableType(db, std::move(ctx)));
- };
- }
-
- bool ReadConstraints(int idxNum, const char* idxStr, int argc);
-
- // Overriden functions from sqlite3_vtab.
- int OpenInternal(sqlite3_vtab_cursor**);
- int BestIndexInternal(sqlite3_index_info*);
-
- int SetStatusAndReturn(base::Status status) {
- if (!status.ok()) {
- sqlite3_free(zErrMsg);
- zErrMsg = sqlite3_mprintf("%s", status.c_message());
- return SQLITE_ERROR;
- }
- return SQLITE_OK;
- }
+ template <typename, typename>
+ friend class TypedSqliteTable;
+ friend class TypedSqliteTableBase;
SqliteTable(const SqliteTable&) = delete;
SqliteTable& operator=(const SqliteTable&) = delete;
+ // The engine class this table is registered with. Used for restoring/saving
+ // the table.
+ SqliteEngine* engine_ = nullptr;
+
// This name of the table. For tables created using CREATE VIRTUAL TABLE, this
// will be the name of the table specified by the query. For automatically
// created tables, this will be the same as the module name passed to
@@ -432,6 +247,177 @@
int best_index_num_ = 0;
};
+class TypedSqliteTableBase : public SqliteTable {
+ protected:
+ struct BaseModuleArg {
+ sqlite3_module module;
+ SqliteEngine* engine;
+ };
+
+ ~TypedSqliteTableBase() override;
+
+ static int xDestroy(sqlite3_vtab*);
+ static int xDestroyFatal(sqlite3_vtab*);
+
+ static int xConnectRestoreTable(sqlite3* xdb,
+ void* arg,
+ int argc,
+ const char* const* argv,
+ sqlite3_vtab** tab,
+ char** pzErr);
+ static int xDisconnectSaveTable(sqlite3_vtab*);
+
+ static int xOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
+ static int xBestIndex(sqlite3_vtab*, sqlite3_index_info*);
+
+ static base::Status DeclareAndAssignVtab(std::unique_ptr<SqliteTable> table,
+ sqlite3_vtab** tab);
+
+ base::Status InitInternal(SqliteEngine* engine,
+ int argc,
+ const char* const* argv);
+
+ int SetStatusAndReturn(base::Status status) {
+ if (!status.ok()) {
+ sqlite3_free(zErrMsg);
+ zErrMsg = sqlite3_mprintf("%s", status.c_message());
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+ }
+};
+
+template <typename SubTable, typename Context>
+class TypedSqliteTable : public TypedSqliteTableBase {
+ public:
+ struct ModuleArg : BaseModuleArg {
+ Context context;
+ };
+
+ static std::unique_ptr<ModuleArg> CreateModuleArg(SqliteEngine* engine,
+ Context ctx,
+ TableType table_type,
+ bool updatable) {
+ auto arg = std::make_unique<ModuleArg>();
+ arg->module = CreateModule(table_type, updatable);
+ arg->engine = engine;
+ arg->context = std::move(ctx);
+ return arg;
+ }
+
+ private:
+ static constexpr sqlite3_module CreateModule(TableType table_type,
+ bool updatable) {
+ sqlite3_module module;
+ memset(&module, 0, sizeof(sqlite3_module));
+ switch (table_type) {
+ case TableType::kEponymousOnly:
+ // Neither xCreate nor xDestroy should ever be called for
+ // eponymous-only tables.
+ module.xCreate = nullptr;
+ module.xDestroy = &xDestroyFatal;
+
+ // xConnect and xDisconnect will automatically be called with
+ // |module_name| == |name|.
+ module.xConnect = &xCreate;
+ module.xDisconnect = &xDestroy;
+ break;
+ case TableType::kExplicitCreate:
+ // xConnect and xDestroy will be called when the table is CREATE-ed and
+ // DROP-ed respectively.
+ module.xCreate = &xCreate;
+ module.xDestroy = &xDestroy;
+
+ // xConnect and xDisconnect can be called at any time.
+ module.xConnect = &xConnectRestoreTable;
+ module.xDisconnect = &xDisconnectSaveTable;
+ break;
+ }
+ module.xOpen = &xOpen;
+ module.xClose = &xClose;
+ module.xBestIndex = &xBestIndex;
+ module.xFindFunction = &xFindFunction;
+ module.xFilter = &xFilter;
+ module.xNext = &xNext;
+ module.xEof = &xEof;
+ module.xColumn = &xColumn;
+ module.xRowid = &xRowid;
+ if (updatable) {
+ module.xUpdate = &xUpdate;
+ }
+ return module;
+ }
+
+ static int xCreate(sqlite3* xdb,
+ void* arg,
+ int argc,
+ const char* const* argv,
+ sqlite3_vtab** tab,
+ char** pzErr) {
+ auto* xdesc = static_cast<ModuleArg*>(arg);
+ std::unique_ptr<SubTable> table(
+ new SubTable(xdb, std::move(xdesc->context)));
+ base::Status status = table->InitInternal(xdesc->engine, argc, argv);
+ if (!status.ok()) {
+ *pzErr = sqlite3_mprintf("%s", status.c_message());
+ return SQLITE_ERROR;
+ }
+ status = DeclareAndAssignVtab(std::move(table), tab);
+ if (!status.ok()) {
+ *pzErr = sqlite3_mprintf("%s", status.c_message());
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+ }
+ static int xClose(sqlite3_vtab_cursor* c) {
+ delete static_cast<typename SubTable::Cursor*>(c);
+ return SQLITE_OK;
+ }
+ static int xFindFunction(sqlite3_vtab* t,
+ int,
+ const char* name,
+ void (**fn)(sqlite3_context*, int, sqlite3_value**),
+ void** args) {
+ return static_cast<SubTable*>(t)->FindFunction(name, fn, args);
+ }
+ static int xFilter(sqlite3_vtab_cursor* vc,
+ int i,
+ const char* s,
+ int a,
+ sqlite3_value** v) {
+ auto* cursor = static_cast<typename SubTable::Cursor*>(vc);
+ bool is_cached = cursor->table()->ReadConstraints(i, s, a);
+ auto history = is_cached ? BaseCursor::FilterHistory::kSame
+ : BaseCursor::FilterHistory::kDifferent;
+ auto* table = static_cast<SubTable*>(cursor->table());
+ return table->SetStatusAndReturn(
+ cursor->Filter(cursor->table()->qc_cache_, v, history));
+ }
+ static int xNext(sqlite3_vtab_cursor* c) {
+ auto* cursor = static_cast<typename SubTable::Cursor*>(c);
+ auto* table = static_cast<SubTable*>(cursor->table());
+ return table->SetStatusAndReturn(cursor->Next());
+ }
+ static int xEof(sqlite3_vtab_cursor* c) {
+ return static_cast<int>(static_cast<typename SubTable::Cursor*>(c)->Eof());
+ }
+ static int xColumn(sqlite3_vtab_cursor* c, sqlite3_context* a, int b) {
+ auto* cursor = static_cast<typename SubTable::Cursor*>(c);
+ auto* table = static_cast<SubTable*>(cursor->table());
+ return table->SetStatusAndReturn(cursor->Column(a, b));
+ }
+ static int xRowid(sqlite3_vtab_cursor*, sqlite3_int64*) {
+ return SQLITE_ERROR;
+ }
+ static int xUpdate(sqlite3_vtab* t,
+ int a,
+ sqlite3_value** v,
+ sqlite3_int64* r) {
+ auto* table = static_cast<SubTable*>(t);
+ return table->SetStatusAndReturn(table->Update(a, v, r));
+ }
+};
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.cc b/src/trace_processor/sqlite/sqlite_tokenizer.cc
new file mode 100644
index 0000000..1766baa
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.cc
@@ -0,0 +1,448 @@
+/*
+ * 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/sqlite/sqlite_tokenizer.h"
+
+#include <ctype.h>
+#include <sqlite3.h>
+#include <optional>
+#include <string_view>
+
+#include "perfetto/base/compiler.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// The contents of this file are ~copied from SQLite with some modifications to
+// minimize the amount copied: i.e. if we can call a libc function/public SQLite
+// API instead of a private one.
+//
+// The changes are as follows:
+// 1. Remove all ifdefs to only keep branches we actually use
+// 2. Change handling of |CC_KYWD0| to remove distinction between different
+// SQLite kewords, reducing how many things we need to copy over.
+// 3. Constants are changed from be macro defines to be values in
+// |SqliteTokenType|.
+
+namespace {
+
+const unsigned char sqlite3CtypeMap[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00..07 ........ */
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
+ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
+
+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
+ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */
+
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */
+};
+
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
+#define CC_KYWD0 1 /* First letter of a keyword */
+#define CC_KYWD 2 /* Alphabetics or '_'. Usable in a keyword */
+#define CC_DIGIT 3 /* Digits */
+#define CC_DOLLAR 4 /* '$' */
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
+#define CC_SPACE 7 /* Space characters */
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
+#define CC_GT 13 /* '>'. Part of > or >= */
+#define CC_EQ 14 /* '='. Part of = or == */
+#define CC_BANG 15 /* '!'. Part of != */
+#define CC_SLASH 16 /* '/'. / or c-style comment */
+#define CC_LP 17 /* '(' */
+#define CC_RP 18 /* ')' */
+#define CC_SEMI 19 /* ';' */
+#define CC_PLUS 20 /* '+' */
+#define CC_STAR 21 /* '*' */
+#define CC_PERCENT 22 /* '%' */
+#define CC_COMMA 23 /* ',' */
+#define CC_AND 24 /* '&' */
+#define CC_TILDA 25 /* '~' */
+#define CC_DOT 26 /* '.' */
+#define CC_ID 27 /* unicode characters usable in IDs */
+#define CC_NUL 29 /* 0x00 */
+#define CC_BOM 30 /* First byte of UTF8 BOM: 0xEF 0xBB 0xBF */
+
+// clang-format off
+static const unsigned char aiClass[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 29, 28, 28, 28, 28, 28, 28, 28, 28, 7, 7, 28, 7, 7, 28, 28,
+/* 1x */ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 9, 28, 28, 28, 2,
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 28, 10, 28, 25, 28,
+/* 8x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ax */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Cx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 30,
+/* Fx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27
+};
+// clang-format on
+
+#define IdChar(C) ((sqlite3CtypeMap[static_cast<unsigned char>(C)] & 0x46) != 0)
+
+// Copy of |sqlite3GetToken| for use by the PerfettoSql transpiler.
+//
+// We copy this function because |sqlite3GetToken| is static to sqlite3.c
+// in most distributions of SQLite so we cannot call it from our code.
+//
+// While we could redefine SQLITE_PRIVATE, pragmatically that will not fly in
+// all the places we build trace processor so we need to resort to making a
+// copy.
+int GetSqliteToken(const unsigned char* z, SqliteTokenType* tokenType) {
+ int i, c;
+ switch (aiClass[*z]) { /* Switch on the character-class of the first byte
+ ** of the token. See the comment on the CC_ defines
+ ** above. */
+ case CC_SPACE: {
+ for (i = 1; isspace(z[i]); i++) {
+ }
+ *tokenType = SqliteTokenType::TK_SPACE;
+ return i;
+ }
+ case CC_MINUS: {
+ if (z[1] == '-') {
+ for (i = 2; (c = z[i]) != 0 && c != '\n'; i++) {
+ }
+ *tokenType = SqliteTokenType::TK_SPACE; /* IMP: R-22934-25134 */
+ return i;
+ } else if (z[1] == '>') {
+ *tokenType = SqliteTokenType::TK_PTR;
+ return 2 + (z[2] == '>');
+ }
+ *tokenType = SqliteTokenType::TK_MINUS;
+ return 1;
+ }
+ case CC_LP: {
+ *tokenType = SqliteTokenType::TK_LP;
+ return 1;
+ }
+ case CC_RP: {
+ *tokenType = SqliteTokenType::TK_RP;
+ return 1;
+ }
+ case CC_SEMI: {
+ *tokenType = SqliteTokenType::TK_SEMI;
+ return 1;
+ }
+ case CC_PLUS: {
+ *tokenType = SqliteTokenType::TK_PLUS;
+ return 1;
+ }
+ case CC_STAR: {
+ *tokenType = SqliteTokenType::TK_STAR;
+ return 1;
+ }
+ case CC_SLASH: {
+ if (z[1] != '*' || z[2] == 0) {
+ *tokenType = SqliteTokenType::TK_SLASH;
+ return 1;
+ }
+ for (i = 3, c = z[2]; (c != '*' || z[i] != '/') && (c = z[i]) != 0; i++) {
+ }
+ if (c)
+ i++;
+ *tokenType = SqliteTokenType::TK_SPACE; /* IMP: R-22934-25134 */
+ return i;
+ }
+ case CC_PERCENT: {
+ *tokenType = SqliteTokenType::TK_REM;
+ return 1;
+ }
+ case CC_EQ: {
+ *tokenType = SqliteTokenType::TK_EQ;
+ return 1 + (z[1] == '=');
+ }
+ case CC_LT: {
+ if ((c = z[1]) == '=') {
+ *tokenType = SqliteTokenType::TK_LE;
+ return 2;
+ } else if (c == '>') {
+ *tokenType = SqliteTokenType::TK_NE;
+ return 2;
+ } else if (c == '<') {
+ *tokenType = SqliteTokenType::TK_LSHIFT;
+ return 2;
+ } else {
+ *tokenType = SqliteTokenType::TK_LT;
+ return 1;
+ }
+ }
+ case CC_GT: {
+ if ((c = z[1]) == '=') {
+ *tokenType = SqliteTokenType::TK_GE;
+ return 2;
+ } else if (c == '>') {
+ *tokenType = SqliteTokenType::TK_RSHIFT;
+ return 2;
+ } else {
+ *tokenType = SqliteTokenType::TK_GT;
+ return 1;
+ }
+ }
+ case CC_BANG: {
+ if (z[1] != '=') {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_NE;
+ return 2;
+ }
+ }
+ case CC_PIPE: {
+ if (z[1] != '|') {
+ *tokenType = SqliteTokenType::TK_BITOR;
+ return 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_CONCAT;
+ return 2;
+ }
+ }
+ case CC_COMMA: {
+ *tokenType = SqliteTokenType::TK_COMMA;
+ return 1;
+ }
+ case CC_AND: {
+ *tokenType = SqliteTokenType::TK_BITAND;
+ return 1;
+ }
+ case CC_TILDA: {
+ *tokenType = SqliteTokenType::TK_BITNOT;
+ return 1;
+ }
+ case CC_QUOTE: {
+ int delim = z[0];
+ for (i = 1; (c = z[i]) != 0; i++) {
+ if (c == delim) {
+ if (z[i + 1] == delim) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ }
+ if (c == '\'') {
+ *tokenType = SqliteTokenType::TK_STRING;
+ return i + 1;
+ } else if (c != 0) {
+ *tokenType = SqliteTokenType::TK_ID;
+ return i + 1;
+ } else {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ }
+ case CC_DOT: {
+ if (!isdigit(z[1])) {
+ *tokenType = SqliteTokenType::TK_DOT;
+ return 1;
+ }
+ [[fallthrough]];
+ }
+ case CC_DIGIT: {
+ *tokenType = SqliteTokenType::TK_INTEGER;
+ if (z[0] == '0' && (z[1] == 'x' || z[1] == 'X') && isxdigit(z[2])) {
+ for (i = 3; isxdigit(z[i]); i++) {
+ }
+ return i;
+ }
+ for (i = 0; isxdigit(z[i]); i++) {
+ }
+ if (z[i] == '.') {
+ i++;
+ while (isxdigit(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_FLOAT;
+ }
+ if ((z[i] == 'e' || z[i] == 'E') &&
+ (isdigit(z[i + 1]) ||
+ ((z[i + 1] == '+' || z[i + 1] == '-') && isdigit(z[i + 2])))) {
+ i += 2;
+ while (isdigit(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_FLOAT;
+ }
+ while (IdChar(z[i])) {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ i++;
+ }
+ return i;
+ }
+ case CC_QUOTE2: {
+ for (i = 1, c = z[0]; c != ']' && (c = z[i]) != 0; i++) {
+ }
+ *tokenType =
+ c == ']' ? SqliteTokenType::TK_ID : SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ case CC_VARNUM: {
+ *tokenType = SqliteTokenType::TK_VARIABLE;
+ for (i = 1; isdigit(z[i]); i++) {
+ }
+ return i;
+ }
+ case CC_DOLLAR:
+ case CC_VARALPHA: {
+ int n = 0;
+ *tokenType = SqliteTokenType::TK_VARIABLE;
+ for (i = 1; (c = z[i]) != 0; i++) {
+ if (IdChar(c)) {
+ n++;
+ } else if (c == '(' && n > 0) {
+ do {
+ i++;
+ } while ((c = z[i]) != 0 && !isspace(c) && c != ')');
+ if (c == ')') {
+ i++;
+ } else {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ }
+ break;
+ } else if (c == ':' && z[i + 1] == ':') {
+ i++;
+ } else {
+ break;
+ }
+ }
+ if (n == 0)
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return i;
+ }
+ case CC_KYWD0: {
+ for (i = 1; aiClass[z[i]] <= CC_KYWD; i++) {
+ }
+ if (IdChar(z[i])) {
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ break;
+ }
+ if (sqlite3_keyword_check(reinterpret_cast<const char*>(z), i)) {
+ *tokenType = SqliteTokenType::TK_GENERIC_KEYWORD;
+ } else {
+ *tokenType = SqliteTokenType::TK_ID;
+ }
+ return i;
+ }
+ case CC_X: {
+ if (z[1] == '\'') {
+ *tokenType = SqliteTokenType::TK_BLOB;
+ for (i = 2; isdigit(z[i]); i++) {
+ }
+ if (z[i] != '\'' || i % 2) {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ while (z[i] && z[i] != '\'') {
+ i++;
+ }
+ }
+ if (z[i])
+ i++;
+ return i;
+ }
+ [[fallthrough]];
+ }
+ case CC_KYWD:
+ case CC_ID: {
+ i = 1;
+ break;
+ }
+ case CC_BOM: {
+ if (z[1] == 0xbb && z[2] == 0xbf) {
+ *tokenType = SqliteTokenType::TK_SPACE;
+ return 3;
+ }
+ i = 1;
+ break;
+ }
+ case CC_NUL: {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 0;
+ }
+ default: {
+ *tokenType = SqliteTokenType::TK_ILLEGAL;
+ return 1;
+ }
+ }
+ while (IdChar(z[i])) {
+ i++;
+ }
+ *tokenType = SqliteTokenType::TK_ID;
+ return i;
+}
+
+} // namespace
+
+SqliteTokenizer::SqliteTokenizer(const char* sql) : ptr_(sql) {}
+
+SqliteTokenizer::Token SqliteTokenizer::Next() {
+ Token token;
+ const char* start = ptr_;
+ int n = GetSqliteToken(unsigned_ptr(), &token.token_type);
+ ptr_ += n;
+ token.str = std::string_view(start, static_cast<uint32_t>(n));
+ return token;
+}
+
+SqliteTokenizer::Token SqliteTokenizer::NextNonWhitespace() {
+ Token t;
+ for (t = Next(); t.token_type == SqliteTokenType::TK_SPACE; t = Next()) {
+ }
+ return t;
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer.h b/src/trace_processor/sqlite/sqlite_tokenizer.h
new file mode 100644
index 0000000..f805d76
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TOKENIZER_H_
+#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TOKENIZER_H_
+
+#include <optional>
+#include <string_view>
+
+namespace perfetto {
+namespace trace_processor {
+
+// List of token types returnable by |SqliteTokenizer|
+// 1:1 matches the defintions in SQLite.
+enum class SqliteTokenType : uint32_t {
+ TK_SEMI = 1,
+ TK_LP = 22,
+ TK_RP = 23,
+ TK_COMMA = 25,
+ TK_NE = 52,
+ TK_EQ = 53,
+ TK_GT = 54,
+ TK_LE = 55,
+ TK_LT = 56,
+ TK_GE = 57,
+ TK_ID = 59,
+ TK_BITAND = 102,
+ TK_BITOR = 103,
+ TK_LSHIFT = 104,
+ TK_RSHIFT = 105,
+ TK_PLUS = 106,
+ TK_MINUS = 107,
+ TK_STAR = 108,
+ TK_SLASH = 109,
+ TK_REM = 110,
+ TK_CONCAT = 111,
+ TK_PTR = 112,
+ TK_BITNOT = 114,
+ TK_STRING = 117,
+ TK_DOT = 141,
+ TK_FLOAT = 153,
+ TK_BLOB = 154,
+ TK_INTEGER = 155,
+ TK_VARIABLE = 156,
+ TK_SPACE = 183,
+ TK_ILLEGAL = 184,
+
+ // Generic constant which replaces all the keywords in SQLite as we do not
+ // care about the distinguishing between the vast majority of them.
+ TK_GENERIC_KEYWORD = 1000,
+};
+
+// Tokenizes SQL statements according to SQLite SQL language specification:
+// https://www2.sqlite.org/hlr40000.html
+//
+// Usage of this class:
+// SqliteTokenizer tzr(my_sql_string.c_str());
+// for (auto t = tzr.Next(); t.token_type != TK_SEMI; t = tzr.Next()) {
+// // Handle t here
+// }
+class SqliteTokenizer {
+ public:
+ // A single SQL token according to the SQLite standard.
+ struct Token {
+ // The string contents of the token.
+ std::string_view str;
+
+ // The type of the token.
+ SqliteTokenType token_type;
+
+ bool operator==(const Token& o) const {
+ return str == o.str && token_type == o.token_type;
+ }
+ };
+
+ explicit SqliteTokenizer(const char* sql);
+
+ // Returns the next SQL token.
+ Token Next();
+
+ // Returns the next SQL token which is not of type TK_SPACE.
+ Token NextNonWhitespace();
+
+ // Returns the pointer to the start of the next token which will be returned.
+ const char* ptr() const { return ptr_; }
+
+ private:
+ const unsigned char* unsigned_ptr() const {
+ return reinterpret_cast<const unsigned char*>(ptr_);
+ }
+
+ const char* ptr_ = nullptr;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_TOKENIZER_H_
diff --git a/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
new file mode 100644
index 0000000..44946b7
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_tokenizer_unittest.cc
@@ -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.
+ */
+
+#include "src/trace_processor/sqlite/sqlite_tokenizer.h"
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using Token = SqliteTokenizer::Token;
+using Type = SqliteTokenType;
+
+class SqliteTokenizerTest : public ::testing::Test {
+ protected:
+ std::vector<SqliteTokenizer::Token> Tokenize(const char* ptr) {
+ SqliteTokenizer tokenizer(ptr);
+ std::vector<SqliteTokenizer::Token> tokens;
+ for (auto t = tokenizer.Next(); !t.str.empty(); t = tokenizer.Next()) {
+ tokens.push_back(t);
+ }
+ return tokens;
+ }
+};
+
+TEST_F(SqliteTokenizerTest, EmptyString) {
+ ASSERT_THAT(Tokenize(""), testing::IsEmpty());
+}
+
+TEST_F(SqliteTokenizerTest, OnlySpace) {
+ ASSERT_THAT(Tokenize(" "), testing::ElementsAre(Token{" ", Type::TK_SPACE}));
+}
+
+TEST_F(SqliteTokenizerTest, SpaceColon) {
+ ASSERT_THAT(Tokenize(" ;"), testing::ElementsAre(Token{" ", Type::TK_SPACE},
+ Token{";", Type::TK_SEMI}));
+}
+
+TEST_F(SqliteTokenizerTest, Select) {
+ ASSERT_THAT(
+ Tokenize("SELECT * FROM slice;"),
+ testing::ElementsAre(
+ Token{"SELECT", Type::TK_GENERIC_KEYWORD}, Token{" ", Type::TK_SPACE},
+ Token{"*", Type::TK_STAR}, Token{" ", Type::TK_SPACE},
+ Token{"FROM", Type::TK_GENERIC_KEYWORD}, Token{" ", Type::TK_SPACE},
+ Token{"slice", Type::TK_ID}, Token{";", Type::TK_SEMI}));
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/stats_table.cc b/src/trace_processor/sqlite/stats_table.cc
index c9b2ea8..296a2f5 100644
--- a/src/trace_processor/sqlite/stats_table.cc
+++ b/src/trace_processor/sqlite/stats_table.cc
@@ -25,9 +25,7 @@
StatsTable::StatsTable(sqlite3*, const TraceStorage* storage)
: storage_(storage) {}
-void StatsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
- SqliteTable::Register<StatsTable>(db, storage, "stats", RegistrationFlags{});
-}
+StatsTable::~StatsTable() = default;
util::Status StatsTable::Init(int, const char* const*, Schema* schema) {
*schema = Schema(
@@ -47,8 +45,8 @@
return util::OkStatus();
}
-std::unique_ptr<SqliteTable::Cursor> StatsTable::CreateCursor() {
- return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
+std::unique_ptr<SqliteTable::BaseCursor> StatsTable::CreateCursor() {
+ return std::unique_ptr<SqliteTable::BaseCursor>(new Cursor(this));
}
int StatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
@@ -56,7 +54,11 @@
}
StatsTable::Cursor::Cursor(StatsTable* table)
- : SqliteTable::Cursor(table), table_(table), storage_(table->storage_) {}
+ : SqliteTable::BaseCursor(table),
+ table_(table),
+ storage_(table->storage_) {}
+
+StatsTable::Cursor::~Cursor() = default;
base::Status StatsTable::Cursor::Filter(const QueryConstraints&,
sqlite3_value**,
diff --git a/src/trace_processor/sqlite/stats_table.h b/src/trace_processor/sqlite/stats_table.h
index 648299f..2824cb6 100644
--- a/src/trace_processor/sqlite/stats_table.h
+++ b/src/trace_processor/sqlite/stats_table.h
@@ -30,20 +30,22 @@
// The stats table contains diagnostic info and errors that are either:
// - Collected at trace time (e.g., ftrace buffer overruns).
// - Generated at parsing time (e.g., clock events out-of-order).
-class StatsTable : public SqliteTable {
+class StatsTable final
+ : public TypedSqliteTable<StatsTable, const TraceStorage*> {
public:
enum Column { kName = 0, kIndex, kSeverity, kSource, kValue, kDescription };
- class Cursor : public SqliteTable::Cursor {
+ class Cursor final : public SqliteTable::BaseCursor {
public:
explicit Cursor(StatsTable*);
+ ~Cursor() final;
// Implementation of SqliteTable::Cursor.
base::Status Filter(const QueryConstraints&,
sqlite3_value**,
- FilterHistory) override;
- base::Status Next() override;
- bool Eof() override;
- base::Status Column(sqlite3_context*, int N) override;
+ FilterHistory);
+ base::Status Next();
+ bool Eof();
+ base::Status Column(sqlite3_context*, int N);
private:
Cursor(Cursor&) = delete;
@@ -58,14 +60,13 @@
TraceStorage::Stats::IndexMap::const_iterator index_{};
};
- static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
StatsTable(sqlite3*, const TraceStorage*);
+ ~StatsTable() final;
// Table implementation.
- util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
- std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
- int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+ util::Status Init(int, const char* const*, SqliteTable::Schema*) final;
+ std::unique_ptr<SqliteTable::BaseCursor> CreateCursor() final;
+ int BestIndex(const QueryConstraints&, BestIndexInfo*) final;
private:
const TraceStorage* const storage_;
diff --git a/src/trace_processor/stdlib/android/BUILD.gn b/src/trace_processor/stdlib/android/BUILD.gn
index 86bdf6d..9596be8 100644
--- a/src/trace_processor/stdlib/android/BUILD.gn
+++ b/src/trace_processor/stdlib/android/BUILD.gn
@@ -18,9 +18,12 @@
deps = [ "startup" ]
sources = [
"battery.sql",
+ "battery_stats.sql",
"binder.sql",
"monitor_contention.sql",
+ "network_packets.sql",
"process_metadata.sql",
"slices.sql",
+ "statsd.sql",
]
}
diff --git a/src/trace_processor/stdlib/android/battery_stats.sql b/src/trace_processor/stdlib/android/battery_stats.sql
new file mode 100644
index 0000000..30cea54
--- /dev/null
+++ b/src/trace_processor/stdlib/android/battery_stats.sql
@@ -0,0 +1,210 @@
+--
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+SELECT IMPORT('common.timestamps');
+
+-- Converts a battery_stats counter value to human readable string.
+--
+-- @arg track STRING The counter track name (e.g. 'battery_stats.audio').
+-- @arg value FLOAT The counter value.
+-- @ret STRING The human-readable name for the counter value.
+SELECT CREATE_FUNCTION(
+ 'ANDROID_BATTERY_STATS_COUNTER_TO_STRING(track STRING, value FLOAT)',
+ 'STRING',
+ '
+ SELECT
+ CASE
+ WHEN ($track = "battery_stats.wifi_scan" OR
+ $track = "battery_stats.wifi_radio" OR
+ $track = "battery_stats.mobile_radio" OR
+ $track = "battery_stats.audio" OR
+ $track = "battery_stats.video" OR
+ $track = "battery_stats.camera" OR
+ $track = "battery_stats.power_save" OR
+ $track = "battery_stats.phone_in_call")
+ THEN
+ CASE $value
+ WHEN 0 THEN "inactive"
+ WHEN 1 THEN "active"
+ ELSE "unknown"
+ END
+ WHEN $track = "battery_stats.wifi"
+ THEN
+ CASE $value
+ WHEN 0 THEN "off"
+ WHEN 1 THEN "on"
+ ELSE "unknown"
+ END
+ WHEN $track = "battery_stats.phone_state"
+ THEN
+ CASE $value
+ WHEN 0 THEN "in"
+ WHEN 1 THEN "out"
+ WHEN 2 THEN "emergency"
+ WHEN 3 THEN "off"
+ ELSE "unknown"
+ END
+ WHEN ($track = "battery_stats.phone_signal_strength" OR
+ $track = "battery_stats.wifi_signal_strength")
+ THEN
+ CASE $value
+ WHEN 0 THEN "none"
+ WHEN 1 THEN "poor"
+ WHEN 2 THEN "moderate"
+ WHEN 3 THEN "good"
+ WHEN 4 THEN "great"
+ ELSE "unknown"
+ END
+ WHEN $track = "battery_stats.wifi_suppl"
+ THEN
+ CASE $value
+ WHEN 0 THEN "invalid"
+ WHEN 1 THEN "disconnected"
+ WHEN 2 THEN "disabled"
+ WHEN 3 THEN "inactive"
+ WHEN 4 THEN "scanning"
+ WHEN 5 THEN "authenticating"
+ WHEN 6 THEN "associating"
+ WHEN 7 THEN "associated"
+ WHEN 8 THEN "4-way-handshake"
+ WHEN 9 THEN "group-handshake"
+ WHEN 10 THEN "completed"
+ WHEN 11 THEN "dormant"
+ WHEN 12 THEN "uninitialized"
+ ELSE "unknown"
+ END
+ WHEN $track = "battery_stats.data_conn"
+ THEN
+ CASE $value
+ WHEN 0 THEN "Out of service"
+ WHEN 1 THEN "2.5G (GPRS)"
+ WHEN 2 THEN "2.7G (EDGE)"
+ WHEN 3 THEN "3G (UMTS)"
+ WHEN 4 THEN "3G (CDMA)"
+ WHEN 5 THEN "3G (EVDO Rel 0)"
+ WHEN 6 THEN "3G (EVDO Rev A)"
+ WHEN 7 THEN "3G (LXRTT)"
+ WHEN 8 THEN "3.5G (HSDPA)"
+ WHEN 9 THEN "3.5G (HSUPA)"
+ WHEN 10 THEN "3.5G (HSPA)"
+ WHEN 11 THEN "2G (IDEN)"
+ WHEN 12 THEN "3G (EVDO Rev B)"
+ WHEN 13 THEN "4G (LTE)"
+ WHEN 14 THEN "3.5G (eHRPD)"
+ WHEN 15 THEN "3.7G (HSPA+)"
+ WHEN 16 THEN "2G (GSM)"
+ WHEN 17 THEN "3G (TD SCDMA)"
+ WHEN 18 THEN "Wifi calling (IWLAN)"
+ WHEN 19 THEN "4.5G (LTE CA)"
+ WHEN 20 THEN "5G (NR)"
+ WHEN 21 THEN "Emergency calls only"
+ WHEN 22 THEN "Other"
+ ELSE "unknown"
+ END
+ ELSE CAST($value AS text)
+ END
+ '
+);
+
+
+-- View of human readable battery stats counter-based states. These are recorded
+-- by BatteryStats as a bitmap where each 'category' has a unique value at any
+-- given time.
+--
+-- @column ts Timestamp in nanoseconds.
+-- @column dur The duration the state was active.
+-- @column track_name The name of the counter track.
+-- @column value The counter value as a number.
+-- @column value_name The counter value as a human-readable string.
+CREATE VIEW android_battery_stats_state AS
+SELECT
+ ts,
+ name AS track_name,
+ CAST(value AS INT64) AS value,
+ ANDROID_BATTERY_STATS_COUNTER_TO_STRING(name, value) AS value_name,
+ IFNULL(LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts, -1) AS dur
+FROM counter
+JOIN counter_track
+ ON counter.track_id = counter_track.id
+WHERE counter_track.name GLOB 'battery_stats.*';
+
+
+-- View of slices derived from battery_stats events. Battery stats records all
+-- events as instants, however some may indicate whether something started or
+-- stopped with a '+' or '-' prefix. Events such as jobs, top apps, foreground
+-- apps or long wakes include these details and allow drawing slices between
+-- instant events found in a trace.
+--
+-- For example, we may see an event like the following on 'battery_stats.top':
+--
+-- -top=10215:"com.google.android.apps.nexuslauncher"
+--
+-- This view will find the associated start ('+top') with the matching suffix
+-- (everything after the '=') to construct a slice. It computes the timestamp
+-- and duration from the events and extract the details as follows:
+--
+-- track_name='battery_stats.top'
+-- str_value='com.google.android.apps.nexuslauncher'
+-- int_value=10215
+--
+-- @column track_name The battery stats track name.
+-- @column ts Timestamp in nanoseconds.
+-- @column dur The duration of the event.
+-- @column str_value The string part of the event identifier.
+-- @column int_value The integer part of the event identifier.
+CREATE VIEW android_battery_stats_event_slices AS
+WITH
+ event_markers AS (
+ SELECT
+ ts,
+ track.name AS track_name,
+ str_split(slice.name, '=', 1) AS key,
+ substr(slice.name, 1, 1) = '+' AS start
+ FROM slice
+ JOIN track
+ ON slice.track_id = track.id
+ WHERE
+ track_name GLOB 'battery_stats.*'
+ AND substr(slice.name, 1, 1) IN ('+', '-')
+ ),
+ with_neighbors AS (
+ SELECT
+ *,
+ LAG(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS last_ts,
+ LEAD(ts) OVER (PARTITION BY track_name, key ORDER BY ts) AS next_ts
+ FROM event_markers
+ ),
+ -- Note: query performance depends on the ability to push down filters on
+ -- the track_name. It would be more clear below to have two queries and union
+ -- them, but doing so prevents push down through the above window functions.
+ event_spans AS (
+ SELECT
+ track_name, key,
+ IIF(start, ts, TRACE_START()) AS ts,
+ IIF(start, next_ts, ts) AS end_ts
+ FROM with_neighbors
+ -- For the majority of events, we take the `start` event and compute the dur
+ -- based on next_ts. In the off chance we get an end event with no prior
+ -- start (matched by the second half of this where), we can create an event
+ -- starting from the beginning of the trace ending at the current event.
+ WHERE (start OR last_ts IS NULL)
+ )
+SELECT
+ ts,
+ IFNULL(end_ts-ts, -1) AS dur,
+ track_name,
+ str_split(key, '"', 1) AS str_value,
+ CAST(str_split(key, ':', 0) AS INT64) AS int_value
+FROM event_spans;
diff --git a/src/trace_processor/stdlib/android/binder.sql b/src/trace_processor/stdlib/android/binder.sql
index 51a9d75..a31fff0 100644
--- a/src/trace_processor/stdlib/android/binder.sql
+++ b/src/trace_processor/stdlib/android/binder.sql
@@ -90,6 +90,7 @@
thread.name AS thread_name,
thread.utid AS utid,
thread.tid AS tid,
+ process.pid AS pid,
process.upid AS upid,
slice.ts,
slice.dur,
@@ -112,6 +113,7 @@
reply_process.name AS server_process,
reply_thread.utid AS server_utid,
reply_thread.tid AS server_tid,
+ reply_process.pid AS server_pid,
reply_process.upid AS server_upid,
aidl.name AS aidl_name
FROM binder_txn
@@ -132,6 +134,7 @@
upid AS client_upid,
utid AS client_utid,
tid AS client_tid,
+ pid AS client_pid,
is_main_thread,
ts AS client_ts,
dur AS client_dur,
@@ -141,6 +144,7 @@
server_upid,
server_utid,
server_tid,
+ server_pid,
server_ts,
server_dur
FROM binder_reply
diff --git a/src/trace_processor/stdlib/android/monitor_contention.sql b/src/trace_processor/stdlib/android/monitor_contention.sql
index c1de58f..9fae0c7 100644
--- a/src/trace_processor/stdlib/android/monitor_contention.sql
+++ b/src/trace_processor/stdlib/android/monitor_contention.sql
@@ -145,7 +145,7 @@
AS
SELECT ancestor.parent_id AS id FROM slice
JOIN slice ancestor ON ancestor.id = slice.parent_id
- WHERE ancestor.name LIKE 'Lock contention on a monitor lock%'
+ WHERE ancestor.name GLOB 'Lock contention on a monitor lock*'
GROUP BY ancestor.id;
-- Contains parsed monitor contention slices.
@@ -192,10 +192,13 @@
slice.dur,
slice.track_id,
thread.is_main_thread AS is_blocked_thread_main,
+ thread.tid AS blocked_thread_tid,
blocking_thread.is_main_thread AS is_blocking_thread_main,
+ blocking_thread.tid AS blocking_thread_tid,
binder_reply.id AS binder_reply_id,
binder_reply.ts AS binder_reply_ts,
- binder_reply_thread.tid AS binder_reply_tid
+ binder_reply_thread.tid AS binder_reply_tid,
+ process.pid
FROM slice
JOIN thread_track
ON thread_track.id = slice.track_id
@@ -208,7 +211,7 @@
LEFT JOIN thread_track binder_reply_thread_track ON binder_reply.track_id = binder_reply_thread_track.id
LEFT JOIN thread binder_reply_thread ON binder_reply_thread_track.utid = binder_reply_thread.utid
JOIN thread blocking_thread ON blocking_thread.tid = blocking_tid AND blocking_thread.upid = thread.upid
-WHERE slice.name LIKE 'monitor contention%'
+WHERE slice.name GLOB 'monitor contention*'
AND slice.dur != -1
AND internal_broken_android_monitor_contention.id IS NULL
AND short_blocking_method IS NOT NULL
@@ -244,13 +247,34 @@
LEFT JOIN android_monitor_contention parent ON child.blocked_utid = parent.blocking_utid
AND parent.ts BETWEEN child.ts AND child.ts + child.dur;
+-- First blocked node on a lock, i.e nodes with |waiter_count| = 0. The |dur| here is adjusted
+-- to only account for the time between the first thread waiting and the first thread to acquire
+-- the lock. That way, the thread state span joins below only compute the thread states where
+-- the blocking thread is actually holding the lock. This avoids counting the time when another
+-- waiter acquired the lock before the first waiter.
+CREATE VIEW internal_first_blocked_contention
+ AS
+SELECT start.id, start.blocking_utid, start.ts, MIN(end.ts + end.dur) - start.ts AS dur
+FROM android_monitor_contention_chain start
+JOIN android_monitor_contention_chain END
+ ON
+ start.blocking_utid = end.blocking_utid
+ AND start.blocking_method = end.blocking_method
+ AND end.ts BETWEEN start.ts AND start.ts + start.dur
+WHERE start.waiter_count = 0
+GROUP BY start.id;
+
CREATE VIEW internal_blocking_thread_state
AS
SELECT utid AS blocking_utid, ts, dur, state, blocked_function
FROM thread_state;
--- Contains the span join of the |android_monitor_contention_chain| with their
--- blocking thread thread state.
+-- Contains the span join of the first waiters in the |android_monitor_contention_chain| with their
+-- blocking_thread thread state.
+
+-- Note that we only span join the duration where the lock was actually held and contended.
+-- This can be less than the duration the lock was 'waited on' when a different waiter acquired the
+-- lock earlier than the first waiter.
--
-- @column parent_id Id of slice blocking the blocking_thread.
-- @column blocking_method Name of the method holding the lock.
@@ -279,14 +303,16 @@
-- @column blocked_function Blocked kernel function of the blocking thread.
CREATE VIRTUAL TABLE android_monitor_contention_chain_thread_state
USING
- SPAN_JOIN(android_monitor_contention_chain PARTITIONED blocking_utid,
+ SPAN_JOIN(internal_first_blocked_contention PARTITIONED blocking_utid,
internal_blocking_thread_state PARTITIONED blocking_utid);
--- Aggregated blocked_functions on the 'blocking thread', the thread holding the lock.
+-- Aggregated thread_states on the 'blocking thread', the thread holding the lock.
-- This builds on the data from |android_monitor_contention_chain| and
-- for each contention slice, it returns the aggregated sum of all the thread states on the
-- blocking thread.
--
+-- Note that this data is only available for the first waiter on a lock.
+--
-- @column id Slice id of the monitor contention.
-- @column thread_state A |thread_state| that occurred in the blocking thread during the contention.
-- @column thread_state_dur Total time the blocking thread spent in the |thread_state| during
@@ -308,6 +334,8 @@
-- for each contention, it returns the aggregated sum of all the kernel
-- blocked function durations on the blocking thread.
--
+-- Note that this data is only available for the first waiter on a lock.
+--
-- @column id Slice id of the monitor contention.
-- @column blocked_function Blocked kernel function in a thread state in the blocking thread during
-- the contention.
diff --git a/src/trace_processor/stdlib/android/network_packets.sql b/src/trace_processor/stdlib/android/network_packets.sql
new file mode 100644
index 0000000..a88615a
--- /dev/null
+++ b/src/trace_processor/stdlib/android/network_packets.sql
@@ -0,0 +1,52 @@
+--
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- Android network packet events (from android.network_packets data source).
+--
+-- @column ts Timestamp in nanoseconds.
+-- @column dur Duration (non-zero only in aggregate events)
+-- @column track_name The track name (interface and direction)
+-- @column package_name Traffic package source (or uid=$X if not found)
+-- @column iface Traffic interface name (linux interface name)
+-- @column direction Traffic direction ('Transmitted' or 'Received')
+-- @column packet_count Number of packets in this event
+-- @column packet_length Number of bytes in this event (wire size)
+-- @column packet_transport Transport used for traffic in this event
+-- @column packet_tcp_flags TCP flags used by tcp frames in this event
+-- @column socket_tag The Android traffic tag of the network socket
+-- @column socket_uid The Linux user id of the network socket
+-- @column local_port The local port number (for udp or tcp only)
+-- @column remote_port The remote port number (for udp or tcp only)
+CREATE VIEW android_network_packets AS
+SELECT
+ ts,
+ dur,
+ track.name AS track_name,
+ slice.name AS package_name,
+ str_split(track.name, ' ', 0) AS iface,
+ str_split(track.name, ' ', 1) AS direction,
+ ifnull(extract_arg(arg_set_id, 'packet_count'), 1) AS packet_count,
+ extract_arg(arg_set_id, 'packet_length') AS packet_length,
+ extract_arg(arg_set_id, 'packet_transport') AS packet_transport,
+ extract_arg(arg_set_id, 'packet_tcp_flags') AS packet_tcp_flags,
+ extract_arg(arg_set_id, 'socket_tag') AS socket_tag,
+ extract_arg(arg_set_id, 'socket_uid') AS socket_uid,
+ extract_arg(arg_set_id, 'local_port') AS local_port,
+ extract_arg(arg_set_id, 'remote_port') AS remote_port
+FROM slice
+JOIN track
+ ON slice.track_id = track.id
+WHERE (track.name GLOB '* Transmitted' OR
+ track.name GLOB '* Received');
diff --git a/src/trace_processor/stdlib/android/process_metadata.sql b/src/trace_processor/stdlib/android/process_metadata.sql
index 20fb82e..6154f00 100644
--- a/src/trace_processor/stdlib/android/process_metadata.sql
+++ b/src/trace_processor/stdlib/android/process_metadata.sql
@@ -50,11 +50,19 @@
LEFT JOIN internal_uid_package_count ON process.android_appid = internal_uid_package_count.uid
LEFT JOIN package_list plist
ON (
- process.android_appid = plist.uid
- AND internal_uid_package_count.uid = plist.uid
- AND (
- -- unique match
- internal_uid_package_count.cnt = 1
- -- or process name starts with the package name
- OR process.name GLOB plist.package_name || '*')
+ (
+ process.android_appid = plist.uid
+ AND internal_uid_package_count.uid = plist.uid
+ AND (
+ -- unique match
+ internal_uid_package_count.cnt = 1
+ -- or process name starts with the package name
+ OR process.name GLOB plist.package_name || '*')
+ )
+ OR
+ (
+ -- isolated processes can only be matched based on the name prefix
+ process.android_appid >= 90000 AND process.android_appid < 100000
+ AND STR_SPLIT(process.name, ':', 0) GLOB plist.package_name || '*'
+ )
);
diff --git a/src/trace_processor/stdlib/android/statsd.sql b/src/trace_processor/stdlib/android/statsd.sql
new file mode 100644
index 0000000..9ce6bf1
--- /dev/null
+++ b/src/trace_processor/stdlib/android/statsd.sql
@@ -0,0 +1,60 @@
+--
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+-- Statsd atoms.
+--
+-- A subset of the slice table containing statsd atom instant events.
+--
+-- @column id,
+-- @column type,
+-- @column ts,
+-- @column dur,
+-- @column arg_set_id,
+-- @column thread_instruction_count,
+-- @column thread_instruction_delta,
+-- @column track_id,
+-- @column category,
+-- @column name,
+-- @column depth,
+-- @column stack_id,
+-- @column parent_stack_id,
+-- @column parent_id,
+-- @column thread_ts,
+-- @column thread_dur,
+CREATE VIEW android_statsd_atoms AS
+SELECT
+ slice.id AS id,
+ slice.type AS type,
+ slice.ts AS ts,
+ slice.dur AS dur,
+ slice.arg_set_id AS arg_set_id,
+ slice.thread_instruction_count AS thread_instruction_count,
+ slice.thread_instruction_delta AS thread_instruction_delta,
+ slice.track_id AS track_id,
+ slice.category AS category,
+ slice.name AS name,
+ slice.depth AS depth,
+ slice.stack_id AS stack_id,
+ slice.parent_stack_id AS parent_stack_id,
+ slice.parent_id AS parent_id,
+ slice.thread_ts AS thread_ts,
+ slice.thread_dur AS thread_dur
+FROM slice
+JOIN track ON slice.track_id = track.id
+WHERE
+ track.name = 'Statsd Atoms';
+
+
diff --git a/src/trace_processor/stdlib/chrome/BUILD.gn b/src/trace_processor/stdlib/chrome/BUILD.gn
index b3ea475..e9881c6 100644
--- a/src/trace_processor/stdlib/chrome/BUILD.gn
+++ b/src/trace_processor/stdlib/chrome/BUILD.gn
@@ -15,5 +15,10 @@
import("../../../../gn/perfetto_sql.gni")
perfetto_sql_source_set("chrome_sql") {
- sources = [ "cpu_powerups.sql" ]
+ sources = [
+ "chrome_scrolls.sql",
+ "cpu_powerups.sql",
+ "histograms.sql",
+ "speedometer.sql",
+ ]
}
diff --git a/src/trace_processor/stdlib/chrome/chrome_scrolls.sql b/src/trace_processor/stdlib/chrome/chrome_scrolls.sql
new file mode 100644
index 0000000..1ad7f31
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/chrome_scrolls.sql
@@ -0,0 +1,68 @@
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+DROP VIEW IF EXISTS chrome_scrolls;
+
+-- Defines slices for all of the individual scrolls in a trace based on the
+-- LatencyInfo-based scroll definition.
+--
+-- @column id The unique identifier of the scroll.
+-- @column ts The start timestamp of the scroll.
+-- @column dur The duration of the scroll.
+--
+-- NOTE: this view of top level scrolls is based on the LatencyInfo definition
+-- of a scroll, which differs subtly from the definition based on
+-- EventLatencies.
+-- TODO(b/278684408): add support for tracking scrolls across multiple Chrome/
+-- WebView instances. Currently gesture_scroll_id unique within an instance, but
+-- is not unique across multiple instances. Switching to an EventLatency based
+-- definition of scrolls should resolve this.
+CREATE VIEW chrome_scrolls AS
+WITH all_scrolls AS (
+ SELECT
+ name,
+ ts,
+ dur,
+ extract_arg(arg_set_id, 'chrome_latency_info.gesture_scroll_id') AS scroll_id
+ FROM slice
+ WHERE name GLOB 'InputLatency::GestureScroll*'
+ AND extract_arg(arg_set_id, 'chrome_latency_info.gesture_scroll_id') IS NOT NULL
+),
+scroll_starts AS (
+ SELECT
+ scroll_id,
+ MIN(ts) AS scroll_start_ts
+ FROM all_scrolls
+ WHERE name = 'InputLatency::GestureScrollBegin'
+ GROUP BY scroll_id
+), scroll_ends AS (
+ SELECT
+ scroll_id,
+ MIN(ts) AS scroll_end_ts
+ FROM all_scrolls
+ WHERE name = 'InputLatency::GestureScrollEnd'
+ GROUP BY scroll_id
+)
+SELECT
+ sa.scroll_id AS id,
+ MIN(ts) AS ts,
+ CAST(MAX(ts + dur) - MIN(ts) AS INT) AS dur,
+ IFNULL(ss.scroll_start_ts, -1) AS scroll_start_ts,
+ IFNULL(se.scroll_end_ts, -1) AS scroll_end_ts
+FROM all_scrolls sa
+ LEFT JOIN scroll_starts ss ON
+ sa.scroll_id = ss.scroll_id
+ LEFT JOIN scroll_ends se ON
+ sa.scroll_id = se.scroll_id
+GROUP BY sa.scroll_id;
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/chrome/histograms.sql b/src/trace_processor/stdlib/chrome/histograms.sql
new file mode 100644
index 0000000..db354e3
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/histograms.sql
@@ -0,0 +1,46 @@
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+DROP VIEW IF EXISTS chrome_histograms;
+
+-- A helper view on top of the histogram events emitted by Chrome.
+-- Requires "disabled-by-default-histogram_samples" Chrome category.
+--
+-- @column name The name of the histogram.
+-- @column value The value of the histogram sample.
+-- @column ts Alias of |slice.ts|.
+-- @column thread_name Thread name.
+-- @column utid Utid of the thread.
+-- @column tid Tid of the thread.
+-- @column process_name Process name.
+-- @column upid Upid of the process.
+-- @column pid Pid of the process.
+CREATE VIEW chrome_histograms AS
+SELECT
+ extract_arg(slice.arg_set_id, "chrome_histogram_sample.name") as name,
+ extract_arg(slice.arg_set_id, "chrome_histogram_sample.sample") as value,
+ ts,
+ thread.name as thread_name,
+ thread.utid as utid,
+ thread.tid as tid,
+ process.name as process_name,
+ process.upid as upid,
+ process.pid as pid
+FROM slice
+JOIN thread_track ON thread_track.id = slice.track_id
+JOIN thread USING (utid)
+JOIN process USING (upid)
+WHERE
+ slice.name = "HistogramSample"
+ AND category = "disabled-by-default-histogram_samples";
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/chrome/speedometer.sql b/src/trace_processor/stdlib/chrome/speedometer.sql
new file mode 100644
index 0000000..652940a
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/speedometer.sql
@@ -0,0 +1,202 @@
+-- Copyright 2023 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- https://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- Annotates a trace with Speedometer 2.1 related information.
+--
+-- The scripts below analyse traces with the following tracing options
+-- enabled:
+--
+-- - Chromium:
+-- "blink.user_timing".
+--
+-- NOTE: A regular speedometer run (e.g. from the website) will generate the
+-- required events. No need to add any extra JS or anything.
+--
+-- Noteworthy tables:
+-- speedometer_mark: List of marks (event slices) emitted by Speedometer.
+-- These are the points in time Speedometer makes a clock reading to
+-- compute intervals of time for the final score.
+-- speedometer_measure_slice: Augmented slices for Speedometer measurements.
+-- These are the intervals of time Speedometer uses to compute the final
+-- score.
+-- speedometer_iteration_slice: Slice that covers one Speedometer iteration
+-- and has the total_time and score for it. If you average all the scores
+-- over all iterations you get the final Speedometer score for the run.
+
+-- List of marks (event slices) emitted by Speedometer.
+-- These are the points in time Speedometer makes a clock reading to compute
+-- intervals of time for the final score.
+--
+-- @column slice_id Slice this data refers to.
+-- @column iteration Speedometer iteration the mark belongs to.
+-- @column suite_name Suite name
+-- @column test_name Test name
+-- @column mark_type Type of mark (start, sync-end, async-end)
+CREATE VIEW internal_chrome_speedometer_mark
+AS
+WITH
+ speedometer_21_suite_name(suite_name) AS (
+ VALUES
+ ('VanillaJS-TodoMVC'),
+ ('Vanilla-ES2015-TodoMVC'),
+ ('Vanilla-ES2015-Babel-Webpack-TodoMVC'),
+ ('React-TodoMVC'),
+ ('React-Redux-TodoMVC'),
+ ('EmberJS-TodoMVC'),
+ ('EmberJS-Debug-TodoMVC'),
+ ('BackboneJS-TodoMVC'),
+ ('AngularJS-TodoMVC'),
+ ('Angular2-TypeScript-TodoMVC'),
+ ('VueJS-TodoMVC'),
+ ('jQuery-TodoMVC'),
+ ('Preact-TodoMVC'),
+ ('Inferno-TodoMVC'),
+ ('Elm-TodoMVC'),
+ ('Flight-TodoMVC')
+ ),
+ speedometer_21_test_name(test_name) AS (
+ VALUES
+ ('Adding100Items'),
+ ('CompletingAllItems'),
+ -- This seems to be an issue with Speedometer 2.1. All tests delete all items,
+ -- but for some reason the test names do not match for all suites.
+ ('DeletingAllItems'),
+ ('DeletingItems')
+ ),
+ speedometer_21_test_mark_type(mark_type) AS (
+ VALUES
+ ('start'),
+ ('sync-end'),
+ ('async-end')
+ ),
+ -- Make sure we only look at slices with names we expect.
+ speedometer_mark_name AS (
+ SELECT
+ s.suite_name || '.' || t.test_name || '-' || m.mark_type AS name,
+ s.suite_name,
+ t.test_name,
+ m.mark_type
+ FROM
+ speedometer_21_suite_name AS s,
+ speedometer_21_test_name AS t,
+ speedometer_21_test_mark_type AS m
+ )
+SELECT
+ s.id AS slice_id,
+ RANK() OVER (PARTITION BY name ORDER BY ts ASC) AS iteration,
+ m.suite_name,
+ m.test_name,
+ m.mark_type
+FROM slice AS s
+JOIN speedometer_mark_name AS m
+ USING (name)
+WHERE category = 'blink.user_timing';
+
+-- Augmented slices for Speedometer measurements.
+-- These are the intervals of time Speedometer uses to compute the final score.
+-- There are two intervals that are measured for every test: sync and async
+-- sync is the time between the start and sync-end marks, async is the time
+-- between the sync-end and async-end marks.
+--
+-- @column iteration Speedometer iteration the mark belongs to.
+-- @column suite_name Suite name
+-- @column test_name Test name
+-- @column measure_type Type of the measure (sync or async)
+-- @column ts Start timestamp of the measure
+-- @column dur Duration of the measure
+CREATE VIEW chrome_speedometer_measure
+AS
+WITH
+ -- Get the 3 test timestamps (start, sync-end, async-end) in one row. Using a
+ -- the LAG window function and partitioning by test. 2 out of the 3 rows
+ -- generated per test will have some NULL ts values.
+ augmented AS (
+ SELECT
+ iteration,
+ suite_name,
+ test_name,
+ ts AS async_end_ts,
+ LAG(ts, 1)
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS sync_end_ts,
+ LAG(ts, 2)
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS start_ts,
+ COUNT()
+ OVER (PARTITION BY iteration, suite_name, test_name ORDER BY ts ASC)
+ AS mark_count
+ FROM internal_chrome_speedometer_mark
+ JOIN slice
+ USING (slice_id)
+ ),
+ filtered AS (
+ SELECT *
+ FROM augmented
+ -- This server 2 purposes: make sure we have all the marks (think truncated
+ -- trace), and remove the NULL ts values due to the LAG window function.
+ WHERE mark_count = 3
+ )
+SELECT
+ iteration,
+ suite_name,
+ test_name,
+ 'async' AS measure_type,
+ sync_end_ts AS ts,
+ async_end_ts - sync_end_ts AS dur
+FROM filtered
+UNION ALL
+SELECT
+ iteration,
+ suite_name,
+ test_name,
+ 'sync' AS measure_type,
+ start_ts AS ts,
+ sync_end_ts - start_ts AS dur
+FROM filtered;
+
+-- Slice that covers one Speedometer iteration.
+-- This slice is actually estimated as a default Speedometer run will not emit
+-- marks to cover this interval. The metrics associated are the same ones
+-- Speedometer would output, but note we use ns precision (Speedometer uses
+-- ~100us) so the actual values might differ a bit. Also note Speedometer
+-- returns the values in ms these here and in ns.
+--
+-- @column iteration Speedometer iteration.
+-- @column ts Start timestamp of the iteration
+-- @column dur Duration of the iteration
+-- @column total Total duration of the measures in this iteration
+-- @column mean Average suite duration for this iteration.
+-- @column geomean Geometric mean of the suite durations for this iteration.
+-- @column score Speedometer score for this iteration (The total score for a
+-- run in the average of all iteration scores).
+CREATE VIEW chrome_speedometer_iteration
+AS
+SELECT
+ iteration,
+ MIN(start) AS ts,
+ MAX(end) - MIN(start) AS dur,
+ SUM(suite_total) AS total,
+ AVG(suite_total)AS mean,
+ -- Compute geometric mean using LN instead of multiplication to prevent
+ -- overflows
+ EXP(AVG(LN(suite_total))) AS geomean,
+ 1e9 / EXP(AVG(LN(suite_total))) * 60 / 3 AS score
+FROM
+ (
+ SELECT
+ iteration, SUM(dur) AS suite_total, MIN(ts) AS start, MAX(ts + dur) AS end
+ FROM chrome_speedometer_measure
+ GROUP BY suite_name, iteration
+ )
+GROUP BY iteration;
diff --git a/src/trace_processor/stdlib/common/cpus.sql b/src/trace_processor/stdlib/common/cpus.sql
index 3caec3f..460c80c 100644
--- a/src/trace_processor/stdlib/common/cpus.sql
+++ b/src/trace_processor/stdlib/common/cpus.sql
@@ -17,9 +17,9 @@
CREATE TABLE internal_cpu_sizes AS
SELECT 0 AS n, 'little' AS size
UNION
-SELECT 1 AS n, 'big' AS size
+SELECT 1 AS n, 'mid' AS size
UNION
-SELECT 2 AS n, 'huge' AS size;
+SELECT 2 AS n, 'big' AS size;
CREATE TABLE internal_ranked_cpus AS
SELECT
@@ -46,7 +46,7 @@
-- homogeneous systems this returns NULL.
--
-- @arg cpu_index INT Index of the CPU whose size we will guess.
--- @ret STRING A descriptive size ('little', 'big', 'huge', etc) or NULL if we have insufficient information.
+-- @ret STRING A descriptive size ('little', 'mid', 'big', etc) or NULL if we have insufficient information.
SELECT CREATE_FUNCTION(
'GUESS_CPU_SIZE(cpu_index INT)',
'STRING',
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 66fc940..67bdd0b 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -42,6 +42,7 @@
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
@@ -457,6 +458,13 @@
const tables::SliceTable& slice_table() const { return slice_table_; }
tables::SliceTable* mutable_slice_table() { return &slice_table_; }
+ const tables::SpuriousSchedWakeupTable& spurious_sched_wakeup_table() const {
+ return spurious_sched_wakeup_table_;
+ }
+ tables::SpuriousSchedWakeupTable* mutable_spurious_sched_wakeup_table() {
+ return &spurious_sched_wakeup_table_;
+ }
+
const tables::FlowTable& flow_table() const { return flow_table_; }
tables::FlowTable* mutable_flow_table() { return &flow_table_; }
@@ -888,6 +896,8 @@
// Slices from CPU scheduling data.
tables::SchedSliceTable sched_slice_table_{&string_pool_};
+ tables::SpuriousSchedWakeupTable spurious_sched_wakeup_table_{&string_pool_};
+
// Additional attributes for virtual track slices (sub-type of
// NestableSlices).
VirtualTrackSlices virtual_track_slices_;
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 9ec397f..b8d69a8 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -23,6 +23,7 @@
"memory_tables.py",
"metadata_tables.py",
"profiler_tables.py",
+ "sched_tables.py",
"slice_tables.py",
"trace_proto_tables.py",
"track_tables.py",
diff --git a/src/trace_processor/tables/metadata_tables.py b/src/trace_processor/tables/metadata_tables.py
index 0219aa9..093db01 100644
--- a/src/trace_processor/tables/metadata_tables.py
+++ b/src/trace_processor/tables/metadata_tables.py
@@ -170,6 +170,7 @@
C('cpu', CppUint32()),
C('utid', CppTableId(THREAD_TABLE)),
C('arg_set_id', CppUint32()),
+ C('common_flags', CppUint32())
],
tabledoc=TableDoc(
doc='''
@@ -177,7 +178,7 @@
table only exists for debugging purposes and should not be relied on
in production usecases (i.e. metrics, standard library etc).
''',
- group='Misc',
+ group='Events',
columns={
'arg_set_id':
ColumnDoc(
@@ -193,7 +194,9 @@
'cpu':
'The CPU this event was emitted on.',
'utid':
- 'The thread this event was emitted on.'
+ 'The thread this event was emitted on.',
+ 'common_flags':
+ 'Ftrace event flags for this event. Currently only emitted for sched_waking events.'
}))
FTRACE_EVENT_TABLE = Table(
@@ -204,12 +207,12 @@
columns=[],
tabledoc=TableDoc(
doc='''
- Contains all the ftrace events in the trace. This table exists only for
- debugging purposes and should not be relied on in production usecases
- (i.e. metrics, standard library etc). Note also that this table might
- be empty if raw ftrace parsing has been disabled.
- ''',
- group='Misc',
+ Contains all the ftrace events in the trace. This table exists only
+ for debugging purposes and should not be relied on in production
+ usecases (i.e. metrics, standard library etc). Note also that this
+ table might be empty if raw ftrace parsing has been disabled.
+ ''',
+ group='Events',
columns={}))
ARG_TABLE = Table(
diff --git a/src/trace_processor/tables/sched_tables.py b/src/trace_processor/tables/sched_tables.py
new file mode 100644
index 0000000..d65c8be
--- /dev/null
+++ b/src/trace_processor/tables/sched_tables.py
@@ -0,0 +1,166 @@
+# 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.
+"""Contains tables for relevant for sched."""
+
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import CppInt32
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppSelfTableId
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from python.generators.trace_processor_table.public import WrappingSqlView
+
+SCHED_SLICE_TABLE = Table(
+ python_module=__file__,
+ class_name='SchedSliceTable',
+ sql_name='sched_slice',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('dur', CppInt64()),
+ C('cpu', CppUint32()),
+ C('utid', CppUint32()),
+ C('end_state', CppString()),
+ C('priority', CppInt32()),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table holds slices with kernel thread scheduling information.
+ These slices are collected when the Linux "ftrace" data source is
+ used with the "sched/switch" and "sched/wakeup*" events enabled.
+
+ The rows in this table will always have a matching row in the
+ |thread_state| table with |thread_state.state| = 'Running'
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ '''The timestamp at the start of the slice (in nanoseconds).''',
+ 'dur':
+ '''The duration of the slice (in nanoseconds).''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'cpu':
+ '''The CPU that the slice executed on.''',
+ 'end_state':
+ '''
+ A string representing the scheduling state of the kernel
+ thread at the end of the slice. The individual characters in
+ the string mean the following: R (runnable), S (awaiting a
+ wakeup), D (in an uninterruptible sleep), T (suspended),
+ t (being traced), X (exiting), P (parked), W (waking),
+ I (idle), N (not contributing to the load average),
+ K (wakeable on fatal signals) and Z (zombie, awaiting
+ cleanup).
+ ''',
+ 'priority':
+ '''The kernel priority that the thread ran at.'''
+ }))
+
+SPURIOUS_SCHED_WAKEUP_TABLE = Table(
+ python_module=__file__,
+ class_name='SpuriousSchedWakeupTable',
+ sql_name='spurious_sched_wakeup',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('thread_state_id', CppInt64()),
+ C('irq_context', CppOptional(CppUint32())),
+ C('utid', CppUint32()),
+ C('waker_utid', CppUint32()),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table contains the scheduling wakeups that occurred while a thread was
+ not blocked, i.e. running or runnable. Such wakeups are not tracked in the
+ |thread_state_table|.
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ 'The timestamp at the start of the slice (in nanoseconds).',
+ 'thread_state_id':
+ 'The id of the row in the thread_state table that this row is associated with.',
+ 'irq_context':
+ '''Whether the wakeup was from interrupt context or process context.''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'waker_utid':
+ '''
+ The unique thread id of the thread which caused a wakeup of
+ this thread.
+ '''
+ }))
+
+THREAD_STATE_TABLE = Table(
+ python_module=__file__,
+ class_name='ThreadStateTable',
+ sql_name='thread_state',
+ columns=[
+ C('ts', CppInt64(), flags=ColumnFlag.SORTED),
+ C('dur', CppInt64()),
+ C('cpu', CppOptional(CppUint32())),
+ C('utid', CppUint32()),
+ C('state', CppString()),
+ C('io_wait', CppOptional(CppUint32())),
+ C('blocked_function', CppOptional(CppString())),
+ C('waker_utid', CppOptional(CppUint32())),
+ C('irq_context', CppOptional(CppUint32())),
+ ],
+ tabledoc=TableDoc(
+ doc='''
+ This table contains the scheduling state of every thread on the
+ system during the trace.
+
+ The rows in this table which have |state| = 'Running', will have a
+ corresponding row in the |sched_slice| table.
+ ''',
+ group='Events',
+ columns={
+ 'ts':
+ 'The timestamp at the start of the slice (in nanoseconds).',
+ 'dur':
+ 'The duration of the slice (in nanoseconds).',
+ 'cpu':
+ '''The CPU that the slice executed on.''',
+ 'irq_context':
+ '''Whether the wakeup was from interrupt context or process context.''',
+ 'utid':
+ '''The thread's unique id in the trace..''',
+ 'state':
+ '''
+ The scheduling state of the thread. Can be "Running" or any
+ of the states described in |sched_slice.end_state|.
+ ''',
+ 'io_wait':
+ 'Indicates whether this thread was blocked on IO.',
+ 'blocked_function':
+ 'The function in the kernel this thread was blocked on.',
+ 'waker_utid':
+ '''
+ The unique thread id of the thread which caused a wakeup of
+ this thread.
+ '''
+ }))
+
+# Keep this list sorted.
+ALL_TABLES = [
+ SCHED_SLICE_TABLE,
+ SPURIOUS_SCHED_WAKEUP_TABLE,
+ THREAD_STATE_TABLE,
+]
diff --git a/src/trace_processor/tables/slice_tables.py b/src/trace_processor/tables/slice_tables.py
index 22bd9b3..f1a4d90 100644
--- a/src/trace_processor/tables/slice_tables.py
+++ b/src/trace_processor/tables/slice_tables.py
@@ -11,9 +11,10 @@
# 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.
-"""Contains tables for relevant for TODO."""
+"""Contains tables for relevant for slices."""
from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
from python.generators.trace_processor_table.public import ColumnFlag
from python.generators.trace_processor_table.public import CppInt32
from python.generators.trace_processor_table.public import CppInt64
@@ -50,91 +51,73 @@
],
wrapping_sql_view=WrappingSqlView('slice'),
tabledoc=TableDoc(
- doc='''''',
- group='Events',
- columns={
- 'ts': '''timestamp of the start of the slice (in nanoseconds)''',
- 'dur': '''duration of the slice (in nanoseconds)''',
- 'arg_set_id': '''''',
- 'thread_instruction_count': '''to the end of the slice.''',
- 'thread_instruction_delta': '''The change in value from''',
- 'track_id': '''''',
- 'category': '''''',
- 'name': '''''',
- 'depth': '''''',
- 'stack_id': '''''',
- 'parent_stack_id': '''''',
- 'parent_id': '''''',
- 'thread_ts': '''''',
- 'thread_dur': ''''''
- }))
-
-SCHED_SLICE_TABLE = Table(
- python_module=__file__,
- class_name='SchedSliceTable',
- sql_name='sched_slice',
- columns=[
- C('ts', CppInt64(), flags=ColumnFlag.SORTED),
- C('dur', CppInt64()),
- C('cpu', CppUint32()),
- C('utid', CppUint32()),
- C('end_state', CppString()),
- C('priority', CppInt32()),
- ],
- tabledoc=TableDoc(
doc='''
- This table holds slices with kernel thread scheduling information.
-These slices are collected when the Linux "ftrace" data source is
-used with the "sched/switch" and "sched/wakeup*" events enabled.
+ Contains slices from userspace which explains what threads were doing
+ during the trace.
''',
group='Events',
columns={
'ts':
- '''The timestamp at the start of the slice (in nanoseconds).''',
+ 'The timestamp at the start of the slice (in nanoseconds).',
'dur':
- '''The duration of the slice (in nanoseconds).''',
- 'utid':
- '''The thread's unique id in the trace..''',
- 'cpu':
- '''The CPU that the slice executed on.''',
- 'end_state':
- '''A string representing the scheduling state of the
-kernel thread at the end of the slice. The individual characters in
-the string mean the following: R (runnable), S (awaiting a wakeup),
-D (in an uninterruptible sleep), T (suspended), t (being traced),
-X (exiting), P (parked), W (waking), I (idle), N (not contributing
-to the load average), K (wakeable on fatal signals) and
-Z (zombie, awaiting cleanup).''',
- 'priority':
- '''The kernel priority that the thread ran at.'''
- }))
-
-THREAD_STATE_TABLE = Table(
- python_module=__file__,
- class_name='ThreadStateTable',
- sql_name='thread_state',
- columns=[
- C('utid', CppUint32()),
- C('ts', CppInt64()),
- C('dur', CppInt64()),
- C('cpu', CppOptional(CppUint32())),
- C('state', CppString()),
- C('io_wait', CppOptional(CppUint32())),
- C('blocked_function', CppOptional(CppString())),
- C('waker_utid', CppOptional(CppUint32())),
- ],
- tabledoc=TableDoc(
- doc='''''',
- group='Events',
- columns={
- 'utid': '''''',
- 'ts': '''''',
- 'dur': '''''',
- 'cpu': '''''',
- 'state': '''''',
- 'io_wait': '''''',
- 'blocked_function': '''''',
- 'waker_utid': ''''''
+ 'The duration of the slice (in nanoseconds).',
+ 'track_id':
+ 'The id of the track this slice is located on.',
+ 'category':
+ '''
+ The "category" of the slice. If this slice originated with
+ track_event, this column contains the category emitted.
+ Otherwise, it is likely to be null (with limited exceptions).
+ ''',
+ 'name':
+ '''
+ The name of the slice. The name describes what was happening
+ during the slice.
+ ''',
+ 'depth':
+ 'The depth of the slice in the current stack of slices.',
+ 'stack_id':
+ '''
+ A unique identifier obtained from the names of all slices
+ in this stack. This is rarely useful and kept around only
+ for legacy reasons.
+ ''',
+ 'parent_stack_id':
+ 'The stack_id for the parent of this slice. Rarely useful.',
+ 'parent_id':
+ '''
+ The id of the parent (i.e. immediate ancestor) slice for this
+ slice
+ ''',
+ 'arg_set_id':
+ ColumnDoc(
+ 'The id of the argument set associated with this slice',
+ joinable='args.arg_set_id'),
+ 'thread_ts':
+ '''
+ The thread timestamp at the start of the slice. This column
+ will only be populated if thread timestamp collection is
+ enabled with track_event.
+ ''',
+ 'thread_dur':
+ ''''
+ The thread time used by this slice. This column will only be
+ populated if thread timestamp collection is enabled with
+ track_event.
+ ''',
+ 'thread_instruction_count':
+ '''
+ The value of the CPU instruction counter at the start of the
+ slice. This column will only be populated if thread
+ instruction collection is enabled with track_event.
+ ''',
+ 'thread_instruction_delta':
+ '''
+ The change in value of the CPU instruction counter between the
+ start and end of the slice. This column will only be
+ populated if thread instruction collection is enabled with
+ track_event.
+ ''',
}))
GPU_SLICE_TABLE = Table(
@@ -208,7 +191,7 @@
parent=SLICE_TABLE,
tabledoc=TableDoc(
doc='''''',
- group='Misc',
+ group='Events',
columns={
'display_frame_token': '''''',
'surface_frame_token': '''''',
@@ -235,7 +218,7 @@
parent=SLICE_TABLE,
tabledoc=TableDoc(
doc='''''',
- group='Misc',
+ group='Events',
columns={
'display_frame_token': '''''',
'surface_frame_token': '''''',
@@ -265,18 +248,35 @@
C('end_bound', CppInt64(), flags=ColumnFlag.HIDDEN),
],
tabledoc=TableDoc(
- doc='''''',
- group='Misc',
+ doc='''
+ An experimental table which "flattens" stacks of slices to contain
+ only the "deepest" slice at any point in time on each track.
+ ''',
+ group='Events',
columns={
- 'ts': '''''',
- 'dur': '''''',
- 'track_id': '''''',
- 'category': '''''',
- 'name': '''''',
- 'arg_set_id': '''''',
- 'source_id': '''''',
- 'start_bound': '''''',
- 'end_bound': ''''''
+ 'ts':
+ '''The timestamp at the start of the slice (in nanoseconds).''',
+ 'dur':
+ '''The duration of the slice (in nanoseconds).''',
+ 'track_id':
+ 'The id of the track this slice is located on.',
+ 'category':
+ '''
+ The "category" of the slice. If this slice originated with
+ track_event, this column contains the category emitted.
+ Otherwise, it is likely to be null (with limited exceptions).
+ ''',
+ 'name':
+ '''
+ The name of the slice. The name describes what was happening
+ during the slice.
+ ''',
+ 'arg_set_id':
+ ColumnDoc(
+ 'The id of the argument set associated with this slice',
+ joinable='args.arg_set_id'),
+ 'source_id':
+ 'The id of the slice which this row originated from.',
}))
# Keep this list sorted.
@@ -286,7 +286,5 @@
EXPERIMENTAL_FLAT_SLICE_TABLE,
GPU_SLICE_TABLE,
GRAPHICS_FRAME_SLICE_TABLE,
- SCHED_SLICE_TABLE,
SLICE_TABLE,
- THREAD_STATE_TABLE,
]
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 3dcf113..ba57b22 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -20,6 +20,7 @@
#include "src/trace_processor/tables/memory_tables_py.h"
#include "src/trace_processor/tables/metadata_tables_py.h"
#include "src/trace_processor/tables/profiler_tables_py.h"
+#include "src/trace_processor/tables/sched_tables_py.h"
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
@@ -71,13 +72,16 @@
ProfilerSmapsTable::~ProfilerSmapsTable() = default;
GpuCounterGroupTable::~GpuCounterGroupTable() = default;
+// sched_tables_py.h
+SchedSliceTable::~SchedSliceTable() = default;
+SpuriousSchedWakeupTable::~SpuriousSchedWakeupTable() = default;
+ThreadStateTable::~ThreadStateTable() = default;
+
// slice_tables_py.h
SliceTable::~SliceTable() = default;
FlowTable::~FlowTable() = default;
-SchedSliceTable::~SchedSliceTable() = default;
GpuSliceTable::~GpuSliceTable() = default;
GraphicsFrameSliceTable::~GraphicsFrameSliceTable() = default;
-ThreadStateTable::~ThreadStateTable() = default;
ExpectedFrameTimelineSliceTable::~ExpectedFrameTimelineSliceTable() = default;
ActualFrameTimelineSliceTable::~ActualFrameTimelineSliceTable() = default;
ExperimentalFlatSliceTable::~ExperimentalFlatSliceTable() = default;
diff --git a/src/trace_processor/tp_metatrace.cc b/src/trace_processor/tp_metatrace.cc
index 79e6dbc..bf44dcf 100644
--- a/src/trace_processor/tp_metatrace.cc
+++ b/src/trace_processor/tp_metatrace.cc
@@ -24,19 +24,14 @@
using ProtoEnum = protos::pbzero::MetatraceCategories;
ProtoEnum MetatraceCategoriesToProtoEnum(MetatraceCategories categories) {
- switch (categories) {
- case MetatraceCategories::TOPLEVEL:
- return ProtoEnum::TOPLEVEL;
- case MetatraceCategories::FUNCTION:
- return ProtoEnum::FUNCTION;
- case MetatraceCategories::QUERY:
- return ProtoEnum::QUERY;
- case MetatraceCategories::ALL:
- return ProtoEnum::ALL;
- case MetatraceCategories::NONE:
- return ProtoEnum::NONE;
- }
- return ProtoEnum::NONE;
+ ProtoEnum result = ProtoEnum::NONE;
+ if (categories & MetatraceCategories::TOPLEVEL)
+ result = static_cast<ProtoEnum>(result | ProtoEnum::TOPLEVEL);
+ if (categories & MetatraceCategories::FUNCTION)
+ result = static_cast<ProtoEnum>(result | ProtoEnum::FUNCTION);
+ if (categories & MetatraceCategories::QUERY)
+ result = static_cast<ProtoEnum>(result | ProtoEnum::QUERY);
+ return result;
}
} // namespace
diff --git a/src/trace_processor/tp_metatrace.h b/src/trace_processor/tp_metatrace.h
index b800483..e4f4627 100644
--- a/src/trace_processor/tp_metatrace.h
+++ b/src/trace_processor/tp_metatrace.h
@@ -172,7 +172,7 @@
record_->duration_ns = now - record_->timestamp_ns;
}
- ScopedEvent(ScopedEvent&& value) {
+ ScopedEvent(ScopedEvent&& value) noexcept {
record_ = value.record_;
record_idx_ = value.record_idx_;
value.record_ = nullptr;
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 5e10fc1..800d6d6 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -52,8 +52,9 @@
#include "src/trace_processor/prelude/functions/create_view_function.h"
#include "src/trace_processor/prelude/functions/import.h"
#include "src/trace_processor/prelude/functions/layout_functions.h"
+#include "src/trace_processor/prelude/functions/math.h"
#include "src/trace_processor/prelude/functions/pprof_functions.h"
-#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sql_function.h"
#include "src/trace_processor/prelude/functions/sqlite3_str_split.h"
#include "src/trace_processor/prelude/functions/stack_functions.h"
#include "src/trace_processor/prelude/functions/to_ftrace.h"
@@ -73,6 +74,7 @@
#include "src/trace_processor/prelude/table_functions/table_function.h"
#include "src/trace_processor/prelude/table_functions/view.h"
#include "src/trace_processor/prelude/tables_views/tables_views.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sql_stats_table.h"
#include "src/trace_processor/sqlite/sqlite_table.h"
@@ -106,13 +108,13 @@
"* FROM sqlite_temp_master)";
template <typename SqlFunction, typename Ptr = typename SqlFunction::Context*>
-void RegisterFunction(sqlite3* db,
+void RegisterFunction(PerfettoSqlEngine* engine,
const char* name,
int argc,
Ptr context = nullptr,
bool deterministic = true) {
- auto status = RegisterSqlFunction<SqlFunction>(
- db, name, argc, std::move(context), deterministic);
+ auto status = engine->RegisterSqlFunction<SqlFunction>(
+ name, argc, std::move(context), deterministic);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
@@ -238,7 +240,7 @@
}
void SetupMetrics(TraceProcessor* tp,
- sqlite3* db,
+ PerfettoSqlEngine* engine,
std::vector<metrics::SqlMetricFile>* sql_metrics,
const std::vector<std::string>& extension_paths) {
const std::vector<std::string> sanitized_extension_paths =
@@ -268,10 +270,11 @@
}
}
- RegisterFunction<metrics::NullIfEmpty>(db, "NULL_IF_EMPTY", 1);
- RegisterFunction<metrics::UnwrapMetricProto>(db, "UNWRAP_METRIC_PROTO", 2);
+ RegisterFunction<metrics::NullIfEmpty>(engine, "NULL_IF_EMPTY", 1);
+ RegisterFunction<metrics::UnwrapMetricProto>(engine, "UNWRAP_METRIC_PROTO",
+ 2);
RegisterFunction<metrics::RunMetric>(
- db, "RUN_METRIC", -1,
+ engine, "RUN_METRIC", -1,
std::unique_ptr<metrics::RunMetric::Context>(
new metrics::RunMetric::Context{tp, sql_metrics}));
@@ -279,8 +282,9 @@
// functions are supported.
{
auto ret = sqlite3_create_function_v2(
- db, "RepeatedField", 1, SQLITE_UTF8, nullptr, nullptr,
- metrics::RepeatedFieldStep, metrics::RepeatedFieldFinal, nullptr);
+ engine->sqlite_engine()->db(), "RepeatedField", 1, SQLITE_UTF8, nullptr,
+ nullptr, metrics::RepeatedFieldStep, metrics::RepeatedFieldFinal,
+ nullptr);
if (ret)
PERFETTO_FATAL("Error initializing RepeatedField");
}
@@ -298,137 +302,6 @@
}
}
-void IncrementCountForStmt(sqlite3_stmt* stmt,
- IteratorImpl::StmtMetadata* metadata) {
- metadata->statement_count++;
-
- // If the stmt is already done, it clearly didn't have any output.
- if (sqlite_utils::IsStmtDone(stmt))
- return;
-
- if (sqlite3_column_count(stmt) == 1) {
- sqlite3_value* value = sqlite3_column_value(stmt, 0);
-
- // If the "VOID" pointer associated to the return value is not null,
- // that means this is a function which is forced to return a value
- // (because all functions in SQLite have to) but doesn't actually
- // wait to (i.e. it wants to be treated like CREATE TABLE or similar).
- // Because of this, ignore the return value of this function.
- // See |WrapSqlFunction| for where this is set.
- if (sqlite3_value_pointer(value, "VOID") != nullptr) {
- return;
- }
-
- // If the statement only has a single column and that column is named
- // "suppress_query_output", treat it as a statement without output for
- // accounting purposes. This allows an escape hatch for cases where the
- // user explicitly wants to ignore functions as having output.
- if (strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
- return;
- }
- }
-
- // Otherwise, the statement has output and so increment the count.
- metadata->statement_count_with_output++;
-}
-
-base::Status PrepareAndStepUntilLastValidStmt(
- sqlite3* db,
- const std::string& sql,
- ScopedStmt* output_stmt,
- IteratorImpl::StmtMetadata* metadata) {
- ScopedStmt prev_stmt;
- // A sql string can contain several statements. Some of them might be comment
- // only, e.g. "SELECT 1; /* comment */; SELECT 2;". Here we process one
- // statement on each iteration. SQLite's sqlite_prepare_v2 (wrapped by
- // PrepareStmt) returns on each iteration a pointer to the unprocessed string.
- //
- // Unfortunately we cannot call PrepareStmt and tokenize all statements
- // upfront because sqlite_prepare_v2 also semantically checks the statement
- // against the schema. In some cases statements might depend on the execution
- // of previous ones (e.e. CREATE VIEW x; SELECT FROM x; DELETE VIEW x;).
- //
- // Also, unfortunately, we need to PrepareStmt to find out if a statement is a
- // comment or a real statement.
- //
- // The logic here is the following:
- // - We invoke PrepareStmt on each statement.
- // - If the statement is a comment we simply skip it.
- // - If the statement is valid, we step once to make sure side effects take
- // effect.
- // - If we encounter a valid statement afterwards, we step internally through
- // all rows of the previous one. This ensures that any further side effects
- // take hold *before* we step into the next statement.
- // - Once no further non-comment statements are encountered, we return an
- // iterator to the last valid statement.
- for (const char* rem_sql = sql.c_str(); rem_sql && rem_sql[0];) {
- ScopedStmt cur_stmt;
- {
- PERFETTO_TP_TRACE(metatrace::Category::QUERY, "QUERY_PREPARE");
- const char* tail = nullptr;
- RETURN_IF_ERROR(sqlite_utils::PrepareStmt(db, rem_sql, &cur_stmt, &tail));
- rem_sql = tail;
- }
-
- // The only situation where we'd have an ok status but also no prepared
- // statement is if the statement of SQL we parsed was a pure comment. In
- // this case, just continue to the next statement.
- if (!cur_stmt)
- continue;
-
- // Before stepping into |cur_stmt|, we need to finish iterating through
- // the previous statement so we don't have two clashing statements (e.g.
- // SELECT * FROM v and DROP VIEW v) partially stepped into.
- if (prev_stmt) {
- PERFETTO_TP_TRACE(metatrace::Category::QUERY, "STMT_STEP_UNTIL_DONE",
- [&prev_stmt](metatrace::Record* record) {
- auto expanded_sql =
- sqlite_utils::ExpandedSqlForStmt(*prev_stmt);
- record->AddArg("SQL", expanded_sql.get());
- });
- RETURN_IF_ERROR(sqlite_utils::StepStmtUntilDone(prev_stmt.get()));
- }
-
- PERFETTO_DLOG("Executing statement: %s", sqlite3_sql(*cur_stmt));
-
- {
- PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "STMT_FIRST_STEP",
- [&cur_stmt](metatrace::Record* record) {
- auto expanded_sql =
- sqlite_utils::ExpandedSqlForStmt(*cur_stmt);
- record->AddArg("SQL", expanded_sql.get());
- });
-
- // Now step once into |cur_stmt| so that when we prepare the next statment
- // we will have executed any dependent bytecode in this one.
- int err = sqlite3_step(*cur_stmt);
- if (err != SQLITE_ROW && err != SQLITE_DONE) {
- return base::ErrStatus(
- "%s", sqlite_utils::FormatErrorMessage(
- prev_stmt.get(), base::StringView(sql), db, err)
- .c_message());
- }
- }
-
- // Increment the neecessary counts for the statement.
- IncrementCountForStmt(cur_stmt.get(), metadata);
-
- // Propogate the current statement to the next iteration.
- prev_stmt = std::move(cur_stmt);
- }
-
- // If we didn't manage to prepare a single statment, that means everything
- // in the SQL was treated as a comment.
- if (!prev_stmt)
- return base::ErrStatus("No valid SQL to run");
-
- // Update the output statment and column count.
- *output_stmt = std::move(prev_stmt);
- metadata->column_count =
- static_cast<uint32_t>(sqlite3_column_count(output_stmt->get()));
- return base::OkStatus();
-}
-
const char* TraceTypeToString(TraceType trace_type) {
switch (trace_type) {
case kUnknownTraceType:
@@ -454,8 +327,8 @@
}
// Register SQL functions only used in local development instances.
-void RegisterDevFunctions(sqlite3* db) {
- RegisterFunction<WriteFile>(db, "WRITE_FILE", 2);
+void RegisterDevFunctions(PerfettoSqlEngine* engine) {
+ RegisterFunction<WriteFile>(engine, "WRITE_FILE", 2);
}
sql_modules::NameToModule GetStdlibModules() {
@@ -511,63 +384,85 @@
context_.content_analyzer.reset(new ProtoContentAnalyzer(&context_));
}
- sqlite3_str_split_init(engine_.db());
+ sqlite3_str_split_init(engine_.sqlite_engine()->db());
RegisterAdditionalModules(&context_);
- InitializePreludeTablesViews(engine_.db());
// New style function registration.
if (cfg.enable_dev_features) {
- RegisterDevFunctions(engine_.db());
+ RegisterDevFunctions(&engine_);
}
- RegisterFunction<Glob>(engine_.db(), "glob", 2);
- RegisterFunction<Hash>(engine_.db(), "HASH", -1);
- RegisterFunction<Base64Encode>(engine_.db(), "BASE64_ENCODE", 1);
- RegisterFunction<Demangle>(engine_.db(), "DEMANGLE", 1);
- RegisterFunction<SourceGeq>(engine_.db(), "SOURCE_GEQ", -1);
- RegisterFunction<ExportJson>(engine_.db(), "EXPORT_JSON", 1,
+ RegisterFunction<Glob>(&engine_, "glob", 2);
+ RegisterFunction<Hash>(&engine_, "HASH", -1);
+ RegisterFunction<Base64Encode>(&engine_, "BASE64_ENCODE", 1);
+ RegisterFunction<Demangle>(&engine_, "DEMANGLE", 1);
+ RegisterFunction<SourceGeq>(&engine_, "SOURCE_GEQ", -1);
+ RegisterFunction<ExportJson>(&engine_, "EXPORT_JSON", 1,
context_.storage.get(), false);
- RegisterFunction<ExtractArg>(engine_.db(), "EXTRACT_ARG", 2,
+ RegisterFunction<ExtractArg>(&engine_, "EXTRACT_ARG", 2,
context_.storage.get());
- RegisterFunction<AbsTimeStr>(engine_.db(), "ABS_TIME_STR", 1,
+ RegisterFunction<AbsTimeStr>(&engine_, "ABS_TIME_STR", 1,
context_.clock_converter.get());
- RegisterFunction<ToMonotonic>(engine_.db(), "TO_MONOTONIC", 1,
+ RegisterFunction<ToMonotonic>(&engine_, "TO_MONOTONIC", 1,
context_.clock_converter.get());
- RegisterFunction<CreateFunction>(
- engine_.db(), "CREATE_FUNCTION", 3,
- std::unique_ptr<CreateFunction::Context>(
- new CreateFunction::Context{engine_.db(), &create_function_state_}));
+ RegisterFunction<CreateFunction>(&engine_, "CREATE_FUNCTION", 3, &engine_);
RegisterFunction<CreateViewFunction>(
- engine_.db(), "CREATE_VIEW_FUNCTION", 3,
+ &engine_, "CREATE_VIEW_FUNCTION", 3,
std::unique_ptr<CreateViewFunction::Context>(
- new CreateViewFunction::Context{engine_.db()}));
- RegisterFunction<Import>(engine_.db(), "IMPORT", 1,
- std::unique_ptr<Import::Context>(new Import::Context{
- engine_.db(), this, &sql_modules_}));
+ new CreateViewFunction::Context{engine_.sqlite_engine()->db()}));
+ RegisterFunction<Import>(
+ &engine_, "IMPORT", 1,
+ std::unique_ptr<Import::Context>(new Import::Context{
+ engine_.sqlite_engine()->db(), this, &sql_modules_}));
RegisterFunction<ToFtrace>(
- engine_.db(), "TO_FTRACE", 1,
+ &engine_, "TO_FTRACE", 1,
std::unique_ptr<ToFtrace::Context>(new ToFtrace::Context{
context_.storage.get(), SystraceSerializer(&context_)}));
// Old style function registration.
// TODO(lalitm): migrate this over to using RegisterFunction once aggregate
// functions are supported.
- RegisterLastNonNullFunction(engine_.db());
- RegisterValueAtMaxTsFunction(engine_.db());
+ RegisterLastNonNullFunction(engine_.sqlite_engine()->db());
+ RegisterValueAtMaxTsFunction(engine_.sqlite_engine()->db());
{
- base::Status status = RegisterStackFunctions(engine_.db(), &context_);
+ base::Status status = RegisterStackFunctions(&engine_, &context_);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
{
- base::Status status = PprofFunctions::Register(engine_.db(), &context_);
+ base::Status status =
+ PprofFunctions::Register(engine_.sqlite_engine()->db(), &context_);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
{
- base::Status status = LayoutFunctions::Register(engine_.db(), &context_);
+ base::Status status =
+ LayoutFunctions::Register(engine_.sqlite_engine()->db(), &context_);
if (!status.ok())
PERFETTO_ELOG("%s", status.c_message());
}
+ {
+ base::Status status = RegisterMathFunctions(engine_);
+ if (!status.ok())
+ PERFETTO_ELOG("%s", status.c_message());
+ }
+
+ const TraceStorage* storage = context_.storage.get();
+
+ // Operator tables.
+ engine_.sqlite_engine()->RegisterVirtualTableModule<SpanJoinOperatorTable>(
+ "span_join", storage, SqliteTable::TableType::kExplicitCreate, false);
+ engine_.sqlite_engine()->RegisterVirtualTableModule<SpanJoinOperatorTable>(
+ "span_left_join", storage, SqliteTable::TableType::kExplicitCreate,
+ false);
+ engine_.sqlite_engine()->RegisterVirtualTableModule<SpanJoinOperatorTable>(
+ "span_outer_join", storage, SqliteTable::TableType::kExplicitCreate,
+ false);
+ engine_.sqlite_engine()->RegisterVirtualTableModule<WindowOperatorTable>(
+ "window", storage, SqliteTable::TableType::kExplicitCreate, true);
+ RegisterCreateViewFunctionModule(engine_.sqlite_engine());
+
+ // Initalize the tables and views in the prelude.
+ InitializePreludeTablesViews(engine_.sqlite_engine()->db());
auto stdlib_modules = GetStdlibModules();
for (auto module_it = stdlib_modules.GetIterator(); module_it; ++module_it) {
@@ -577,18 +472,13 @@
PERFETTO_ELOG("%s", status.c_message());
}
- SetupMetrics(this, engine_.db(), &sql_metrics_,
- cfg.skip_builtin_metric_paths);
+ SetupMetrics(this, &engine_, &sql_metrics_, cfg.skip_builtin_metric_paths);
- const TraceStorage* storage = context_.storage.get();
-
- SqlStatsTable::RegisterTable(engine_.db(), storage);
- StatsTable::RegisterTable(engine_.db(), storage);
-
- // Operator tables.
- SpanJoinOperatorTable::RegisterTable(engine_.db(), storage);
- WindowOperatorTable::RegisterTable(engine_.db(), storage);
- CreateViewFunction::RegisterTable(engine_.db());
+ // Legacy tables.
+ engine_.sqlite_engine()->RegisterVirtualTableModule<SqlStatsTable>(
+ "sqlstats", storage, SqliteTable::TableType::kEponymousOnly, false);
+ engine_.sqlite_engine()->RegisterVirtualTableModule<StatsTable>(
+ "stats", storage, SqliteTable::TableType::kEponymousOnly, false);
// Tables dynamically generated at query time.
RegisterTableFunction(std::unique_ptr<ExperimentalFlamegraph>(
@@ -640,6 +530,7 @@
RegisterDbTable(storage->flow_table());
RegisterDbTable(storage->slice_table());
RegisterDbTable(storage->sched_slice_table());
+ RegisterDbTable(storage->spurious_sched_wakeup_table());
RegisterDbTable(storage->thread_state_table());
RegisterDbTable(storage->gpu_slice_table());
@@ -733,7 +624,8 @@
context_.storage->InternString(TraceTypeToString(context_.trace_type));
context_.metadata_tracker->SetMetadata(metadata::trace_type,
Variadic::String(trace_type_id));
- BuildBoundsTable(engine_.db(), context_.storage->GetTraceTimestampBoundsNs());
+ BuildBoundsTable(engine_.sqlite_engine()->db(),
+ context_.storage->GetTraceTimestampBoundsNs());
}
void TraceProcessorImpl::NotifyEndOfFile() {
@@ -771,7 +663,8 @@
// TraceProcessorStorageImpl::NotifyEndOfFile, this will be counted in
// trace bounds: this is important for parsers like ninja which wait until
// the end to flush all their data.
- BuildBoundsTable(engine_.db(), context_.storage->GetTraceTimestampBoundsNs());
+ BuildBoundsTable(engine_.sqlite_engine()->db(),
+ context_.storage->GetTraceTimestampBoundsNs());
TraceProcessorStorageImpl::DestroyContext();
}
@@ -815,23 +708,18 @@
context_.storage->mutable_sql_stats()->RecordQueryBegin(
sql, base::GetWallTimeNs().count());
- ScopedStmt stmt;
- IteratorImpl::StmtMetadata metadata;
- base::Status status =
- PrepareAndStepUntilLastValidStmt(engine_.db(), sql, &stmt, &metadata);
- PERFETTO_DCHECK((status.ok() && stmt) || (!status.ok() && !stmt));
-
+ base::StatusOr<PerfettoSqlEngine::ExecutionResult> result =
+ engine_.ExecuteUntilLastStatement(sql);
std::unique_ptr<IteratorImpl> impl(
- new IteratorImpl(this, engine_.db(), status, std::move(stmt),
- std::move(metadata), sql_stats_row));
+ new IteratorImpl(this, std::move(result), sql_stats_row));
return Iterator(std::move(impl));
}
void TraceProcessorImpl::InterruptQuery() {
- if (!engine_.db())
+ if (!engine_.sqlite_engine()->db())
return;
query_interrupted_.store(true);
- sqlite3_interrupt(engine_.db());
+ sqlite3_interrupt(engine_.sqlite_engine()->db());
}
bool TraceProcessorImpl::IsRootMetricField(const std::string& metric_name) {
@@ -923,7 +811,7 @@
prev_path.c_str(), path.c_str(), metric.proto_field_name->c_str());
}
- InsertIntoTraceMetricsTable(engine_.db(), no_ext_name);
+ InsertIntoTraceMetricsTable(engine_.sqlite_engine()->db(), no_ext_name);
}
sql_metrics_.emplace_back(metric);
@@ -951,7 +839,7 @@
auto fn_name = desc.full_name().substr(desc.package_name().size() + 1);
std::replace(fn_name.begin(), fn_name.end(), '.', '_');
RegisterFunction<metrics::BuildProto>(
- engine_.db(), fn_name.c_str(), -1,
+ &engine_, fn_name.c_str(), -1,
std::unique_ptr<metrics::BuildProto::Context>(
new metrics::BuildProto::Context{this, &pool_, i}));
}
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 88b4b6e..973ec35 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -34,6 +34,7 @@
#include "src/trace_processor/prelude/functions/create_view_function.h"
#include "src/trace_processor/prelude/functions/import.h"
#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include "src/trace_processor/sqlite/perfetto_sql_engine.h"
#include "src/trace_processor/sqlite/query_cache.h"
#include "src/trace_processor/sqlite/scoped_db.h"
#include "src/trace_processor/sqlite/sqlite_engine.h"
@@ -120,11 +121,7 @@
bool IsRootMetricField(const std::string& metric_name);
- SqliteEngine engine_;
-
- // State necessary for CREATE_FUNCTION invocations. We store this here as we
- // need to finalize any prepared statements *before* we destroy the database.
- CreateFunction::State create_function_state_;
+ PerfettoSqlEngine engine_;
DescriptorPool pool_;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index da08183..44df9a9 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -763,7 +763,7 @@
builds. The features behind this flag can
break at any time without any warning.
-Standard library:
+Standard library:
--add-sql-module MODULE_PATH Files from the directory will be treated
as a new SQL module and can be used for
IMPORT. The name of the directory is the
@@ -1016,10 +1016,11 @@
return command_line_options;
}
-void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
- const void* data,
- int size,
- std::vector<std::string>& skip_prefixes) {
+void ExtendPoolWithBinaryDescriptor(
+ google::protobuf::DescriptorPool& pool,
+ const void* data,
+ int size,
+ const std::vector<std::string>& skip_prefixes) {
google::protobuf::FileDescriptorSet desc_set;
PERFETTO_CHECK(desc_set.ParseFromArray(data, size));
for (const auto& file_desc : desc_set.file()) {
@@ -1237,7 +1238,8 @@
}
base::Status LoadMetricExtensionProtos(const std::string& proto_root,
- const std::string& mount_path) {
+ const std::string& mount_path,
+ google::protobuf::DescriptorPool& pool) {
if (!base::FileExists(proto_root)) {
return base::ErrStatus(
"Directory %s does not exist. Metric extension directory must contain "
@@ -1262,6 +1264,11 @@
serialized_filedescset.data(),
static_cast<int>(serialized_filedescset.size()));
+ // Extend the pool for any subsequent reflection-based operations
+ // (e.g. output json)
+ ExtendPoolWithBinaryDescriptor(
+ pool, serialized_filedescset.data(),
+ static_cast<int>(serialized_filedescset.size()), {});
RETURN_IF_ERROR(g_tp->ExtendMetricsProto(serialized_filedescset.data(),
serialized_filedescset.size()));
@@ -1293,7 +1300,8 @@
return base::OkStatus();
}
-base::Status LoadMetricExtension(const MetricExtension& extension) {
+base::Status LoadMetricExtension(const MetricExtension& extension,
+ google::protobuf::DescriptorPool& pool) {
const std::string& disk_path = extension.disk_path();
const std::string& virtual_path = extension.virtual_path();
@@ -1305,8 +1313,8 @@
// Note: Proto files must be loaded first, because we determine whether an SQL
// file is a metric or not by checking if the name matches a field of the root
// TraceMetrics proto.
- RETURN_IF_ERROR(LoadMetricExtensionProtos(disk_path + "protos/",
- kMetricProtoRoot + virtual_path));
+ RETURN_IF_ERROR(LoadMetricExtensionProtos(
+ disk_path + "protos/", kMetricProtoRoot + virtual_path, pool));
RETURN_IF_ERROR(LoadMetricExtensionSql(disk_path + "sql/", virtual_path));
return base::OkStatus();
@@ -1585,11 +1593,21 @@
tp->EnableMetatrace(metatrace_config);
}
+ // Descriptor pool used for printing output as textproto. Building on top of
+ // generated pool so default protos in google.protobuf.descriptor.proto are
+ // available.
+ // For some insane reason, the descriptor pool is not movable so we need to
+ // create it here so we can create references and pass it everywhere.
+ google::protobuf::DescriptorPool pool(
+ google::protobuf::DescriptorPool::generated_pool());
+ RETURN_IF_ERROR(PopulateDescriptorPool(pool, metric_extensions));
+
// We load all the metric extensions even when --run-metrics arg is not there,
// because we want the metrics to be available in interactive mode or when
// used in UI using httpd.
+ // Metric extensions are also used to populate the descriptor pool.
for (const auto& extension : metric_extensions) {
- RETURN_IF_ERROR(LoadMetricExtension(extension));
+ RETURN_IF_ERROR(LoadMetricExtension(extension, pool));
}
base::TimeNanos t_load{};
@@ -1616,14 +1634,6 @@
RETURN_IF_ERROR(RunQueries(options.pre_metrics_path, false));
}
- // Descriptor pool used for printing output as textproto. Building on top of
- // generated pool so default protos in google.protobuf.descriptor.proto are
- // available.
- // For some insane reason, the descriptor pool is not movable so we need to
- // create it here so we can create references and pass it everywhere.
- google::protobuf::DescriptorPool pool(
- google::protobuf::DescriptorPool::generated_pool());
- RETURN_IF_ERROR(PopulateDescriptorPool(pool, metric_extensions));
std::vector<MetricNameAndPath> metrics;
if (!options.metric_names.empty()) {
diff --git a/src/trace_processor/util/interned_message_view.h b/src/trace_processor/util/interned_message_view.h
index 3422b8b..f6a9a13 100644
--- a/src/trace_processor/util/interned_message_view.h
+++ b/src/trace_processor/util/interned_message_view.h
@@ -71,7 +71,9 @@
if (PERFETTO_TYPE_IDENTIFIER &&
strcmp(decoder_type_,
// GCC complains if this arg can be null.
- PERFETTO_TYPE_IDENTIFIER ? PERFETTO_TYPE_IDENTIFIER : "") != 0) {
+ static_cast<bool>(PERFETTO_TYPE_IDENTIFIER)
+ ? PERFETTO_TYPE_IDENTIFIER
+ : "") != 0) {
PERFETTO_FATAL(
"Interning entry accessed under different types! previous type: "
"%s. new type: %s.",
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 459b640..859cc8b 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -34,6 +34,7 @@
#include "test/gtest_and_gmock.h"
#include "protos/perfetto/trace/ftrace/dpu.gen.h"
+#include "protos/perfetto/trace/ftrace/f2fs.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.gen.h"
#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
@@ -62,6 +63,7 @@
using testing::Pair;
using testing::Property;
using testing::Return;
+using testing::SizeIs;
using testing::StartsWith;
namespace perfetto {
@@ -3303,5 +3305,113 @@
EXPECT_THAT(AllTracePackets(), IsEmpty());
}
+// Kernel code:
+// trace_f2fs_truncate_partial_nodes(... nid = {1,2,3}, depth = 4, err = 0)
+//
+// After kernel commit 0b04d4c0542e("f2fs: Fix
+// f2fs_truncate_partial_nodes ftrace event")
+static ExamplePage g_f2fs_truncate_partial_nodes_new{
+ "b281660544_new",
+ R"(
+00000000: 1555 c3e4 cb07 0000 3c00 0000 0000 0000 .U......<.......
+00000010: 3e33 0b87 2700 0000 0c00 0000 7d02 0000 >3..'.......}...
+00000020: c638 0000 3900 e00f 0000 0000 b165 0000 .8..9........e..
+00000030: 0000 0000 0100 0000 0200 0000 0300 0000 ................
+00000040: 0400 0000 0000 0000 0000 0000 0000 0000 ................
+ )",
+};
+
+TEST_F(CpuReaderParsePagePayloadTest, F2fsTruncatePartialNodesNew) {
+ const ExamplePage* test_case = &g_f2fs_truncate_partial_nodes_new;
+
+ ProtoTranslationTable* table = GetTable(test_case->name);
+ auto page = PageFromXxd(test_case->data);
+
+ FtraceDataSourceConfig ds_config = EmptyConfig();
+ ds_config.event_filter.AddEnabledEvent(table->EventToFtraceId(
+ GroupAndName("f2fs", "f2fs_truncate_partial_nodes")));
+
+ const uint8_t* parse_pos = page.get();
+ std::optional<CpuReader::PageHeader> page_header =
+ CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
+
+ const uint8_t* page_end = page.get() + base::kPageSize;
+ ASSERT_TRUE(page_header.has_value());
+ EXPECT_FALSE(page_header->lost_events);
+ EXPECT_LE(parse_pos + page_header->size, page_end);
+
+ size_t evt_bytes = CpuReader::ParsePagePayload(
+ parse_pos, &page_header.value(), table, &ds_config,
+ CreateBundler(ds_config), &metadata_);
+
+ EXPECT_LT(0u, evt_bytes);
+
+ auto bundle = GetBundle();
+ ASSERT_THAT(bundle.event(), SizeIs(1));
+ auto& event = bundle.event()[0];
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().dev(), 65081u);
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().ino(), 26033u);
+ // This field is disabled in ftrace_proto_gen.cc
+ EXPECT_FALSE(event.f2fs_truncate_partial_nodes().has_nid());
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().depth(), 4);
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().err(), 0);
+}
+
+// Kernel code:
+// trace_f2fs_truncate_partial_nodes(... nid = {1,2,3}, depth = 4, err = 0)
+//
+// Before kernel commit 0b04d4c0542e("f2fs: Fix
+// f2fs_truncate_partial_nodes ftrace event")
+static ExamplePage g_f2fs_truncate_partial_nodes_old{
+ "b281660544_old",
+ R"(
+00000000: 8f90 aa0d 9e00 0000 3c00 0000 0000 0000 ........<.......
+00000010: 3e97 0295 0e01 0000 0c00 0000 7d02 0000 >...........}...
+00000020: 8021 0000 3900 e00f 0000 0000 0d66 0000 .!..9........f..
+00000030: 0000 0000 0100 0000 0200 0000 0300 0000 ................
+00000040: 0400 0000 0000 0000 0000 0000 0000 0000 ................
+ )",
+};
+
+TEST_F(CpuReaderParsePagePayloadTest, F2fsTruncatePartialNodesOld) {
+ const ExamplePage* test_case = &g_f2fs_truncate_partial_nodes_old;
+
+ ProtoTranslationTable* table = GetTable(test_case->name);
+ auto page = PageFromXxd(test_case->data);
+
+ FtraceDataSourceConfig ds_config = EmptyConfig();
+ auto id = table->EventToFtraceId(
+ GroupAndName("f2fs", "f2fs_truncate_partial_nodes"));
+ PERFETTO_LOG("Enabling: %zu", id);
+ ds_config.event_filter.AddEnabledEvent(id);
+
+ const uint8_t* parse_pos = page.get();
+ std::optional<CpuReader::PageHeader> page_header =
+ CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
+
+ const uint8_t* page_end = page.get() + base::kPageSize;
+ ASSERT_TRUE(page_header.has_value());
+ EXPECT_FALSE(page_header->lost_events);
+ EXPECT_LE(parse_pos + page_header->size, page_end);
+
+ size_t evt_bytes = CpuReader::ParsePagePayload(
+ parse_pos, &page_header.value(), table, &ds_config,
+ CreateBundler(ds_config), &metadata_);
+
+ EXPECT_LT(0u, evt_bytes);
+
+ auto bundle = GetBundle();
+ ASSERT_THAT(bundle.event(), SizeIs(1));
+ auto& event = bundle.event()[0];
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().dev(), 65081u);
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().ino(), 26125u);
+ // This field is disabled in ftrace_proto_gen.cc
+ EXPECT_FALSE(event.f2fs_truncate_partial_nodes().has_nid());
+ // Due to a kernel bug, nid[1] is parsed as depth.
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().depth(), 2);
+ // Due to a kernel bug, nid[2] is parsed as err.
+ EXPECT_EQ(event.f2fs_truncate_partial_nodes().err(), 3);
+}
+
} // namespace
} // namespace perfetto
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index f17f6b6..6a8cca3 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -4266,9 +4266,6 @@
"ino", 2, ProtoSchemaType::kUint64,
TranslationStrategy::kInvalidTranslationStrategy},
{kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
- "nid", 3, ProtoSchemaType::kUint32,
- TranslationStrategy::kInvalidTranslationStrategy},
- {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
"depth", 4, ProtoSchemaType::kInt32,
TranslationStrategy::kInvalidTranslationStrategy},
{kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
diff --git a/src/traced/probes/ftrace/proto_translation_table.cc b/src/traced/probes/ftrace/proto_translation_table.cc
index 5b8c09e..9dcde14 100644
--- a/src/traced/probes/ftrace/proto_translation_table.cc
+++ b/src/traced/probes/ftrace/proto_translation_table.cc
@@ -124,7 +124,7 @@
if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
ftrace_field.is_signed, &field->ftrace_type)) {
- PERFETTO_FATAL(
+ PERFETTO_DFATAL(
"Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
"size:%d "
"signed:%d)",
diff --git a/src/traced/probes/ftrace/test/data/b281660544_new/available_events b/src/traced/probes/ftrace/test/data/b281660544_new/available_events
new file mode 100644
index 0000000..5588a96
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_new/available_events
@@ -0,0 +1 @@
+f2fs:f2fs_truncate_partial_nodes
diff --git a/src/traced/probes/ftrace/test/data/b281660544_new/events/f2fs/f2fs_truncate_partial_nodes/format b/src/traced/probes/ftrace/test/data/b281660544_new/events/f2fs/f2fs_truncate_partial_nodes/format
new file mode 100644
index 0000000..2f5a8ab
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_new/events/f2fs/f2fs_truncate_partial_nodes/format
@@ -0,0 +1,15 @@
+name: f2fs_truncate_partial_nodes
+ID: 637
+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:dev_t dev; offset:8; size:4; signed:0;
+ field:ino_t ino; offset:16; size:8; signed:0;
+ field:nid_t nid[3]; offset:24; size:12; signed:0;
+ field:int depth; offset:36; size:4; signed:1;
+ field:int err; offset:40; size:4; signed:1;
+
+print fmt: "dev = (%d,%d), ino = %lu, nid[0] = %u, nid[1] = %u, nid[2] = %u, depth = %d, err = %d", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) ((REC->dev) & ((1U << 20) - 1))), (unsigned long)REC->ino, (unsigned int)REC->nid[0], (unsigned int)REC->nid[1], (unsigned int)REC->nid[2], REC->depth, REC->err
diff --git a/src/traced/probes/ftrace/test/data/b281660544_new/events/header_page b/src/traced/probes/ftrace/test/data/b281660544_new/events/header_page
new file mode 100644
index 0000000..276dce9
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_new/events/header_page
@@ -0,0 +1,4 @@
+ field: u64 timestamp; offset:0; size:8; signed:0;
+ field: local_t commit; offset:8; size:8; signed:1;
+ field: int overwrite; offset:8; size:1; signed:1;
+ field: char data; offset:16; size:4080; signed:1;
diff --git a/src/traced/probes/ftrace/test/data/b281660544_old/available_events b/src/traced/probes/ftrace/test/data/b281660544_old/available_events
new file mode 100644
index 0000000..5588a96
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_old/available_events
@@ -0,0 +1 @@
+f2fs:f2fs_truncate_partial_nodes
diff --git a/src/traced/probes/ftrace/test/data/b281660544_old/events/f2fs/f2fs_truncate_partial_nodes/format b/src/traced/probes/ftrace/test/data/b281660544_old/events/f2fs/f2fs_truncate_partial_nodes/format
new file mode 100644
index 0000000..15f6a64
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_old/events/f2fs/f2fs_truncate_partial_nodes/format
@@ -0,0 +1,15 @@
+name: f2fs_truncate_partial_nodes
+ID: 637
+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:dev_t dev; offset:8; size:4; signed:0;
+ field:ino_t ino; offset:16; size:8; signed:0;
+ field:nid_t nid[3]; offset:24; size:4; signed:0;
+ field:int depth; offset:28; size:4; signed:1;
+ field:int err; offset:32; size:4; signed:1;
+
+print fmt: "dev = (%d,%d), ino = %lu, nid[0] = %u, nid[1] = %u, nid[2] = %u, depth = %d, err = %d", ((unsigned int) ((REC->dev) >> 20)), ((unsigned int) ((REC->dev) & ((1U << 20) - 1))), (unsigned long)REC->ino, (unsigned int)REC->nid[0], (unsigned int)REC->nid[1], (unsigned int)REC->nid[2], REC->depth, REC->err
diff --git a/src/traced/probes/ftrace/test/data/b281660544_old/events/header_page b/src/traced/probes/ftrace/test/data/b281660544_old/events/header_page
new file mode 100644
index 0000000..276dce9
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/b281660544_old/events/header_page
@@ -0,0 +1,4 @@
+ field: u64 timestamp; offset:0; size:8; signed:0;
+ field: local_t commit; offset:8; size:8; signed:1;
+ field: int overwrite; offset:8; size:1; signed:1;
+ field: char data; offset:16; size:4080; signed:1;
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index 8100e42..9ce614c 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -96,6 +96,7 @@
record_thread_names_ = cfg.record_thread_names();
dump_all_procs_on_start_ = cfg.scan_all_processes_on_start();
resolve_process_fds_ = cfg.resolve_process_fds();
+ scan_smaps_rollup_ = cfg.scan_smaps_rollup();
enable_on_demand_dumps_ = true;
for (auto quirk = cfg.quirks(); quirk; ++quirk) {
@@ -495,6 +496,11 @@
if (proc_status.empty())
continue;
+ if (scan_smaps_rollup_) {
+ std::string proc_smaps_rollup = ReadProcPidFile(pid, "smaps_rollup");
+ proc_status.append(proc_smaps_rollup);
+ }
+
if (!WriteMemCounters(pid, proc_status)) {
// If WriteMemCounters() fails the pid is very likely a kernel thread
// that has a valid /proc/[pid]/status but no memory values. In this
@@ -606,6 +612,38 @@
GetOrCreateStatsProcess(pid)->set_vm_swap_kb(counter);
cached.vm_swap_kb = counter;
}
+ // The entries below come from smaps_rollup, WriteAllProcessStats merges
+ // everything into the same buffer for convenience.
+ } else if (strcmp(key.data(), "Rss") == 0) {
+ auto counter = ToU32(value.data());
+ if (counter != cached.smr_rss_kb) {
+ GetOrCreateStatsProcess(pid)->set_smr_rss_kb(counter);
+ cached.smr_rss_kb = counter;
+ }
+ } else if (strcmp(key.data(), "Pss") == 0) {
+ auto counter = ToU32(value.data());
+ if (counter != cached.smr_pss_kb) {
+ GetOrCreateStatsProcess(pid)->set_smr_pss_kb(counter);
+ cached.smr_pss_kb = counter;
+ }
+ } else if (strcmp(key.data(), "Pss_Anon") == 0) {
+ auto counter = ToU32(value.data());
+ if (counter != cached.smr_pss_anon_kb) {
+ GetOrCreateStatsProcess(pid)->set_smr_pss_anon_kb(counter);
+ cached.smr_pss_anon_kb = counter;
+ }
+ } else if (strcmp(key.data(), "Pss_File") == 0) {
+ auto counter = ToU32(value.data());
+ if (counter != cached.smr_pss_file_kb) {
+ GetOrCreateStatsProcess(pid)->set_smr_pss_file_kb(counter);
+ cached.smr_pss_file_kb = counter;
+ }
+ } else if (strcmp(key.data(), "Pss_Shmem") == 0) {
+ auto counter = ToU32(value.data());
+ if (counter != cached.smr_pss_shmem_kb) {
+ GetOrCreateStatsProcess(pid)->set_smr_pss_shmem_kb(counter);
+ cached.smr_pss_shmem_kb = counter;
+ }
}
key.clear();
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 706ee2a..4989b3c 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -87,6 +87,11 @@
uint32_t vm_locked_kb = std::numeric_limits<uint32_t>::max();
uint32_t vm_hvm_kb = std::numeric_limits<uint32_t>::max();
int oom_score_adj = std::numeric_limits<int>::max();
+ uint32_t smr_rss_kb = std::numeric_limits<uint32_t>::max();
+ uint32_t smr_pss_kb = std::numeric_limits<uint32_t>::max();
+ uint32_t smr_pss_anon_kb = std::numeric_limits<uint32_t>::max();
+ uint32_t smr_pss_file_kb = std::numeric_limits<uint32_t>::max();
+ uint32_t smr_pss_shmem_kb = std::numeric_limits<uint32_t>::max();
// ctime + stime from /proc/pid/stat
uint64_t cpu_time = std::numeric_limits<uint64_t>::max();
@@ -160,6 +165,7 @@
bool enable_on_demand_dumps_ = true;
bool dump_all_procs_on_start_ = false;
bool resolve_process_fds_ = false;
+ bool scan_smaps_rollup_ = false;
// This set contains PIDs as per the Linux kernel notion of a PID (which is
// really a TID). In practice this set will contain all TIDs for all processes
diff --git a/src/traced/probes/ps/process_stats_data_source_unittest.cc b/src/traced/probes/ps/process_stats_data_source_unittest.cc
index c87f0bd..5a09ebe 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -381,6 +381,10 @@
return ret.ToStdString();
}));
+ // By default scan_smaps_rollup is off and /proc/<pid>/smaps_rollup
+ // shouldn't be read.
+ EXPECT_CALL(*data_source, ReadProcPidFile(pid, "smaps_rollup")).Times(0);
+
EXPECT_CALL(*data_source, ReadProcPidFile(pid, "oom_score_adj"))
.WillRepeatedly(Invoke(
[checkpoint, kPids, &iter](int32_t inner_pid, const std::string&) {
@@ -535,5 +539,95 @@
EXPECT_THAT(nstid, ElementsAre(3));
}
+TEST_F(ProcessStatsDataSourceTest, ScanSmapsRollupIsOn) {
+ DataSourceConfig ds_config;
+ ProcessStatsConfig cfg;
+ cfg.set_proc_stats_poll_ms(1);
+ cfg.set_resolve_process_fds(true);
+ cfg.set_scan_smaps_rollup(true);
+ cfg.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
+ ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
+ auto data_source = GetProcessStatsDataSource(ds_config);
+
+ // Populate a fake /proc/ directory.
+ auto fake_proc = base::TempDir::Create();
+ const int kPids[] = {1, 2};
+ std::vector<std::string> dirs_to_delete;
+ for (int pid : kPids) {
+ base::StackString<256> path("%s/%d", fake_proc.path().c_str(), pid);
+ dirs_to_delete.push_back(path.ToStdString());
+ EXPECT_EQ(mkdir(path.c_str(), 0755), 0)
+ << "mkdir('" << path.c_str() << "') failed";
+ }
+
+ auto checkpoint = task_runner_.CreateCheckpoint("all_done");
+ const auto fake_proc_path = fake_proc.path();
+ EXPECT_CALL(*data_source, OpenProcDir())
+ .WillRepeatedly(Invoke([&fake_proc_path] {
+ return base::ScopedDir(opendir(fake_proc_path.c_str()));
+ }));
+ EXPECT_CALL(*data_source, GetProcMountpoint())
+ .WillRepeatedly(
+ Invoke([&fake_proc_path] { return fake_proc_path.c_str(); }));
+
+ const int kNumIters = 4;
+ int iter = 0;
+ for (int pid : kPids) {
+ EXPECT_CALL(*data_source, ReadProcPidFile(pid, "status"))
+ .WillRepeatedly(
+ Invoke([checkpoint, &iter](int32_t p, const std::string&) {
+ base::StackString<1024> ret(
+ "Name: pid_10\nVmSize: %d kB\nVmRSS:\t%d kB\n",
+ p * 100 + iter * 10 + 1, p * 100 + iter * 10 + 2);
+ return ret.ToStdString();
+ }));
+ EXPECT_CALL(*data_source, ReadProcPidFile(pid, "smaps_rollup"))
+ .WillRepeatedly(
+ Invoke([checkpoint, &iter](int32_t p, const std::string&) {
+ base::StackString<1024> ret(
+ "Name: pid_10\nRss: %d kB\nPss:\t%d kB\n",
+ p * 100 + iter * 10 + 4, p * 100 + iter * 10 + 5);
+ return ret.ToStdString();
+ }));
+
+ EXPECT_CALL(*data_source, ReadProcPidFile(pid, "oom_score_adj"))
+ .WillRepeatedly(Invoke(
+ [checkpoint, kPids, &iter](int32_t inner_pid, const std::string&) {
+ auto oom_score = inner_pid * 100 + iter * 10 + 3;
+ if (inner_pid == kPids[base::ArraySize(kPids) - 1]) {
+ if (++iter == kNumIters)
+ checkpoint();
+ }
+ return std::to_string(oom_score);
+ }));
+ }
+
+ data_source->Start();
+ task_runner_.RunUntilCheckpoint("all_done");
+ data_source->Flush(1 /* FlushRequestId */, []() {});
+
+ std::vector<protos::gen::ProcessStats::Process> processes;
+ auto trace = writer_raw_->GetAllTracePackets();
+ for (const auto& packet : trace) {
+ for (const auto& process : packet.process_stats().processes()) {
+ processes.push_back(process);
+ }
+ }
+ ASSERT_EQ(processes.size(), kNumIters * base::ArraySize(kPids));
+ iter = 0;
+ for (const auto& proc_counters : processes) {
+ int32_t pid = proc_counters.pid();
+ ASSERT_EQ(static_cast<int>(proc_counters.smr_rss_kb()),
+ pid * 100 + iter * 10 + 4);
+ ASSERT_EQ(static_cast<int>(proc_counters.smr_pss_kb()),
+ pid * 100 + iter * 10 + 5);
+ if (pid == kPids[base::ArraySize(kPids) - 1])
+ iter++;
+ }
+ for (auto path = dirs_to_delete.rbegin(); path != dirs_to_delete.rend();
+ path++)
+ base::Rmdir(*path);
+}
+
} // namespace
} // namespace perfetto
diff --git a/src/traced/probes/statsd_client/statsd_binder_data_source.cc b/src/traced/probes/statsd_client/statsd_binder_data_source.cc
index 627d4bc..df38bda 100644
--- a/src/traced/probes/statsd_client/statsd_binder_data_source.cc
+++ b/src/traced/probes/statsd_client/statsd_binder_data_source.cc
@@ -202,7 +202,7 @@
// static
const ProbesDataSource::Descriptor StatsdBinderDataSource::descriptor = {
- /*name*/ "android.statsd_binder",
+ /*name*/ "android.statsd",
/*flags*/ Descriptor::kFlagsNone,
/*fill_descriptor_func*/ nullptr,
};
@@ -242,30 +242,20 @@
const uint8_t* data,
size_t sz) {
ShellDataDecoder message(data, sz);
+ if (message.has_atom()) {
+ TraceWriter::TracePacketHandle packet = writer_->NewTracePacket();
- bool parse_error = false;
- auto timestamps_it = message.timestamp_nanos(&parse_error);
- std::vector<int64_t> timestamps;
- if (!parse_error) {
- for (; timestamps_it; ++timestamps_it) {
- timestamps.push_back(*timestamps_it);
- }
+ // The root packet gets the timestamp of *now* to aid in
+ // a) Packet sorting in trace_processor
+ // b) So we have some useful record of timestamp in case the statsd
+ // one gets broken in some exciting way.
+ packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
- TraceWriter::TracePacketHandle packet;
- size_t i = 0;
- for (auto it = message.atom(); it; ++it) {
- packet = writer_->NewTracePacket();
- if (i < timestamps.size()) {
- packet->set_timestamp(static_cast<uint64_t>(timestamps[i++]));
- } else {
- packet->set_timestamp(
- static_cast<uint64_t>(base::GetBootTimeNs().count()));
- }
- auto* statsd_atom = packet->set_statsd_atom();
- auto* atom = statsd_atom->add_atom();
- atom->AppendRawProtoBytes(it->data(), it->size());
- packet->Finalize();
- }
+ // Now put all the data. We rely on ShellData and StatsdAtom
+ // matching format exactly.
+ packet->AppendBytes(protos::pbzero::TracePacket::kStatsdAtomFieldNumber,
+ message.begin(),
+ static_cast<size_t>(message.end() - message.begin()));
}
// If we have the pending flush in progress resolve that:
diff --git a/src/traced/service/BUILD.gn b/src/traced/service/BUILD.gn
index e0770db..cb142e5 100644
--- a/src/traced/service/BUILD.gn
+++ b/src/traced/service/BUILD.gn
@@ -43,6 +43,10 @@
"../../tracing/core:service",
"../../tracing/ipc/service",
]
+ if (enable_perfetto_zlib) {
+ deps += [ "../../tracing/core:zlib_compressor" ]
+ }
+
sources = [
"builtin_producer.cc",
"builtin_producer.h",
diff --git a/src/traced/service/service.cc b/src/traced/service/service.cc
index 72f19ac..c9d6335 100644
--- a/src/traced/service/service.cc
+++ b/src/traced/service/service.cc
@@ -43,6 +43,10 @@
#include <sys/system_properties.h>
#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include "src/tracing/core/zlib_compressor.h"
+#endif
+
namespace perfetto {
namespace {
#if defined(PERFETTO_SET_SOCKET_PERMISSIONS)
@@ -158,7 +162,11 @@
base::UnixTaskRunner task_runner;
std::unique_ptr<ServiceIPCHost> svc;
- svc = ServiceIPCHost::CreateInstance(&task_runner);
+ TracingService::InitOpts init_opts = {};
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+ init_opts.compressor_fn = &ZlibCompressFn;
+#endif
+ svc = ServiceIPCHost::CreateInstance(&task_runner, init_opts);
// When built as part of the Android tree, the two socket are created and
// bound by init and their fd number is passed in two env variables.
diff --git a/src/tracing/core/BUILD.gn b/src/tracing/core/BUILD.gn
index ea1cb90..3fe90ea 100644
--- a/src/tracing/core/BUILD.gn
+++ b/src/tracing/core/BUILD.gn
@@ -83,6 +83,21 @@
}
}
+if (enable_perfetto_zlib) {
+ source_set("zlib_compressor") {
+ deps = [
+ ":core",
+ "../../../gn:default_deps",
+ "../../../gn:zlib",
+ "../../../include/perfetto/tracing",
+ ]
+ sources = [
+ "zlib_compressor.cc",
+ "zlib_compressor.h",
+ ]
+ }
+}
+
perfetto_unittest_source_set("unittests") {
testonly = true
deps = [
@@ -99,6 +114,14 @@
"../../base:test_support",
"../test:test_support",
]
+
+ if (enable_perfetto_zlib) {
+ deps += [
+ ":zlib_compressor",
+ "../../../gn:zlib",
+ ]
+ }
+
sources = [
"histogram_unittest.cc",
"id_allocator_unittest.cc",
@@ -110,6 +133,10 @@
"trace_packet_unittest.cc",
]
+ if (enable_perfetto_zlib) {
+ sources += [ "zlib_compressor_unittest.cc" ]
+ }
+
# These tests rely on test_task_runner.h which
# has no Windows implementation.
if (!is_win) {
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 7a15a0a..b32a658 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -16,9 +16,6 @@
#include "src/tracing/core/tracing_service_impl.h"
-#include "perfetto/base/build_config.h"
-#include "perfetto/tracing/core/forward_decls.h"
-
#include <errno.h>
#include <limits.h>
#include <string.h>
@@ -304,15 +301,18 @@
// static
std::unique_ptr<TracingService> TracingService::CreateInstance(
std::unique_ptr<SharedMemory::Factory> shm_factory,
- base::TaskRunner* task_runner) {
+ base::TaskRunner* task_runner,
+ InitOpts init_opts) {
return std::unique_ptr<TracingService>(
- new TracingServiceImpl(std::move(shm_factory), task_runner));
+ new TracingServiceImpl(std::move(shm_factory), task_runner, init_opts));
}
TracingServiceImpl::TracingServiceImpl(
std::unique_ptr<SharedMemory::Factory> shm_factory,
- base::TaskRunner* task_runner)
+ base::TaskRunner* task_runner,
+ InitOpts init_opts)
: task_runner_(task_runner),
+ init_opts_(init_opts),
shm_factory_(std::move(shm_factory)),
uid_(base::GetCurrentUserId()),
buffer_ids_(kMaxTraceBufferID),
@@ -580,8 +580,8 @@
cfg.duration_ms(), max_duration_ms);
}
- const bool has_trigger_config = cfg.trigger_config().trigger_mode() !=
- TraceConfig::TriggerConfig::UNSPECIFIED;
+ const bool has_trigger_config =
+ GetTriggerMode(cfg) != TraceConfig::TriggerConfig::UNSPECIFIED;
if (has_trigger_config &&
(cfg.trigger_config().trigger_timeout_ms() == 0 ||
cfg.trigger_config().trigger_timeout_ms() > max_duration_ms)) {
@@ -603,6 +603,16 @@
"The trace config specified an invalid trigger_mode");
}
+ if (cfg.trigger_config().use_clone_snapshot_if_available() &&
+ cfg.trigger_config().trigger_mode() !=
+ TraceConfig::TriggerConfig::STOP_TRACING) {
+ MaybeLogUploadEvent(
+ cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerMode);
+ return PERFETTO_SVC_ERR(
+ "trigger_mode must be STOP_TRACING when "
+ "use_clone_snapshot_if_available=true");
+ }
+
if (has_trigger_config && cfg.duration_ms() != 0) {
MaybeLogUploadEvent(
cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingDurationWithTrigger);
@@ -610,8 +620,8 @@
"duration_ms was set, this must not be set for traces with triggers.");
}
- if (cfg.trigger_config().trigger_mode() ==
- TraceConfig::TriggerConfig::STOP_TRACING &&
+ if ((GetTriggerMode(cfg) == TraceConfig::TriggerConfig::STOP_TRACING ||
+ GetTriggerMode(cfg) == TraceConfig::TriggerConfig::CLONE_SNAPSHOT) &&
cfg.write_into_file()) {
// We don't support this usecase because there are subtle assumptions which
// break around TracingServiceEvents and windowed sorting (i.e. if we don't
@@ -623,8 +633,8 @@
cfg, uuid,
PerfettoStatsdAtom::kTracedEnableTracingStopTracingWriteIntoFile);
return PERFETTO_SVC_ERR(
- "Specifying trigger mode STOP_TRACING and write_into_file together is "
- "unsupported");
+ "Specifying trigger mode STOP_TRACING/CLONE_SNAPSHOT and "
+ "write_into_file together is unsupported");
}
std::unordered_set<std::string> triggers;
@@ -846,6 +856,17 @@
tracing_session->bytes_written_into_file = 0;
}
+ if (!cfg.compress_from_cli() &&
+ cfg.compression_type() == TraceConfig::COMPRESSION_TYPE_DEFLATE) {
+ if (init_opts_.compressor_fn) {
+ tracing_session->compress_deflate = true;
+ } else {
+ PERFETTO_LOG(
+ "COMPRESSION_TYPE_DEFLATE is not supported in the current build "
+ "configuration. Skipping compression");
+ }
+ }
+
// Initialize the log buffers.
bool did_allocate_all_buffers = true;
bool invalid_buffer_config = false;
@@ -936,7 +957,7 @@
bool has_start_trigger = false;
auto weak_this = weak_ptr_factory_.GetWeakPtr();
- switch (cfg.trigger_config().trigger_mode()) {
+ switch (GetTriggerMode(cfg)) {
case TraceConfig::TriggerConfig::UNSPECIFIED:
// no triggers are specified so this isn't a trace that is using triggers.
PERFETTO_DCHECK(!has_trigger_config);
@@ -953,6 +974,7 @@
cfg.trigger_config().trigger_timeout_ms());
break;
case TraceConfig::TriggerConfig::STOP_TRACING:
+ case TraceConfig::TriggerConfig::CLONE_SNAPSHOT:
// Update the tracing_session's duration_ms to ensure that if no trigger
// is received the session will end and be cleaned up equal to the
// timeout.
@@ -1238,7 +1260,7 @@
// If this trace was using STOP_TRACING triggers and we've seen
// one, then the trigger overrides the normal timeout. In this
// case we just return and let the other task clean up this trace.
- if (tracing_session_ptr->config.trigger_config().trigger_mode() ==
+ if (GetTriggerMode(tracing_session_ptr->config) ==
TraceConfig::TriggerConfig::STOP_TRACING &&
!tracing_session_ptr->received_triggers.empty())
return;
@@ -1478,7 +1500,7 @@
std::string triggered_session_name;
base::Uuid triggered_session_uuid;
TracingSessionID triggered_session_id = 0;
- int trigger_mode = 0;
+ auto trigger_mode = TraceConfig::TriggerConfig::UNSPECIFIED;
uint64_t trigger_name_hash = hash.digest();
size_t count_in_window =
@@ -1537,8 +1559,7 @@
triggered_session_name = tracing_session.config.unique_session_name();
triggered_session_uuid.set_lsb_msb(tracing_session.trace_uuid.lsb(),
tracing_session.trace_uuid.msb());
- trigger_mode = static_cast<int>(
- tracing_session.config.trigger_config().trigger_mode());
+ trigger_mode = GetTriggerMode(tracing_session.config);
const bool triggers_already_received =
!tracing_session.received_triggers.empty();
@@ -1546,7 +1567,7 @@
{static_cast<uint64_t>(now_ns), iter->name(), producer->name_,
producer->uid_});
auto weak_this = weak_ptr_factory_.GetWeakPtr();
- switch (tracing_session.config.trigger_config().trigger_mode()) {
+ switch (trigger_mode) {
case TraceConfig::TriggerConfig::START_TRACING:
// If the session has already been triggered and moved past
// CONFIGURED then we don't need to repeat StartTracing. This would
@@ -1593,6 +1614,24 @@
// will happen shortly.
iter->stop_delay_ms());
break;
+
+ case TraceConfig::TriggerConfig::CLONE_SNAPSHOT:
+ trigger_activated = true;
+ MaybeLogUploadEvent(
+ tracing_session.config, tracing_session.trace_uuid,
+ PerfettoStatsdAtom::kTracedTriggerCloneSnapshot, iter->name());
+ task_runner_->PostDelayedTask(
+ [weak_this, tsid] {
+ if (!weak_this)
+ return;
+ auto* tsess = weak_this->GetTracingSession(tsid);
+ if (!tsess || !tsess->consumer_maybe_null)
+ return;
+ tsess->consumer_maybe_null->NotifyCloneSnapshotTrigger();
+ },
+ iter->stop_delay_ms());
+ break;
+
case TraceConfig::TriggerConfig::UNSPECIFIED:
PERFETTO_ELOG("Trigger activated but trigger mode unspecified.");
break;
@@ -2299,6 +2338,8 @@
MaybeFilterPackets(tracing_session, &packets);
+ MaybeCompressPackets(tracing_session, &packets);
+
if (!*has_more) {
// We've observed some extremely high memory usage by scudo after
// MaybeFilterPackets in the past. The original bug (b/195145848) is fixed
@@ -2351,6 +2392,16 @@
}
}
+void TracingServiceImpl::MaybeCompressPackets(
+ TracingSession* tracing_session,
+ std::vector<TracePacket>* packets) {
+ if (!tracing_session->compress_deflate) {
+ return;
+ }
+
+ init_opts_.compressor_fn(packets);
+}
+
bool TracingServiceImpl::WriteIntoFile(TracingSession* tracing_session,
std::vector<TracePacket> packets) {
if (!tracing_session->write_into_file) {
@@ -3490,7 +3541,7 @@
TracingSession* session = FindTracingSessionWithMaxBugreportScore();
if (!session) {
consumer->consumer_->OnSessionCloned(
- false, "No tracing sessions eligible for bugreport found");
+ {false, "No tracing sessions eligible for bugreport found", {}});
return;
}
tsid = session->id;
@@ -3503,15 +3554,18 @@
final_flush_outcome);
if (!weak_this || !weak_consumer)
return;
- base::Status result =
- weak_this->DoCloneSession(&*weak_consumer, tsid, final_flush_outcome);
- weak_consumer->consumer_->OnSessionCloned(result.ok(), result.message());
+ base::Uuid uuid;
+ base::Status result = weak_this->DoCloneSession(&*weak_consumer, tsid,
+ final_flush_outcome, &uuid);
+ weak_consumer->consumer_->OnSessionCloned(
+ {result.ok(), result.message(), uuid});
});
}
base::Status TracingServiceImpl::DoCloneSession(ConsumerEndpointImpl* consumer,
TracingSessionID src_tsid,
- bool final_flush_outcome) {
+ bool final_flush_outcome,
+ base::Uuid* new_uuid) {
PERFETTO_DLOG("CloneSession(%" PRIu64 ") started, consumer uid: %d", src_tsid,
static_cast<int>(consumer->uid_));
@@ -3562,6 +3616,7 @@
cloned_session->state = TracingSession::CLONED_READ_ONLY;
cloned_session->trace_uuid = base::Uuidv4(); // Generate a new UUID.
+ *new_uuid = cloned_session->trace_uuid;
for (auto& kv : buf_snaps) {
BufferID buf_global_id = kv.first;
@@ -3582,6 +3637,7 @@
cloned_session->flushes_requested = src->flushes_requested;
cloned_session->flushes_succeeded = src->flushes_succeeded;
cloned_session->flushes_failed = src->flushes_failed;
+ cloned_session->compress_deflate = src->compress_deflate;
if (src->trace_filter) {
// Copy the trace filter.
cloned_session->trace_filter.reset(
@@ -3807,6 +3863,15 @@
observable_events->set_all_data_sources_started(true);
}
+void TracingServiceImpl::ConsumerEndpointImpl::NotifyCloneSnapshotTrigger() {
+ if (!(observable_events_mask_ & ObservableEvents::TYPE_CLONE_TRIGGER_HIT)) {
+ return;
+ }
+ auto* observable_events = AddObservableEvents();
+ auto* clone_trig = observable_events->mutable_clone_trigger_hit();
+ clone_trig->set_tracing_session_id(static_cast<int64_t>(tracing_session_id_));
+}
+
ObservableEvents*
TracingServiceImpl::ConsumerEndpointImpl::AddObservableEvents() {
PERFETTO_DCHECK_THREAD(thread_checker_);
@@ -3903,11 +3968,13 @@
TracingServiceCapabilities caps;
caps.set_has_query_capabilities(true);
caps.set_has_trace_config_output_path(true);
+ caps.set_has_clone_session(true);
caps.add_observable_events(ObservableEvents::TYPE_DATA_SOURCES_INSTANCES);
caps.add_observable_events(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
- static_assert(ObservableEvents::Type_MAX ==
- ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED,
- "");
+ caps.add_observable_events(ObservableEvents::TYPE_CLONE_TRIGGER_HIT);
+ static_assert(
+ ObservableEvents::Type_MAX == ObservableEvents::TYPE_CLONE_TRIGGER_HIT,
+ "");
callback(caps);
}
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 7cefb9b..8d067fd 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -87,8 +87,9 @@
// tracing_integration_test.cc and b/195065199
// This is a rough threshold to determine how many bytes to read from the
- // buffers on each iteration when writing into a file. Since filtering
- // allocates memory, this limits the amount of memory allocated.
+ // buffers on each iteration when writing into a file. Since filtering and
+ // compression allocate memory, this effectively limits the amount of memory
+ // allocated.
static constexpr size_t kWriteIntoFileChunkSize = 1024 * 1024ul;
// The implementation behind the service endpoint exposed to each producer.
@@ -209,6 +210,7 @@
~ConsumerEndpointImpl() override;
void NotifyOnTracingDisabled(const std::string& error);
+ void NotifyCloneSnapshotTrigger();
// TracingService::ConsumerEndpoint implementation.
void EnableTracing(const TraceConfig&, base::ScopedFile) override;
@@ -264,7 +266,8 @@
};
explicit TracingServiceImpl(std::unique_ptr<SharedMemory::Factory>,
- base::TaskRunner*);
+ base::TaskRunner*,
+ InitOpts = {});
~TracingServiceImpl() override;
// Called by ProducerEndpointImpl.
@@ -565,6 +568,9 @@
// Whether we put the system info into the trace output yet.
bool did_emit_system_info = false;
+ // Whether we should compress TracePackets after reading them.
+ bool compress_deflate = false;
+
// The number of received triggers we've emitted into the trace output.
size_t num_triggers_emitted_into_trace = 0;
@@ -723,7 +729,8 @@
TraceBuffer* GetBufferByID(BufferID);
base::Status DoCloneSession(ConsumerEndpointImpl*,
TracingSessionID,
- bool final_flush_outcome);
+ bool final_flush_outcome,
+ base::Uuid*);
// Returns true if `*tracing_session` is waiting for a trigger that hasn't
// happened.
@@ -744,6 +751,10 @@
void MaybeFilterPackets(TracingSession* tracing_session,
std::vector<TracePacket>* packets);
+ // If `*tracing_session` has compression enabled, compress `*packets`.
+ void MaybeCompressPackets(TracingSession* tracing_session,
+ std::vector<TracePacket>* packets);
+
// If `*tracing_session` is configured to write into a file, writes `packets`
// into the file.
//
@@ -765,6 +776,7 @@
TracingSessionID);
base::TaskRunner* const task_runner_;
+ const InitOpts init_opts_;
std::unique_ptr<SharedMemory::Factory> shm_factory_;
ProducerID last_producer_id_ = 0;
DataSourceInstanceID last_data_source_instance_id_ = 0;
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 6542723..fb175e9 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -49,6 +49,11 @@
#include "protos/perfetto/trace/trace_uuid.gen.h"
#include "protos/perfetto/trace/trigger.gen.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#include <zlib.h>
+#include "src/tracing/core/zlib_compressor.h"
+#endif
+
using ::testing::_;
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
@@ -103,6 +108,53 @@
return HasTriggerModeInternal(arg, mode);
}
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+std::string Decompress(const std::string& data) {
+ uint8_t out[1024];
+
+ z_stream stream{};
+ stream.next_in = reinterpret_cast<uint8_t*>(const_cast<char*>(data.data()));
+ stream.avail_in = static_cast<unsigned int>(data.size());
+
+ EXPECT_EQ(inflateInit(&stream), Z_OK);
+ std::string s;
+
+ int ret;
+ do {
+ stream.next_out = out;
+ stream.avail_out = sizeof(out);
+ ret = inflate(&stream, Z_NO_FLUSH);
+ EXPECT_NE(ret, Z_STREAM_ERROR);
+ EXPECT_NE(ret, Z_NEED_DICT);
+ EXPECT_NE(ret, Z_DATA_ERROR);
+ EXPECT_NE(ret, Z_MEM_ERROR);
+ s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
+ } while (ret != Z_STREAM_END);
+
+ inflateEnd(&stream);
+ return s;
+}
+
+std::vector<protos::gen::TracePacket> DecompressTrace(
+ const std::vector<protos::gen::TracePacket> compressed) {
+ std::vector<protos::gen::TracePacket> decompressed;
+
+ for (const protos::gen::TracePacket& c : compressed) {
+ if (c.compressed_packets().empty()) {
+ decompressed.push_back(c);
+ continue;
+ }
+
+ std::string s = Decompress(c.compressed_packets());
+ protos::gen::Trace t;
+ EXPECT_TRUE(t.ParseFromString(s));
+ decompressed.insert(decompressed.end(), t.packet().begin(),
+ t.packet().end());
+ }
+ return decompressed;
+}
+#endif // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
} // namespace
class TracingServiceImplTest : public testing::Test {
@@ -110,11 +162,14 @@
using DataSourceInstanceState =
TracingServiceImpl::DataSourceInstance::DataSourceInstanceState;
- TracingServiceImplTest() {
+ TracingServiceImplTest() { InitializeSvcWithOpts({}); }
+
+ void InitializeSvcWithOpts(TracingService::InitOpts init_opts) {
auto shm_factory =
std::unique_ptr<SharedMemory::Factory>(new TestSharedMemory::Factory());
svc.reset(static_cast<TracingServiceImpl*>(
- TracingService::CreateInstance(std::move(shm_factory), &task_runner)
+ TracingService::CreateInstance(std::move(shm_factory), &task_runner,
+ init_opts)
.release()));
svc->min_write_period_ms_ = 1;
}
@@ -1631,6 +1686,312 @@
ASSERT_EQ(6u, connect_producer_and_get_id("6"));
}
+TEST_F(TracingServiceImplTest, CompressionConfiguredButUnsupported) {
+ // Initialize the service without support for compression.
+ TracingService::InitOpts init_opts;
+ init_opts.compressor_fn = nullptr;
+ InitializeSvcWithOpts(init_opts);
+
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+ producer->RegisterDataSource("data_source");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(4096);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("data_source");
+ ds_config->set_target_buffer(0);
+ // Ask for compression in the config.
+ trace_config.set_compression_type(TraceConfig::COMPRESSION_TYPE_DEFLATE);
+ consumer->EnableTracing(trace_config);
+
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("data_source");
+ producer->WaitForDataSourceStart("data_source");
+
+ std::unique_ptr<TraceWriter> writer =
+ producer->CreateTraceWriter("data_source");
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-1");
+ }
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-2");
+ }
+
+ writer->Flush();
+ writer.reset();
+
+ consumer->DisableTracing();
+ producer->WaitForDataSourceStop("data_source");
+ consumer->WaitForTracingDisabled();
+
+ // The packets should NOT be compressed.
+ std::vector<protos::gen::TracePacket> packets = consumer->ReadBuffers();
+ EXPECT_THAT(packets, Not(IsEmpty()));
+ EXPECT_THAT(
+ packets,
+ Each(Property(&protos::gen::TracePacket::has_compressed_packets, false)));
+ EXPECT_THAT(packets, Contains(Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str,
+ Eq("payload-1")))));
+ EXPECT_THAT(packets, Contains(Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str,
+ Eq("payload-2")))));
+}
+
+#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+TEST_F(TracingServiceImplTest, CompressionFromCli) {
+ TracingService::InitOpts init_opts;
+ init_opts.compressor_fn = ZlibCompressFn;
+ InitializeSvcWithOpts(init_opts);
+
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+ producer->RegisterDataSource("data_source");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(4096);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("data_source");
+ ds_config->set_target_buffer(0);
+ trace_config.set_compression_type(TraceConfig::COMPRESSION_TYPE_DEFLATE);
+ // When compress_from_cli is enabled, the service shouldn't do compression
+ trace_config.set_compress_from_cli(true);
+ consumer->EnableTracing(trace_config);
+
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("data_source");
+ producer->WaitForDataSourceStart("data_source");
+
+ std::unique_ptr<TraceWriter> writer =
+ producer->CreateTraceWriter("data_source");
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-1");
+ }
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-2");
+ }
+
+ writer->Flush();
+ writer.reset();
+
+ consumer->DisableTracing();
+ producer->WaitForDataSourceStop("data_source");
+ consumer->WaitForTracingDisabled();
+
+ std::vector<protos::gen::TracePacket> packets = consumer->ReadBuffers();
+ EXPECT_THAT(packets, Contains(Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str,
+ Eq("payload-1")))));
+ EXPECT_THAT(packets, Contains(Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str,
+ Eq("payload-2")))));
+}
+
+TEST_F(TracingServiceImplTest, CompressionReadIpc) {
+ TracingService::InitOpts init_opts;
+ init_opts.compressor_fn = ZlibCompressFn;
+ InitializeSvcWithOpts(init_opts);
+
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+ producer->RegisterDataSource("data_source");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(4096);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("data_source");
+ ds_config->set_target_buffer(0);
+ trace_config.set_compression_type(TraceConfig::COMPRESSION_TYPE_DEFLATE);
+ consumer->EnableTracing(trace_config);
+
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("data_source");
+ producer->WaitForDataSourceStart("data_source");
+
+ std::unique_ptr<TraceWriter> writer =
+ producer->CreateTraceWriter("data_source");
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-1");
+ }
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-2");
+ }
+
+ writer->Flush();
+ writer.reset();
+
+ consumer->DisableTracing();
+ producer->WaitForDataSourceStop("data_source");
+ consumer->WaitForTracingDisabled();
+
+ std::vector<protos::gen::TracePacket> compressed_packets =
+ consumer->ReadBuffers();
+ EXPECT_THAT(compressed_packets, Not(IsEmpty()));
+ EXPECT_THAT(compressed_packets,
+ Each(Property(&protos::gen::TracePacket::compressed_packets,
+ Not(IsEmpty()))));
+ std::vector<protos::gen::TracePacket> decompressed_packets =
+ DecompressTrace(compressed_packets);
+ EXPECT_THAT(decompressed_packets,
+ Contains(Property(
+ &protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, Eq("payload-1")))));
+ EXPECT_THAT(decompressed_packets,
+ Contains(Property(
+ &protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, Eq("payload-2")))));
+}
+
+TEST_F(TracingServiceImplTest, CompressionWriteIntoFile) {
+ TracingService::InitOpts init_opts;
+ init_opts.compressor_fn = ZlibCompressFn;
+ InitializeSvcWithOpts(init_opts);
+
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+ producer->RegisterDataSource("data_source");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(4096);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("data_source");
+ ds_config->set_target_buffer(0);
+ trace_config.set_write_into_file(true);
+ trace_config.set_compression_type(TraceConfig::COMPRESSION_TYPE_DEFLATE);
+ base::TempFile tmp_file = base::TempFile::Create();
+ consumer->EnableTracing(trace_config, base::ScopedFile(dup(tmp_file.fd())));
+
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("data_source");
+ producer->WaitForDataSourceStart("data_source");
+
+ std::unique_ptr<TraceWriter> writer =
+ producer->CreateTraceWriter("data_source");
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-1");
+ }
+ {
+ auto tp = writer->NewTracePacket();
+ tp->set_for_testing()->set_str("payload-2");
+ }
+
+ writer->Flush();
+ writer.reset();
+
+ consumer->DisableTracing();
+ producer->WaitForDataSourceStop("data_source");
+ consumer->WaitForTracingDisabled();
+
+ // Verify the contents of the file.
+ std::string trace_raw;
+ ASSERT_TRUE(base::ReadFile(tmp_file.path().c_str(), &trace_raw));
+ protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_raw));
+ EXPECT_THAT(trace.packet(), Not(IsEmpty()));
+ EXPECT_THAT(trace.packet(),
+ Each(Property(&protos::gen::TracePacket::compressed_packets,
+ Not(IsEmpty()))));
+ std::vector<protos::gen::TracePacket> decompressed_packets =
+ DecompressTrace(trace.packet());
+ EXPECT_THAT(decompressed_packets,
+ Contains(Property(
+ &protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, Eq("payload-1")))));
+ EXPECT_THAT(decompressed_packets,
+ Contains(Property(
+ &protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, Eq("payload-2")))));
+}
+
+TEST_F(TracingServiceImplTest, CloneSessionWithCompression) {
+ TracingService::InitOpts init_opts;
+ init_opts.compressor_fn = ZlibCompressFn;
+ InitializeSvcWithOpts(init_opts);
+
+ // The consumer the creates the initial tracing session.
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ // The consumer that clones it and reads back the data.
+ std::unique_ptr<MockConsumer> consumer2 = CreateMockConsumer();
+ consumer2->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+
+ producer->RegisterDataSource("ds_1");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(32);
+ auto* ds_cfg = trace_config.add_data_sources()->mutable_config();
+ ds_cfg->set_name("ds_1");
+ trace_config.set_compression_type(TraceConfig::COMPRESSION_TYPE_DEFLATE);
+
+ consumer->EnableTracing(trace_config);
+ producer->WaitForTracingSetup();
+
+ producer->WaitForDataSourceSetup("ds_1");
+
+ producer->WaitForDataSourceStart("ds_1");
+
+ std::unique_ptr<TraceWriter> writer = producer->CreateTraceWriter("ds_1");
+
+ // Add some data.
+ static constexpr size_t kNumTestPackets = 20;
+ for (size_t i = 0; i < kNumTestPackets; i++) {
+ auto tp = writer->NewTracePacket();
+ std::string payload("payload" + std::to_string(i));
+ tp->set_for_testing()->set_str(payload.c_str(), payload.size());
+ tp->set_timestamp(static_cast<uint64_t>(i));
+ }
+
+ auto clone_done = task_runner.CreateCheckpoint("clone_done");
+ EXPECT_CALL(*consumer2, OnSessionCloned(_))
+ .WillOnce(Invoke([clone_done](const Consumer::OnSessionClonedArgs&) {
+ clone_done();
+ }));
+ consumer2->CloneSession(1);
+ // CloneSession() will implicitly issue a flush. Linearize with that.
+ producer->WaitForFlush(std::vector<TraceWriter*>{writer.get()});
+ task_runner.RunUntilCheckpoint("clone_done");
+
+ // Delete the initial tracing session.
+ consumer->DisableTracing();
+ consumer->FreeBuffers();
+ producer->WaitForDataSourceStop("ds_1");
+ consumer->WaitForTracingDisabled();
+
+ // Read back the cloned trace and check that it's compressed
+ std::vector<protos::gen::TracePacket> compressed_packets =
+ consumer2->ReadBuffers();
+ EXPECT_THAT(compressed_packets, Not(IsEmpty()));
+ EXPECT_THAT(compressed_packets,
+ Each(Property(&protos::gen::TracePacket::compressed_packets,
+ Not(IsEmpty()))));
+}
+
+#endif // PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+
// Note: file_write_period_ms is set to a large enough to have exactly one flush
// of the tracing buffers (and therefore at most one synchronization section),
// unless the test runs unrealistically slowly, or the implementation of the
@@ -4071,7 +4432,9 @@
// Message 0: root Trace proto.
filt.AddNestedField(1 /* root trace.packet*/, 1);
filt.EndMessage();
- // Message 1: TracePacket proto. Allow only the `for_testing` sub-field.
+ // Message 1: TracePacket proto. Allow only the `for_testing` and `trace_uuid`
+ // sub-fields.
+ filt.AddSimpleField(protos::pbzero::TracePacket::kTraceUuidFieldNumber);
filt.AddSimpleField(protos::pbzero::TracePacket::kForTestingFieldNumber);
filt.EndMessage();
trace_config.mutable_trace_filter()->set_bytecode(filt.Serialize());
@@ -4100,8 +4463,17 @@
}
auto clone_done = task_runner.CreateCheckpoint("clone_done");
- EXPECT_CALL(*consumer2, OnSessionCloned(true, ""))
- .WillOnce(InvokeWithoutArgs(clone_done));
+ base::Uuid clone_uuid;
+ EXPECT_CALL(*consumer2, OnSessionCloned(_))
+ .WillOnce(Invoke(
+ [clone_done, &clone_uuid](const Consumer::OnSessionClonedArgs& args) {
+ ASSERT_TRUE(args.success);
+ ASSERT_TRUE(args.error.empty());
+ ASSERT_NE(args.uuid.msb(), 0);
+ ASSERT_NE(args.uuid.lsb(), 0);
+ clone_uuid = args.uuid;
+ clone_done();
+ }));
consumer2->CloneSession(1);
// CloneSession() will implicitly issue a flush. Linearize with that.
producer->WaitForFlush({writers[0].get(), writers[1].get()});
@@ -4145,6 +4517,16 @@
// Check that the `timestamp` field is filtered out.
EXPECT_THAT(packets,
Each(Property(&protos::gen::TracePacket::has_timestamp, false)));
+
+ // Check that the UUID in the trace matches the UUID passed to to the
+ // OnCloneSession consumer API.
+ EXPECT_THAT(
+ packets,
+ Contains(Property(
+ &protos::gen::TracePacket::trace_uuid,
+ AllOf(
+ Property(&protos::gen::TraceUuid::msb, Eq(clone_uuid.msb())),
+ Property(&protos::gen::TraceUuid::lsb, Eq(clone_uuid.lsb()))))));
}
TEST_F(TracingServiceImplTest, InvalidBufferSizes) {
diff --git a/src/tracing/core/virtual_destructors.cc b/src/tracing/core/virtual_destructors.cc
index f98d6ff..d38a776 100644
--- a/src/tracing/core/virtual_destructors.cc
+++ b/src/tracing/core/virtual_destructors.cc
@@ -38,6 +38,6 @@
// TODO(primiano): make pure virtual after various 3way patches.
void ConsumerEndpoint::CloneSession(TracingSessionID) {}
-void Consumer::OnSessionCloned(bool, const std::string&) {}
+void Consumer::OnSessionCloned(const OnSessionClonedArgs&) {}
} // namespace perfetto
diff --git a/src/tracing/core/zlib_compressor.cc b/src/tracing/core/zlib_compressor.cc
new file mode 100644
index 0000000..1a9689e
--- /dev/null
+++ b/src/tracing/core/zlib_compressor.cc
@@ -0,0 +1,173 @@
+/*
+ * 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/tracing/core/zlib_compressor.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
+#error "Zlib must be enabled to compile this file."
+#endif
+
+#include <zlib.h>
+
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+namespace {
+
+struct Preamble {
+ uint32_t size;
+ std::array<uint8_t, 16> buf;
+};
+
+template <uint32_t id>
+Preamble GetPreamble(size_t sz) {
+ Preamble preamble;
+ uint8_t* ptr = preamble.buf.data();
+ constexpr uint32_t tag = protozero::proto_utils::MakeTagLengthDelimited(id);
+ ptr = protozero::proto_utils::WriteVarInt(tag, ptr);
+ ptr = protozero::proto_utils::WriteVarInt(sz, ptr);
+ preamble.size =
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr) -
+ reinterpret_cast<uintptr_t>(preamble.buf.data()));
+ PERFETTO_DCHECK(preamble.size < preamble.buf.size());
+ return preamble;
+}
+
+Slice PreambleToSlice(const Preamble& preamble) {
+ Slice slice = Slice::Allocate(preamble.size);
+ memcpy(slice.own_data(), preamble.buf.data(), preamble.size);
+ return slice;
+}
+
+// A compressor for `TracePacket`s that uses zlib. The class is exposed for
+// testing.
+class ZlibPacketCompressor {
+ public:
+ ZlibPacketCompressor();
+ ~ZlibPacketCompressor();
+
+ // Can be called multiple times, before Finish() is called.
+ void PushPacket(const TracePacket& packet);
+
+ // Returned the compressed data. Can be called at most once. After this call,
+ // the object is unusable (PushPacket should not be called) and must be
+ // destroyed.
+ TracePacket Finish();
+
+ private:
+ void PushData(const void* data, uint32_t size);
+ void NewOutputSlice();
+ void PushCurSlice();
+
+ z_stream stream_;
+ size_t total_new_slices_size_ = 0;
+ std::vector<Slice> new_slices_;
+ std::unique_ptr<uint8_t[]> cur_slice_;
+};
+
+ZlibPacketCompressor::ZlibPacketCompressor() {
+ memset(&stream_, 0, sizeof(stream_));
+ int status = deflateInit(&stream_, 6);
+ PERFETTO_CHECK(status == Z_OK);
+}
+
+ZlibPacketCompressor::~ZlibPacketCompressor() {
+ int status = deflateEnd(&stream_);
+ PERFETTO_CHECK(status == Z_OK);
+}
+
+void ZlibPacketCompressor::PushPacket(const TracePacket& packet) {
+ // We need to be able to tokenize packets in the compressed stream, so we
+ // prefix a proto preamble to each packet. The compressed stream looks like a
+ // valid Trace proto.
+ Preamble preamble =
+ GetPreamble<protos::pbzero::Trace::kPacketFieldNumber>(packet.size());
+ PushData(preamble.buf.data(), preamble.size);
+ for (const Slice& slice : packet.slices()) {
+ PushData(slice.start, static_cast<uint32_t>(slice.size));
+ }
+}
+
+void ZlibPacketCompressor::PushData(const void* data, uint32_t size) {
+ stream_.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(data));
+ stream_.avail_in = static_cast<uInt>(size);
+ while (stream_.avail_in != 0) {
+ if (stream_.avail_out == 0) {
+ NewOutputSlice();
+ }
+ int status = deflate(&stream_, Z_NO_FLUSH);
+ PERFETTO_CHECK(status == Z_OK);
+ }
+}
+
+TracePacket ZlibPacketCompressor::Finish() {
+ for (;;) {
+ int status = deflate(&stream_, Z_FINISH);
+ if (status == Z_STREAM_END)
+ break;
+ PERFETTO_CHECK(status == Z_OK || status == Z_BUF_ERROR);
+ NewOutputSlice();
+ }
+
+ PushCurSlice();
+
+ TracePacket packet;
+ packet.AddSlice(PreambleToSlice(
+ GetPreamble<protos::pbzero::TracePacket::kCompressedPacketsFieldNumber>(
+ total_new_slices_size_)));
+ for (auto& slice : new_slices_) {
+ packet.AddSlice(std::move(slice));
+ }
+ return packet;
+}
+
+void ZlibPacketCompressor::NewOutputSlice() {
+ PushCurSlice();
+ cur_slice_ = std::make_unique<uint8_t[]>(kZlibCompressSliceSize);
+ stream_.next_out = reinterpret_cast<Bytef*>(cur_slice_.get());
+ stream_.avail_out = kZlibCompressSliceSize;
+}
+
+void ZlibPacketCompressor::PushCurSlice() {
+ if (cur_slice_) {
+ total_new_slices_size_ += kZlibCompressSliceSize - stream_.avail_out;
+ new_slices_.push_back(Slice::TakeOwnership(
+ std::move(cur_slice_), kZlibCompressSliceSize - stream_.avail_out));
+ }
+}
+
+} // namespace
+
+void ZlibCompressFn(std::vector<TracePacket>* packets) {
+ if (packets->empty()) {
+ return;
+ }
+
+ ZlibPacketCompressor stream;
+
+ for (const TracePacket& packet : *packets) {
+ stream.PushPacket(packet);
+ }
+
+ TracePacket packet = stream.Finish();
+
+ packets->clear();
+ packets->push_back(std::move(packet));
+}
+
+} // namespace perfetto
diff --git a/src/trace_processor/db/storage.cc b/src/tracing/core/zlib_compressor.h
similarity index 61%
copy from src/trace_processor/db/storage.cc
copy to src/tracing/core/zlib_compressor.h
index 4799d04..1962c48 100644
--- a/src/trace_processor/db/storage.cc
+++ b/src/tracing/core/zlib_compressor.h
@@ -14,14 +14,20 @@
* limitations under the License.
*/
-#include "src/trace_processor/db/storage.h"
+#ifndef SRC_TRACING_CORE_ZLIB_COMPRESSOR_H_
+#define SRC_TRACING_CORE_ZLIB_COMPRESSOR_H_
+
+#include <vector>
+
+#include "perfetto/ext/tracing/core/trace_packet.h"
namespace perfetto {
-namespace trace_processor {
-namespace column {
-Storage::~Storage() = default;
+// Matches TracingServiceImpl::kMaxTracePacketSliceSize. Exposed for testing.
+static constexpr size_t kZlibCompressSliceSize = 128 * 1024 - 512;
-} // namespace column
-} // namespace trace_processor
+void ZlibCompressFn(std::vector<TracePacket>*);
+
} // namespace perfetto
+
+#endif // SRC_TRACING_CORE_ZLIB_COMPRESSOR_H_
diff --git a/src/tracing/core/zlib_compressor_unittest.cc b/src/tracing/core/zlib_compressor_unittest.cc
new file mode 100644
index 0000000..2471c1b
--- /dev/null
+++ b/src/tracing/core/zlib_compressor_unittest.cc
@@ -0,0 +1,178 @@
+/*
+ * 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/tracing/core/zlib_compressor.h"
+
+#include <random>
+
+#include <zlib.h>
+
+#include "protos/perfetto/trace/test_event.gen.h"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "src/tracing/core/tracing_service_impl.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace {
+
+using ::testing::Each;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::IsEmpty;
+using ::testing::Le;
+using ::testing::Not;
+using ::testing::Property;
+using ::testing::SizeIs;
+
+template <typename F>
+TracePacket CreateTracePacket(F fill_function) {
+ protos::gen::TracePacket msg;
+ fill_function(&msg);
+ std::vector<uint8_t> buf = msg.SerializeAsArray();
+ Slice slice = Slice::Allocate(buf.size());
+ memcpy(slice.own_data(), buf.data(), buf.size());
+ perfetto::TracePacket packet;
+ packet.AddSlice(std::move(slice));
+ return packet;
+}
+
+// Return a copy of the `old` trace packets that owns its own slices data.
+TracePacket CopyTracePacket(const TracePacket& old) {
+ TracePacket ret;
+ for (const Slice& slice : old.slices()) {
+ auto new_slice = Slice::Allocate(slice.size);
+ memcpy(new_slice.own_data(), slice.start, slice.size);
+ ret.AddSlice(std::move(new_slice));
+ }
+ return ret;
+}
+
+std::vector<TracePacket> CopyTracePackets(const std::vector<TracePacket>& old) {
+ std::vector<TracePacket> ret;
+ ret.reserve(old.size());
+ for (const TracePacket& trace_packet : old) {
+ ret.push_back(CopyTracePacket(trace_packet));
+ }
+ return ret;
+}
+std::string RandomString(size_t size) {
+ std::default_random_engine rnd(0);
+ std::uniform_int_distribution<> dist(0, 255);
+ std::string s;
+ s.resize(size);
+ for (size_t i = 0; i < s.size(); i++)
+ s[i] = static_cast<char>(dist(rnd));
+ return s;
+}
+
+std::string Decompress(const std::string& data) {
+ uint8_t out[1024];
+
+ z_stream stream{};
+ stream.next_in = reinterpret_cast<uint8_t*>(const_cast<char*>(data.data()));
+ stream.avail_in = static_cast<unsigned int>(data.size());
+
+ EXPECT_EQ(inflateInit(&stream), Z_OK);
+ std::string s;
+
+ int ret;
+ do {
+ stream.next_out = out;
+ stream.avail_out = sizeof(out);
+ ret = inflate(&stream, Z_NO_FLUSH);
+ EXPECT_NE(ret, Z_STREAM_ERROR);
+ EXPECT_NE(ret, Z_NEED_DICT);
+ EXPECT_NE(ret, Z_DATA_ERROR);
+ EXPECT_NE(ret, Z_MEM_ERROR);
+ s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
+ } while (ret != Z_STREAM_END);
+
+ inflateEnd(&stream);
+ return s;
+}
+
+static_assert(kZlibCompressSliceSize ==
+ TracingServiceImpl::kMaxTracePacketSliceSize);
+
+TEST(ZlibCompressFnTest, Empty) {
+ std::vector<TracePacket> packets;
+
+ ZlibCompressFn(&packets);
+
+ EXPECT_THAT(packets, IsEmpty());
+}
+
+TEST(ZlibCompressFnTest, End2EndCompressAndDecompress) {
+ std::vector<TracePacket> packets;
+
+ packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
+ auto* for_testing = msg->mutable_for_testing();
+ for_testing->set_str("abc");
+ }));
+ packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
+ auto* for_testing = msg->mutable_for_testing();
+ for_testing->set_str("def");
+ }));
+
+ ZlibCompressFn(&packets);
+
+ ASSERT_THAT(packets, SizeIs(1));
+ protos::gen::TracePacket compressed_packet_proto;
+ ASSERT_TRUE(compressed_packet_proto.ParseFromString(
+ packets[0].GetRawBytesForTesting()));
+ const std::string& data = compressed_packet_proto.compressed_packets();
+ EXPECT_THAT(data, Not(IsEmpty()));
+ protos::gen::Trace subtrace;
+ ASSERT_TRUE(subtrace.ParseFromString(Decompress(data)));
+ EXPECT_THAT(
+ subtrace.packet(),
+ ElementsAre(Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, "abc")),
+ Property(&protos::gen::TracePacket::for_testing,
+ Property(&protos::gen::TestEvent::str, "def"))));
+}
+
+TEST(ZlibCompressFnTest, MaxSliceSize) {
+ std::vector<TracePacket> packets;
+
+ constexpr size_t kStopOutputSize =
+ TracingServiceImpl::kMaxTracePacketSliceSize + 2000;
+
+ TracePacket compressed_packet;
+ while (compressed_packet.size() < kStopOutputSize) {
+ packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
+ auto* for_testing = msg->mutable_for_testing();
+ for_testing->set_str(RandomString(65536));
+ }));
+ {
+ std::vector<TracePacket> packets_copy = CopyTracePackets(packets);
+ ZlibCompressFn(&packets_copy);
+ ASSERT_THAT(packets_copy, SizeIs(1));
+ compressed_packet = std::move(packets_copy[0]);
+ }
+ }
+
+ EXPECT_GE(compressed_packet.slices().size(), 2u);
+ ASSERT_GT(compressed_packet.size(),
+ TracingServiceImpl::kMaxTracePacketSliceSize);
+ EXPECT_THAT(compressed_packet.slices(),
+ Each(Field(&Slice::size,
+ Le(TracingServiceImpl::kMaxTracePacketSliceSize))));
+}
+
+} // namespace
+} // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 181237f..5169a2b 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -644,8 +644,7 @@
}
void TracingMuxerImpl::ConsumerImpl::OnSessionCloned(
- bool /*success*/,
- const std::string& /*error*/) {
+ const OnSessionClonedArgs&) {
// CloneSession is not exposed in the SDK. This should never happen.
PERFETTO_DCHECK(false);
}
@@ -1165,7 +1164,8 @@
}
// Only allow certain interceptors for now.
if (descriptor.name() != "test_interceptor" &&
- descriptor.name() != "console") {
+ descriptor.name() != "console" &&
+ descriptor.name() != "etwexport") {
PERFETTO_ELOG(
"Interceptors are experimental. If you want to use them, please "
"get in touch with the project maintainers "
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index 4c87bb9..54d2fc3 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -300,7 +300,7 @@
void OnAttach(bool success, const TraceConfig&) override;
void OnTraceStats(bool success, const TraceStats&) override;
void OnObservableEvents(const ObservableEvents&) override;
- void OnSessionCloned(bool, const std::string&) override;
+ void OnSessionCloned(const OnSessionClonedArgs&) override;
void NotifyStartComplete();
void NotifyError(const TracingError&);
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
index 7eecd09..cf34361 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
@@ -479,10 +479,11 @@
// If the IPC fails, we are talking to an older version of the service
// that didn't support CloneSession at all.
weak_this->consumer_->OnSessionCloned(
- false, "CloneSession IPC not supported");
+ {false, "CloneSession IPC not supported", {}});
} else {
- weak_this->consumer_->OnSessionCloned(response->success(),
- response->error());
+ base::Uuid uuid(response->uuid_lsb(), response->uuid_msb());
+ weak_this->consumer_->OnSessionCloned(
+ {response->success(), response->error(), uuid});
}
});
consumer_port_.CloneSession(req, std::move(async_response));
diff --git a/src/tracing/ipc/service/consumer_ipc_service.cc b/src/tracing/ipc/service/consumer_ipc_service.cc
index 2f45713..ac52864 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.cc
+++ b/src/tracing/ipc/service/consumer_ipc_service.cc
@@ -480,14 +480,15 @@
}
void ConsumerIPCService::RemoteConsumer::OnSessionCloned(
- bool success,
- const std::string& error) {
+ const OnSessionClonedArgs& args) {
if (!clone_session_response.IsBound())
return;
auto resp = ipc::AsyncResult<protos::gen::CloneSessionResponse>::Create();
- resp->set_success(success);
- resp->set_error(error);
+ resp->set_success(args.success);
+ resp->set_error(args.error);
+ resp->set_uuid_msb(args.uuid.msb());
+ resp->set_uuid_lsb(args.uuid.lsb());
std::move(clone_session_response).Resolve(std::move(resp));
}
diff --git a/src/tracing/ipc/service/consumer_ipc_service.h b/src/tracing/ipc/service/consumer_ipc_service.h
index d570c51..f1424d9 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.h
+++ b/src/tracing/ipc/service/consumer_ipc_service.h
@@ -94,7 +94,7 @@
void OnAttach(bool, const TraceConfig&) override;
void OnTraceStats(bool, const TraceStats&) override;
void OnObservableEvents(const ObservableEvents&) override;
- void OnSessionCloned(bool, const std::string&) override;
+ void OnSessionCloned(const OnSessionClonedArgs&) override;
void CloseObserveEventsResponseStream();
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.cc b/src/tracing/ipc/service/service_ipc_host_impl.cc
index 85029a2..1df674e 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.cc
+++ b/src/tracing/ipc/service/service_ipc_host_impl.cc
@@ -40,12 +40,15 @@
// Implements the publicly exposed factory method declared in
// include/tracing/posix_ipc/posix_service_host.h.
std::unique_ptr<ServiceIPCHost> ServiceIPCHost::CreateInstance(
- base::TaskRunner* task_runner) {
- return std::unique_ptr<ServiceIPCHost>(new ServiceIPCHostImpl(task_runner));
+ base::TaskRunner* task_runner,
+ TracingService::InitOpts init_opts) {
+ return std::unique_ptr<ServiceIPCHost>(
+ new ServiceIPCHostImpl(task_runner, init_opts));
}
-ServiceIPCHostImpl::ServiceIPCHostImpl(base::TaskRunner* task_runner)
- : task_runner_(task_runner) {}
+ServiceIPCHostImpl::ServiceIPCHostImpl(base::TaskRunner* task_runner,
+ TracingService::InitOpts init_opts)
+ : task_runner_(task_runner), init_opts_(init_opts) {}
ServiceIPCHostImpl::~ServiceIPCHostImpl() {}
@@ -95,7 +98,8 @@
std::unique_ptr<SharedMemory::Factory> shm_factory(
new PosixSharedMemory::Factory());
#endif
- svc_ = TracingService::CreateInstance(std::move(shm_factory), task_runner_);
+ svc_ = TracingService::CreateInstance(std::move(shm_factory), task_runner_,
+ init_opts_);
if (!producer_ipc_port_ || !consumer_ipc_port_) {
Shutdown();
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.h b/src/tracing/ipc/service/service_ipc_host_impl.h
index dda7e5b..4ccfa65 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.h
+++ b/src/tracing/ipc/service/service_ipc_host_impl.h
@@ -33,7 +33,8 @@
// producer_ipc_service.cc and consumer_ipc_service.cc.
class ServiceIPCHostImpl : public ServiceIPCHost {
public:
- ServiceIPCHostImpl(base::TaskRunner*);
+ explicit ServiceIPCHostImpl(base::TaskRunner*,
+ TracingService::InitOpts init_opts = {});
~ServiceIPCHostImpl() override;
// ServiceIPCHost implementation.
@@ -51,6 +52,7 @@
void Shutdown();
base::TaskRunner* const task_runner_;
+ const TracingService::InitOpts init_opts_;
std::unique_ptr<TracingService> svc_; // The service business logic.
// The IPC host that listens on the Producer socket. It owns the
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 0755e02..7fdc70c 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -3472,7 +3472,8 @@
"foo", perfetto::DynamicString{std::string("Event5")},
::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
PERFETTO_INTERNAL_TRACK_EVENT(
- "foo", "Event6", ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
+ "foo", perfetto::StaticString{"Event6"},
+ ::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END);
auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
ASSERT_EQ(6u, slices.size());
diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h
index 3b6ad2b..2253d93 100644
--- a/src/tracing/test/mock_consumer.h
+++ b/src/tracing/test/mock_consumer.h
@@ -83,7 +83,7 @@
MOCK_METHOD(void, OnAttach, (bool, const TraceConfig&), (override));
MOCK_METHOD(void, OnTraceStats, (bool, const TraceStats&), (override));
MOCK_METHOD(void, OnObservableEvents, (const ObservableEvents&), (override));
- MOCK_METHOD(void, OnSessionCloned, (bool, const std::string&), (override));
+ MOCK_METHOD(void, OnSessionCloned, (const OnSessionClonedArgs&), (override));
// gtest doesn't support move-only types. This wrapper is here jut to pass
// a pointer to the vector (rather than the vector itself) to the mock method.
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index 05fa126..29949a8 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -97,7 +97,7 @@
MOCK_METHOD(void, OnAttach, (bool, const TraceConfig&), (override));
MOCK_METHOD(void, OnTraceStats, (bool, const TraceStats&), (override));
MOCK_METHOD(void, OnObservableEvents, (const ObservableEvents&), (override));
- MOCK_METHOD(void, OnSessionCloned, (bool, const std::string&), (override));
+ MOCK_METHOD(void, OnSessionCloned, (const OnSessionClonedArgs&), (override));
// Workaround, gmock doesn't support yet move-only types, passing a pointer.
void OnTraceData(std::vector<TracePacket> packets, bool has_more) {
diff --git a/test/cmdline_integrationtest.cc b/test/cmdline_integrationtest.cc
index cbc3555..f8c7a43 100644
--- a/test/cmdline_integrationtest.cc
+++ b/test/cmdline_integrationtest.cc
@@ -46,6 +46,7 @@
using ::testing::ContainsRegex;
using ::testing::Each;
using ::testing::ElementsAreArray;
+using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::Property;
using ::testing::SizeIs;
@@ -80,6 +81,13 @@
return trace_config;
}
+class ScopedFileRemove {
+ public:
+ explicit ScopedFileRemove(const std::string& path) : path_(path) {}
+ ~ScopedFileRemove() { remove(path_.c_str()); }
+ std::string path_;
+};
+
class PerfettoCmdlineTest : public ::testing::Test {
public:
void SetUp() override {
@@ -137,8 +145,10 @@
// This is in common to the 3 TEST_F SaveForBugreport* fixtures, which differ
// only in the config, passed here as input.
void RunBugreportTest(protos::gen::TraceConfig trace_config,
- bool check_original_trace = true) {
+ bool check_original_trace = true,
+ bool use_explicit_clone = false) {
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
@@ -149,9 +159,10 @@
},
trace_config.SerializeAsString());
- auto perfetto_br_proc = ExecPerfetto({
- "--save-for-bugreport",
- });
+ Exec perfetto_br_proc =
+ use_explicit_clone
+ ? ExecPerfetto({"--out", GetBugreportTracePath(), "--clone", "-1"})
+ : ExecPerfetto({"--save-for-bugreport"});
// Start the service and connect a simple fake producer.
StartServiceIfRequiredNoNewExecsAfterThis();
@@ -364,6 +375,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
"-o",
@@ -458,6 +470,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
"-o",
@@ -562,6 +575,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
"--dropbox",
@@ -616,6 +630,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
"-o",
@@ -719,6 +734,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
std::string triggers = R"(
activate_triggers: "trigger_name_2"
activate_triggers: "trigger_name"
@@ -782,6 +798,7 @@
// (could deadlock) to fork after we've spawned some threads which might
// printf (and thus hold locks).
const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
auto perfetto_proc = ExecPerfetto(
{
"-o",
@@ -829,11 +846,100 @@
protos::gen::Trace trace;
ASSERT_TRUE(trace.ParseFromString(trace_str));
EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size());
- for (const auto& packet : trace.packet()) {
- if (packet.has_trigger()) {
- EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
- }
+ EXPECT_THAT(trace.packet(),
+ Contains(Property(&protos::gen::TracePacket::trigger,
+ Property(&protos::gen::Trigger::trigger_name,
+ Eq("trigger_name")))));
+}
+
+TEST_F(PerfettoCmdlineTest, TriggerCloneSnapshot) {
+ constexpr size_t kMessageCount = 2;
+ constexpr size_t kMessageSize = 2;
+ protos::gen::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(kMessageCount);
+ ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ auto* trigger_cfg = trace_config.mutable_trigger_config();
+ trigger_cfg->set_trigger_mode(
+ protos::gen::TraceConfig::TriggerConfig::CLONE_SNAPSHOT);
+ trigger_cfg->set_trigger_timeout_ms(600000);
+ auto* trigger = trigger_cfg->add_triggers();
+ trigger->set_name("trigger_name");
+ // |stop_delay_ms| must be long enough that we can write the packets in
+ // before the trace finishes. This has to be long enough for the slowest
+ // emulator. But as short as possible to prevent the test running a long
+ // time.
+ trigger->set_stop_delay_ms(500);
+
+ // We have to construct all the processes we want to fork before we start the
+ // service with |StartServiceIfRequired()|. this is because it is unsafe
+ // (could deadlock) to fork after we've spawned some threads which might
+ // printf (and thus hold locks).
+ const std::string path = RandomTraceFileName();
+ ScopedFileRemove remove_on_test_exit(path);
+ auto perfetto_proc = ExecPerfetto(
+ {
+ "-o",
+ path,
+ "-c",
+ "-",
+ },
+ trace_config.SerializeAsString());
+
+ std::string triggers = R"(
+ activate_triggers: "trigger_name"
+ )";
+ auto trigger_proc = ExecPerfetto(
+ {
+ "-c",
+ "-",
+ "--txt",
+ },
+ triggers);
+
+ // Start the service and connect a simple fake producer.
+ StartServiceIfRequiredNoNewExecsAfterThis();
+ auto* fake_producer = ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+
+ std::thread background_trace([&perfetto_proc]() {
+ std::string stderr_str;
+ EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str;
+ });
+
+ WaitForProducerEnabled();
+ // Wait for the producer to start, and then write out 11 packets, before the
+ // trace actually starts (the trigger is seen).
+ auto on_data_written = task_runner_.CreateCheckpoint("data_written_1");
+ fake_producer->ProduceEventBatch(WrapTask(on_data_written));
+ task_runner_.RunUntilCheckpoint("data_written_1");
+
+ EXPECT_EQ(0, trigger_proc.Run(&stderr_)) << "stderr: " << stderr_;
+
+ // Now we need to wait that the `perfetto_proc` creates the snapshot trace
+ // file in the trace/path.0 file (appending .0). Once that is done we can
+ // kill the perfetto cmd (otherwise it will keep running for the whole
+ // trigger_timeout_ms, unlike the case of STOP_TRACING.
+ std::string snapshot_path = path + ".0";
+ for (int i = 0; i < 100 && !base::FileExists(snapshot_path); i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
+ ASSERT_TRUE(base::FileExists(snapshot_path));
+
+ perfetto_proc.SendSigterm();
+ background_trace.join();
+
+ std::string trace_str;
+ base::ReadFile(snapshot_path, &trace_str);
+ protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size());
+ EXPECT_THAT(trace.packet(),
+ Contains(Property(&protos::gen::TracePacket::trigger,
+ Property(&protos::gen::Trigger::trigger_name,
+ Eq("trigger_name")))));
}
TEST_F(PerfettoCmdlineTest, SaveForBugreport) {
@@ -848,6 +954,21 @@
RunBugreportTest(std::move(trace_config));
}
+TEST_F(PerfettoCmdlineTest, Clone) {
+ TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+ RunBugreportTest(std::move(trace_config), /*check_original_trace=*/true,
+ /*use_explicit_clone=*/true);
+}
+
+// Regression test for b/279753347 .
+TEST_F(PerfettoCmdlineTest, UnavailableBugreportLeavesNoEmptyFiles) {
+ ScopedFileRemove remove_on_test_exit(GetBugreportTracePath());
+ Exec perfetto_br_proc = ExecPerfetto({"--save-for-bugreport"});
+ StartServiceIfRequiredNoNewExecsAfterThis();
+ perfetto_br_proc.Run(&stderr_);
+ ASSERT_FALSE(base::FileExists(GetBugreportTracePath()));
+}
+
// Tests that SaveTraceForBugreport() works also if the trace has triggers
// defined and those triggers have not been hit. This is a regression test for
// b/188008375 .
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 230fe01..b56822b 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -40,6 +40,7 @@
"long_trace.cfg",
"mm_events.cfg",
"scheduling.cfg",
+ "snapshot.cfg",
"summary.cfg",
"sys_stats.cfg",
"thermal.cfg",
diff --git a/test/configs/snapshot.cfg b/test/configs/snapshot.cfg
new file mode 100644
index 0000000..eb2c197
--- /dev/null
+++ b/test/configs/snapshot.cfg
@@ -0,0 +1,49 @@
+unique_session_name: "test_snap"
+
+buffers {
+ size_kb: 32768
+ fill_policy: RING_BUFFER
+}
+
+# Enable various data sources as usual.
+data_sources {
+ config {
+ name: "linux.ftrace"
+ target_buffer: 0
+ ftrace_config {
+ ftrace_events: "cpu_frequency"
+ ftrace_events: "cpu_idle"
+ ftrace_events: "sched_process_exec"
+ ftrace_events: "sched_process_exit"
+ ftrace_events: "sched_process_fork"
+ ftrace_events: "sched_process_free"
+ ftrace_events: "sched_process_hang"
+ ftrace_events: "sched_process_wait"
+ ftrace_events: "sched_switch"
+ ftrace_events: "sched_wakeup_new"
+ ftrace_events: "sched_wakeup"
+ ftrace_events: "sched_waking"
+ ftrace_events: "task_newtask"
+ ftrace_events: "task_rename"
+ ftrace_events: "tracing_mark_write"
+ }
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ }
+}
+
+
+trigger_config {
+ trigger_mode: STOP_TRACING
+ use_clone_snapshot_if_available: true
+ trigger_timeout_ms: 300000
+ triggers {
+ name: "xxx"
+ stop_delay_ms: 0
+ }
+}
diff --git a/test/configs/statsd.cfg b/test/configs/statsd.cfg
index 10d5b0d..a384b57 100644
--- a/test/configs/statsd.cfg
+++ b/test/configs/statsd.cfg
@@ -5,7 +5,7 @@
data_sources {
config {
- name: "android.statsd_binder"
+ name: "android.statsd"
target_buffer: 0
statsd_tracing_config {
push_atom_id: ATOM_FLASHLIGHT_STATE_CHANGED
diff --git a/test/cts/reporter/reporter_test_cts.cc b/test/cts/reporter/reporter_test_cts.cc
index f9294f4..8745dcf 100644
--- a/test/cts/reporter/reporter_test_cts.cc
+++ b/test/cts/reporter/reporter_test_cts.cc
@@ -42,6 +42,14 @@
trace_config.add_buffers()->set_size_kb(1024);
trace_config.set_duration_ms(200);
trace_config.set_allow_user_build_tracing(true);
+ trace_config.set_unique_session_name("TestEndToEndReport");
+
+ // Make the trace as small as possible (see b/282508742).
+ auto* builtin = trace_config.mutable_builtin_data_sources();
+ builtin->set_disable_clock_snapshotting(true);
+ builtin->set_disable_system_info(true);
+ builtin->set_disable_service_events(true);
+ builtin->set_disable_chunk_usage_histograms(true);
auto* ds_config = trace_config.add_data_sources()->mutable_config();
ds_config->set_name("android.perfetto.FakeProducer");
@@ -72,6 +80,7 @@
auto perfetto_proc = Exec("perfetto",
{
"--upload",
+ "--no-guardrails",
"-c",
"-",
},
diff --git a/test/data/chrome_5672_histograms.pftrace.gz.sha256 b/test/data/chrome_5672_histograms.pftrace.gz.sha256
new file mode 100644
index 0000000..5d22333
--- /dev/null
+++ b/test/data/chrome_5672_histograms.pftrace.gz.sha256
@@ -0,0 +1 @@
+a09bd44078ac71bcfbc901b0544750e8344d0d0f6f96e220f700a5a53fa932ee
\ No newline at end of file
diff --git a/test/data/sched_wakeup_trace.atr.sha256 b/test/data/sched_wakeup_trace.atr.sha256
new file mode 100644
index 0000000..496b259
--- /dev/null
+++ b/test/data/sched_wakeup_trace.atr.sha256
@@ -0,0 +1 @@
+ae61181ded60bf214859c2072b90dca49226338901d368e6aea329681bff30db
\ No newline at end of file
diff --git a/test/data/speedometer.perfetto_trace.gz.sha256 b/test/data/speedometer.perfetto_trace.gz.sha256
new file mode 100644
index 0000000..d330773
--- /dev/null
+++ b/test/data/speedometer.perfetto_trace.gz.sha256
@@ -0,0 +1 @@
+8a159b354d74a3ca0d38ce9cd071ef47de322db4261ee266bfafe04d70310529
\ 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 b78630c..71dc5bf 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 @@
-59f5feeab8b216aab64795b4fb620d9386d45769e46cd3b7df12e0b8c85dca9a
\ No newline at end of file
+24a0c7c0bd17908474dda832f51aca06226c44f93043b6b6ff8b698d013818ea
\ 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 39ac1e4..2e2f6fd 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 @@
-f146aec8fe3dff85764c2760a8726e38f085006c1a217aca89b0a847390684cf
\ No newline at end of file
+01576322a83634df3f5d4fee6c7dac5c3835f926b7de752b99e827e5688fec2d
\ 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 cf2b69d..58c3975 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 @@
-a238887636bc86b86e179fc5bc11024cd2b5151797365146b4f94c6de6994280
\ No newline at end of file
+f618d3e05c466f49a4e130b02b1ab597dc8e0d51285c801fa9af3c6e9df505a5
\ 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 29aeeb0..9d7b91c 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 @@
-40026a723cf944adb25668cc6618513271aff14cd7271804b4e208bf6a9817d8
\ No newline at end of file
+86d0bc258ef11ba82a78f4b28a1463fb4b732545b95f2ee8e8a4d6271370a030
\ 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 4af29fe..312b01f 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 @@
-81154f3a88aee01576eba17432269818f1431166cb6071256ee1c35e14851271
\ No newline at end of file
+c8e073f0030da0c6d2dd514eed99a8e0eed79ec75c18e177812de73bf0440cf2
\ 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 2a17dfb..0332663 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 @@
-3ae58540e40e9597d92aa15735e253d14ed9fc87753e543695226fce51481004
\ No newline at end of file
+b841b628cd3908f434d28ad0234656ceaeb4a418659200e2670343d2f885fa68
\ 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 bf8f89b..69e55e1 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 @@
-db689629c3ba9a51e74d48edd3e801bf062dd985ed5210e5a9b4f1bce50f29a7
\ No newline at end of file
+e7dc92a76eec326637bebaa176482197c621f48bc066fc761d0b1d19513c8c21
\ 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 6c30778..67a8dd4 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 @@
-eae8d6c700a16b5062736c54e4fe0ffab569ffd839715138e74cd257fdb014fa
\ No newline at end of file
+2d66039e67f93ea58155d19d3be5aba40d3d6f0053f9d1ada63e47828af28abe
\ 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 5117a65..623260e 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 @@
-5767e81834101bf14dcd1917544f1605b8dbb8aa747a7eefd1c52f4db891575f
\ No newline at end of file
+b51e0a5035eab217bc4339800e8a6c4025bfc7d5240844c152fbc867b28f75ba
\ 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 ff3aafa..679bd39 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 @@
-ab1a1b7c948e008fa5d44a4471dc24977f3ab2d592c1ceeaa0ab4afac5bb2336
\ No newline at end of file
+99d3e463fe3812942825829a388bb2d5b11d73010c3213b11d09884dfc1663b5
\ 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 f4e56d2..21fefdd 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 @@
-d7c89c766d8db408de06f79654e2f81d8c761c08c4451991447807468df0f3d5
\ No newline at end of file
+a151d536d2061b45910d894b1735ddc4eac97c4102754d7f1e1b31ebde368675
\ 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 46f0129..849be2d 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 @@
-e596f8037a578f1e58a33bbe08f5d5621b1d37e55456409e5f8799a6897eedb9
\ No newline at end of file
+4b07a28b92a72568615003beb4eb086adc9be581a27baa2ea593c5e88802647b
\ 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 23be44d..8e93988 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 @@
-b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ No newline at end of file
+56ec21cedc480d6b1b2a894bc70411c89424386a7910d62a09a6a5f1a5921c15
\ 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 d26043d..df63f69 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 @@
-ef5dfa6588634af3b6ac4950d751f7f8b73eeaae7f74c9e45d65ede4134451fe
\ No newline at end of file
+10931936be5a35ed461fce47ede47225ffc750a85a13462a614366f26160ca11
\ 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 0abbaa7..2afc1a3 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 @@
-d62fb2e2f4067552d4f9d4170b4ffa45c74171b4de750292ded957cb6b12b669
\ No newline at end of file
+04e0762424dae233fb13d5cfff51a2d3075ad62220538010d0ab376fc79022a9
\ 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 ad17707..6272735 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 @@
-a16bc9d707e713ef1ce9110a74790bbbeaf27e1fc7f798152c18e9c81bcedbbf
\ No newline at end of file
+c421aafc09b9ce506fd6eaa1dc358855182cd18704715491f83b7d49828c5826
\ 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 23be44d..8e93988 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 @@
-b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ No newline at end of file
+56ec21cedc480d6b1b2a894bc70411c89424386a7910d62a09a6a5f1a5921c15
\ 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 ad17707..6272735 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 @@
-a16bc9d707e713ef1ce9110a74790bbbeaf27e1fc7f798152c18e9c81bcedbbf
\ No newline at end of file
+c421aafc09b9ce506fd6eaa1dc358855182cd18704715491f83b7d49828c5826
\ 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 796c14f..6272735 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 @@
-7c2317088fd3ada6af276ab517e67163b091f7e9bd98e443794397fe58b61ae3
\ No newline at end of file
+c421aafc09b9ce506fd6eaa1dc358855182cd18704715491f83b7d49828c5826
\ 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 4324721..a494f13 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 @@
-9d502e8458f85884c7bf1a6411aa3425c63ccf102e6f537318733704e7a416fb
\ No newline at end of file
+8de36401497e509ab127d202b83a056c40c2f5f461254f1ea79fe7a4861b059e
\ 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 060bb57..cbb5609 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 @@
-da73a7a9d403491d5de58ae013e8b66337c9bf48422ae2ce212ab91bc33a6d5c
\ No newline at end of file
+7b0fb778fef603ab28c5bc8459db420d2c9d6c6a7a3e5b4e6547b3a2fb067249
\ 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 23be44d..8e93988 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 @@
-b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ No newline at end of file
+56ec21cedc480d6b1b2a894bc70411c89424386a7910d62a09a6a5f1a5921c15
\ 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 ad17707..6272735 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 @@
-a16bc9d707e713ef1ce9110a74790bbbeaf27e1fc7f798152c18e9c81bcedbbf
\ No newline at end of file
+c421aafc09b9ce506fd6eaa1dc358855182cd18704715491f83b7d49828c5826
\ 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 ad17707..6272735 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 @@
-a16bc9d707e713ef1ce9110a74790bbbeaf27e1fc7f798152c18e9c81bcedbbf
\ No newline at end of file
+c421aafc09b9ce506fd6eaa1dc358855182cd18704715491f83b7d49828c5826
\ No newline at end of file
diff --git a/test/end_to_end_benchmark.cc b/test/end_to_end_benchmark.cc
index ac7f8fa..ce63780 100644
--- a/test/end_to_end_benchmark.cc
+++ b/test/end_to_end_benchmark.cc
@@ -232,7 +232,7 @@
void ConstantRateProducerArgs(benchmark::internal::Benchmark* b) {
int message_count = IsBenchmarkFunctionalOnly() ? 2 * 1024 : 128 * 1024;
- int min_speed = IsBenchmarkFunctionalOnly() ? 64 : 8;
+ int min_speed = IsBenchmarkFunctionalOnly() ? 128 : 8;
int max_speed = 128;
for (int speed = min_speed; speed <= max_speed; speed *= 2) {
b->Args({message_count, 128, speed});
@@ -242,7 +242,7 @@
void SaturateCpuConsumerArgs(benchmark::internal::Benchmark* b) {
int min_payload = 8;
- int max_payload = IsBenchmarkFunctionalOnly() ? 16 : 64 * 1024;
+ int max_payload = IsBenchmarkFunctionalOnly() ? 8 : 64 * 1024;
for (int bytes = min_payload; bytes <= max_payload; bytes *= 2) {
b->Args({bytes, 0 /* speed */});
}
diff --git a/test/test_helper.cc b/test/test_helper.cc
index cbc3aca..49a9961 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -290,7 +290,7 @@
void TestHelper::OnObservableEvents(const ObservableEvents&) {}
-void TestHelper::OnSessionCloned(bool, const std::string&) {}
+void TestHelper::OnSessionCloned(const OnSessionClonedArgs&) {}
// static
const char* TestHelper::GetDefaultModeConsumerSocketName() {
diff --git a/test/test_helper.h b/test/test_helper.h
index dd18b4c..2cabd98 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -290,7 +290,7 @@
void OnAttach(bool, const TraceConfig&) override;
void OnTraceStats(bool, const TraceStats&) override;
void OnObservableEvents(const ObservableEvents&) override;
- void OnSessionCloned(bool, const std::string&) override;
+ void OnSessionCloned(const OnSessionClonedArgs&) override;
// Starts the tracing service if in kStartDaemons mode.
void StartServiceIfRequired();
diff --git a/test/trace_processor/diff_tests/android/android_battery_stats_event_slices.out b/test/trace_processor/diff_tests/android/android_battery_stats_event_slices.out
new file mode 100644
index 0000000..82cd36c
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_battery_stats_event_slices.out
@@ -0,0 +1,4 @@
+"ts","dur","track_name","str_value","int_value"
+1000,8000,"battery_stats.top","mail",123
+3000,-1,"battery_stats.job","mail_job",456
+1000,3000,"battery_stats.job","video_job",789
diff --git a/test/trace_processor/diff_tests/android/android_battery_stats_state.out b/test/trace_processor/diff_tests/android/android_battery_stats_state.out
new file mode 100644
index 0000000..eb16850
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_battery_stats_state.out
@@ -0,0 +1,4 @@
+"ts","track_name","value","value_name","dur"
+1000,"battery_stats.audio",1,"active",-1
+1000,"battery_stats.data_conn",13,"4G (LTE)",3000
+4000,"battery_stats.data_conn",20,"5G (NR)",-1
diff --git a/test/trace_processor/diff_tests/android/android_binder_metric.out b/test/trace_processor/diff_tests/android/android_binder_metric.out
index 8191521..09dc248 100644
--- a/test/trace_processor/diff_tests/android/android_binder_metric.out
+++ b/test/trace_processor/diff_tests/android/android_binder_metric.out
@@ -234,11 +234,13 @@
client_ts: 25827352153
client_dur: 86322
client_tid: 422
+ client_pid: 415
server_process: "system_server"
server_thread: "binder:641_5"
server_ts: 25827417672
server_dur: 11316
server_tid: 1600
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -271,11 +273,13 @@
client_ts: 25827531554
client_dur: 41057
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25827545050
server_dur: 18590
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -308,11 +312,13 @@
client_ts: 25927698833
client_dur: 43619
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25927713489
server_dur: 18478
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -345,11 +351,13 @@
client_ts: 26027844321
client_dur: 76502
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 26027869620
server_dur: 38328
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -382,11 +390,13 @@
client_ts: 26128022950
client_dur: 93620
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 26128060441
server_dur: 42549
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -419,11 +429,13 @@
client_ts: 26228226807
client_dur: 92197
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 26228257962
server_dur: 47691
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -456,11 +468,13 @@
client_ts: 26328427074
client_dur: 88516
client_tid: 422
+ client_pid: 415
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 26328457296
server_dur: 45253
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -493,11 +507,13 @@
client_ts: 21625430256
client_dur: 106084
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21625451288
server_dur: 25329
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -530,11 +546,13 @@
client_ts: 21651104412
client_dur: 1553854
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21651127883
server_dur: 24255
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -567,11 +585,13 @@
client_ts: 21681078075
client_dur: 6957581
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21681097331
server_dur: 20398
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -604,11 +624,13 @@
client_ts: 21713184564
client_dur: 1238385
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21713209886
server_dur: 8733
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -641,11 +663,13 @@
client_ts: 21745330426
client_dur: 2415345
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21745354167
server_dur: 24535
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -678,11 +702,13 @@
client_ts: 21772841582
client_dur: 60073
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21772864934
server_dur: 21372
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -715,11 +741,13 @@
client_ts: 21797991492
client_dur: 58946
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21798015547
server_dur: 20885
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -752,11 +780,13 @@
client_ts: 21823141587
client_dur: 61370
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21823167028
server_dur: 21656
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -789,11 +819,13 @@
client_ts: 21848290804
client_dur: 60709
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21848316259
server_dur: 20391
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -826,11 +858,13 @@
client_ts: 21873440775
client_dur: 55934
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21873461764
server_dur: 21136
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -863,11 +897,13 @@
client_ts: 21898589558
client_dur: 58875
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21898611789
server_dur: 20494
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -900,11 +936,13 @@
client_ts: 21923738957
client_dur: 64169
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21923761253
server_dur: 25784
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -937,11 +975,13 @@
client_ts: 21948891420
client_dur: 64699
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21948920277
server_dur: 20540
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -974,11 +1014,13 @@
client_ts: 21974296208
client_dur: 104989
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21974341636
server_dur: 25187
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1011,11 +1053,13 @@
client_ts: 21999539011
client_dur: 71202
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21999568501
server_dur: 20357
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1048,11 +1092,13 @@
client_ts: 22024711257
client_dur: 328183
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22024736108
server_dur: 25338
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1085,11 +1131,13 @@
client_ts: 22050132357
client_dur: 59459
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22050156562
server_dur: 20679
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1122,11 +1170,13 @@
client_ts: 22075288214
client_dur: 58461
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22075311405
server_dur: 20809
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1159,11 +1209,13 @@
client_ts: 22100443015
client_dur: 66715
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22100466870
server_dur: 19761
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1196,11 +1248,13 @@
client_ts: 22125600618
client_dur: 65545
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22125623322
server_dur: 20702
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1233,11 +1287,13 @@
client_ts: 22150759363
client_dur: 56369
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22150779993
server_dur: 22255
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1270,11 +1326,13 @@
client_ts: 22175906178
client_dur: 63847
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22175929226
server_dur: 25370
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1307,11 +1365,13 @@
client_ts: 22201561660
client_dur: 209987
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22201735902
server_dur: 20781
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1344,11 +1404,13 @@
client_ts: 22227603566
client_dur: 61556
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22227627247
server_dur: 19354
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1381,11 +1443,13 @@
client_ts: 22252767831
client_dur: 65418
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22252793033
server_dur: 24322
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1418,11 +1482,13 @@
client_ts: 22277925922
client_dur: 297059
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22278187629
server_dur: 20875
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1455,11 +1521,13 @@
client_ts: 22303375651
client_dur: 55580
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22303396802
server_dur: 20656
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1492,11 +1560,13 @@
client_ts: 22328804156
client_dur: 83145
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22328852158
server_dur: 21212
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1529,11 +1599,13 @@
client_ts: 22354003523
client_dur: 56819
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22354025291
server_dur: 20568
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1566,11 +1638,13 @@
client_ts: 22379156345
client_dur: 66779
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22379180900
server_dur: 25632
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1603,11 +1677,13 @@
client_ts: 22404448968
client_dur: 67438
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22404473541
server_dur: 24816
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1640,11 +1716,13 @@
client_ts: 22429616334
client_dur: 74108
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22429639907
server_dur: 32292
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1677,11 +1755,13 @@
client_ts: 22455238568
client_dur: 55788
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22455259634
server_dur: 20555
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1714,11 +1794,13 @@
client_ts: 22480383875
client_dur: 56554
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22480404678
server_dur: 20572
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1751,11 +1833,13 @@
client_ts: 22505531489
client_dur: 59218
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22505553139
server_dur: 22859
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1788,11 +1872,13 @@
client_ts: 22531090402
client_dur: 56749
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22531111630
server_dur: 20210
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1825,11 +1911,13 @@
client_ts: 22556242635
client_dur: 57252
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22556262293
server_dur: 20917
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1862,11 +1950,13 @@
client_ts: 22581426858
client_dur: 266380
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22581641694
server_dur: 31182
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1905,11 +1995,13 @@
client_ts: 22606961662
client_dur: 557886
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22606985795
server_dur: 21756
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1942,11 +2034,13 @@
client_ts: 22632616662
client_dur: 57814
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22632638644
server_dur: 20728
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -1979,11 +2073,13 @@
client_ts: 22657890096
client_dur: 63825
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22657912861
server_dur: 22058
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2016,11 +2112,13 @@
client_ts: 22683093531
client_dur: 80792
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22683116244
server_dur: 21278
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2059,11 +2157,13 @@
client_ts: 22708307977
client_dur: 67445
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22708332522
server_dur: 26370
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2096,11 +2196,13 @@
client_ts: 22733506276
client_dur: 119221
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22733593661
server_dur: 19257
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2133,11 +2235,13 @@
client_ts: 22758727072
client_dur: 446508
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22759138073
server_dur: 20676
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2170,11 +2274,13 @@
client_ts: 22784311059
client_dur: 161051
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22784409798
server_dur: 20874
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2207,11 +2313,13 @@
client_ts: 22809748047
client_dur: 57852
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22809770020
server_dur: 20538
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2244,11 +2352,13 @@
client_ts: 22834902012
client_dur: 93361
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22834959559
server_dur: 21369
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2281,11 +2391,13 @@
client_ts: 22860898758
client_dur: 82717
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22860942436
server_dur: 21910
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2318,11 +2430,13 @@
client_ts: 22886110503
client_dur: 99316
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22886176653
server_dur: 20849
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2355,11 +2469,13 @@
client_ts: 22911318220
client_dur: 66992
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22911345648
server_dur: 22362
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2392,11 +2508,13 @@
client_ts: 22936549304
client_dur: 58969
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22936570719
server_dur: 21297
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2429,11 +2547,13 @@
client_ts: 22961701152
client_dur: 676548
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22962339912
server_dur: 22166
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2466,11 +2586,13 @@
client_ts: 22987512708
client_dur: 79649
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22987551432
server_dur: 24288
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2509,11 +2631,13 @@
client_ts: 23013143578
client_dur: 61635
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23013168634
server_dur: 20531
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2546,11 +2670,13 @@
client_ts: 23038642421
client_dur: 137175
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23038736570
server_dur: 20651
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2583,11 +2709,13 @@
client_ts: 23063886880
client_dur: 55663
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23063908358
server_dur: 16544
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2620,11 +2748,13 @@
client_ts: 23089198686
client_dur: 68926
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23089221371
server_dur: 23561
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2663,11 +2793,13 @@
client_ts: 23114443451
client_dur: 64468
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23114469373
server_dur: 23341
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2700,11 +2832,13 @@
client_ts: 23139601722
client_dur: 78228
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23139640251
server_dur: 21320
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2737,11 +2871,13 @@
client_ts: 23164771069
client_dur: 91260
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23164821900
server_dur: 21423
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2774,11 +2910,13 @@
client_ts: 23189996807
client_dur: 214050
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23190162200
server_dur: 20825
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2817,11 +2955,13 @@
client_ts: 23215317200
client_dur: 57982
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23215338281
server_dur: 21069
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2854,11 +2994,13 @@
client_ts: 23240472994
client_dur: 61104
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23240494382
server_dur: 22737
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2891,11 +3033,13 @@
client_ts: 23265633084
client_dur: 257686
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23265855074
server_dur: 21119
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2928,11 +3072,13 @@
client_ts: 23294001031
client_dur: 68360
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23294024310
server_dur: 25000
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -2965,11 +3111,13 @@
client_ts: 23319166709
client_dur: 255922
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23319381423
server_dur: 24946
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3002,11 +3150,13 @@
client_ts: 23344525034
client_dur: 66275
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23344548135
server_dur: 25737
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3039,11 +3189,13 @@
client_ts: 23369688943
client_dur: 61329
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23369711803
server_dur: 22061
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3076,11 +3228,13 @@
client_ts: 23394850079
client_dur: 540888
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23395351729
server_dur: 22192
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3113,11 +3267,13 @@
client_ts: 23420492464
client_dur: 65743
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23420518127
server_dur: 22480
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3150,11 +3306,13 @@
client_ts: 23451713078
client_dur: 68421
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23451738606
server_dur: 24478
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3187,11 +3345,13 @@
client_ts: 23476881893
client_dur: 64782
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23476905553
server_dur: 24602
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3224,11 +3384,13 @@
client_ts: 23502042821
client_dur: 820095
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23502825079
server_dur: 23414
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3261,11 +3423,13 @@
client_ts: 23527974198
client_dur: 845116
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23528782089
server_dur: 23126
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3298,11 +3462,13 @@
client_ts: 23553917567
client_dur: 59738
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23553940355
server_dur: 21362
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3335,11 +3501,13 @@
client_ts: 23579071838
client_dur: 411519
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23579443790
server_dur: 25365
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3372,11 +3540,13 @@
client_ts: 23604582593
client_dur: 210023
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23604751577
server_dur: 24165
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3409,11 +3579,13 @@
client_ts: 23629892322
client_dur: 194538
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23630049856
server_dur: 21631
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3446,11 +3618,13 @@
client_ts: 23655179880
client_dur: 142025
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23655273690
server_dur: 30591
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3483,11 +3657,13 @@
client_ts: 23680421874
client_dur: 72852
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23680450743
server_dur: 26736
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3520,11 +3696,13 @@
client_ts: 23705597440
client_dur: 470856
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23706033599
server_dur: 21348
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3557,11 +3735,13 @@
client_ts: 23731172177
client_dur: 184006
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23731318438
server_dur: 21088
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3594,11 +3774,13 @@
client_ts: 23757576836
client_dur: 69175
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23757598859
server_dur: 22748
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3631,11 +3813,13 @@
client_ts: 23782737996
client_dur: 59451
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23782758417
server_dur: 25123
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3668,11 +3852,13 @@
client_ts: 23807891211
client_dur: 62509
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23807914516
server_dur: 23582
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3705,11 +3891,13 @@
client_ts: 23833055418
client_dur: 163286
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23833182220
server_dur: 22315
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3742,11 +3930,13 @@
client_ts: 23858313038
client_dur: 57714
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23858335729
server_dur: 20454
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3779,11 +3969,13 @@
client_ts: 23883462809
client_dur: 59212
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23883487934
server_dur: 19666
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3816,11 +4008,13 @@
client_ts: 23908617410
client_dur: 67454
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23908643220
server_dur: 25095
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3853,11 +4047,13 @@
client_ts: 23933826058
client_dur: 54993
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23933846583
server_dur: 19834
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3890,11 +4086,13 @@
client_ts: 23958974344
client_dur: 56757
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23958997349
server_dur: 19668
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3927,11 +4125,13 @@
client_ts: 23984212731
client_dur: 65425
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23984244337
server_dur: 18760
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -3964,11 +4164,13 @@
client_ts: 24009326493
client_dur: 55937
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24009348026
server_dur: 20878
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4001,11 +4203,13 @@
client_ts: 24034474905
client_dur: 323973
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24034762131
server_dur: 21724
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4038,11 +4242,13 @@
client_ts: 24059888481
client_dur: 269355
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24060121507
server_dur: 21099
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4075,11 +4281,13 @@
client_ts: 24085507070
client_dur: 65864
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24085532155
server_dur: 24607
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4112,11 +4320,13 @@
client_ts: 24110668836
client_dur: 62135
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24110692176
server_dur: 23546
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4149,11 +4359,13 @@
client_ts: 24135822325
client_dur: 55790
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24135843301
server_dur: 20745
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4186,11 +4398,13 @@
client_ts: 24160973601
client_dur: 64296
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24160998105
server_dur: 24373
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4223,11 +4437,13 @@
client_ts: 24186129350
client_dur: 61214
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24186153807
server_dur: 21100
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4260,11 +4476,13 @@
client_ts: 24211335205
client_dur: 99239
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24211379278
server_dur: 25032
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4297,11 +4515,13 @@
client_ts: 24236559912
client_dur: 85757
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24236597491
server_dur: 27319
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4334,11 +4554,13 @@
client_ts: 24261743662
client_dur: 72120
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24261771646
server_dur: 25209
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4371,11 +4593,13 @@
client_ts: 24286913419
client_dur: 330217
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24287198196
server_dur: 22848
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4408,11 +4632,13 @@
client_ts: 24312358034
client_dur: 69020
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24312388724
server_dur: 21476
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4451,11 +4677,13 @@
client_ts: 24337574519
client_dur: 104255
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24337613079
server_dur: 33967
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4488,11 +4716,13 @@
client_ts: 24362804629
client_dur: 58822
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24362826557
server_dur: 20783
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4525,11 +4755,13 @@
client_ts: 24387968494
client_dur: 63171
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24387991414
server_dur: 24460
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4562,11 +4794,13 @@
client_ts: 24414399633
client_dur: 3357310
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24414422286
server_dur: 20459
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4599,11 +4833,13 @@
client_ts: 24443111092
client_dur: 63218
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24443132289
server_dur: 23717
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4636,11 +4872,13 @@
client_ts: 24468268772
client_dur: 63940
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24468295057
server_dur: 21634
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4673,11 +4911,13 @@
client_ts: 24493507908
client_dur: 75072
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24493536500
server_dur: 22590
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4710,11 +4950,13 @@
client_ts: 24518715570
client_dur: 83147
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24518746589
server_dur: 20133
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4747,11 +4989,13 @@
client_ts: 24544028919
client_dur: 105487
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24544069334
server_dur: 24026
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4784,11 +5028,13 @@
client_ts: 24569328112
client_dur: 106610
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24569369501
server_dur: 41826
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4821,11 +5067,13 @@
client_ts: 24594543032
client_dur: 45871
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24594558488
server_dur: 20382
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4852,11 +5100,13 @@
client_ts: 24619690623
client_dur: 58336
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24619710131
server_dur: 25779
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4883,11 +5133,13 @@
client_ts: 24644930551
client_dur: 105892
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24644975052
server_dur: 34388
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4920,11 +5172,13 @@
client_ts: 24670207638
client_dur: 54225
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24670224041
server_dur: 26208
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4951,11 +5205,13 @@
client_ts: 24695363832
client_dur: 47347
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24695380382
server_dur: 19885
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -4982,11 +5238,13 @@
client_ts: 24720504163
client_dur: 41635
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24720517121
server_dur: 18951
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5013,11 +5271,13 @@
client_ts: 24745651155
client_dur: 50201
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24745667169
server_dur: 21953
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5044,11 +5304,13 @@
client_ts: 24770795510
client_dur: 45248
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24770810644
server_dur: 19730
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5075,11 +5337,13 @@
client_ts: 24795949562
client_dur: 44458
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24795964339
server_dur: 19474
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5106,11 +5370,13 @@
client_ts: 24821098319
client_dur: 45736
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24821113887
server_dur: 19916
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5137,11 +5403,13 @@
client_ts: 24846533416
client_dur: 52565
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24846550147
server_dur: 20978
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5168,11 +5436,13 @@
client_ts: 24871959612
client_dur: 33762
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24871969021
server_dur: 15109
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5199,11 +5469,13 @@
client_ts: 24897089502
client_dur: 36235
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24897101194
server_dur: 15306
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5230,11 +5502,13 @@
client_ts: 24922327702
client_dur: 47487
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24922343062
server_dur: 20895
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5261,11 +5535,13 @@
client_ts: 24947478390
client_dur: 50011
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24947495490
server_dur: 21244
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5292,11 +5568,13 @@
client_ts: 24972625739
client_dur: 46864
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24972642054
server_dur: 20460
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5323,11 +5601,13 @@
client_ts: 24997766928
client_dur: 105830
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24997800832
server_dur: 45031
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5360,11 +5640,13 @@
client_ts: 25023405381
client_dur: 86423
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25023438732
server_dur: 29757
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5397,11 +5679,13 @@
client_ts: 25048609139
client_dur: 56222
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25048631235
server_dur: 16655
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5434,11 +5718,13 @@
client_ts: 25073875780
client_dur: 84120
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25073898205
server_dur: 15854
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5471,11 +5757,13 @@
client_ts: 25099064916
client_dur: 204188
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25099198462
server_dur: 49025
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5508,11 +5796,13 @@
client_ts: 25124642124
client_dur: 68889
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25124666670
server_dur: 29243
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5539,11 +5829,13 @@
client_ts: 25149891522
client_dur: 60500
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25149912505
server_dur: 27000
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5570,11 +5862,13 @@
client_ts: 25175121734
client_dur: 65293
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25175141892
server_dur: 23570
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5607,11 +5901,13 @@
client_ts: 25200283959
client_dur: 44045
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25200298152
server_dur: 19339
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5638,11 +5934,13 @@
client_ts: 25225419452
client_dur: 43144
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25225434070
server_dur: 18615
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5669,11 +5967,13 @@
client_ts: 25250561510
client_dur: 82814
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25250589325
server_dur: 36944
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5706,11 +6006,13 @@
client_ts: 25275837002
client_dur: 111602
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25275893327
server_dur: 33040
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5743,11 +6045,13 @@
client_ts: 25301779154
client_dur: 81058
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25301805031
server_dur: 24119
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5780,11 +6084,13 @@
client_ts: 25327028096
client_dur: 144639
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25327077936
server_dur: 53753
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5817,11 +6123,13 @@
client_ts: 25352301178
client_dur: 837973
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25353047151
server_dur: 48734
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5854,11 +6162,13 @@
client_ts: 25378250275
client_dur: 77754
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25378276198
server_dur: 28616
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5891,11 +6201,13 @@
client_ts: 25403423456
client_dur: 65101
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25403445450
server_dur: 23191
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -5929,11 +6241,13 @@
client_ts: 25403532822
client_dur: 243238
client_tid: 492
+ client_pid: 492
server_process: "system_server"
server_thread: "binder:641_4"
server_ts: 25403547799
server_dur: 210707
server_tid: 1596
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -5972,11 +6286,13 @@
client_ts: 25403800150
client_dur: 77678
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25403821750
server_dur: 43829
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6009,11 +6325,13 @@
client_ts: 25429003446
client_dur: 55342
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25429021995
server_dur: 20582
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6046,11 +6364,13 @@
client_ts: 25454157095
client_dur: 68221
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25454182918
server_dur: 20352
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6083,11 +6403,13 @@
client_ts: 25479325945
client_dur: 157863
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25479422370
server_dur: 35765
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6120,11 +6442,13 @@
client_ts: 25504631134
client_dur: 1082750
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25505253998
server_dur: 109396
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6158,11 +6482,13 @@
client_ts: 25505818197
client_dur: 3125407
client_tid: 492
+ client_pid: 492
server_process: "system_server"
server_thread: "binder:641_4"
server_ts: 25505891588
server_dur: 3000749
server_tid: 1596
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -6220,11 +6546,13 @@
client_ts: 25508998675
client_dur: 379026
client_tid: 492
+ client_pid: 492
server_process: "system_server"
server_thread: "binder:641_4"
server_ts: 25509052778
server_dur: 272193
server_tid: 1596
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6257,11 +6585,13 @@
client_ts: 25512878756
client_dur: 151351
client_tid: 492
+ client_pid: 492
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25512939518
server_dur: 62943
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6294,11 +6624,13 @@
client_ts: 21612276580
client_dur: 39977
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21612292301
server_dur: 14064
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6331,11 +6663,13 @@
client_ts: 21637405403
client_dur: 60035
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21637427285
server_dur: 24550
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6368,11 +6702,13 @@
client_ts: 21662560974
client_dur: 372941
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21662897046
server_dur: 22907
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6405,11 +6741,13 @@
client_ts: 21688024281
client_dur: 59297
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21688047484
server_dur: 21173
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6442,11 +6780,13 @@
client_ts: 21713127573
client_dur: 63596
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21713152373
server_dur: 21155
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6479,11 +6819,13 @@
client_ts: 21738283156
client_dur: 60407
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21738305184
server_dur: 24886
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6516,11 +6858,13 @@
client_ts: 21763445363
client_dur: 70801
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21763467940
server_dur: 30748
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6553,11 +6897,13 @@
client_ts: 21788612931
client_dur: 57076
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21788635008
server_dur: 21359
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6590,11 +6936,13 @@
client_ts: 21813762065
client_dur: 61236
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21813783295
server_dur: 25042
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6627,11 +6975,13 @@
client_ts: 21838919344
client_dur: 59886
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21838943470
server_dur: 20686
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6664,11 +7014,13 @@
client_ts: 21864144722
client_dur: 71604
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21864171886
server_dur: 25966
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6701,11 +7053,13 @@
client_ts: 21889317261
client_dur: 517889
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21889524828
server_dur: 21638
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6738,11 +7092,13 @@
client_ts: 21914927560
client_dur: 59832
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21914949348
server_dur: 23104
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6775,11 +7131,13 @@
client_ts: 21940080190
client_dur: 63873
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21940103851
server_dur: 24784
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6812,11 +7170,13 @@
client_ts: 21965236304
client_dur: 87480
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21965262841
server_dur: 47604
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -6855,11 +7215,13 @@
client_ts: 21990454958
client_dur: 89222
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21990491103
server_dur: 25934
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6892,11 +7254,13 @@
client_ts: 22015656850
client_dur: 62246
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22015682089
server_dur: 21161
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6929,11 +7293,13 @@
client_ts: 22040814163
client_dur: 64221
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22040838835
server_dur: 24377
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -6966,11 +7332,13 @@
client_ts: 22066737714
client_dur: 128820
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22066831151
server_dur: 19563
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7003,11 +7371,13 @@
client_ts: 22091960087
client_dur: 58325
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22091982875
server_dur: 21344
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7040,11 +7410,13 @@
client_ts: 22117113923
client_dur: 63410
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22117137718
server_dur: 23868
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7077,11 +7449,13 @@
client_ts: 22142267357
client_dur: 59986
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22142291376
server_dur: 21134
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7114,11 +7488,13 @@
client_ts: 22167423522
client_dur: 59967
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22167445520
server_dur: 22511
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7151,11 +7527,13 @@
client_ts: 22192579344
client_dur: 60820
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22192603786
server_dur: 21006
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7188,11 +7566,13 @@
client_ts: 22217730341
client_dur: 55283
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22217750903
server_dur: 20680
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7225,11 +7605,13 @@
client_ts: 22242880300
client_dur: 71100
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22242905309
server_dur: 26682
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7262,11 +7644,13 @@
client_ts: 22268043393
client_dur: 409558
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22268414904
server_dur: 21405
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7299,11 +7683,13 @@
client_ts: 22293548146
client_dur: 63871
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22293572042
server_dur: 22980
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7336,11 +7722,13 @@
client_ts: 22318716098
client_dur: 339826
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22318736854
server_dur: 23010
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7373,11 +7761,13 @@
client_ts: 22344146679
client_dur: 110188
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22344218394
server_dur: 24011
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7410,11 +7800,13 @@
client_ts: 22369334226
client_dur: 94388
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22369355609
server_dur: 48856
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7447,11 +7839,13 @@
client_ts: 22394536230
client_dur: 62982
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22394561551
server_dur: 22059
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7484,11 +7878,13 @@
client_ts: 22419697576
client_dur: 61323
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22419722756
server_dur: 21994
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7521,11 +7917,13 @@
client_ts: 22445031509
client_dur: 50515
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22445050820
server_dur: 17128
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7558,11 +7956,13 @@
client_ts: 22470174653
client_dur: 55822
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22470196239
server_dur: 21286
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7595,11 +7995,13 @@
client_ts: 22495325972
client_dur: 68180
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22495350975
server_dur: 26711
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7632,11 +8034,13 @@
client_ts: 22521360985
client_dur: 234231
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22521382730
server_dur: 21186
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7669,11 +8073,13 @@
client_ts: 22546697891
client_dur: 98444
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22546761058
server_dur: 19881
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7706,11 +8112,13 @@
client_ts: 22571892907
client_dur: 66101
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22571916881
server_dur: 25795
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7743,11 +8151,13 @@
client_ts: 22597062070
client_dur: 259462
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22597272531
server_dur: 34173
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7780,11 +8190,13 @@
client_ts: 22622418577
client_dur: 59816
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22622443274
server_dur: 21430
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7817,11 +8229,13 @@
client_ts: 22650180912
client_dur: 473997
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22650255041
server_dur: 19254
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7854,11 +8268,13 @@
client_ts: 22675764005
client_dur: 70615
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22675789713
server_dur: 25069
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7891,11 +8307,13 @@
client_ts: 22701432879
client_dur: 450491
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22701762194
server_dur: 23978
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7928,11 +8346,13 @@
client_ts: 22726978606
client_dur: 401005
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22727339544
server_dur: 23475
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -7965,11 +8385,13 @@
client_ts: 22752478293
client_dur: 586574
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22753025360
server_dur: 21422
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8002,11 +8424,13 @@
client_ts: 22778161650
client_dur: 349397
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22778472830
server_dur: 22980
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8039,11 +8463,13 @@
client_ts: 22803612048
client_dur: 67634
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22803636072
server_dur: 23917
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8076,11 +8502,13 @@
client_ts: 22828777874
client_dur: 318230
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22828822556
server_dur: 26877
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8113,11 +8541,13 @@
client_ts: 22854196228
client_dur: 561632
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22854720488
server_dur: 21460
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8150,11 +8580,13 @@
client_ts: 22879856680
client_dur: 63364
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22879882339
server_dur: 22238
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8187,11 +8619,13 @@
client_ts: 22905018627
client_dur: 66122
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22905041577
server_dur: 26326
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8224,11 +8658,13 @@
client_ts: 22930183511
client_dur: 67161
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22930213119
server_dur: 23605
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8261,11 +8697,13 @@
client_ts: 22955349414
client_dur: 156341
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22955463649
server_dur: 24599
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8298,11 +8736,13 @@
client_ts: 22980607439
client_dur: 65794
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22980629549
server_dur: 26192
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8335,11 +8775,13 @@
client_ts: 23005774838
client_dur: 62936
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23005798704
server_dur: 23801
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8372,11 +8814,13 @@
client_ts: 23030941978
client_dur: 89285
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23030973182
server_dur: 37855
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8409,11 +8853,13 @@
client_ts: 23056174448
client_dur: 200618
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23056329883
server_dur: 23980
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8446,11 +8892,13 @@
client_ts: 23081467052
client_dur: 59125
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23081486828
server_dur: 24680
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8483,11 +8931,13 @@
client_ts: 23106620846
client_dur: 61616
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23106642188
server_dur: 24012
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8520,11 +8970,13 @@
client_ts: 23131777328
client_dur: 123447
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23131861232
server_dur: 22637
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8557,11 +9009,13 @@
client_ts: 23157002672
client_dur: 79156
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23157030248
server_dur: 27226
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8594,11 +9048,13 @@
client_ts: 23182173903
client_dur: 376646
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23182500065
server_dur: 21132
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8631,11 +9087,13 @@
client_ts: 23207650439
client_dur: 67278
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23207680535
server_dur: 23155
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8668,11 +9126,13 @@
client_ts: 23233851428
client_dur: 892848
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23233871293
server_dur: 23312
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8705,11 +9165,13 @@
client_ts: 23259841421
client_dur: 63174
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23259863994
server_dur: 23889
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8742,11 +9204,13 @@
client_ts: 23284999517
client_dur: 56437
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23285019554
server_dur: 21307
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8779,11 +9243,13 @@
client_ts: 23310151865
client_dur: 642229
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23310751409
server_dur: 26103
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8816,11 +9282,13 @@
client_ts: 23335905400
client_dur: 357982
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23336222503
server_dur: 25821
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8853,11 +9321,13 @@
client_ts: 23361328882
client_dur: 63784
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23361351852
server_dur: 24700
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8890,11 +9360,13 @@
client_ts: 23386869959
client_dur: 66376
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23386894766
server_dur: 25613
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8927,11 +9399,13 @@
client_ts: 23412468379
client_dur: 144242
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23412492591
server_dur: 28309
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -8964,11 +9438,13 @@
client_ts: 23437712307
client_dur: 61646
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23437733692
server_dur: 21877
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9001,11 +9477,13 @@
client_ts: 23462870779
client_dur: 64694
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23462898239
server_dur: 21829
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9038,11 +9516,13 @@
client_ts: 23488042638
client_dur: 69970
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23488068942
server_dur: 26303
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9075,11 +9555,13 @@
client_ts: 23513211192
client_dur: 57998
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23513232838
server_dur: 22355
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9112,11 +9594,13 @@
client_ts: 23538364057
client_dur: 90805
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23538417034
server_dur: 21543
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9149,11 +9633,13 @@
client_ts: 23563555747
client_dur: 62301
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23563577896
server_dur: 23477
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9186,11 +9672,13 @@
client_ts: 23588716088
client_dur: 122069
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23588796199
server_dur: 21803
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9223,11 +9711,13 @@
client_ts: 23613942195
client_dur: 406560
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23614311041
server_dur: 21665
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9260,11 +9750,13 @@
client_ts: 23639449764
client_dur: 297834
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23639707345
server_dur: 24585
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9297,11 +9789,13 @@
client_ts: 23664840926
client_dur: 64026
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23664863549
server_dur: 25828
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9334,11 +9828,13 @@
client_ts: 23689999571
client_dur: 60667
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23690020989
server_dur: 24039
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9371,11 +9867,13 @@
client_ts: 23715668567
client_dur: 55651
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23715687865
server_dur: 20534
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9408,11 +9906,13 @@
client_ts: 23740820398
client_dur: 69779
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23740846289
server_dur: 26005
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9445,11 +9945,13 @@
client_ts: 23765983216
client_dur: 63111
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23766008333
server_dur: 22489
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9482,11 +9984,13 @@
client_ts: 23791142714
client_dur: 62184
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23791165515
server_dur: 24176
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9519,11 +10023,13 @@
client_ts: 23816748979
client_dur: 97527
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23816810994
server_dur: 19216
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9556,11 +10062,13 @@
client_ts: 23841937008
client_dur: 265351
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23842045870
server_dur: 21030
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9593,11 +10101,13 @@
client_ts: 23867298197
client_dur: 65269
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23867323342
server_dur: 24762
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9630,11 +10140,13 @@
client_ts: 23892458661
client_dur: 61895
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23892481485
server_dur: 23511
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9667,11 +10179,13 @@
client_ts: 23917612205
client_dur: 60756
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23917632161
server_dur: 27790
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9704,11 +10218,13 @@
client_ts: 23942767445
client_dur: 56639
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23942789248
server_dur: 20150
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9741,11 +10257,13 @@
client_ts: 23967917345
client_dur: 61714
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23967941212
server_dur: 23677
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9778,11 +10296,13 @@
client_ts: 23993073217
client_dur: 61426
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23993098746
server_dur: 21011
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9815,11 +10335,13 @@
client_ts: 24018227157
client_dur: 154788
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24018346321
server_dur: 21354
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9852,11 +10374,13 @@
client_ts: 24043472861
client_dur: 56072
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24043492721
server_dur: 20952
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9889,11 +10413,13 @@
client_ts: 24068623147
client_dur: 61422
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24068647003
server_dur: 23307
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9926,11 +10452,13 @@
client_ts: 24094174551
client_dur: 60440
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24094198265
server_dur: 20963
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -9963,11 +10491,13 @@
client_ts: 24119330336
client_dur: 62796
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24119353765
server_dur: 24014
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10000,11 +10530,13 @@
client_ts: 24144486541
client_dur: 57521
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24144507336
server_dur: 20651
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10037,11 +10569,13 @@
client_ts: 24169644272
client_dur: 59318
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24169667507
server_dur: 21848
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10074,11 +10608,13 @@
client_ts: 24194796993
client_dur: 61133
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24194818919
server_dur: 24981
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10111,11 +10647,13 @@
client_ts: 24219976971
client_dur: 80874
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24220009018
server_dur: 24029
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10148,11 +10686,13 @@
client_ts: 24245182141
client_dur: 75715
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24245212188
server_dur: 24869
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10185,11 +10725,13 @@
client_ts: 24270354623
client_dur: 72978
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24270381508
server_dur: 24460
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10222,11 +10764,13 @@
client_ts: 24296497808
client_dur: 74119
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24296524147
server_dur: 25619
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10259,11 +10803,13 @@
client_ts: 24321677437
client_dur: 1068351
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24322701227
server_dur: 22458
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10296,11 +10842,13 @@
client_ts: 24349464998
client_dur: 600120
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24349498235
server_dur: 21721
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10333,11 +10881,13 @@
client_ts: 24375203138
client_dur: 200814
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24375371665
server_dur: 20774
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10370,11 +10920,13 @@
client_ts: 24400499298
client_dur: 1655381
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24400525814
server_dur: 20740
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10407,11 +10959,13 @@
client_ts: 24427255856
client_dur: 74073
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24427281368
server_dur: 33400
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10444,11 +10998,13 @@
client_ts: 24452425092
client_dur: 66019
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24452453488
server_dur: 22713
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10481,11 +11037,13 @@
client_ts: 24477584081
client_dur: 77570
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24477603311
server_dur: 20359
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10518,11 +11076,13 @@
client_ts: 24502766882
client_dur: 85030
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24502792394
server_dur: 34540
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10555,11 +11115,13 @@
client_ts: 24528063792
client_dur: 88744
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24528100762
server_dur: 19035
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10592,11 +11154,13 @@
client_ts: 24553269933
client_dur: 102641
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24553320732
server_dur: 19753
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10629,11 +11193,13 @@
client_ts: 24578496582
client_dur: 75733
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24578522139
server_dur: 31879
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10666,11 +11232,13 @@
client_ts: 24603666160
client_dur: 32902
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24603676548
server_dur: 13734
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10703,11 +11271,13 @@
client_ts: 24628827055
client_dur: 77433
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24628855199
server_dur: 31376
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10740,11 +11310,13 @@
client_ts: 24654016554
client_dur: 82862
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24654047419
server_dur: 27392
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10777,11 +11349,13 @@
client_ts: 24679300128
client_dur: 51067
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24679317330
server_dur: 23251
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10814,11 +11388,13 @@
client_ts: 24704445330
client_dur: 53313
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24704463794
server_dur: 20543
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10851,11 +11427,13 @@
client_ts: 24729604221
client_dur: 73960
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24729643378
server_dur: 19819
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10888,11 +11466,13 @@
client_ts: 24754787089
client_dur: 79191
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24754812563
server_dur: 21216
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10925,11 +11505,13 @@
client_ts: 24779973546
client_dur: 61655
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24779997866
server_dur: 21840
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10962,11 +11544,13 @@
client_ts: 24805151631
client_dur: 65179
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24805176625
server_dur: 24502
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -10999,11 +11583,13 @@
client_ts: 24830317956
client_dur: 66289
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24830344368
server_dur: 21990
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11036,11 +11622,13 @@
client_ts: 24855481377
client_dur: 65228
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24855502595
server_dur: 27360
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11073,11 +11661,13 @@
client_ts: 24880648226
client_dur: 55511
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24880667129
server_dur: 21590
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11110,11 +11700,13 @@
client_ts: 24909335836
client_dur: 55761
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24909354477
server_dur: 24104
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11147,11 +11739,13 @@
client_ts: 24934490094
client_dur: 58244
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24934509872
server_dur: 22084
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11184,11 +11778,13 @@
client_ts: 24959652634
client_dur: 81523
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24959684793
server_dur: 28526
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11221,11 +11817,13 @@
client_ts: 24984851591
client_dur: 68470
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24984877747
server_dur: 26966
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11258,11 +11856,13 @@
client_ts: 25010040890
client_dur: 90947
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25010071568
server_dur: 37280
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11295,11 +11895,13 @@
client_ts: 25035275877
client_dur: 59761
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25035296221
server_dur: 25210
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11332,11 +11934,13 @@
client_ts: 25060426543
client_dur: 81204
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25060443769
server_dur: 22941
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11369,11 +11973,13 @@
client_ts: 25085672790
client_dur: 95011
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25085716849
server_dur: 37413
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11406,11 +12012,13 @@
client_ts: 25110861456
client_dur: 53363
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25110879022
server_dur: 22707
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11443,11 +12051,13 @@
client_ts: 25136012642
client_dur: 51978
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25136030960
server_dur: 22166
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11480,11 +12090,13 @@
client_ts: 25161156360
client_dur: 68046
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25161174689
server_dur: 37221
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11517,11 +12129,13 @@
client_ts: 25186325904
client_dur: 37128
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25186338341
server_dur: 14526
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11554,11 +12168,13 @@
client_ts: 25211748648
client_dur: 44699
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25211763861
server_dur: 18676
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11591,11 +12207,13 @@
client_ts: 25236887649
client_dur: 51265
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25236905107
server_dur: 21519
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11628,11 +12246,13 @@
client_ts: 25262053873
client_dur: 82396
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25262085792
server_dur: 26917
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11665,11 +12285,13 @@
client_ts: 25287387704
client_dur: 103899
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25287423221
server_dur: 43908
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11702,11 +12324,13 @@
client_ts: 25312712971
client_dur: 248879
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25312865501
server_dur: 53943
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11739,11 +12363,13 @@
client_ts: 25338145653
client_dur: 118734
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25338183347
server_dur: 50843
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11776,11 +12402,13 @@
client_ts: 25363427959
client_dur: 127185
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25363473345
server_dur: 52393
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11813,11 +12441,13 @@
client_ts: 25388666272
client_dur: 311561
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25388926887
server_dur: 27556
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -11850,11 +12480,13 @@
client_ts: 25389019468
client_dur: 2257654
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_2"
server_ts: 25389272037
server_dur: 1993270
server_tid: 656
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -11912,11 +12544,13 @@
client_ts: 25391362853
client_dur: 2138663
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_1"
server_ts: 25391432268
server_dur: 2057673
server_tid: 655
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -11973,11 +12607,13 @@
client_ts: 25393529331
client_dur: 72907
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25393544112
server_dur: 48279
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12010,11 +12646,13 @@
client_ts: 25418715954
client_dur: 46115
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25418731360
server_dur: 20385
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12047,11 +12685,13 @@
client_ts: 25443850387
client_dur: 44974
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25443866014
server_dur: 19042
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12084,11 +12724,13 @@
client_ts: 25469057379
client_dur: 66877
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25469081511
server_dur: 26836
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12121,11 +12763,13 @@
client_ts: 25494306485
client_dur: 127198
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25494352512
server_dur: 49015
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12158,11 +12802,13 @@
client_ts: 25519756306
client_dur: 101379
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25519800487
server_dur: 31778
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12196,11 +12842,13 @@
client_ts: 25519893501
client_dur: 154940
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_4"
server_ts: 25519915012
server_dur: 115492
server_tid: 1596
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12234,11 +12882,13 @@
client_ts: 25520078379
client_dur: 176159
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_1"
server_ts: 25520102430
server_dur: 134309
server_tid: 655
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12272,11 +12922,13 @@
client_ts: 25520281012
client_dur: 123400
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_2"
server_ts: 25520299524
server_dur: 88243
server_tid: 656
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12309,11 +12961,13 @@
client_ts: 25520423828
client_dur: 343243
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_3"
server_ts: 25520612948
server_dur: 123445
server_tid: 1595
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12346,11 +13000,13 @@
client_ts: 25520890215
client_dur: 293220
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25521075472
server_dur: 82900
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12384,11 +13040,13 @@
client_ts: 25521216286
client_dur: 489554
client_tid: 537
+ client_pid: 537
server_process: "system_server"
server_thread: "binder:641_3"
server_ts: 25521243526
server_dur: 435659
server_tid: 1595
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -12427,11 +13085,13 @@
client_ts: 25523480228
client_dur: 2277042
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25523653053
server_dur: 2085804
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -12476,11 +13136,13 @@
client_ts: 25525828575
client_dur: 674113
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25526007038
server_dur: 466892
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -12525,11 +13187,13 @@
client_ts: 25529470300
client_dur: 103937
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25529493228
server_dur: 62956
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12562,11 +13226,13 @@
client_ts: 25529642910
client_dur: 106592
client_tid: 537
+ client_pid: 537
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25529669348
server_dur: 64544
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12599,11 +13265,13 @@
client_ts: 21610888219
client_dur: 1404460
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21610912981
server_dur: 24345
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12636,11 +13304,13 @@
client_ts: 21713165109
client_dur: 1236372
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21713185409
server_dur: 12103
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12673,11 +13343,13 @@
client_ts: 21817588572
client_dur: 329198
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21817646283
server_dur: 29874
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12710,11 +13382,13 @@
client_ts: 21918044833
client_dur: 54295
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 21918066018
server_dur: 22515
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12747,11 +13421,13 @@
client_ts: 22018230040
client_dur: 4199277
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22018253018
server_dur: 20817
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12784,11 +13460,13 @@
client_ts: 22128615818
client_dur: 66352
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22128641106
server_dur: 21332
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12821,11 +13499,13 @@
client_ts: 22231107021
client_dur: 6202865
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22231126269
server_dur: 21216
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12858,11 +13538,13 @@
client_ts: 22338230784
client_dur: 5927748
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22338257085
server_dur: 22271
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12895,11 +13577,13 @@
client_ts: 22444315008
client_dur: 360477
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22444338001
server_dur: 22884
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12932,11 +13616,13 @@
client_ts: 22545342573
client_dur: 1367091
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22545364958
server_dur: 22936
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -12969,11 +13655,13 @@
client_ts: 22646870376
client_dur: 61414
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22646893001
server_dur: 24570
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13006,11 +13694,13 @@
client_ts: 22747766863
client_dur: 1605566
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22749199929
server_dur: 25123
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13043,11 +13733,13 @@
client_ts: 22849527732
client_dur: 3775048
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22849787260
server_dur: 22559
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13080,11 +13772,13 @@
client_ts: 22955387074
client_dur: 5918097
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 22955504595
server_dur: 12187
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13117,11 +13811,13 @@
client_ts: 23063243768
client_dur: 1153069
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23063268117
server_dur: 21864
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13154,11 +13850,13 @@
client_ts: 23171834613
client_dur: 468230
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23171888187
server_dur: 23659
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13191,11 +13889,13 @@
client_ts: 23274482309
client_dur: 56450
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23274502428
server_dur: 22204
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13228,11 +13928,13 @@
client_ts: 23375517889
client_dur: 2282317
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23377319838
server_dur: 23748
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13265,11 +13967,13 @@
client_ts: 23479410599
client_dur: 60331
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23479434099
server_dur: 24056
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13302,11 +14006,13 @@
client_ts: 23581096165
client_dur: 380036
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23581329034
server_dur: 23039
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13339,11 +14045,13 @@
client_ts: 23683521915
client_dur: 66608
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23683548740
server_dur: 23628
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13376,11 +14084,13 @@
client_ts: 23788017588
client_dur: 110886
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23788091294
server_dur: 22793
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13413,11 +14123,13 @@
client_ts: 23892497829
client_dur: 5298146
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23892520529
server_dur: 12354
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13450,11 +14162,13 @@
client_ts: 23997916363
client_dur: 128256
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 23997934258
server_dur: 22034
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13487,11 +14201,13 @@
client_ts: 24100588444
client_dur: 79761
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24100613596
server_dur: 21941
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13524,11 +14240,13 @@
client_ts: 24203608109
client_dur: 1476962
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24203662175
server_dur: 25824
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13561,11 +14279,13 @@
client_ts: 24305487641
client_dur: 69000
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24305515718
server_dur: 25348
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13598,11 +14318,13 @@
client_ts: 24405940362
client_dur: 114844
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24405962114
server_dur: 22212
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13635,11 +14357,13 @@
client_ts: 24506183075
client_dur: 76130
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24506212466
server_dur: 21817
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13672,11 +14396,13 @@
client_ts: 24606672569
client_dur: 71411
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24606692709
server_dur: 37402
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13709,11 +14435,13 @@
client_ts: 24706847915
client_dur: 77826
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24706871511
server_dur: 21505
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13746,11 +14474,13 @@
client_ts: 24807614065
client_dur: 142762
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24807635682
server_dur: 20956
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13783,11 +14513,13 @@
client_ts: 24909374599
client_dur: 3171973
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24909393940
server_dur: 10997
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13820,11 +14552,13 @@
client_ts: 25012679868
client_dur: 112287
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25012708944
server_dur: 35550
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13857,11 +14591,13 @@
client_ts: 25112924292
client_dur: 43230
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25112938063
server_dur: 18263
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13894,11 +14630,13 @@
client_ts: 25213072326
client_dur: 33395
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25213083753
server_dur: 13289
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13931,11 +14669,13 @@
client_ts: 25313259127
client_dur: 150680
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25313317394
server_dur: 52236
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -13968,11 +14708,13 @@
client_ts: 25415404685
client_dur: 1470768
client_tid: 1225
+ client_pid: 555
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25416823355
server_dur: 22986
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14005,11 +14747,13 @@
client_ts: 25417416478
client_dur: 321332
client_tid: 1225
+ client_pid: 555
server_process: "system_server"
server_thread: "binder:641_4"
server_ts: 25417428728
server_dur: 140719
server_tid: 1596
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14042,11 +14786,13 @@
client_ts: 25867907972
client_dur: 68305
client_tid: 522
+ client_pid: 496
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25867933710
server_dur: 25394
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14079,11 +14825,13 @@
client_ts: 21648847518
client_dur: 138863
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21648864955
server_dur: 110424
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14116,11 +14864,13 @@
client_ts: 21649020222
client_dur: 1298536
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21649025816
server_dur: 1271373
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14153,11 +14903,13 @@
client_ts: 21650405554
client_dur: 21176
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21650412827
server_dur: 7662
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14190,11 +14942,13 @@
client_ts: 21732179696
client_dur: 66330
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21732194327
server_dur: 42279
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14227,11 +14981,13 @@
client_ts: 21732276479
client_dur: 998493
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21732281653
server_dur: 980816
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14264,11 +15020,13 @@
client_ts: 21747805001
client_dur: 32253
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21747815991
server_dur: 13234
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14301,11 +15059,13 @@
client_ts: 21815501160
client_dur: 67864
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21815515197
server_dur: 44624
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14338,11 +15098,13 @@
client_ts: 21815599518
client_dur: 1843570
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21815604505
server_dur: 1825932
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14375,11 +15137,13 @@
client_ts: 21817527536
client_dur: 20911
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21817534229
server_dur: 6822
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14412,11 +15176,13 @@
client_ts: 21898832978
client_dur: 61578
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21898846685
server_dur: 38670
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14449,11 +15215,13 @@
client_ts: 21898923783
client_dur: 1096630
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21898928634
server_dur: 1080195
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14486,11 +15254,13 @@
client_ts: 21914447745
client_dur: 30172
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21914458873
server_dur: 11417
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14523,11 +15293,13 @@
client_ts: 21982278493
client_dur: 137675
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21982310594
server_dur: 80114
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14560,11 +15332,13 @@
client_ts: 21982477699
client_dur: 1235182
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21982492293
server_dur: 1187140
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14597,11 +15371,13 @@
client_ts: 21983894427
client_dur: 56732
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 21983913100
server_dur: 18080
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14634,11 +15410,13 @@
client_ts: 22065483496
client_dur: 63685
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22065497670
server_dur: 40803
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14671,11 +15449,13 @@
client_ts: 22065575735
client_dur: 1062604
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22065580382
server_dur: 1045862
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14708,11 +15488,13 @@
client_ts: 22081125903
client_dur: 31676
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22081137566
server_dur: 12008
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14745,11 +15527,13 @@
client_ts: 22148826292
client_dur: 66432
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22148840763
server_dur: 42889
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14782,11 +15566,13 @@
client_ts: 22148922210
client_dur: 1055199
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22148927117
server_dur: 1037009
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14819,11 +15605,13 @@
client_ts: 22150072810
client_dur: 21373
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22150080025
server_dur: 7715
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14856,11 +15644,13 @@
client_ts: 22232166904
client_dur: 64562
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22232181958
server_dur: 40112
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14893,11 +15683,13 @@
client_ts: 22232260319
client_dur: 1084947
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22232265525
server_dur: 1065811
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14930,11 +15722,13 @@
client_ts: 22247787616
client_dur: 29515
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22247797746
server_dur: 12125
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -14967,11 +15761,13 @@
client_ts: 22315503408
client_dur: 75460
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22315519682
server_dur: 49022
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15004,11 +15800,13 @@
client_ts: 22315610938
client_dur: 1046448
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22315616049
server_dur: 1029776
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15041,11 +15839,13 @@
client_ts: 22316735903
client_dur: 21152
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22316742808
server_dur: 7783
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15078,11 +15878,13 @@
client_ts: 22398839076
client_dur: 65963
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22398854027
server_dur: 42248
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15115,11 +15917,13 @@
client_ts: 22398960806
client_dur: 1018998
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22398966568
server_dur: 1000410
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15152,11 +15956,13 @@
client_ts: 22414462930
client_dur: 32955
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22414474372
server_dur: 13302
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15189,11 +15995,13 @@
client_ts: 22482179365
client_dur: 71661
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22482194253
server_dur: 47932
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15226,11 +16034,13 @@
client_ts: 22482282793
client_dur: 955159
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22482287911
server_dur: 938324
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15263,11 +16073,13 @@
client_ts: 22497465809
client_dur: 39624
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22497477915
server_dur: 17691
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15300,11 +16112,13 @@
client_ts: 22565521231
client_dur: 66993
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22565536155
server_dur: 42982
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15337,11 +16151,13 @@
client_ts: 22565618391
client_dur: 1026658
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22565623264
server_dur: 1009748
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15374,11 +16190,13 @@
client_ts: 22581241130
client_dur: 37185
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22581254325
server_dur: 15879
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15411,11 +16229,13 @@
client_ts: 22648855410
client_dur: 78080
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22648872617
server_dur: 51463
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15448,11 +16268,13 @@
client_ts: 22648965876
client_dur: 1080456
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22648971095
server_dur: 1061465
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15485,11 +16307,13 @@
client_ts: 22650134321
client_dur: 21145
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22650140974
server_dur: 7869
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15522,11 +16346,13 @@
client_ts: 22732183976
client_dur: 84922
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22732199541
server_dur: 59268
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15559,11 +16385,13 @@
client_ts: 22732300442
client_dur: 1084256
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22732305487
server_dur: 1066498
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15596,11 +16424,13 @@
client_ts: 22749316211
client_dur: 37389
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22749329367
server_dur: 15449
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15633,11 +16463,13 @@
client_ts: 22815554390
client_dur: 80259
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22815571850
server_dur: 52908
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15670,11 +16502,13 @@
client_ts: 22815665677
client_dur: 1846724
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22815670690
server_dur: 1827939
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15707,11 +16541,13 @@
client_ts: 22817599826
client_dur: 21169
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22817606552
server_dur: 8080
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15744,11 +16580,13 @@
client_ts: 22898837991
client_dur: 66016
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22898853740
server_dur: 41111
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15781,11 +16619,13 @@
client_ts: 22898932620
client_dur: 1881472
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22898937509
server_dur: 1862476
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15818,11 +16658,13 @@
client_ts: 22914460532
client_dur: 34598
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22914472324
server_dur: 14518
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15855,11 +16697,13 @@
client_ts: 22982182870
client_dur: 80394
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22982200371
server_dur: 52999
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15892,11 +16736,13 @@
client_ts: 22982296391
client_dur: 1052212
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22982301600
server_dur: 1033314
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15929,11 +16775,13 @@
client_ts: 22983445756
client_dur: 23169
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 22983452175
server_dur: 10357
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -15966,11 +16814,13 @@
client_ts: 23065513683
client_dur: 74423
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23065529019
server_dur: 49067
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16003,11 +16853,13 @@
client_ts: 23065619219
client_dur: 1146958
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23065624303
server_dur: 1129990
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16040,11 +16892,13 @@
client_ts: 23081285291
client_dur: 45213
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23081307603
server_dur: 14325
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16077,11 +16931,13 @@
client_ts: 23148835709
client_dur: 76791
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23148852272
server_dur: 50863
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16114,11 +16970,13 @@
client_ts: 23148944247
client_dur: 1986947
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23148949481
server_dur: 1965225
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16151,11 +17009,13 @@
client_ts: 23151027818
client_dur: 25087
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23151036483
server_dur: 9385
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16188,11 +17048,13 @@
client_ts: 23232183799
client_dur: 75511
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23232201226
server_dur: 48102
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16225,11 +17087,13 @@
client_ts: 23232290686
client_dur: 1140969
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23232295646
server_dur: 1123404
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16262,11 +17126,13 @@
client_ts: 23249891699
client_dur: 37797
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23249904526
server_dur: 16397
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16299,11 +17165,13 @@
client_ts: 23315566931
client_dur: 110557
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23315592450
server_dur: 69421
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16336,11 +17204,13 @@
client_ts: 23315722066
client_dur: 2606042
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23315729839
server_dur: 2583671
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16373,11 +17243,13 @@
client_ts: 23318420005
client_dur: 21345
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23318427118
server_dur: 7958
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16410,11 +17282,13 @@
client_ts: 23398873863
client_dur: 68770
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23398889934
server_dur: 41798
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16447,11 +17321,13 @@
client_ts: 23398973230
client_dur: 1176570
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23398978271
server_dur: 1158688
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16484,11 +17360,13 @@
client_ts: 23416727246
client_dur: 33867
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23416739503
server_dur: 14319
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16521,11 +17399,13 @@
client_ts: 23482185608
client_dur: 80279
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23482202317
server_dur: 53655
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16558,11 +17438,13 @@
client_ts: 23482297909
client_dur: 1040841
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23482303086
server_dur: 1022859
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16595,11 +17477,13 @@
client_ts: 23483420403
client_dur: 20295
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23483427104
server_dur: 6793
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16632,11 +17516,13 @@
client_ts: 23566095373
client_dur: 80168
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23566112317
server_dur: 53405
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16669,11 +17555,13 @@
client_ts: 23566207004
client_dur: 1080032
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23566212869
server_dur: 1062341
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16706,11 +17594,13 @@
client_ts: 23581426699
client_dur: 33178
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23581438242
server_dur: 13572
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16743,11 +17633,13 @@
client_ts: 23648877211
client_dur: 74827
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23648892093
server_dur: 50840
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16780,11 +17672,13 @@
client_ts: 23648984124
client_dur: 1869563
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23648989106
server_dur: 1850394
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16817,11 +17711,13 @@
client_ts: 23650943350
client_dur: 22389
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23650950419
server_dur: 8699
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16854,11 +17750,13 @@
client_ts: 23732162997
client_dur: 66948
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23732178223
server_dur: 42065
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16891,11 +17789,13 @@
client_ts: 23732260275
client_dur: 1099825
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23732265173
server_dur: 1083011
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16928,11 +17828,13 @@
client_ts: 23747796852
client_dur: 29683
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23747806637
server_dur: 12580
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -16965,11 +17867,13 @@
client_ts: 23815516337
client_dur: 68846
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23815530730
server_dur: 44894
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17002,11 +17906,13 @@
client_ts: 23815619259
client_dur: 986718
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23815623956
server_dur: 969888
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17039,11 +17945,13 @@
client_ts: 23816691292
client_dur: 20206
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23816697719
server_dur: 7427
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17076,11 +17984,13 @@
client_ts: 23898849538
client_dur: 73093
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23898865505
server_dur: 44882
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17113,11 +18023,13 @@
client_ts: 23898957000
client_dur: 1065096
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23898962241
server_dur: 1045751
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17150,11 +18062,13 @@
client_ts: 23914455173
client_dur: 29880
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23914466130
server_dur: 11108
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17187,11 +18101,13 @@
client_ts: 23982180549
client_dur: 85583
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23982199040
server_dur: 52914
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17224,11 +18140,13 @@
client_ts: 23982305851
client_dur: 1744711
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23982314504
server_dur: 1722762
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17261,11 +18179,13 @@
client_ts: 23984133705
client_dur: 20045
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 23984139858
server_dur: 7402
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17298,11 +18218,13 @@
client_ts: 24065542758
client_dur: 68668
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24065557894
server_dur: 43859
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17335,11 +18257,13 @@
client_ts: 24065652205
client_dur: 1049759
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24065657696
server_dur: 1032583
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17372,11 +18296,13 @@
client_ts: 24081125997
client_dur: 31826
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24081137112
server_dur: 13041
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17409,11 +18335,13 @@
client_ts: 24148820113
client_dur: 68214
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24148833979
server_dur: 44630
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17446,11 +18374,13 @@
client_ts: 24148918715
client_dur: 1009266
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24148923375
server_dur: 991906
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17483,11 +18413,13 @@
client_ts: 24150011467
client_dur: 21064
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24150018449
server_dur: 7359
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17520,11 +18452,13 @@
client_ts: 24247778973
client_dur: 34911
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24247791507
server_dur: 14684
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17557,11 +18491,13 @@
client_ts: 24248842389
client_dur: 66174
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24248855559
server_dur: 43223
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17594,11 +18530,13 @@
client_ts: 24248937934
client_dur: 1130965
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24248943066
server_dur: 1111247
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17631,11 +18569,13 @@
client_ts: 24250153754
client_dur: 21489
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24250161172
server_dur: 7362
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17668,11 +18608,13 @@
client_ts: 24332164030
client_dur: 78622
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24332193334
server_dur: 39186
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17705,11 +18647,13 @@
client_ts: 24332271286
client_dur: 1233475
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24332276288
server_dur: 1215090
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17742,11 +18686,13 @@
client_ts: 24350341616
client_dur: 97785
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24350364644
server_dur: 50768
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17779,11 +18725,13 @@
client_ts: 24415506341
client_dur: 73094
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24415521413
server_dur: 48860
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17816,11 +18764,13 @@
client_ts: 24415610395
client_dur: 1963175
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24415614985
server_dur: 1944970
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17853,11 +18803,13 @@
client_ts: 24417688619
client_dur: 22666
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24417696179
server_dur: 8868
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17890,11 +18842,13 @@
client_ts: 24498858408
client_dur: 72316
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24498874905
server_dur: 44364
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17927,11 +18881,13 @@
client_ts: 24498965251
client_dur: 2212172
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24498971364
server_dur: 2189623
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -17964,11 +18920,13 @@
client_ts: 24514530326
client_dur: 46445
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24514545922
server_dur: 16967
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18001,11 +18959,13 @@
client_ts: 24582239101
client_dur: 102168
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24582262241
server_dur: 62646
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18038,11 +18998,13 @@
client_ts: 24582383933
client_dur: 1125278
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24582393122
server_dur: 1094530
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18075,11 +19037,13 @@
client_ts: 24583627699
client_dur: 36229
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24583638897
server_dur: 12637
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18112,11 +19076,13 @@
client_ts: 24665516749
client_dur: 77492
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24665535013
server_dur: 46956
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18149,11 +19115,13 @@
client_ts: 24665629297
client_dur: 1056190
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24665650779
server_dur: 1018462
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18186,11 +19154,13 @@
client_ts: 24681436859
client_dur: 36053
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24681447493
server_dur: 17169
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18223,11 +19193,13 @@
client_ts: 24748868138
client_dur: 84885
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24748886644
server_dur: 54090
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18260,11 +19232,13 @@
client_ts: 24748988510
client_dur: 1030023
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24748994792
server_dur: 1007690
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18297,11 +19271,13 @@
client_ts: 24750114696
client_dur: 25844
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24750122620
server_dur: 9559
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18334,11 +19310,13 @@
client_ts: 24832172264
client_dur: 71964
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24832186234
server_dur: 48177
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18371,11 +19349,13 @@
client_ts: 24832278767
client_dur: 2319199
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24832284387
server_dur: 2299722
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18408,11 +19388,13 @@
client_ts: 24848194066
client_dur: 38990
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24848207084
server_dur: 16882
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18445,11 +19427,13 @@
client_ts: 24915569035
client_dur: 92319
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24915587307
server_dur: 61522
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18482,11 +19466,13 @@
client_ts: 24915703770
client_dur: 1154960
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24915710935
server_dur: 1133005
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18519,11 +19505,13 @@
client_ts: 24916964211
client_dur: 24607
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24916971111
server_dur: 10684
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18556,11 +19544,13 @@
client_ts: 24998945706
client_dur: 111229
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24998970836
server_dur: 66036
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18593,11 +19583,13 @@
client_ts: 24999109626
client_dur: 1079778
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 24999121401
server_dur: 1042002
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18630,11 +19622,13 @@
client_ts: 25014488039
client_dur: 70943
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25014504840
server_dur: 24296
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18667,11 +19661,13 @@
client_ts: 25082202078
client_dur: 73854
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25082217860
server_dur: 47920
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18704,11 +19700,13 @@
client_ts: 25082306875
client_dur: 1782258
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25082312116
server_dur: 1764384
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18741,11 +19739,13 @@
client_ts: 25084166020
client_dur: 20222
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25084172595
server_dur: 7394
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18778,11 +19778,13 @@
client_ts: 25165504670
client_dur: 81307
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25165518960
server_dur: 55941
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18815,11 +19817,13 @@
client_ts: 25165619958
client_dur: 997114
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25165625294
server_dur: 978831
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18852,11 +19856,13 @@
client_ts: 25181108704
client_dur: 38312
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25181120018
server_dur: 17603
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18889,11 +19895,13 @@
client_ts: 25248839993
client_dur: 71834
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25248855062
server_dur: 47208
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18926,11 +19934,13 @@
client_ts: 25248942032
client_dur: 1065822
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25248946700
server_dur: 1047977
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -18963,11 +19973,13 @@
client_ts: 25250092465
client_dur: 21121
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25250099244
server_dur: 7872
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19000,11 +20012,13 @@
client_ts: 25332433123
client_dur: 129185
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25332463293
server_dur: 75363
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19037,11 +20051,13 @@
client_ts: 25332622145
client_dur: 3970213
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25332636462
server_dur: 3919134
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19074,11 +20090,13 @@
client_ts: 25347937130
client_dur: 63143
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25347956107
server_dur: 23630
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19111,11 +20129,13 @@
client_ts: 25415532955
client_dur: 74958
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25415548486
server_dur: 48910
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19148,11 +20168,13 @@
client_ts: 25415639063
client_dur: 1045725
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25415643854
server_dur: 1028657
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19185,11 +20207,13 @@
client_ts: 25416779828
client_dur: 22459
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25416786530
server_dur: 8393
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19222,11 +20246,13 @@
client_ts: 25499090883
client_dur: 135524
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25499122517
server_dur: 77879
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19259,11 +20285,13 @@
client_ts: 25499287661
client_dur: 1179689
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25499301640
server_dur: 1130895
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19296,11 +20324,13 @@
client_ts: 25514592061
client_dur: 62604
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25514612449
server_dur: 22109
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19333,11 +20363,13 @@
client_ts: 25582338627
client_dur: 162705
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25582368275
server_dur: 92253
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19370,11 +20402,13 @@
client_ts: 25582559609
client_dur: 1051443
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25582572935
server_dur: 992794
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19407,11 +20441,13 @@
client_ts: 25583792668
client_dur: 57270
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25583811204
server_dur: 18520
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19444,11 +20480,13 @@
client_ts: 25665575887
client_dur: 98551
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25665594994
server_dur: 67512
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19481,11 +20519,13 @@
client_ts: 25665714191
client_dur: 2056246
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25665719790
server_dur: 2036405
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19518,11 +20558,13 @@
client_ts: 25681400816
client_dur: 89610
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25681427461
server_dur: 37612
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19555,11 +20597,13 @@
client_ts: 25748935127
client_dur: 126067
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25748964935
server_dur: 75428
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19592,11 +20636,13 @@
client_ts: 25749129537
client_dur: 1130001
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25749140148
server_dur: 1092654
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19629,11 +20675,13 @@
client_ts: 25750403211
client_dur: 43991
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25750417428
server_dur: 15659
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19666,11 +20714,13 @@
client_ts: 25832201262
client_dur: 76032
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25832217990
server_dur: 49551
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19703,11 +20753,13 @@
client_ts: 25832312368
client_dur: 1052337
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25832317105
server_dur: 1035144
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19740,11 +20792,13 @@
client_ts: 25847799175
client_dur: 40678
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25847812958
server_dur: 17906
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19777,11 +20831,13 @@
client_ts: 25855749248
client_dur: 31727
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25855758456
server_dur: 14151
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19814,11 +20870,13 @@
client_ts: 25865415841
client_dur: 46578
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25865424168
server_dur: 29991
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19851,11 +20909,13 @@
client_ts: 25865488539
client_dur: 1091161
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25865493112
server_dur: 1074785
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19888,11 +20948,13 @@
client_ts: 25882229417
client_dur: 49798
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25882240791
server_dur: 29545
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19925,11 +20987,13 @@
client_ts: 25882302427
client_dur: 976002
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25882306957
server_dur: 960192
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19962,11 +21026,13 @@
client_ts: 25915516257
client_dur: 67861
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25915531128
server_dur: 43477
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -19999,11 +21065,13 @@
client_ts: 25915614740
client_dur: 879798
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25915619820
server_dur: 861862
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20036,11 +21104,13 @@
client_ts: 25947791827
client_dur: 36462
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25947803365
server_dur: 16366
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20073,11 +21143,13 @@
client_ts: 25965634137
client_dur: 62809
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25965647687
server_dur: 39248
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20110,11 +21182,13 @@
client_ts: 25965727363
client_dur: 1082911
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25965732380
server_dur: 1065715
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20147,11 +21221,13 @@
client_ts: 25966880090
client_dur: 18647
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25966885882
server_dur: 6742
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20184,11 +21260,13 @@
client_ts: 25998857609
client_dur: 68802
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25998873383
server_dur: 43179
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20221,11 +21299,13 @@
client_ts: 25998956453
client_dur: 896029
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 25998961673
server_dur: 878692
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20258,11 +21338,13 @@
client_ts: 26064480113
client_dur: 41059
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26064494262
server_dur: 17230
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20295,11 +21377,13 @@
client_ts: 26082167007
client_dur: 68814
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26082181896
server_dur: 44307
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20332,11 +21416,13 @@
client_ts: 26082265345
client_dur: 984160
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26082270124
server_dur: 967726
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20369,11 +21455,13 @@
client_ts: 26083328041
client_dur: 20356
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26083334137
server_dur: 7873
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20406,11 +21494,13 @@
client_ts: 26165543672
client_dur: 76748
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26165564279
server_dur: 46139
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20443,11 +21533,13 @@
client_ts: 26165664730
client_dur: 952016
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26165670566
server_dur: 934833
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20480,11 +21572,13 @@
client_ts: 26181134030
client_dur: 39663
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26181147708
server_dur: 17312
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20517,11 +21611,13 @@
client_ts: 26265553005
client_dur: 106037
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26265570929
server_dur: 76133
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20554,11 +21650,13 @@
client_ts: 26265695797
client_dur: 1070288
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26265701257
server_dur: 1051768
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20591,11 +21689,13 @@
client_ts: 26266851930
client_dur: 21080
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26266858935
server_dur: 7493
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20628,11 +21728,13 @@
client_ts: 26348993760
client_dur: 74635
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26349012914
server_dur: 45210
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20665,11 +21767,13 @@
client_ts: 26349100408
client_dur: 985507
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26349105368
server_dur: 967525
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20702,11 +21806,13 @@
client_ts: 26364884926
client_dur: 48465
client_tid: 496
+ client_pid: 496
server_process: "/vendor/bin/hw/android.hardware.graphics.composer3-service.ranchu"
server_thread: "binder:446_1"
server_ts: 26364907480
server_dur: 16844
server_tid: 507
+ server_pid: 446
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20739,11 +21845,13 @@
client_ts: 25527594973
client_dur: 961513
client_tid: 431
+ client_pid: 431
server_process: "system_server"
server_thread: "system_server"
server_ts: 25528486848
server_dur: 41164
server_tid: 641
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20776,11 +21884,13 @@
client_ts: 25519230900
client_dur: 67687
client_tid: 458
+ client_pid: 458
server_process: "system_server"
server_thread: "system-server-i"
server_ts: 25519257217
server_dur: 19303
server_tid: 665
+ server_pid: 641
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20813,11 +21923,13 @@
client_ts: 25887934200
client_dur: 73511
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25887963950
server_dur: 31341
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20850,11 +21962,13 @@
client_ts: 25924014206
client_dur: 48397
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25924032607
server_dur: 19471
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20887,11 +22001,13 @@
client_ts: 25924552433
client_dur: 51388
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25924572649
server_dur: 16951
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20924,11 +22040,13 @@
client_ts: 25925236390
client_dur: 40800
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25925252802
server_dur: 11692
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20961,11 +22079,13 @@
client_ts: 25925312006
client_dur: 22098
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25925321018
server_dur: 4977
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -20998,11 +22118,13 @@
client_ts: 25925340685
client_dur: 18485
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25925348223
server_dur: 3660
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21035,11 +22157,13 @@
client_ts: 25925364763
client_dur: 18634
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25925372247
server_dur: 3490
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21072,11 +22196,13 @@
client_ts: 26046721012
client_dur: 137219
client_tid: 1625
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 26046826730
server_dur: 20407
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21109,11 +22235,13 @@
client_ts: 25848902022
client_dur: 109438
client_tid: 662
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25848918031
server_dur: 71184
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21147,11 +22275,13 @@
client_ts: 25849035817
client_dur: 85138
client_tid: 662
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25849046771
server_dur: 23591
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21184,11 +22314,13 @@
client_ts: 25965522657
client_dur: 87636
client_tid: 662
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25965536704
server_dur: 31166
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -21221,11 +22353,13 @@
client_ts: 26046475353
client_dur: 139999
client_tid: 662
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 26046494430
server_dur: 36023
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21252,11 +22386,13 @@
client_ts: 26049659202
client_dur: 66793
client_tid: 662
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 26049676304
server_dur: 21699
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21283,11 +22419,13 @@
client_ts: 25852597933
client_dur: 72360
client_tid: 663
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25852614647
server_dur: 40436
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21321,11 +22459,13 @@
client_ts: 25852691734
client_dur: 40316
client_tid: 663
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25852702316
server_dur: 18235
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21358,11 +22498,13 @@
client_ts: 25851140935
client_dur: 82845
client_tid: 661
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25851157134
server_dur: 45749
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21396,11 +22538,13 @@
client_ts: 25851245190
client_dur: 40441
client_tid: 661
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25851255329
server_dur: 18715
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21434,11 +22578,13 @@
client_ts: 25854136251
client_dur: 140850
client_tid: 661
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25854153001
server_dur: 42359
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21471,11 +22617,13 @@
client_ts: 25982476503
client_dur: 71952
client_tid: 660
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25982503060
server_dur: 29743
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21508,11 +22656,13 @@
client_ts: 25873131715
client_dur: 117082
client_tid: 659
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25873153628
server_dur: 22969
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21539,11 +22689,13 @@
client_ts: 25883734665
client_dur: 152980
client_tid: 659
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25883839992
server_dur: 25887
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21576,11 +22728,13 @@
client_ts: 25537922715
client_dur: 260041
client_tid: 1600
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.lights-service.example"
server_thread: "android.hardwar"
server_ts: 25537947459
server_dur: 137623
server_tid: 448
+ server_pid: 448
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21608,11 +22762,13 @@
client_ts: 25285800698
client_dur: 1963972
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25285972364
server_dur: 1764197
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -21670,11 +22826,13 @@
client_ts: 25359359930
client_dur: 58056845
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25359406757
server_dur: 57997245
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -21726,11 +22884,13 @@
client_ts: 25417583831
client_dur: 159127
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25417600127
server_dur: 117766
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21764,11 +22924,13 @@
client_ts: 25417795254
client_dur: 84143
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25417807548
server_dur: 52980
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21802,11 +22964,13 @@
client_ts: 25417900256
client_dur: 72685
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25417909455
server_dur: 47502
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21840,11 +23004,13 @@
client_ts: 25417989778
client_dur: 70644
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418000174
server_dur: 44675
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21878,11 +23044,13 @@
client_ts: 25418084967
client_dur: 63335
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418094122
server_dur: 43952
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21916,11 +23084,13 @@
client_ts: 25418162868
client_dur: 73089
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418174366
server_dur: 51193
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21954,11 +23124,13 @@
client_ts: 25418257945
client_dur: 71217
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418268205
server_dur: 50582
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -21992,11 +23164,13 @@
client_ts: 25418344237
client_dur: 68933
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418354448
server_dur: 48033
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22030,11 +23204,13 @@
client_ts: 25418434003
client_dur: 70051
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418444322
server_dur: 49218
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22068,11 +23244,13 @@
client_ts: 25418523244
client_dur: 71860
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418533528
server_dur: 51097
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22106,11 +23284,13 @@
client_ts: 25418613235
client_dur: 80217
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418623429
server_dur: 59922
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22144,11 +23324,13 @@
client_ts: 25418707467
client_dur: 100848
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25418716983
server_dur: 80176
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22182,11 +23364,13 @@
client_ts: 25418859801
client_dur: 96287
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25418874922
server_dur: 66129
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22220,11 +23404,13 @@
client_ts: 25418990456
client_dur: 73140
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419002227
server_dur: 50140
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22258,11 +23444,13 @@
client_ts: 25419082851
client_dur: 69691
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419093189
server_dur: 48614
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22296,11 +23484,13 @@
client_ts: 25419197218
client_dur: 97019
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419208385
server_dur: 75308
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22334,11 +23524,13 @@
client_ts: 25419309331
client_dur: 68503
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419319559
server_dur: 48070
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22372,11 +23564,13 @@
client_ts: 25419402979
client_dur: 68957
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419413041
server_dur: 48592
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22410,11 +23604,13 @@
client_ts: 25419490548
client_dur: 70455
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419500948
server_dur: 49673
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22448,11 +23644,13 @@
client_ts: 25419576883
client_dur: 68126
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419587154
server_dur: 47213
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22486,11 +23684,13 @@
client_ts: 25419662926
client_dur: 68985
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419673103
server_dur: 48313
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22524,11 +23724,13 @@
client_ts: 25419747914
client_dur: 70204
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419758511
server_dur: 49425
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22562,11 +23764,13 @@
client_ts: 25419838530
client_dur: 74294
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419851530
server_dur: 50943
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22600,11 +23804,13 @@
client_ts: 25419930878
client_dur: 71254
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25419941349
server_dur: 50447
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22638,11 +23844,13 @@
client_ts: 25420022065
client_dur: 70064
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420034446
server_dur: 47514
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22676,11 +23884,13 @@
client_ts: 25420107549
client_dur: 67997
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420117365
server_dur: 47959
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22714,11 +23924,13 @@
client_ts: 25420191395
client_dur: 67798
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420201429
server_dur: 47599
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22752,11 +23964,13 @@
client_ts: 25420275173
client_dur: 68378
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420285912
server_dur: 47561
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22790,11 +24004,13 @@
client_ts: 25420359212
client_dur: 68447
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420369692
server_dur: 47721
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22828,11 +24044,13 @@
client_ts: 25420458125
client_dur: 69217
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420468424
server_dur: 48683
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22866,11 +24084,13 @@
client_ts: 25420542827
client_dur: 68982
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420553053
server_dur: 48593
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22904,11 +24124,13 @@
client_ts: 25420642092
client_dur: 75336
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420652418
server_dur: 54692
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22942,11 +24164,13 @@
client_ts: 25420732600
client_dur: 66989
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420742486
server_dur: 46762
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -22980,11 +24204,13 @@
client_ts: 25420814670
client_dur: 66733
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420824622
server_dur: 46663
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23018,11 +24244,13 @@
client_ts: 25420898454
client_dur: 67283
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420908490
server_dur: 47258
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23056,11 +24284,13 @@
client_ts: 25420980515
client_dur: 68119
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25420990555
server_dur: 47881
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23094,11 +24324,13 @@
client_ts: 25421062960
client_dur: 67691
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421073099
server_dur: 47279
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23132,11 +24364,13 @@
client_ts: 25421149398
client_dur: 67456
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421159582
server_dur: 46812
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23170,11 +24404,13 @@
client_ts: 25421234249
client_dur: 102474
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421244402
server_dur: 81689
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23208,11 +24444,13 @@
client_ts: 25421351001
client_dur: 68629
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421361736
server_dur: 50268
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23246,11 +24484,13 @@
client_ts: 25421437438
client_dur: 78505
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421447770
server_dur: 60705
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23284,11 +24524,13 @@
client_ts: 25421529017
client_dur: 90619
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25421536869
server_dur: 70139
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23322,11 +24564,13 @@
client_ts: 25421667924
client_dur: 93173
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421681338
server_dur: 67854
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23360,11 +24604,13 @@
client_ts: 25421792054
client_dur: 79868
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25421807359
server_dur: 55975
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23398,11 +24644,13 @@
client_ts: 25421889199
client_dur: 85495
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25421897198
server_dur: 65834
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23436,11 +24684,13 @@
client_ts: 25422008441
client_dur: 90387
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25422024828
server_dur: 62034
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23474,11 +24724,13 @@
client_ts: 25422133767
client_dur: 75807
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25422147676
server_dur: 51244
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23512,11 +24764,13 @@
client_ts: 25422225432
client_dur: 98464
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25422233734
server_dur: 79168
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23550,11 +24804,13 @@
client_ts: 25422355100
client_dur: 93353
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25422370044
server_dur: 64908
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23588,11 +24844,13 @@
client_ts: 25422488580
client_dur: 81077
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25422503734
server_dur: 56902
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23626,11 +24884,13 @@
client_ts: 25422585669
client_dur: 81637
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25422593474
server_dur: 62945
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23664,11 +24924,13 @@
client_ts: 25422698364
client_dur: 307058
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25422912325
server_dur: 72852
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23702,11 +24964,13 @@
client_ts: 25423038416
client_dur: 85760
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423050636
server_dur: 59617
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23740,11 +25004,13 @@
client_ts: 25423141970
client_dur: 65309
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423147589
server_dur: 46373
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23778,11 +25044,13 @@
client_ts: 25423222741
client_dur: 83982
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423241349
server_dur: 52179
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23816,11 +25084,13 @@
client_ts: 25423321767
client_dur: 84883
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25423330170
server_dur: 64541
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23854,11 +25124,13 @@
client_ts: 25423444205
client_dur: 97794
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423460614
server_dur: 64145
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23892,11 +25164,13 @@
client_ts: 25423571069
client_dur: 79325
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423585310
server_dur: 56042
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23930,11 +25204,13 @@
client_ts: 25423667089
client_dur: 80196
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25423674807
server_dur: 61267
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -23968,11 +25244,13 @@
client_ts: 25423778912
client_dur: 85732
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423789294
server_dur: 63712
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24006,11 +25284,13 @@
client_ts: 25423899033
client_dur: 83874
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25423913207
server_dur: 61126
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24044,11 +25324,13 @@
client_ts: 25423999018
client_dur: 80807
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25424006723
server_dur: 62351
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24082,11 +25364,13 @@
client_ts: 25424119494
client_dur: 90723
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424133267
server_dur: 65314
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24120,11 +25404,13 @@
client_ts: 25424244832
client_dur: 75424
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424257485
server_dur: 54108
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24158,11 +25444,13 @@
client_ts: 25424340551
client_dur: 68134
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424352371
server_dur: 47952
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24196,11 +25484,13 @@
client_ts: 25424423268
client_dur: 79235
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25424430889
server_dur: 60700
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24234,11 +25524,13 @@
client_ts: 25424532071
client_dur: 132824
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424548055
server_dur: 63569
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24272,11 +25564,13 @@
client_ts: 25424695404
client_dur: 78895
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424709439
server_dur: 55753
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24310,11 +25604,13 @@
client_ts: 25424790712
client_dur: 84106
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25424798759
server_dur: 65429
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24348,11 +25644,13 @@
client_ts: 25424904112
client_dur: 86631
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_3"
server_ts: 25424916724
server_dur: 62466
server_tid: 1590
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24386,11 +25684,13 @@
client_ts: 25425012627
client_dur: 82418
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425020923
server_dur: 61511
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24424,11 +25724,13 @@
client_ts: 25425134890
client_dur: 80492
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425151484
server_dur: 55304
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24462,11 +25764,13 @@
client_ts: 25425231522
client_dur: 103387
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25425239219
server_dur: 85661
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -24506,11 +25810,13 @@
client_ts: 25425372618
client_dur: 91185
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425384448
server_dur: 68305
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24544,11 +25850,13 @@
client_ts: 25425498321
client_dur: 82574
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425511999
server_dur: 60168
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24582,11 +25890,13 @@
client_ts: 25425597030
client_dur: 96570
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25425604832
server_dur: 77508
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24620,11 +25930,13 @@
client_ts: 25425734906
client_dur: 90504
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425748243
server_dur: 65377
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24658,11 +25970,13 @@
client_ts: 25425853178
client_dur: 75664
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425868250
server_dur: 50805
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24696,11 +26010,13 @@
client_ts: 25425949096
client_dur: 75914
client_tid: 1591
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25425967109
server_dur: 49564
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24733,11 +26049,13 @@
client_ts: 25507770941
client_dur: 659345
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25507817302
server_dur: 493959
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24764,11 +26082,13 @@
client_ts: 25508483480
client_dur: 113431
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25508520735
server_dur: 37596
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24795,11 +26115,13 @@
client_ts: 25512346526
client_dur: 247626
client_tid: 665
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.sensors-service.example"
server_thread: "android.hardwar"
server_ts: 25512379433
server_dur: 68665
server_tid: 458
+ server_pid: 458
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24832,11 +26154,13 @@
client_ts: 25519832521
client_dur: 244039
client_tid: 665
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.sensors-service.example"
server_thread: "android.hardwar"
server_ts: 25519857925
server_dur: 186450
server_tid: 458
+ server_pid: 458
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24863,11 +26187,13 @@
client_ts: 25520140841
client_dur: 73832
client_tid: 665
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.sensors-service.example"
server_thread: "android.hardwar"
server_ts: 25520164682
server_dur: 18571
server_tid: 458
+ server_pid: 458
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24894,11 +26220,13 @@
client_ts: 25523026906
client_dur: 340409
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25523191948
server_dur: 40297
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24931,11 +26259,13 @@
client_ts: 25524188687
client_dur: 89696
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25524226273
server_dur: 31748
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -24968,11 +26298,13 @@
client_ts: 25524630643
client_dur: 80450
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25524666614
server_dur: 25632
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25005,11 +26337,13 @@
client_ts: 25524881306
client_dur: 64099
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25524906781
server_dur: 18982
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25042,11 +26376,13 @@
client_ts: 25525155622
client_dur: 182063
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25525206343
server_dur: 111535
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25079,11 +26415,13 @@
client_ts: 25886972081
client_dur: 392512
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25887243579
server_dur: 100309
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25116,11 +26454,13 @@
client_ts: 25887384160
client_dur: 146196
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25887501006
server_dur: 15631
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25153,11 +26493,13 @@
client_ts: 25887600584
client_dur: 263828
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25887779478
server_dur: 69332
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25190,11 +26532,13 @@
client_ts: 25888119355
client_dur: 387727
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25888158284
server_dur: 330571
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25221,11 +26565,13 @@
client_ts: 25888835343
client_dur: 324156
client_tid: 665
+ client_pid: 641
server_process: "/system/bin/hwservicemanager"
server_thread: "hwservicemanage"
server_ts: 25888991319
server_dur: 112375
server_tid: 247
+ server_pid: 247
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25258,11 +26604,13 @@
client_ts: 24562258099
client_dur: 95711
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24562294551
server_dur: 25989
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25295,11 +26643,13 @@
client_ts: 24576142302
client_dur: 100932
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24576178251
server_dur: 35440
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25326,11 +26676,13 @@
client_ts: 24577928034
client_dur: 98898
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24577965065
server_dur: 33433
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25357,11 +26709,13 @@
client_ts: 24579825258
client_dur: 91999
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24579857006
server_dur: 32771
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25388,11 +26742,13 @@
client_ts: 24586203617
client_dur: 160861
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24586241455
server_dur: 29609
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25425,11 +26781,13 @@
client_ts: 24588923637
client_dur: 381582
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24588962975
server_dur: 29095
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25462,11 +26820,13 @@
client_ts: 24592040006
client_dur: 240562
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24592076186
server_dur: 28582
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25499,11 +26859,13 @@
client_ts: 24594213703
client_dur: 673648
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24594243436
server_dur: 22881
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25536,11 +26898,13 @@
client_ts: 24595958354
client_dur: 77980
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24595993807
server_dur: 22950
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25573,11 +26937,13 @@
client_ts: 24597520537
client_dur: 49553
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24597542932
server_dur: 16294
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25610,11 +26976,13 @@
client_ts: 24598897106
client_dur: 68689
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24598922372
server_dur: 18191
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25647,11 +27015,13 @@
client_ts: 24600413274
client_dur: 57521
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24600434769
server_dur: 13168
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25684,11 +27054,13 @@
client_ts: 24602399966
client_dur: 80715
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24602431540
server_dur: 23433
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25721,11 +27093,13 @@
client_ts: 24603839586
client_dur: 50942
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24603864064
server_dur: 11482
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25758,11 +27132,13 @@
client_ts: 24605137324
client_dur: 53391
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24605161847
server_dur: 16108
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25795,11 +27171,13 @@
client_ts: 24607833798
client_dur: 79225
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24607864767
server_dur: 25864
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25832,11 +27210,13 @@
client_ts: 24609098413
client_dur: 86504
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24609132077
server_dur: 29462
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25869,11 +27249,13 @@
client_ts: 24646418600
client_dur: 104600
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24646446859
server_dur: 25546
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25900,11 +27282,13 @@
client_ts: 24671385803
client_dur: 170289
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24671409853
server_dur: 37825
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25931,11 +27315,13 @@
client_ts: 24681379300
client_dur: 57187
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24681399705
server_dur: 21305
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25962,11 +27348,13 @@
client_ts: 24682251210
client_dur: 182307
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24682273023
server_dur: 20664
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -25993,11 +27381,13 @@
client_ts: 24683246275
client_dur: 207024
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24683267459
server_dur: 20968
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26024,11 +27414,13 @@
client_ts: 24685075466
client_dur: 189680
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24685097014
server_dur: 20394
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26055,11 +27447,13 @@
client_ts: 24687047835
client_dur: 52825
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24687065149
server_dur: 20404
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26086,11 +27480,13 @@
client_ts: 24690752961
client_dur: 1138197
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24690773594
server_dur: 20275
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26117,11 +27513,13 @@
client_ts: 24692956765
client_dur: 967152
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24692973830
server_dur: 19601
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26148,11 +27546,13 @@
client_ts: 24699086985
client_dur: 2761757
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24699105127
server_dur: 19717
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26191,11 +27591,13 @@
client_ts: 24704026795
client_dur: 151503
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24704048621
server_dur: 19875
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26222,11 +27624,13 @@
client_ts: 24707823403
client_dur: 418749
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24707844258
server_dur: 19420
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26253,11 +27657,13 @@
client_ts: 24715413797
client_dur: 55453
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24715432924
server_dur: 20515
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26284,11 +27690,13 @@
client_ts: 24718094584
client_dur: 396933
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24718112985
server_dur: 20123
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26315,11 +27723,13 @@
client_ts: 24720389105
client_dur: 56955
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24720410588
server_dur: 19497
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26346,11 +27756,13 @@
client_ts: 24722237372
client_dur: 57988
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24722254479
server_dur: 19636
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26377,11 +27789,13 @@
client_ts: 24724223102
client_dur: 52789
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24724240486
server_dur: 19628
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26408,11 +27822,13 @@
client_ts: 24727963525
client_dur: 54516
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24727983399
server_dur: 19277
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26439,11 +27855,13 @@
client_ts: 24729275294
client_dur: 69527
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24729308987
server_dur: 19817
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26470,11 +27888,13 @@
client_ts: 24730464621
client_dur: 56830
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24730486611
server_dur: 19083
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26501,11 +27921,13 @@
client_ts: 24731541792
client_dur: 59775
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24731563743
server_dur: 19533
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26532,11 +27954,13 @@
client_ts: 24732635124
client_dur: 62661
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24732657580
server_dur: 22874
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26563,11 +27987,13 @@
client_ts: 24733694931
client_dur: 58749
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24733718525
server_dur: 19626
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26594,11 +28020,13 @@
client_ts: 24734519268
client_dur: 56933
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24734541289
server_dur: 19505
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26625,11 +28053,13 @@
client_ts: 24735621739
client_dur: 59911
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24735644076
server_dur: 19698
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26656,11 +28086,13 @@
client_ts: 24737578050
client_dur: 70375
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24737599805
server_dur: 19338
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26687,11 +28119,13 @@
client_ts: 24738379435
client_dur: 59307
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24738401334
server_dur: 19247
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26718,11 +28152,13 @@
client_ts: 24739381820
client_dur: 59860
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24739404070
server_dur: 19274
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26749,11 +28185,13 @@
client_ts: 24740296188
client_dur: 60137
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24740318642
server_dur: 19217
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26780,11 +28218,13 @@
client_ts: 24741062753
client_dur: 60144
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24741084854
server_dur: 19235
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26811,11 +28251,13 @@
client_ts: 24742188157
client_dur: 62717
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24742210951
server_dur: 22644
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26842,11 +28284,13 @@
client_ts: 24743215720
client_dur: 57215
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24743238157
server_dur: 19299
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26873,11 +28317,13 @@
client_ts: 24744310059
client_dur: 62304
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24744332347
server_dur: 22570
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26904,11 +28350,13 @@
client_ts: 24745726015
client_dur: 42535
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24745741462
server_dur: 13274
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26935,11 +28383,13 @@
client_ts: 24746812940
client_dur: 62487
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24746836091
server_dur: 21679
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26966,11 +28416,13 @@
client_ts: 24747614390
client_dur: 60851
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24747636567
server_dur: 21322
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -26997,11 +28449,13 @@
client_ts: 24748641464
client_dur: 98796
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24748705431
server_dur: 19737
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27028,11 +28482,13 @@
client_ts: 24749789307
client_dur: 58174
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24749812809
server_dur: 19265
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27059,11 +28515,13 @@
client_ts: 24753793235
client_dur: 285781
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24753872138
server_dur: 21667
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27090,11 +28548,13 @@
client_ts: 24760573761
client_dur: 55871
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24760593814
server_dur: 19818
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27121,11 +28581,13 @@
client_ts: 24761811419
client_dur: 104650
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24761830688
server_dur: 19717
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27152,11 +28614,13 @@
client_ts: 24763891343
client_dur: 52306
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24763908905
server_dur: 19517
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27183,11 +28647,13 @@
client_ts: 24764906537
client_dur: 55552
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24764927197
server_dur: 19435
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27214,11 +28680,13 @@
client_ts: 24766255714
client_dur: 69186
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24766285051
server_dur: 22399
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27245,11 +28713,13 @@
client_ts: 24767227873
client_dur: 55587
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24767248556
server_dur: 19425
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27276,11 +28746,13 @@
client_ts: 24770081943
client_dur: 54467
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24770101200
server_dur: 19938
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27307,11 +28779,13 @@
client_ts: 24773596028
client_dur: 197569
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24773614039
server_dur: 28282
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27338,11 +28812,13 @@
client_ts: 24785387433
client_dur: 2028794
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24785409117
server_dur: 20078
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27369,11 +28845,13 @@
client_ts: 24788209976
client_dur: 153903
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24788233234
server_dur: 20622
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27400,11 +28878,13 @@
client_ts: 24789221986
client_dur: 57706
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24789245188
server_dur: 19214
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27431,11 +28911,13 @@
client_ts: 24790705709
client_dur: 65167
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24790730301
server_dur: 25107
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27462,11 +28944,13 @@
client_ts: 24791500958
client_dur: 57742
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24791523460
server_dur: 19471
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27493,11 +28977,13 @@
client_ts: 24792307538
client_dur: 58314
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24792330556
server_dur: 19368
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27524,11 +29010,13 @@
client_ts: 24793372795
client_dur: 57390
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24793395000
server_dur: 19464
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27555,11 +29043,13 @@
client_ts: 24794492101
client_dur: 54114
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24794511315
server_dur: 19523
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27586,11 +29076,13 @@
client_ts: 24795410099
client_dur: 54987
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24795430573
server_dur: 19258
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27617,11 +29109,13 @@
client_ts: 24796677839
client_dur: 55067
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24796698242
server_dur: 19086
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27648,11 +29142,13 @@
client_ts: 24797815386
client_dur: 53079
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24797833699
server_dur: 19213
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27679,11 +29175,13 @@
client_ts: 24799449503
client_dur: 56617
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24799470930
server_dur: 19513
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27710,11 +29208,13 @@
client_ts: 24805536696
client_dur: 49858
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24805554922
server_dur: 16943
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27741,11 +29241,13 @@
client_ts: 24808769073
client_dur: 53981
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24808788665
server_dur: 19040
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27772,11 +29274,13 @@
client_ts: 24821382491
client_dur: 48977
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24821399252
server_dur: 17308
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27803,11 +29307,13 @@
client_ts: 24825799132
client_dur: 58481
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24825818665
server_dur: 20241
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27834,11 +29340,13 @@
client_ts: 24836015985
client_dur: 60190
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24836036269
server_dur: 24240
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27865,11 +29373,13 @@
client_ts: 24838902949
client_dur: 155997
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24838939369
server_dur: 21040
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27902,11 +29412,13 @@
client_ts: 24842361861
client_dur: 70409
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24842390226
server_dur: 21512
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27939,11 +29451,13 @@
client_ts: 24849556375
client_dur: 183165
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24849690991
server_dur: 24294
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -27976,11 +29490,13 @@
client_ts: 24871639354
client_dur: 68462
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24871671014
server_dur: 22834
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28013,11 +29529,13 @@
client_ts: 24873845755
client_dur: 1096098
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24873871989
server_dur: 21071
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28044,11 +29562,13 @@
client_ts: 24877992364
client_dur: 187259
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24878014368
server_dur: 20564
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28081,11 +29601,13 @@
client_ts: 24881598747
client_dur: 82762
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24881620007
server_dur: 37794
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28118,11 +29640,13 @@
client_ts: 24882762008
client_dur: 343179
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24883063470
server_dur: 18976
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28155,11 +29679,13 @@
client_ts: 24884363797
client_dur: 138909
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24884469472
server_dur: 19017
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28192,11 +29718,13 @@
client_ts: 24886610735
client_dur: 64238
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24886632996
server_dur: 20186
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28229,11 +29757,13 @@
client_ts: 24888078440
client_dur: 422798
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24888463594
server_dur: 19313
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28266,11 +29796,13 @@
client_ts: 24891297979
client_dur: 58221
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24891321099
server_dur: 19916
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28303,11 +29835,13 @@
client_ts: 24893930550
client_dur: 54133
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24893950723
server_dur: 20568
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28340,11 +29874,13 @@
client_ts: 24896595580
client_dur: 67589
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24896624063
server_dur: 20320
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28377,11 +29913,13 @@
client_ts: 24903024474
client_dur: 206934
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24903154456
server_dur: 20235
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28414,11 +29952,13 @@
client_ts: 24915150978
client_dur: 62178
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24915179748
server_dur: 21871
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28451,11 +29991,13 @@
client_ts: 24921237169
client_dur: 1189989
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24921585066
server_dur: 22945
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28488,11 +30030,13 @@
client_ts: 24924342216
client_dur: 260104
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24924548644
server_dur: 20604
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28525,11 +30069,13 @@
client_ts: 24932907687
client_dur: 61496
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24932929991
server_dur: 21719
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28562,11 +30108,13 @@
client_ts: 24936245311
client_dur: 89729
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24936266250
server_dur: 20798
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28593,11 +30141,13 @@
client_ts: 24943311799
client_dur: 214359
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24943331082
server_dur: 20072
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28624,11 +30174,13 @@
client_ts: 24967426046
client_dur: 238772
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 24967447878
server_dur: 21398
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28655,11 +30207,13 @@
client_ts: 25003883218
client_dur: 324891
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25003925054
server_dur: 43609
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28686,11 +30240,13 @@
client_ts: 25025076994
client_dur: 190930
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25025118986
server_dur: 30435
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28723,11 +30279,13 @@
client_ts: 25034377812
client_dur: 271653
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25034403338
server_dur: 27240
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28754,11 +30312,13 @@
client_ts: 25036622369
client_dur: 268217
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25036650886
server_dur: 26015
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28785,11 +30345,13 @@
client_ts: 25048460597
client_dur: 56503
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25048480970
server_dur: 20752
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28816,11 +30378,13 @@
client_ts: 25056764479
client_dur: 56793
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25056784331
server_dur: 20103
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28847,11 +30411,13 @@
client_ts: 25073721612
client_dur: 55061
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25073740814
server_dur: 20447
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28878,11 +30444,13 @@
client_ts: 25110161805
client_dur: 81154
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25110196375
server_dur: 24505
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28915,11 +30483,13 @@
client_ts: 25118254173
client_dur: 89345
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25118275515
server_dur: 30281
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28946,11 +30516,13 @@
client_ts: 25126211905
client_dur: 156793
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25126252664
server_dur: 31152
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -28983,11 +30555,13 @@
client_ts: 25128591519
client_dur: 86261
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25128613762
server_dur: 22079
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29014,11 +30588,13 @@
client_ts: 25137455943
client_dur: 138560
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25137492801
server_dur: 24108
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29051,11 +30627,13 @@
client_ts: 25171098007
client_dur: 86786
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25171119721
server_dur: 24637
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29082,11 +30660,13 @@
client_ts: 25176666011
client_dur: 68313
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25176690933
server_dur: 22908
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29125,11 +30705,13 @@
client_ts: 25180015030
client_dur: 119578
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25180094139
server_dur: 21082
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29168,11 +30750,13 @@
client_ts: 25185495297
client_dur: 90240
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25185546375
server_dur: 20903
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29211,11 +30795,13 @@
client_ts: 25190169442
client_dur: 56523
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25190190035
server_dur: 19360
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29248,11 +30834,13 @@
client_ts: 25192159947
client_dur: 67609
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25192191721
server_dur: 19584
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29285,11 +30873,13 @@
client_ts: 25194833721
client_dur: 54386
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25194853231
server_dur: 19541
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29316,11 +30906,13 @@
client_ts: 25197865886
client_dur: 55290
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25197885904
server_dur: 19555
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29347,11 +30939,13 @@
client_ts: 25205510080
client_dur: 53971
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25205529822
server_dur: 19108
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29378,11 +30972,13 @@
client_ts: 25210682525
client_dur: 54427
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25210701854
server_dur: 19875
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29409,11 +31005,13 @@
client_ts: 25212773102
client_dur: 143598
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25212807375
server_dur: 20140
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29446,11 +31044,13 @@
client_ts: 25219095678
client_dur: 62570
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25219117631
server_dur: 20590
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29490,11 +31090,13 @@
client_ts: 25230101202
client_dur: 295436
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25230125660
server_dur: 202423
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29528,11 +31130,13 @@
client_ts: 25243511980
client_dur: 489650
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25243544499
server_dur: 438512
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -29590,11 +31194,13 @@
client_ts: 25244949065
client_dur: 33302645
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25244971300
server_dur: 33241468
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -29676,11 +31282,13 @@
client_ts: 25279371214
client_dur: 141670
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25279387389
server_dur: 110471
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29714,11 +31322,13 @@
client_ts: 25279567724
client_dur: 1117204
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25279592927
server_dur: 1062729
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -29776,11 +31386,13 @@
client_ts: 25280736368
client_dur: 173449
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25280756522
server_dur: 131586
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29814,11 +31426,13 @@
client_ts: 25280932813
client_dur: 166964
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25280946041
server_dur: 122533
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29852,11 +31466,13 @@
client_ts: 25281131360
client_dur: 127300
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281145719
server_dur: 98609
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29890,11 +31506,13 @@
client_ts: 25281273755
client_dur: 152610
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281315273
server_dur: 97815
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29928,11 +31546,13 @@
client_ts: 25281454812
client_dur: 120876
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281470206
server_dur: 94381
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -29966,11 +31586,13 @@
client_ts: 25281590129
client_dur: 151723
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281611020
server_dur: 119089
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30004,11 +31626,13 @@
client_ts: 25281756115
client_dur: 115379
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281769371
server_dur: 91666
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30042,11 +31666,13 @@
client_ts: 25281884499
client_dur: 116250
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25281896268
server_dur: 93727
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30080,11 +31706,13 @@
client_ts: 25282021405
client_dur: 113709
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282032972
server_dur: 91541
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30118,11 +31746,13 @@
client_ts: 25282147043
client_dur: 114363
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282159024
server_dur: 91525
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30156,11 +31786,13 @@
client_ts: 25282273296
client_dur: 113496
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282285050
server_dur: 91191
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30194,11 +31826,13 @@
client_ts: 25282398433
client_dur: 133314
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282413336
server_dur: 107722
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30232,11 +31866,13 @@
client_ts: 25282543082
client_dur: 113069
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282556151
server_dur: 89003
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30270,11 +31906,13 @@
client_ts: 25282667987
client_dur: 110166
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282679437
server_dur: 87774
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30308,11 +31946,13 @@
client_ts: 25282789771
client_dur: 113837
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282802242
server_dur: 90943
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30346,11 +31986,13 @@
client_ts: 25282914877
client_dur: 111627
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25282927602
server_dur: 88554
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30384,11 +32026,13 @@
client_ts: 25283038152
client_dur: 1992379
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_2"
server_ts: 25284866843
server_dur: 141149
server_tid: 548
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30421,11 +32065,13 @@
client_ts: 25374394471
client_dur: 2049689
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25374452290
server_dur: 56976
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30452,11 +32098,13 @@
client_ts: 25376513735
client_dur: 1298945
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25376552511
server_dur: 1229818
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -30513,11 +32161,13 @@
client_ts: 25380873939
client_dur: 94827
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25380918552
server_dur: 29189
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30544,11 +32194,13 @@
client_ts: 25382555538
client_dur: 499495
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25382942898
server_dur: 73464
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30581,11 +32233,13 @@
client_ts: 25383224119
client_dur: 475348
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25383318022
server_dur: 71453
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30618,11 +32272,13 @@
client_ts: 25384246889
client_dur: 2183818
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386357038
server_dur: 45022
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30655,11 +32311,13 @@
client_ts: 25386462748
client_dur: 100360
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386487821
server_dur: 60382
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30686,11 +32344,13 @@
client_ts: 25386597595
client_dur: 98608
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386619074
server_dur: 61591
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30717,11 +32377,13 @@
client_ts: 25386719729
client_dur: 86786
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386741757
server_dur: 49965
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30748,11 +32410,13 @@
client_ts: 25386829528
client_dur: 89755
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386849083
server_dur: 55739
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30779,11 +32443,13 @@
client_ts: 25386948016
client_dur: 83210
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25386968208
server_dur: 48426
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30810,11 +32476,13 @@
client_ts: 25387051427
client_dur: 82153
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25387070661
server_dur: 48379
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30841,11 +32509,13 @@
client_ts: 25387162195
client_dur: 82295
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25387182559
server_dur: 47439
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30872,11 +32542,13 @@
client_ts: 25387268275
client_dur: 86930
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25387287442
server_dur: 52733
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30903,11 +32575,13 @@
client_ts: 25400532103
client_dur: 58804
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25400550889
server_dur: 23976
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30934,11 +32608,13 @@
client_ts: 25400815946
client_dur: 53189
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25400833837
server_dur: 20666
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30965,11 +32641,13 @@
client_ts: 25426768392
client_dur: 59432
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25426787956
server_dur: 24158
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -30996,11 +32674,13 @@
client_ts: 25428341210
client_dur: 87943
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25428360065
server_dur: 51321
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31028,11 +32708,13 @@
client_ts: 25428597840
client_dur: 1897216
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25428614184
server_dur: 1721051
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -31108,11 +32790,13 @@
client_ts: 25430545529
client_dur: 5604165
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25430555701
server_dur: 5580114
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -31176,11 +32860,13 @@
client_ts: 25436197115
client_dur: 165295
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_2"
server_ts: 25436268304
server_dur: 84720
server_tid: 541
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31214,11 +32900,13 @@
client_ts: 25436454693
client_dur: 101710
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25436467750
server_dur: 68620
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31258,11 +32946,13 @@
client_ts: 25436579821
client_dur: 1131075
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_2"
server_ts: 25436605605
server_dur: 1092472
server_tid: 541
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31296,11 +32986,13 @@
client_ts: 25437807385
client_dur: 35309
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_2"
server_ts: 25437821151
server_dur: 13284
server_tid: 541
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31334,11 +33026,13 @@
client_ts: 25437906939
client_dur: 108314
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25437930754
server_dur: 65111
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31384,11 +33078,13 @@
client_ts: 25438037598
client_dur: 1231074
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25438067211
server_dur: 1188167
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31428,11 +33124,13 @@
client_ts: 25439303208
client_dur: 56463
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25439340083
server_dur: 11933
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31466,11 +33164,13 @@
client_ts: 25439404915
client_dur: 28249
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25439417069
server_dur: 8812
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31504,11 +33204,13 @@
client_ts: 25439834153
client_dur: 149238
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25439854310
server_dur: 63659
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31548,11 +33250,13 @@
client_ts: 25440007897
client_dur: 617701
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25440026034
server_dur: 581007
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31586,11 +33290,13 @@
client_ts: 25440650480
client_dur: 37954
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25440669130
server_dur: 11644
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31624,11 +33330,13 @@
client_ts: 25440744629
client_dur: 130786
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25440759041
server_dur: 97590
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31668,11 +33376,13 @@
client_ts: 25440898977
client_dur: 1131318
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25440930592
server_dur: 1086590
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -31718,11 +33428,13 @@
client_ts: 25442065948
client_dur: 37271
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_1"
server_ts: 25442083599
server_dur: 12197
server_tid: 557
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31756,11 +33468,13 @@
client_ts: 25442154751
client_dur: 91298
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25442164438
server_dur: 64020
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31800,11 +33514,13 @@
client_ts: 25442267983
client_dur: 969844
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25442291581
server_dur: 932552
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31844,11 +33560,13 @@
client_ts: 25443270028
client_dur: 50947
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25443301118
server_dur: 11975
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -31882,11 +33600,13 @@
client_ts: 25443369306
client_dur: 97383
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25443387190
server_dur: 61382
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -31926,11 +33646,13 @@
client_ts: 25443488838
client_dur: 710384
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25443518308
server_dur: 668611
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -31976,11 +33698,13 @@
client_ts: 25444231572
client_dur: 52027
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25444264016
server_dur: 12085
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32014,11 +33738,13 @@
client_ts: 25444518761
client_dur: 104456
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25444539803
server_dur: 63755
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32058,11 +33784,13 @@
client_ts: 25444646123
client_dur: 413789
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25444674530
server_dur: 371297
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32096,11 +33824,13 @@
client_ts: 25445078459
client_dur: 37933
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25445097539
server_dur: 11521
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32134,11 +33864,13 @@
client_ts: 25445162965
client_dur: 98821
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25445176700
server_dur: 65755
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32178,11 +33910,13 @@
client_ts: 25445283998
client_dur: 1223250
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25445325570
server_dur: 1169074
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32228,11 +33962,13 @@
client_ts: 25446539928
client_dur: 55845
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25446574718
server_dur: 12523
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32266,11 +34002,13 @@
client_ts: 25446655842
client_dur: 102770
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25446673983
server_dur: 64748
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32310,11 +34048,13 @@
client_ts: 25446780771
client_dur: 484620
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25446802921
server_dur: 383705
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32354,11 +34094,13 @@
client_ts: 25447296204
client_dur: 32106
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25447307403
server_dur: 12025
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32392,11 +34134,13 @@
client_ts: 25447383428
client_dur: 99654
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25447399103
server_dur: 64249
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32436,11 +34180,13 @@
client_ts: 25447504358
client_dur: 1088603
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25447527457
server_dur: 1052788
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -32486,11 +34232,13 @@
client_ts: 25448618882
client_dur: 65160
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25448662606
server_dur: 12297
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32524,11 +34272,13 @@
client_ts: 25448732540
client_dur: 90622
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25448745055
server_dur: 61605
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32568,11 +34318,13 @@
client_ts: 25448844089
client_dur: 1341222
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25448878397
server_dur: 1294973
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32612,11 +34364,13 @@
client_ts: 25450214696
client_dur: 132324
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25450325232
server_dur: 12314
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32650,11 +34404,13 @@
client_ts: 25450401044
client_dur: 34066
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25450413421
server_dur: 8832
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32688,11 +34444,13 @@
client_ts: 25450477847
client_dur: 108090
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25450488460
server_dur: 82190
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32732,11 +34490,13 @@
client_ts: 25450607703
client_dur: 1259215
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25450647353
server_dur: 1203353
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32776,11 +34536,13 @@
client_ts: 25451902370
client_dur: 111453
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25451993380
server_dur: 11814
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32814,11 +34576,13 @@
client_ts: 25452994340
client_dur: 109726
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25453017159
server_dur: 64867
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32858,11 +34622,13 @@
client_ts: 25453126725
client_dur: 1046699
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25453215693
server_dur: 328275
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32896,11 +34662,13 @@
client_ts: 25454206920
client_dur: 37100
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25454216327
server_dur: 14099
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -32934,11 +34702,13 @@
client_ts: 25454305892
client_dur: 105614
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25454321058
server_dur: 71322
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -32978,11 +34748,13 @@
client_ts: 25454433615
client_dur: 1182599
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25454454326
server_dur: 1148464
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -33022,11 +34794,13 @@
client_ts: 25455648152
client_dur: 89055
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25455717688
server_dur: 10552
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33060,11 +34834,13 @@
client_ts: 25455783097
client_dur: 77504
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25455796471
server_dur: 53966
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33098,11 +34874,13 @@
client_ts: 25455870323
client_dur: 183133
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25455880236
server_dur: 164382
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33136,11 +34914,13 @@
client_ts: 25456454160
client_dur: 31893
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25456468473
server_dur: 7849
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33174,11 +34954,13 @@
client_ts: 25456548523
client_dur: 98167
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25456560742
server_dur: 75793
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -33218,11 +35000,13 @@
client_ts: 25456659359
client_dur: 202533
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25456689304
server_dur: 163129
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33256,11 +35040,13 @@
client_ts: 25456872988
client_dur: 29135
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/idmap2d"
server_thread: "binder:541_3"
server_ts: 25456888657
server_dur: 6402
server_tid: 1598
+ server_pid: 541
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33293,11 +35079,13 @@
client_ts: 25494548191
client_dur: 258095
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25494622231
server_dur: 150527
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33330,11 +35118,13 @@
client_ts: 25495244473
client_dur: 325048
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25495400405
server_dur: 128258
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33367,11 +35157,13 @@
client_ts: 25504141418
client_dur: 297361
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25504207270
server_dur: 133515
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33398,11 +35190,13 @@
client_ts: 25511329109
client_dur: 678596
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25511805543
server_dur: 136659
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33435,11 +35229,13 @@
client_ts: 25517948532
client_dur: 163240
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25518009843
server_dur: 44297
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33472,11 +35268,13 @@
client_ts: 25518739183
client_dur: 3265864
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25518779467
server_dur: 374561
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33509,11 +35307,13 @@
client_ts: 25523067296
client_dur: 1153803
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25523345752
server_dur: 24699
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33546,11 +35346,13 @@
client_ts: 25529407157
client_dur: 1243348
client_tid: 641
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.health-service.cuttlefish"
server_thread: "android.hardwar"
server_ts: 25529886580
server_dur: 502254
server_tid: 431
+ server_pid: 431
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -33589,11 +35391,13 @@
client_ts: 25530820205
client_dur: 1517480
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25530877699
server_dur: 79639
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33626,11 +35430,13 @@
client_ts: 25538312793
client_dur: 177283
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25538360355
server_dur: 73011
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33663,11 +35469,13 @@
client_ts: 25538521832
client_dur: 115418
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25538557132
server_dur: 51687
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33700,11 +35508,13 @@
client_ts: 25555649759
client_dur: 401986
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25555822535
server_dur: 166864
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33737,11 +35547,13 @@
client_ts: 25562439114
client_dur: 360669
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25562601598
server_dur: 147343
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33774,11 +35586,13 @@
client_ts: 25564138751
client_dur: 227016
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25564207677
server_dur: 112964
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33811,11 +35625,13 @@
client_ts: 25564950504
client_dur: 216354
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25565018623
server_dur: 104733
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33848,11 +35664,13 @@
client_ts: 25565861693
client_dur: 154693
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25565923809
server_dur: 44929
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33886,11 +35704,13 @@
client_ts: 25566098278
client_dur: 3268381
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25566134660
server_dur: 2297021
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33923,11 +35743,13 @@
client_ts: 25578643057
client_dur: 230121
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25578709142
server_dur: 126093
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33954,11 +35776,13 @@
client_ts: 25579716604
client_dur: 216367
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25579776806
server_dur: 121078
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -33985,11 +35809,13 @@
client_ts: 25584224256
client_dur: 249444
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25584297755
server_dur: 135137
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34016,11 +35842,13 @@
client_ts: 25585086651
client_dur: 284206
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25585152822
server_dur: 129855
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34047,11 +35875,13 @@
client_ts: 25587741520
client_dur: 249445
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25587810843
server_dur: 138515
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34078,11 +35908,13 @@
client_ts: 25588452300
client_dur: 243841
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25588512250
server_dur: 145542
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34109,11 +35941,13 @@
client_ts: 25607046968
client_dur: 141541
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25607085845
server_dur: 83802
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34140,11 +35974,13 @@
client_ts: 25626771532
client_dur: 407539
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25627067466
server_dur: 61702
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34177,11 +36013,13 @@
client_ts: 25627247935
client_dur: 118074
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25627290693
server_dur: 38186
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34208,11 +36046,13 @@
client_ts: 25627461473
client_dur: 244900
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25627519338
server_dur: 149891
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34239,11 +36079,13 @@
client_ts: 25632313567
client_dur: 258248
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25632388552
server_dur: 141000
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34270,11 +36112,13 @@
client_ts: 25637260298
client_dur: 196517
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25637328529
server_dur: 88341
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34301,11 +36145,13 @@
client_ts: 25656975183
client_dur: 72577
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25656999460
server_dur: 31187
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34332,11 +36178,13 @@
client_ts: 25657158859
client_dur: 49143
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25657175302
server_dur: 18337
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34363,11 +36211,13 @@
client_ts: 25659444951
client_dur: 67480
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25659467572
server_dur: 27682
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34394,11 +36244,13 @@
client_ts: 25710903029
client_dur: 196994
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25710975935
server_dur: 69286
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34431,11 +36283,13 @@
client_ts: 25779384485
client_dur: 6404465
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25779465833
server_dur: 115210
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34468,11 +36322,13 @@
client_ts: 25785903212
client_dur: 173175
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25785969981
server_dur: 74609
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34505,11 +36361,13 @@
client_ts: 25786187023
client_dur: 71969
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25786216656
server_dur: 16443
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34542,11 +36400,13 @@
client_ts: 25788338381
client_dur: 66978
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25788365130
server_dur: 26182
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34579,11 +36439,13 @@
client_ts: 25788426430
client_dur: 44938
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25788444575
server_dur: 12371
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34616,11 +36478,13 @@
client_ts: 25794664939
client_dur: 125489
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25794702859
server_dur: 70821
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34647,11 +36511,13 @@
client_ts: 25803399428
client_dur: 125121
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25803446233
server_dur: 61602
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34684,11 +36550,13 @@
client_ts: 25805561097
client_dur: 258904
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25805581639
server_dur: 24195
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34715,11 +36583,13 @@
client_ts: 25806638177
client_dur: 383567
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25806656214
server_dur: 348438
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34746,11 +36616,13 @@
client_ts: 25807042562
client_dur: 45260
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25807056567
server_dur: 14991
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34777,11 +36649,13 @@
client_ts: 25807129635
client_dur: 626529
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25807318058
server_dur: 335442
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "D"
@@ -34832,11 +36706,13 @@
client_ts: 25807794682
client_dur: 197460
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25807808849
server_dur: 31598
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34869,11 +36745,13 @@
client_ts: 25808041191
client_dur: 152874
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808112547
server_dur: 38477
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34906,11 +36784,13 @@
client_ts: 25808300543
client_dur: 216110
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808488934
server_dur: 5816
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34943,11 +36823,13 @@
client_ts: 25808536171
client_dur: 29720
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808549475
server_dur: 6142
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -34980,11 +36862,13 @@
client_ts: 25808580143
client_dur: 23193
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808589636
server_dur: 4653
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35017,11 +36901,13 @@
client_ts: 25808614611
client_dur: 24483
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808626104
server_dur: 4244
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35054,11 +36940,13 @@
client_ts: 25808649459
client_dur: 19535
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808656163
server_dur: 4039
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35091,11 +36979,13 @@
client_ts: 25808679113
client_dur: 22778
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808688986
server_dur: 3889
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35128,11 +37018,13 @@
client_ts: 25808712146
client_dur: 21102
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808720606
server_dur: 3664
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35165,11 +37057,13 @@
client_ts: 25808743343
client_dur: 49190
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808773138
server_dur: 4014
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35202,11 +37096,13 @@
client_ts: 25808803408
client_dur: 47244
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808832840
server_dur: 3744
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35239,11 +37135,13 @@
client_ts: 25808861280
client_dur: 23141
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808871693
server_dur: 4083
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35276,11 +37174,13 @@
client_ts: 25808894304
client_dur: 22212
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808903846
server_dur: 4193
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35313,11 +37213,13 @@
client_ts: 25808927915
client_dur: 23489
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808938201
server_dur: 4441
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35350,11 +37252,13 @@
client_ts: 25808963769
client_dur: 21106
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25808972274
server_dur: 4301
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35387,11 +37291,13 @@
client_ts: 25808996827
client_dur: 22150
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809006677
server_dur: 3852
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35424,11 +37330,13 @@
client_ts: 25809029487
client_dur: 30653
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809048418
server_dur: 3146
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35461,11 +37369,13 @@
client_ts: 25809070927
client_dur: 105314
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809156958
server_dur: 4090
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35498,11 +37408,13 @@
client_ts: 25809187458
client_dur: 21199
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809196575
server_dur: 3729
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35535,11 +37447,13 @@
client_ts: 25809219112
client_dur: 20851
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809227683
server_dur: 3856
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35572,11 +37486,13 @@
client_ts: 25809250949
client_dur: 271118
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809502291
server_dur: 4522
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35609,11 +37525,13 @@
client_ts: 25809543172
client_dur: 29191
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809555589
server_dur: 4714
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35646,11 +37564,13 @@
client_ts: 25809583743
client_dur: 23569
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809592470
server_dur: 3948
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35683,11 +37603,13 @@
client_ts: 25809618388
client_dur: 38876
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809637992
server_dur: 8798
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35720,11 +37642,13 @@
client_ts: 25809757746
client_dur: 171172
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809770125
server_dur: 89637
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -35763,11 +37687,13 @@
client_ts: 25809948987
client_dur: 120274
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25809967298
server_dur: 27536
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35800,11 +37726,13 @@
client_ts: 25810106019
client_dur: 110073
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25810132739
server_dur: 5193
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35837,11 +37765,13 @@
client_ts: 25810239347
client_dur: 148792
client_tid: 641
+ client_pid: 641
server_process: "/apex/com.android.hardware.vibrator/bin/hw/android.hardware.vibrator-service.example"
server_thread: "android.hardwar"
server_ts: 25810364813
server_dur: 3865
server_tid: 481
+ server_pid: 481
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35874,11 +37804,13 @@
client_ts: 25810497904
client_dur: 126359
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25810531969
server_dur: 73542
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35905,11 +37837,13 @@
client_ts: 25810646589
client_dur: 91503
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25810669576
server_dur: 52662
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35936,11 +37870,13 @@
client_ts: 25810948647
client_dur: 113503
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25810980358
server_dur: 64314
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35967,11 +37903,13 @@
client_ts: 25811246975
client_dur: 366342
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25811269261
server_dur: 324989
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -35998,11 +37936,13 @@
client_ts: 25811641918
client_dur: 83600
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25811659549
server_dur: 47010
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -36035,11 +37975,13 @@
client_ts: 25811827364
client_dur: 105911
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25811855742
server_dur: 60946
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36066,11 +38008,13 @@
client_ts: 25814468030
client_dur: 128573
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25814502303
server_dur: 75807
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36097,11 +38041,13 @@
client_ts: 25824457100
client_dur: 243653
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25824484172
server_dur: 202360
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -36134,11 +38080,13 @@
client_ts: 25826929585
client_dur: 134380
client_tid: 1618
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25826953314
server_dur: 57893
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36171,11 +38119,13 @@
client_ts: 25827095345
client_dur: 59551
client_tid: 1618
+ client_pid: 641
server_process: "/apex/com.android.os.statsd/bin/statsd"
server_thread: "binder:415_3"
server_ts: 25827112546
server_dur: 28045
server_tid: 422
+ server_pid: 415
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36208,11 +38158,13 @@
client_ts: 25828178041
client_dur: 75912
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25828192051
server_dur: 26713
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36245,11 +38197,13 @@
client_ts: 25828289955
client_dur: 48095
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25828303964
server_dur: 23818
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36282,11 +38236,13 @@
client_ts: 25833585425
client_dur: 213739
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25833674904
server_dur: 59579
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36319,11 +38275,13 @@
client_ts: 25834911169
client_dur: 66864
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25834940005
server_dur: 22905
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36356,11 +38314,13 @@
client_ts: 25835009103
client_dur: 41035
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25835029405
server_dur: 9917
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36393,11 +38353,13 @@
client_ts: 25835084036
client_dur: 39828
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25835102285
server_dur: 11104
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36430,11 +38392,13 @@
client_ts: 25835225817
client_dur: 39668
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25835244300
server_dur: 10401
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36467,11 +38431,13 @@
client_ts: 25835491757
client_dur: 94706
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25835523199
server_dur: 50044
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36504,11 +38470,13 @@
client_ts: 25838116144
client_dur: 101371
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25838145038
server_dur: 57786
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36535,11 +38503,13 @@
client_ts: 25854689410
client_dur: 72379
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25854716713
server_dur: 29735
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36572,11 +38542,13 @@
client_ts: 25854776152
client_dur: 84994
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25854792697
server_dur: 55053
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36609,11 +38581,13 @@
client_ts: 25856165265
client_dur: 41372
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25856179451
server_dur: 11065
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36646,11 +38620,13 @@
client_ts: 25859386674
client_dur: 62804
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25859406671
server_dur: 23198
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36677,11 +38653,13 @@
client_ts: 25860119896
client_dur: 89982
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25860140536
server_dur: 54911
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36708,11 +38686,13 @@
client_ts: 25861940989
client_dur: 112198
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25861960190
server_dur: 69698
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36745,11 +38725,13 @@
client_ts: 25862411224
client_dur: 72918
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25862426577
server_dur: 43364
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36782,11 +38764,13 @@
client_ts: 25862572364
client_dur: 43335
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25862583679
server_dur: 21214
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36819,11 +38803,13 @@
client_ts: 25862644016
client_dur: 35084
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25862653442
server_dur: 16522
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36856,11 +38842,13 @@
client_ts: 25862703240
client_dur: 43193
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25862712076
server_dur: 25328
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36893,11 +38881,13 @@
client_ts: 25863309305
client_dur: 56073
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25863321886
server_dur: 30965
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36930,11 +38920,13 @@
client_ts: 25863414967
client_dur: 48999
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25863426312
server_dur: 27403
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -36967,11 +38959,13 @@
client_ts: 25864046858
client_dur: 61808
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864062070
server_dur: 32956
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37004,11 +38998,13 @@
client_ts: 25864143436
client_dur: 51337
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864153386
server_dur: 30980
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37041,11 +39037,13 @@
client_ts: 25864216828
client_dur: 37990
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864226073
server_dur: 19004
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37078,11 +39076,13 @@
client_ts: 25864279759
client_dur: 37961
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864288819
server_dur: 19366
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37115,11 +39115,13 @@
client_ts: 25864345023
client_dur: 59115
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864354391
server_dur: 17778
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37152,11 +39154,13 @@
client_ts: 25864449726
client_dur: 51629
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864460717
server_dur: 26406
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37189,11 +39193,13 @@
client_ts: 25864530175
client_dur: 40230
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864539824
server_dur: 19580
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37226,11 +39232,13 @@
client_ts: 25864603443
client_dur: 64958
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864612742
server_dur: 44603
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37263,11 +39271,13 @@
client_ts: 25864687472
client_dur: 38679
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864696221
server_dur: 19520
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37300,11 +39310,13 @@
client_ts: 25864748376
client_dur: 41144
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864757238
server_dur: 21548
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37337,11 +39349,13 @@
client_ts: 25864814870
client_dur: 37926
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864824299
server_dur: 17299
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37374,11 +39388,13 @@
client_ts: 25864878443
client_dur: 41432
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864887850
server_dur: 21263
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37411,11 +39427,13 @@
client_ts: 25864943354
client_dur: 46446
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25864953070
server_dur: 26259
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37448,11 +39466,13 @@
client_ts: 25865010220
client_dur: 42768
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25865018943
server_dur: 22857
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37485,11 +39505,13 @@
client_ts: 25865078882
client_dur: 42845
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25865088374
server_dur: 22790
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37522,11 +39544,13 @@
client_ts: 25865143746
client_dur: 43541
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25865152587
server_dur: 24329
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37559,11 +39583,13 @@
client_ts: 25865208871
client_dur: 37989
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25865217954
server_dur: 18361
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37596,11 +39622,13 @@
client_ts: 25865269510
client_dur: 180816
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25865278803
server_dur: 44675
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -37639,11 +39667,13 @@
client_ts: 25865509380
client_dur: 1204393
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25866655580
server_dur: 37015
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37676,11 +39706,13 @@
client_ts: 25866765533
client_dur: 52077
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25866778140
server_dur: 25314
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37713,11 +39745,13 @@
client_ts: 25866847306
client_dur: 36125
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25866856611
server_dur: 17873
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37750,11 +39784,13 @@
client_ts: 25866907704
client_dur: 31809
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25866916674
server_dur: 14121
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37787,11 +39823,13 @@
client_ts: 25866963934
client_dur: 42576
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25866972903
server_dur: 18950
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37824,11 +39862,13 @@
client_ts: 25867031282
client_dur: 35871
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867040421
server_dur: 17984
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37861,11 +39901,13 @@
client_ts: 25867092641
client_dur: 34611
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867101335
server_dur: 17329
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37898,11 +39940,13 @@
client_ts: 25867155069
client_dur: 43809
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867163773
server_dur: 26243
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37935,11 +39979,13 @@
client_ts: 25867229729
client_dur: 32631
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867238857
server_dur: 14033
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -37972,11 +40018,13 @@
client_ts: 25867283252
client_dur: 40731
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867292366
server_dur: 21993
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38009,11 +40057,13 @@
client_ts: 25867343753
client_dur: 34724
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867353084
server_dur: 16650
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38046,11 +40096,13 @@
client_ts: 25867401523
client_dur: 50745
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867410223
server_dur: 32741
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38083,11 +40135,13 @@
client_ts: 25867475251
client_dur: 46815
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867485001
server_dur: 26611
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38120,11 +40174,13 @@
client_ts: 25867547843
client_dur: 36696
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867557467
server_dur: 18125
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38157,11 +40213,13 @@
client_ts: 25867605981
client_dur: 37415
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867614819
server_dur: 19663
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38194,11 +40252,13 @@
client_ts: 25867663292
client_dur: 35879
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867672122
server_dur: 17373
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38231,11 +40291,13 @@
client_ts: 25867751966
client_dur: 40848
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867761875
server_dur: 19072
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38269,11 +40331,13 @@
client_ts: 25867845030
client_dur: 180504
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25867860282
server_dur: 154468
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "R"
@@ -38318,11 +40382,13 @@
client_ts: 25872260669
client_dur: 83399
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25872278450
server_dur: 47096
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38355,11 +40421,13 @@
client_ts: 25885439966
client_dur: 1549817
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25886054638
server_dur: 24145
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38392,11 +40460,13 @@
client_ts: 25885831452
client_dur: 1452935
client_tid: 1623
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25886206332
server_dur: 305512
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38429,11 +40499,13 @@
client_ts: 25887309449
client_dur: 115655
client_tid: 1623
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25887326271
server_dur: 18512
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38460,11 +40532,13 @@
client_ts: 25887452954
client_dur: 283867
client_tid: 1623
+ client_pid: 641
server_process: "/vendor/bin/hw/android.hardware.input.processor-service.example"
server_thread: "android.hardwar"
server_ts: 25887467255
server_dur: 38055
server_tid: 447
+ server_pid: 447
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38491,11 +40565,13 @@
client_ts: 25892252212
client_dur: 2274293
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25894481596
server_dur: 27702
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38528,11 +40604,13 @@
client_ts: 25921269728
client_dur: 400263
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25921599983
server_dur: 52448
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38565,11 +40643,13 @@
client_ts: 25921747025
client_dur: 209278
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25921878602
server_dur: 66173
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38602,11 +40682,13 @@
client_ts: 25922308759
client_dur: 230930
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25922366800
server_dur: 58914
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38639,11 +40721,13 @@
client_ts: 25926152660
client_dur: 106426
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25926184604
server_dur: 59798
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38676,11 +40760,13 @@
client_ts: 25935520343
client_dur: 106898
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25935553945
server_dur: 59988
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38713,11 +40799,13 @@
client_ts: 25936096478
client_dur: 38431
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25936114070
server_dur: 13878
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38750,11 +40838,13 @@
client_ts: 25936154115
client_dur: 304852
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25936427202
server_dur: 13388
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38787,11 +40877,13 @@
client_ts: 25939273894
client_dur: 44659
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25939290557
server_dur: 19990
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38824,11 +40916,13 @@
client_ts: 25939575552
client_dur: 81712
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25939602201
server_dur: 44933
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38861,11 +40955,13 @@
client_ts: 25944988176
client_dur: 71994
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25945018540
server_dur: 25217
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38898,11 +40994,13 @@
client_ts: 25948938076
client_dur: 82091
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25948980882
server_dur: 27091
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38935,11 +41033,13 @@
client_ts: 25951851055
client_dur: 445134
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_3"
server_ts: 25951902501
server_dur: 183967
server_tid: 1575
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -38972,11 +41072,13 @@
client_ts: 25952609949
client_dur: 44997
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_2"
server_ts: 25952623297
server_dur: 18258
server_tid: 522
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39003,11 +41105,13 @@
client_ts: 25954386897
client_dur: 107947
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/surfaceflinger"
server_thread: "binder:496_3"
server_ts: 25954404617
server_dur: 18018
server_tid: 1575
+ server_pid: 496
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39040,11 +41144,13 @@
client_ts: 25955417145
client_dur: 248980
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25955442657
server_dur: 23870
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39078,11 +41184,13 @@
client_ts: 25955702503
client_dur: 1028567
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25955716400
server_dur: 923564
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39109,11 +41217,13 @@
client_ts: 25957071212
client_dur: 244964
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25957109467
server_dur: 68828
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39146,11 +41256,13 @@
client_ts: 25957345364
client_dur: 86035
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25957361868
server_dur: 39522
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39183,11 +41295,13 @@
client_ts: 25957477722
client_dur: 38342
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25957490997
server_dur: 14607
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39220,11 +41334,13 @@
client_ts: 25957561902
client_dur: 115285
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/vold"
server_thread: "binder:255_2"
server_ts: 25957584457
server_dur: 69879
server_tid: 255
+ server_pid: 255
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39257,11 +41373,13 @@
client_ts: 25957701552
client_dur: 31673
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/vold"
server_thread: "binder:255_2"
server_ts: 25957712540
server_dur: 11337
server_tid: 255
+ server_pid: 255
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39294,11 +41412,13 @@
client_ts: 25957917714
client_dur: 81359
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25957965656
server_dur: 19953
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39331,11 +41451,13 @@
client_ts: 25958179475
client_dur: 57952
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25958197817
server_dur: 12909
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39368,11 +41490,13 @@
client_ts: 25958259037
client_dur: 37816
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25958272198
server_dur: 12201
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39406,11 +41530,13 @@
client_ts: 25958323889
client_dur: 934386
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25958335597
server_dur: 816593
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39438,11 +41564,13 @@
client_ts: 25959279820
client_dur: 780373
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/installd"
server_thread: "binder:548_1"
server_ts: 25959289960
server_dur: 697719
server_tid: 565
+ server_pid: 548
thread_states {
thread_state_type: "binder_reply"
thread_state: "R+"
@@ -39475,11 +41603,13 @@
client_ts: 25960212392
client_dur: 138312
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25960246748
server_dur: 59922
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39512,11 +41642,13 @@
client_ts: 25960703658
client_dur: 185272
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25960733334
server_dur: 55452
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
@@ -39549,11 +41681,13 @@
client_ts: 25964272455
client_dur: 108953
client_tid: 641
+ client_pid: 641
server_process: "/system/bin/servicemanager"
server_thread: "servicemanager"
server_ts: 25964304630
server_dur: 66130
server_tid: 243
+ server_pid: 243
thread_states {
thread_state_type: "binder_reply"
thread_state: "Running"
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
index aa63194..c4e2b2b 100644
--- a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.out
@@ -5,6 +5,7 @@
process {
name: "com.android.systemui"
uid: 10001
+ pid: 1000
}
ts: 2000000
dur: 15000000
@@ -22,6 +23,7 @@
process {
name: "com.google.android.apps.nexuslauncher"
uid: 10002
+ pid: 2000
}
ts: 2000000
dur: 15000000
@@ -39,9 +41,10 @@
process {
name: "com.google.android.third.process"
uid: 10003
+ pid: 3000
}
ts: 2000000
- dur: 150000000
+ dur: 180000000
blocking_calls {
name: "Contending for pthread mutex"
cnt: 1
@@ -57,6 +60,20 @@
min_dur_ms: 10
}
blocking_calls {
+ name: "ExpNotRow#onMeasure(BigTextStyle)"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
+ name: "ExpNotRow#onMeasure(MessagingStyle)"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
name: "ImageDecoder#decodeBitmap"
cnt: 1
total_dur_ms: 10
@@ -85,6 +102,13 @@
min_dur_ms: 10
}
blocking_calls {
+ name: "NotificationStackScrollLayout#onMeasure"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
name: "SuspendThreadByThreadId <...>"
cnt: 1
total_dur_ms: 10
@@ -147,6 +171,7 @@
process {
name: "com.android.systemui"
uid: 10001
+ pid: 1000
}
ts: 20000000
dur: 10000000
@@ -164,6 +189,7 @@
process {
name: "com.android.systemui"
uid: 10001
+ pid: 1000
}
ts: 22000000
dur: 10000000
@@ -175,4 +201,29 @@
min_dur_ms: 10
}
}
+ cuj {
+ id: 6
+ name: "WITH_NAMED_BINDER_TRANSACTION"
+ process {
+ name: "com.android.systemui"
+ uid: 10001
+ pid: 1000
+ }
+ ts: 40000000
+ dur: 10000000
+ blocking_calls {
+ name: "AIDL::java::IWindowManager::hasNavigationBar::server"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ blocking_calls {
+ name: "binder transaction"
+ cnt: 1
+ total_dur_ms: 10
+ max_dur_ms: 10
+ min_dur_ms: 10
+ }
+ }
}
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
index 7573b76..7ec8cb9 100755
--- a/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_cuj_metric.py
@@ -26,11 +26,13 @@
# List of blocking calls
blocking_call_names = [
'monitor contention with something else', 'SuspendThreadByThreadId 123',
- 'LoadApkAssetsFd 123', 'binder transaction',
- 'inflate', 'Lock contention on thread list lock (owner tid: 1665)',
+ 'LoadApkAssetsFd 123', 'binder transaction', 'inflate',
+ 'Lock contention on thread list lock (owner tid: 1665)',
'CancellableContinuationImpl#123', 'relayoutWindow*', 'measure', 'layout',
'configChanged', 'Contending for pthread mutex',
'ImageDecoder#decodeBitmap', 'ImageDecoder#decodeDrawable',
+ 'NotificationStackScrollLayout#onMeasure',
+ 'ExpNotRow#onMeasure(MessagingStyle)', 'ExpNotRow#onMeasure(BigTextStyle)',
'Should not be in the metric'
]
@@ -45,6 +47,19 @@
trace.add_atrace_async_end(ts=ts_end, tid=pid, pid=pid, buf=buf)
+def add_binder_transaction(trace, tx_pid, rx_pid, start_ts, end_ts):
+ trace.add_binder_transaction(
+ transaction_id=tx_pid,
+ ts_start=start_ts,
+ ts_end=end_ts,
+ tid=tx_pid,
+ pid=tx_pid,
+ reply_id=rx_pid,
+ reply_ts_start=start_ts,
+ reply_ts_end=end_ts,
+ reply_tid=rx_pid,
+ reply_pid=rx_pid)
+
# Adds a set of predefined blocking calls in places near the cuj boundaries to
# verify that only the portion inside the cuj is counted in the metric.
def add_cuj_with_blocking_calls(trace, cuj_name, pid):
@@ -148,6 +163,31 @@
pid=pid)
+def add_cuj_with_named_binder_transaction(pid, rx_pid):
+ cuj_begin = 40_000_000
+ cuj_end = 50_000_000
+
+ add_async_trace(
+ trace,
+ ts=cuj_begin,
+ ts_end=cuj_end,
+ buf="L<WITH_NAMED_BINDER_TRANSACTION>",
+ pid=pid)
+
+ add_binder_transaction(
+ trace, tx_pid=pid, rx_pid=rx_pid, start_ts=cuj_begin, end_ts=cuj_end)
+
+ # Slice inside the binder reply, to give a name to the binder call.
+ # The named binder slice introduced should be the length of the entire
+ # transaction even if the "name" slice only covers some of the binder reply.
+ add_main_thread_atrace(
+ trace,
+ ts=cuj_begin + 1_000_000,
+ ts_end=cuj_end - 1_000_000,
+ buf="AIDL::java::IWindowManager::hasNavigationBar::server",
+ pid=rx_pid)
+
+
def add_process(trace, package_name, uid, pid):
trace.add_package_list(ts=0, name=package_name, uid=uid, version_code=1)
trace.add_process(
@@ -180,6 +220,8 @@
add_overlapping_cujs_with_blocking_calls(trace, pid=SYSUI_PID,
start_ts=20_000_000)
+add_cuj_with_named_binder_transaction(pid=SYSUI_PID, rx_pid=LAUNCHER_PID)
+
# Note that J<*> events are not tested here.
# See test_android_blocking_calls_on_jank_cujs.
sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
index 68b3c9f..89d7c63 100644
--- a/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
+++ b/test/trace_processor/diff_tests/android/android_blocking_calls_on_jank_cuj_metric.out
@@ -15,6 +15,7 @@
apk_version_code: 1
debuggable: false
}
+ pid: 1000
}
ts: 0
dur: 115000000
@@ -42,6 +43,7 @@
apk_version_code: 1
debuggable: false
}
+ pid: 1000
}
ts: 0
dur: 802000000
diff --git a/test/trace_processor/diff_tests/android/android_monitor_contention.out b/test/trace_processor/diff_tests/android/android_monitor_contention.out
index 02a6412..8940be9 100644
--- a/test/trace_processor/diff_tests/android/android_monitor_contention.out
+++ b/test/trace_processor/diff_tests/android/android_monitor_contention.out
@@ -12,7 +12,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "PackageManager"
+ blocked_thread_tid: 693
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -39,7 +42,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -66,7 +72,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -93,7 +102,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -120,7 +132,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1737055785896
@@ -154,7 +169,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "StorageUserConn"
+ blocked_thread_tid: 1759
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -171,7 +189,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "StorageUserConn"
+ blocked_thread_tid: 1759
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -208,7 +229,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -225,7 +249,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -242,7 +269,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -259,7 +289,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -276,7 +309,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -293,7 +329,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -310,7 +349,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -327,7 +369,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -344,7 +389,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -361,7 +409,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -378,7 +429,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -395,7 +449,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -412,7 +469,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -429,7 +489,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -446,7 +509,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -463,7 +529,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -480,7 +549,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -497,7 +569,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -514,7 +589,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -531,7 +609,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -548,7 +629,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -565,7 +649,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -582,7 +669,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -599,7 +689,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -616,7 +709,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -633,7 +729,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -650,7 +749,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -667,7 +769,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -684,7 +789,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -701,7 +809,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -718,7 +829,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -735,7 +849,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -752,7 +869,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -769,7 +889,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -786,7 +909,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -803,7 +929,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -820,7 +949,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -837,7 +969,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -854,7 +989,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -871,7 +1009,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -888,7 +1029,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -905,7 +1049,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -922,7 +1069,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -939,7 +1089,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -956,7 +1109,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -973,7 +1129,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -990,7 +1149,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1007,7 +1169,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1024,7 +1189,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1041,14 +1209,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.display"
+ blocked_thread_tid: 663
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "R"
- thread_state_dur: 122815
- thread_state_count: 1
- }
}
node {
node_id: 914
@@ -1063,7 +1229,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1090,7 +1259,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1117,7 +1289,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1144,7 +1319,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "binder:642_11"
+ blocked_thread_tid: 2505
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1182,13 +1360,16 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
thread_state: "R+"
- thread_state_dur: 7143444
- thread_state_count: 4
+ thread_state_dur: 7127675
+ thread_state_count: 3
}
thread_states {
thread_state: "Running"
@@ -1210,13 +1391,16 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
thread_state: "R+"
- thread_state_dur: 7143444
- thread_state_count: 4
+ thread_state_dur: 7127675
+ thread_state_count: 3
}
thread_states {
thread_state: "Running"
@@ -1237,24 +1421,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_12"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
- thread_states {
- thread_state: "R+"
- thread_state_dur: 7100855
- thread_state_count: 1
- }
- thread_states {
- thread_state: "Running"
- thread_state_dur: 40711
- thread_state_count: 1
- }
- thread_states {
- thread_state: "S"
- thread_state_dur: 2814860
- thread_state_count: 1
- }
}
node {
node_id: 983
@@ -1269,14 +1441,12 @@
waiter_count: 2
blocking_thread_name: "binder:642_12"
blocked_thread_name: "binder:642_2"
+ blocked_thread_tid: 658
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 3054413
- thread_state_count: 1
- }
}
node {
node_id: 994
@@ -1291,7 +1461,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1308,7 +1481,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1325,7 +1501,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1342,7 +1521,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1359,7 +1541,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1376,7 +1561,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1393,7 +1581,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1410,7 +1601,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1427,7 +1621,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1444,7 +1641,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1461,7 +1661,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1478,7 +1681,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1495,7 +1701,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1512,7 +1721,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1529,7 +1741,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1546,7 +1761,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1563,7 +1781,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1580,7 +1801,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_12"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2720
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -1597,7 +1821,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1624,7 +1851,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1651,7 +1881,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1678,7 +1911,10 @@
waiter_count: 0
blocking_thread_name: "batterystats-handler"
blocked_thread_name: "binder:642_11"
+ blocked_thread_tid: 2505
+ blocking_thread_tid: 676
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1710,7 +1946,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1737,7 +1976,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1764,7 +2006,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1791,7 +2036,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1818,7 +2066,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1845,7 +2096,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1872,7 +2126,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1899,7 +2156,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1926,7 +2186,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1953,7 +2216,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -1980,7 +2246,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2007,7 +2276,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2034,7 +2306,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2061,7 +2336,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2088,7 +2366,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2115,7 +2396,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2142,7 +2426,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2169,7 +2456,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_11"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 2505
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2196,7 +2486,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2223,7 +2516,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2245,7 +2541,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "binder:642_2"
+ blocked_thread_tid: 658
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
binder_reply_ts: 1737145697570
@@ -2274,7 +2573,10 @@
waiter_count: 0
blocking_thread_name: "Thread-45"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 3486
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -2301,7 +2603,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2328,7 +2633,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2355,7 +2663,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2382,7 +2693,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2410,7 +2724,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "binder:642_14"
+ blocked_thread_tid: 3485
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -2442,7 +2759,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -2479,7 +2799,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2496,7 +2819,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2513,7 +2839,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2530,7 +2859,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "binder:642_11"
+ blocked_thread_tid: 2505
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1737164232343
@@ -2549,7 +2881,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2566,7 +2901,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2583,7 +2921,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2600,7 +2941,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2617,7 +2961,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2634,7 +2981,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2651,7 +3001,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2668,7 +3021,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2685,7 +3041,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2702,7 +3061,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2719,7 +3081,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2736,7 +3101,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2753,7 +3121,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2770,7 +3141,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2787,7 +3161,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2804,7 +3181,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2821,7 +3201,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2838,7 +3221,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -2855,7 +3241,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2882,7 +3271,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2909,7 +3301,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2936,7 +3331,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2963,7 +3361,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -2990,7 +3391,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3017,7 +3421,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3044,7 +3451,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3071,7 +3481,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3098,7 +3511,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3125,7 +3541,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3152,7 +3571,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3179,7 +3601,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -3206,7 +3631,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3233,7 +3661,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3260,7 +3691,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3287,7 +3721,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "binder:642_11"
+ blocked_thread_tid: 2505
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1737183173575
@@ -3316,7 +3753,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "binder:642_14"
+ blocked_thread_tid: 3485
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3343,7 +3783,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3370,7 +3813,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3397,7 +3843,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3424,7 +3873,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3451,7 +3903,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3478,7 +3933,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3505,7 +3963,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3532,7 +3993,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3559,7 +4023,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3586,7 +4053,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3613,7 +4083,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3640,7 +4113,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3667,7 +4143,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3694,7 +4173,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3721,7 +4203,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3748,7 +4233,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3775,7 +4263,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3802,7 +4293,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_14"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 3485
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3829,7 +4323,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3856,7 +4353,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3883,7 +4383,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3910,7 +4413,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -3947,7 +4453,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -3964,14 +4473,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_2"
+ blocked_thread_tid: 658
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 3567249
- thread_state_count: 1
- }
}
node {
node_id: 1778
@@ -3986,14 +4493,12 @@
waiter_count: 2
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_A"
+ blocked_thread_tid: 1675
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 8523903
- thread_state_count: 1
- }
}
node {
node_id: 1786
@@ -4008,14 +4513,12 @@
waiter_count: 3
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_8"
+ blocked_thread_tid: 1548
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 9607080
- thread_state_count: 1
- }
}
node {
node_id: 1819
@@ -4030,7 +4533,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_F"
blocked_thread_name: "binder:642_1"
+ blocked_thread_tid: 657
+ blocking_thread_tid: 2029
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4062,14 +4568,12 @@
waiter_count: 4
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_13"
+ blocked_thread_tid: 2721
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 10733654
- thread_state_count: 1
- }
}
node {
node_id: 1825
@@ -4084,7 +4588,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4101,7 +4608,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4118,7 +4628,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4135,7 +4648,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4152,7 +4668,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4169,7 +4688,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4186,7 +4708,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4203,7 +4728,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4220,7 +4748,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4237,7 +4768,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4254,7 +4788,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4271,7 +4808,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4288,7 +4828,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4305,7 +4848,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4322,7 +4868,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4339,7 +4888,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -4356,7 +4908,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4383,7 +4938,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4405,7 +4963,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4432,7 +4993,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_2"
+ blocked_thread_tid: 658
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4459,7 +5023,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_A"
+ blocked_thread_tid: 1675
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4481,7 +5048,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_8"
+ blocked_thread_tid: 1548
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4508,7 +5078,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_13"
+ blocked_thread_tid: 2721
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4530,7 +5103,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_13"
blocked_thread_name: "binder:642_12"
+ blocked_thread_tid: 2720
+ blocking_thread_tid: 2721
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1737229638872
@@ -4564,7 +5140,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_13"
blocked_thread_name: "binder:642_8"
+ blocked_thread_tid: 1548
+ blocking_thread_tid: 2721
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4591,29 +5170,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_13"
blocked_thread_name: "binder:642_A"
+ blocked_thread_tid: 1675
+ blocking_thread_tid: 2721
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "R"
- thread_state_dur: 59191
- thread_state_count: 1
- }
- thread_states {
- thread_state: "R+"
- thread_state_dur: 223496
- thread_state_count: 1
- }
- thread_states {
- thread_state: "Running"
- thread_state_dur: 36279
- thread_state_count: 2
- }
- thread_states {
- thread_state: "S"
- thread_state_dur: 583889
- thread_state_count: 1
- }
}
node {
node_id: 2289
@@ -4628,14 +5190,12 @@
waiter_count: 2
blocking_thread_name: "binder:642_13"
blocked_thread_name: "binder:642_E"
+ blocked_thread_tid: 1934
+ blocking_thread_tid: 2721
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "S"
- thread_state_dur: 1514707
- thread_state_count: 1
- }
}
node {
node_id: 2352
@@ -4650,7 +5210,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_8"
+ blocked_thread_tid: 1548
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4672,7 +5235,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_A"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1675
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4699,7 +5265,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "binder:642_E"
+ blocked_thread_tid: 1934
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4721,7 +5290,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -4743,7 +5315,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4770,7 +5345,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4797,7 +5375,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4824,7 +5405,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4851,7 +5435,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4878,7 +5465,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4905,7 +5495,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4932,7 +5525,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4959,7 +5555,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -4986,7 +5585,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5013,7 +5615,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5040,7 +5645,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5067,7 +5675,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5094,7 +5705,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5121,7 +5735,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5148,7 +5765,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5175,7 +5795,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5212,7 +5835,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
}
@@ -5229,7 +5855,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 672
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5256,7 +5885,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5283,7 +5915,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "tworkPolicy.uid"
+ blocked_thread_tid: 1193
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5310,7 +5945,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5337,7 +5975,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5359,7 +6000,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5386,7 +6030,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5413,7 +6060,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5440,7 +6090,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5467,7 +6120,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5494,7 +6150,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5521,7 +6180,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5548,7 +6210,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5575,7 +6240,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5602,7 +6270,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5629,7 +6300,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5656,7 +6330,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5683,7 +6360,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5710,7 +6390,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5737,7 +6420,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5764,7 +6450,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5791,7 +6480,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5818,7 +6510,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "PackageManager"
+ blocked_thread_tid: 693
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5845,7 +6540,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "tworkPolicy.uid"
+ blocked_thread_tid: 1193
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5872,7 +6570,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5899,7 +6600,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5926,7 +6630,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 670
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -5953,7 +6660,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -5980,7 +6690,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6007,7 +6720,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "android.fg"
+ blocked_thread_tid: 660
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6034,7 +6750,10 @@
waiter_count: 0
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -6076,31 +6795,14 @@
waiter_count: 1
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "binder:642_1"
+ blocked_thread_tid: 657
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1739927686578
binder_reply_tid: 657
- thread_states {
- thread_state: "R"
- thread_state_dur: 819671
- thread_state_count: 3
- }
- thread_states {
- thread_state: "R+"
- thread_state_dur: 600233
- thread_state_count: 2
- }
- thread_states {
- thread_state: "Running"
- thread_state_dur: 890957
- thread_state_count: 5
- }
- thread_states {
- thread_state: "S"
- thread_state_dur: 623113
- thread_state_count: 2
- }
}
node {
node_id: 13948
@@ -6115,7 +6817,10 @@
waiter_count: 2
blocking_thread_name: "StorageManagerService"
blocked_thread_name: "binder:642_E"
+ blocked_thread_tid: 1934
+ blocking_thread_tid: 743
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1739931677940
@@ -6134,19 +6839,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_E"
blocked_thread_name: "StorageManagerS"
+ blocked_thread_tid: 743
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "Running"
- thread_state_dur: 480064
- thread_state_count: 1
- }
- thread_states {
- thread_state: "S"
- thread_state_dur: 1685676
- thread_state_count: 1
- }
}
node {
node_id: 13979
@@ -6161,7 +6859,10 @@
waiter_count: 2
blocking_thread_name: "binder:642_E"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
}
@@ -6178,7 +6879,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6200,7 +6904,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6227,7 +6934,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "PackageManager"
+ blocked_thread_tid: 693
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6254,7 +6964,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6281,7 +6994,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6308,7 +7024,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_1"
+ blocked_thread_tid: 657
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6335,7 +7054,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6352,7 +7074,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6369,7 +7094,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6386,21 +7114,14 @@
waiter_count: 1
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_13"
+ blocked_thread_tid: 2721
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1739956996641
binder_reply_tid: 2721
- thread_states {
- thread_state: "R"
- thread_state_dur: 70662
- thread_state_count: 1
- }
- thread_states {
- thread_state: "Running"
- thread_state_dur: 92726
- thread_state_count: 1
- }
}
node {
node_id: 14154
@@ -6415,7 +7136,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6432,7 +7156,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6449,7 +7176,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6466,7 +7196,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6483,7 +7216,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6500,7 +7236,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6517,7 +7256,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6534,7 +7276,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6551,7 +7296,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6568,7 +7316,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6585,7 +7336,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -6617,7 +7371,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6634,7 +7391,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "batterystats-ha"
+ blocked_thread_tid: 676
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6651,7 +7411,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6668,7 +7431,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "PowerManagerSer"
+ blocked_thread_tid: 687
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -6685,7 +7451,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3519
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6712,7 +7481,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "binder:642_E"
+ blocked_thread_tid: 1934
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
binder_reply_ts: 1739981897430
@@ -6741,29 +7513,12 @@
waiter_count: 1
blocking_thread_name: "main"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
- thread_states {
- thread_state: "R"
- thread_state_dur: 105075
- thread_state_count: 1
- }
- thread_states {
- thread_state: "R+"
- thread_state_dur: 69326
- thread_state_count: 1
- }
- thread_states {
- thread_state: "Running"
- thread_state_dur: 722485
- thread_state_count: 3
- }
- thread_states {
- thread_state: "S"
- thread_state_dur: 267110
- thread_state_count: 2
- }
}
node {
node_id: 14623
@@ -6778,14 +7533,12 @@
waiter_count: 2
blocking_thread_name: "main"
blocked_thread_name: "StorageManagerS"
+ blocked_thread_tid: 743
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
- thread_states {
- thread_state: "S"
- thread_state_dur: 1129545
- thread_state_count: 1
- }
}
node {
node_id: 14626
@@ -6800,7 +7553,10 @@
waiter_count: 3
blocking_thread_name: "main"
blocked_thread_name: "binder:642_13"
+ blocked_thread_tid: 2721
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
binder_reply_ts: 1739982622780
@@ -6819,14 +7575,12 @@
waiter_count: 3
blocking_thread_name: "binder:642_E"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
- thread_states {
- thread_state: "Running"
- thread_state_dur: 388360
- thread_state_count: 1
- }
}
node {
node_id: 14730
@@ -6841,7 +7595,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3487
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -6868,7 +7625,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3519
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6895,7 +7655,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_13"
blocked_thread_name: "system_server"
+ blocked_thread_tid: 642
+ blocking_thread_tid: 2721
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -6922,7 +7685,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3487
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -6949,7 +7715,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3519
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -6976,7 +7745,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "binder:642_1"
+ blocked_thread_tid: 657
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
binder_reply_ts: 1740012085111
@@ -7010,7 +7782,10 @@
waiter_count: 0
blocking_thread_name: "android.bg"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3519
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7037,7 +7812,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "binder:642_13"
+ blocked_thread_tid: 2721
+ blocking_thread_tid: 642
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: true
binder_reply_ts: 1740024094690
@@ -7066,7 +7844,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "StorageManagerS"
+ blocked_thread_tid: 743
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7098,7 +7879,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -7115,14 +7899,12 @@
waiter_count: 1
blocking_thread_name: "binder:642_E"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
- thread_states {
- thread_state: "R+"
- thread_state_dur: 473837
- thread_state_count: 1
- }
}
node {
node_id: 15361
@@ -7137,7 +7919,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -7154,7 +7939,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -7171,7 +7959,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_E"
blocked_thread_name: "tworkPolicy.uid"
+ blocked_thread_tid: 1193
+ blocking_thread_tid: 1934
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
}
@@ -7188,7 +7979,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "fg"
+ blocked_thread_tid: 3516
+ blocking_thread_tid: 3487
process_name: "com.android.providers.media.module"
+ pid: 3487
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -7215,7 +8009,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7242,7 +8039,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7269,7 +8069,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7296,7 +8099,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7323,7 +8129,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7350,7 +8159,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7377,7 +8189,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7404,7 +8219,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7431,7 +8249,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7458,7 +8279,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7485,7 +8309,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7512,7 +8339,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7539,7 +8369,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7566,7 +8399,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7593,7 +8429,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7620,7 +8459,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7647,7 +8489,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7674,7 +8519,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7701,7 +8549,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7728,7 +8579,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7755,7 +8609,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7782,7 +8639,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7809,7 +8669,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7836,7 +8699,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7863,7 +8729,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7890,7 +8759,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7917,7 +8789,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7944,7 +8819,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_1"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 657
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -7971,7 +8849,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -7998,7 +8879,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8025,7 +8909,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8052,7 +8939,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8079,7 +8969,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8106,7 +8999,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8133,7 +9029,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8160,7 +9059,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8187,7 +9089,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8214,7 +9119,10 @@
waiter_count: 0
blocking_thread_name: "android.ui"
blocked_thread_name: "android.bg"
+ blocked_thread_tid: 670
+ blocking_thread_tid: 661
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8246,7 +9154,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8273,7 +9184,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "ActivityManager"
+ blocked_thread_tid: 671
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8300,7 +9214,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8327,7 +9244,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8354,7 +9274,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8381,7 +9304,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8408,7 +9334,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8435,7 +9364,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8462,7 +9394,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8489,7 +9424,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8516,7 +9454,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8543,7 +9484,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8570,7 +9514,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8597,7 +9544,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8624,7 +9574,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8651,7 +9604,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8678,7 +9634,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8705,7 +9664,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8732,7 +9694,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8759,7 +9724,10 @@
waiter_count: 0
blocking_thread_name: "binder:642_8"
blocked_thread_name: "android.ui"
+ blocked_thread_tid: 661
+ blocking_thread_tid: 1548
process_name: "system_server"
+ pid: 642
is_blocked_thread_main: false
is_blocking_thread_main: false
thread_states {
@@ -8786,7 +9754,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8813,7 +9784,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8840,7 +9814,10 @@
waiter_count: 0
blocking_thread_name: "sAsyncHandlerThread"
blocked_thread_name: "d.process.media"
+ blocked_thread_tid: 2003
+ blocking_thread_tid: 2128
process_name: "android.process.media"
+ pid: 2003
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8867,7 +9844,10 @@
waiter_count: 0
blocking_thread_name: "SysUiBg"
blocked_thread_name: "ndroid.systemui"
+ blocked_thread_tid: 1253
+ blocking_thread_tid: 1331
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8894,7 +9874,10 @@
waiter_count: 0
blocking_thread_name: "SysUiBg"
blocked_thread_name: "ndroid.systemui"
+ blocked_thread_tid: 1253
+ blocking_thread_tid: 1331
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8921,7 +9904,10 @@
waiter_count: 0
blocking_thread_name: "SysUiBg"
blocked_thread_name: "ndroid.systemui"
+ blocked_thread_tid: 1253
+ blocking_thread_tid: 1331
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8948,7 +9934,10 @@
waiter_count: 0
blocking_thread_name: "SysUiBg"
blocked_thread_name: "ndroid.systemui"
+ blocked_thread_tid: 1253
+ blocking_thread_tid: 1331
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -8975,7 +9964,10 @@
waiter_count: 0
blocking_thread_name: "SysUiBg"
blocked_thread_name: "ndroid.systemui"
+ blocked_thread_tid: 1253
+ blocking_thread_tid: 1331
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: true
is_blocking_thread_main: false
thread_states {
@@ -9002,7 +9994,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "SysUiBg"
+ blocked_thread_tid: 1331
+ blocking_thread_tid: 1253
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -9024,7 +10019,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "plugin"
+ blocked_thread_tid: 1341
+ blocking_thread_tid: 1253
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
@@ -9051,7 +10049,10 @@
waiter_count: 0
blocking_thread_name: "main"
blocked_thread_name: "RenderThread"
+ blocked_thread_tid: 1436
+ blocking_thread_tid: 1253
process_name: "com.android.systemui"
+ pid: 1253
is_blocked_thread_main: false
is_blocking_thread_main: true
thread_states {
diff --git a/test/trace_processor/diff_tests/android/android_network_activity.out b/test/trace_processor/diff_tests/android/android_network_activity.out
new file mode 100644
index 0000000..eca9f86
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_network_activity.out
@@ -0,0 +1,4 @@
+"package_name","ts","dur","packet_count","packet_length"
+"uid=123",1000,1010,2,100
+"uid=123",3000,2500,4,200
+"uid=456",1005,2110,3,350
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
index 25190b0..7d683cf 100644
--- a/test/trace_processor/diff_tests/android/tests.py
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -111,6 +111,172 @@
""",
out=Path('android_system_property_slice.out'))
+ def test_android_battery_stats_event_slices(self):
+ # The following has three events
+ # * top (123, mail) from 1000 to 9000 explicit
+ # * job (456, mail_job) starting at 3000 (end is inferred as trace end)
+ # * job (789, video_job) ending at 4000 (start is inferred as trace start)
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ packet {
+ ftrace_events {
+ cpu: 1
+ event {
+ timestamp: 1000
+ pid: 1
+ print {
+ buf: "N|1000|battery_stats.top|+top=123:\"mail\"\n"
+ }
+ }
+ event {
+ timestamp: 3000
+ pid: 1
+ print {
+ buf: "N|1000|battery_stats.job|+job=456:\"mail_job\"\n"
+ }
+ }
+ event {
+ timestamp: 4000
+ pid: 1
+ print {
+ buf: "N|1000|battery_stats.job|-job=789:\"video_job\"\n"
+ }
+ }
+ event {
+ timestamp: 9000
+ pid: 1
+ print {
+ buf: "N|1000|battery_stats.top|-top=123:\"mail\"\n"
+ }
+ }
+ }
+ }
+ """),
+ query="""
+ SELECT IMPORT('android.battery_stats');
+ SELECT * FROM android_battery_stats_event_slices
+ ORDER BY str_value;
+ """,
+ out=Path('android_battery_stats_event_slices.out'))
+
+ def test_android_battery_stats_counters(self):
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ packet {
+ ftrace_events {
+ cpu: 1
+ event {
+ timestamp: 1000
+ pid: 1
+ print {
+ buf: "C|1000|battery_stats.data_conn|13\n"
+ }
+ }
+ event {
+ timestamp: 4000
+ pid: 1
+ print {
+ buf: "C|1000|battery_stats.data_conn|20\n"
+ }
+ }
+ event {
+ timestamp: 1000
+ pid: 1
+ print {
+ buf: "C|1000|battery_stats.audio|1\n"
+ }
+ }
+ }
+ }
+ """),
+ query="""
+ SELECT IMPORT('android.battery_stats');
+ SELECT * FROM android_battery_stats_state
+ ORDER BY ts, track_name;
+ """,
+ out=Path('android_battery_stats_state.out'))
+
+ def test_android_network_activity(self):
+ # The following should have three activity regions:
+ # * uid=123 from 1000 to 2010 (note: end is max(ts)+idle_ns)
+ # * uid=456 from 1005 to 3115 (note: doesn't group with above due to name)
+ # * Also tests that groups form based on (ts+dur), not just start ts.
+ # * uid=123 from 3000 to 5500 (note: gap between 1010 to 3000 > idle_ns)
+ # Note: packet_timestamps are delta encoded from the base timestamp.
+ return DiffTestBlueprint(
+ trace=TextProto(r"""
+ packet {
+ timestamp: 0
+ network_packet_bundle {
+ ctx {
+ direction: DIR_EGRESS
+ interface: "wlan"
+ uid: 123
+ }
+ packet_timestamps: [
+ 1000, 1010,
+ 3000, 3050, 4000, 4500
+ ],
+ packet_lengths: [
+ 50, 50,
+ 50, 50, 50, 50
+ ],
+ }
+ }
+ packet {
+ timestamp: 1005
+ network_packet_bundle {
+ ctx {
+ direction: DIR_EGRESS
+ interface: "wlan"
+ uid: 456
+ }
+ total_duration: 100
+ total_packets: 2
+ total_length: 300
+ }
+ }
+ packet {
+ timestamp: 2015
+ network_packet_bundle {
+ ctx {
+ direction: DIR_EGRESS
+ interface: "wlan"
+ uid: 456
+ }
+ total_duration: 100
+ total_packets: 1
+ total_length: 50
+ }
+ }
+ packet {
+ timestamp: 0
+ network_packet_bundle {
+ ctx {
+ direction: DIR_INGRESS
+ interface: "loopback"
+ uid: 123
+ }
+ packet_timestamps: [6000]
+ packet_lengths: [100]
+ }
+ }
+ """),
+ query="""
+ SELECT RUN_METRIC(
+ 'android/network_activity_template.sql',
+ 'view_name', 'android_network_activity',
+ 'group_by', 'package_name',
+ 'filter', 'iface = "wlan"',
+ 'idle_ns', '1000',
+ 'quant_ns', '100'
+ );
+
+ SELECT * FROM android_network_activity
+ ORDER BY package_name, ts;
+ """,
+ out=Path('android_network_activity.out'))
+
def test_binder_sync_binder_metrics(self):
return DiffTestBlueprint(
trace=DataPath('android_binder_metric_trace.atr'),
@@ -285,41 +451,19 @@
13934,"D",11950576,1
"""))
- def test_monitor_contention_chain_extraction(self):
+ def test_monitor_contention_chain_extraction_parent(self):
return DiffTestBlueprint(
trace=DataPath('android_monitor_contention_trace.atr'),
query="""
SELECT IMPORT('android.monitor_contention');
- SELECT
- IIF(parent_id IS NULL, "", parent_id) AS parent_id,
- blocking_method,
- blocked_method,
- short_blocking_method,
- short_blocked_method,
- blocking_src,
- blocked_src,
- waiter_count,
- blocked_utid,
- blocked_thread_name,
- blocking_utid,
- blocking_thread_name,
- upid,
- process_name,
- id,
- ts,
- dur,
- is_blocked_thread_main,
- is_blocking_thread_main,
- IIF(binder_reply_id IS NULL, "", binder_reply_id) AS binder_reply_id,
- IIF(binder_reply_ts IS NULL, "", binder_reply_ts) AS binder_reply_ts,
- IIF(binder_reply_tid IS NULL, "", binder_reply_tid) AS binder_reply_tid
- FROM android_monitor_contention_chain
+ SELECT * FROM android_monitor_contention_chain
+ WHERE parent_id IS NOT NULL
ORDER BY dur DESC
LIMIT 1;
""",
out=Csv("""
- "parent_id","blocking_method","blocked_method","short_blocking_method","short_blocked_method","blocking_src","blocked_src","waiter_count","blocked_utid","blocked_thread_name","blocking_utid","blocking_thread_name","upid","process_name","id","ts","dur","is_blocked_thread_main","is_blocking_thread_main","binder_reply_id","binder_reply_ts","binder_reply_tid"
- "","void com.android.server.am.ActivityManagerService.forceStopPackage(java.lang.String, int)","boolean com.android.server.am.ActivityManagerService.unbindService(android.app.IServiceConnection)","com.android.server.am.ActivityManagerService.forceStopPackage","com.android.server.am.ActivityManagerService.unbindService","ActivityManagerService.java:3992","ActivityManagerService.java:12719",0,640,"StorageUserConn",495,"binder:642_1",250,"system_server",327,1737063410007,46114664,0,0,"","",""
+ "parent_id","blocking_method","blocked_method","short_blocking_method","short_blocked_method","blocking_src","blocked_src","waiter_count","blocked_utid","blocked_thread_name","blocking_utid","blocking_thread_name","blocking_tid","upid","process_name","id","ts","dur","track_id","is_blocked_thread_main","blocked_thread_tid","is_blocking_thread_main","blocking_thread_tid","binder_reply_id","binder_reply_ts","binder_reply_tid","pid"
+ 956,"void com.android.server.am.AppProfiler.collectPssInBackground()","void com.android.server.am.ProcessRecord.setPid(int)","com.android.server.am.AppProfiler.collectPssInBackground","com.android.server.am.ProcessRecord.setPid","AppProfiler.java:514","ProcessRecord.java:596",0,656,"binder:642_12",506,"android.bg",670,250,"system_server",949,1737122781871,7301144,1236,0,2720,0,670,"[NULL]","[NULL]","[NULL]",642
"""))
def test_monitor_contention_metric(self):
diff --git a/test/trace_processor/diff_tests/atrace/tests_general.py b/test/trace_processor/diff_tests/atrace/tests_general.py
deleted file mode 100644
index 59417c0..0000000
--- a/test/trace_processor/diff_tests/atrace/tests_general.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class AtraceGeneral(DiffTestModule):
-
- def test_android_b2b_async_begin_list_slices(self):
- return DiffTestBlueprint(
- trace=Path('android_b2b_async_begin.textproto'),
- query="""
-SELECT ts, dur, name
-FROM slice;
-""",
- out=Csv("""
-"ts","dur","name"
-1000,30,"multistart"
-1015,45,"multistart"
-1030,20,"multistart"
-"""))
-
- def test_process_track_slices_android_async_slice(self):
- return DiffTestBlueprint(
- trace=Path('android_async_slice.textproto'),
- query="""
-SELECT
- ts,
- dur,
- pid,
- slice.name AS slice_name,
- process_track.name AS track_name
-FROM slice
-JOIN process_track ON slice.track_id = process_track.id
-JOIN process USING (upid);
-""",
- out=Path('process_track_slices_android_async_slice.out'))
-
- def test_async_track_atrace_process_track_slices(self):
- return DiffTestBlueprint(
- trace=Path('async_track_atrace.py'),
- query="""
-SELECT
- ts,
- dur,
- pid,
- slice.name AS slice_name,
- process_track.name AS track_name
-FROM slice
-JOIN process_track ON slice.track_id = process_track.id
-JOIN process USING (upid);
-""",
- out=Csv("""
-"ts","dur","pid","slice_name","track_name"
-50,25,1,"ev","track"
-55,15,1,"ev","track"
-60,5,2,"ev","track"
-"""))
-
- def test_sys_write_and_atrace(self):
- return DiffTestBlueprint(
- trace=Path('sys_write_and_atrace.py'),
- query="""
-SELECT slice.ts, slice.dur, slice.name, slice.depth
-FROM slice
-JOIN thread_track ON (slice.track_id = thread_track.id)
-JOIN thread USING (utid)
-WHERE tid = 42;
-""",
- out=Csv("""
-"ts","dur","name","depth"
-100,100,"sys_write",0
-300,50,"sys_write",0
-350,300,"test",0
-600,50,"sys_write",1
-"""))
diff --git a/test/trace_processor/diff_tests/camera/tests_general.py b/test/trace_processor/diff_tests/camera/tests_general.py
deleted file mode 100644
index 7277796..0000000
--- a/test/trace_processor/diff_tests/camera/tests_general.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class CameraGeneral(DiffTestModule):
-
- def test_camera_ion_mem_trace_android_camera(self):
- return DiffTestBlueprint(
- trace=DataPath('camera-ion-mem-trace'),
- query=Metric('android_camera'),
- out=TextProto(r"""
-android_camera {
- gc_rss_and_dma {
- min: 47779840.0
- max: 2529583104.0
- avg: 1459479416.3297353
- }
-}
-"""))
-
- def test_camera_ion_mem_trace_android_camera_unagg(self):
- return DiffTestBlueprint(
- trace=DataPath('camera-ion-mem-trace'),
- query=Metric('android_camera_unagg'),
- out=Path('camera-ion-mem-trace_android_camera_unagg.out'))
diff --git a/test/trace_processor/diff_tests/chrome/chrome_scroll_check.py b/test/trace_processor/diff_tests/chrome/chrome_scroll_check.py
new file mode 100644
index 0000000..4c51da4
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_scroll_check.py
@@ -0,0 +1,49 @@
+#!/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 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.
+
+# Discarded events that do not get to GPU are invisible for UMA metric and
+# therefore should be excluded in trace-based metric. This tests ensures that's
+# the case.
+
+from os import sys
+
+import synth_common
+
+from synth_common import ms_to_ns
+trace = synth_common.create_trace()
+
+from chrome_scroll_helper import ChromeScrollHelper
+
+helper = ChromeScrollHelper(trace, start_id=1234, start_gesture_id=5678)
+
+# First scroll
+helper.begin(from_ms=0, dur_ms=10)
+helper.update(from_ms=15, dur_ms=10)
+helper.update(from_ms=30, dur_ms=10)
+helper.end(from_ms=45, dur_ms=10)
+
+# Second scroll
+helper.begin(from_ms=60, dur_ms=10)
+helper.update(from_ms=75, dur_ms=10)
+helper.end(from_ms=90, dur_ms=10)
+
+# Third scroll, won't have a GestureScrollEnd value.
+helper.begin(from_ms=120, dur_ms=10)
+helper.update(from_ms=135, dur_ms=10)
+helper.update(from_ms=150, dur_ms=10)
+helper.update(from_ms=150, dur_ms=10)
+helper.update(from_ms=180, dur_ms=10)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/chrome/chrome_scroll_helper.py b/test/trace_processor/diff_tests/chrome/chrome_scroll_helper.py
new file mode 100644
index 0000000..7b7cee1
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_scroll_helper.py
@@ -0,0 +1,66 @@
+#!/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 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.
+
+# Discarded events that do not get to GPU are invisible for UMA metric and
+# therefore should be excluded in trace-based metric. This tests ensures that's
+# the case.
+
+import synth_common
+
+from synth_common import ms_to_ns
+trace = synth_common.create_trace()
+
+
+class ChromeScrollHelper:
+
+ def __init__(self, trace, start_id, start_gesture_id):
+ self.trace = trace
+ self.id = start_id
+ self.gesture_id = start_gesture_id
+
+ def begin(self, from_ms, dur_ms):
+ self.trace.add_input_latency_event_slice(
+ "GestureScrollBegin",
+ ts=ms_to_ns(from_ms),
+ dur=ms_to_ns(dur_ms),
+ track=self.id,
+ trace_id=self.id,
+ gesture_scroll_id=self.gesture_id,
+ )
+ self.id += 1
+
+ def update(self, from_ms, dur_ms, gets_to_gpu=True):
+ self.trace.add_input_latency_event_slice(
+ "GestureScrollUpdate",
+ ts=ms_to_ns(from_ms),
+ dur=ms_to_ns(dur_ms),
+ track=self.id,
+ trace_id=self.id,
+ gesture_scroll_id=self.gesture_id,
+ gets_to_gpu=gets_to_gpu,
+ is_coalesced=False,
+ )
+ self.id += 1
+
+ def end(self, from_ms, dur_ms):
+ self.trace.add_input_latency_event_slice(
+ "GestureScrollEnd",
+ ts=ms_to_ns(from_ms),
+ dur=ms_to_ns(dur_ms),
+ track=self.id,
+ trace_id=self.id,
+ gesture_scroll_id=self.gesture_id)
+ self.id += 1
+ self.gesture_id += 1
diff --git a/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
index dae2c44..b13137f 100644
--- a/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
+++ b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_v2.out
@@ -1,4 +1,3 @@
"scroll_processing_ms","scroll_jank_processing_ms","scroll_jank_percentage"
12374.560000,154.217000,1.246242
-
diff --git a/test/trace_processor/diff_tests/chrome/chrome_speedometer.out b/test/trace_processor/diff_tests/chrome/chrome_speedometer.out
new file mode 100644
index 0000000..2a52485
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_speedometer.out
@@ -0,0 +1,11 @@
+"iteration","ts","dur","total","mean","geomean","score","num_measurements"
+1,693997310311984,7020297000,5191976000,"324498500.0","254177934.4","78.7",96
+2,694004414619984,6308034000,4600497000,"287531062.5","224382472.1","89.1",96
+3,694010770005984,5878289000,4209484000,"263092750.0","200261720.2","99.9",96
+4,694016699502984,5934578000,4213632000,"263352000.0","201163561.9","99.4",96
+5,694022683560984,5952163000,4259111000,"266194437.5","203014932.4","98.5",96
+6,694028690570984,5966530000,4269728000,"266858000.0","204306068.2","97.9",96
+7,694034719276984,5853043000,4219351000,"263709437.5","200358118.8","99.8",96
+8,694040637173984,6087435000,4261576000,"266348500.0","202962356.2","98.5",96
+9,694046772284984,6040820000,4245060000,"265316250.0","199331263.1","100.3",96
+10,694052857814984,6063770000,4388487000,"274280437.5","208004176.2","96.2",96
diff --git a/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql b/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql
new file mode 100644
index 0000000..374d72e
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_speedometer_test.sql
@@ -0,0 +1,20 @@
+SELECT IMPORT('chrome.speedometer');
+
+SELECT
+ iteration,
+ ts,
+ dur,
+ total,
+ format('%.1f', mean) AS mean,
+ format('%.1f', geomean) AS geomean,
+ format('%.1f', score) AS score,
+ num_measurements
+FROM
+ chrome_speedometer_iteration,
+ (
+ SELECT iteration, COUNT(*) AS num_measurements
+ FROM chrome_speedometer_measure
+ GROUP BY iteration
+ )
+USING (iteration)
+ORDER BY iteration;
diff --git a/test/trace_processor/diff_tests/chrome/chrome_tasks.out b/test/trace_processor/diff_tests/chrome/chrome_tasks.out
index ef33837..7a75cec 100644
--- a/test/trace_processor/diff_tests/chrome/chrome_tasks.out
+++ b/test/trace_processor/diff_tests/chrome/chrome_tasks.out
@@ -1,5 +1,5 @@
-"full_name","task_type","count"
+"name","task_type","count"
"OnLibevent","other",2208
"RunTask(posted_from=components/favicon/core/large_icon_worker.cc:OnIconLookupComplete)","scheduler",694
"RunTask(posted_from=components/history/core/browser/history_service.cc:GetLargestFaviconForURL)","scheduler",694
@@ -16,7 +16,6 @@
"sendTouchEvent","java",194
"viz.mojom.CompositorFrameSinkClient message (hash=3114070324)","mojo",178
"viz.mojom.CompositorFrameSink message (hash=3089589715)","mojo",170
-"RunTask(posted_from=mojo/public/cpp/system/simple_watcher.cc:ArmOrNotify)","scheduler",158
"RunTask(posted_from=cc/scheduler/scheduler.cc:ScheduleBeginImplFrameDeadline)","scheduler",149
"RunTask(posted_from=net/quic/quic_chromium_alarm_factory.cc:SetImpl)","scheduler",135
"RunTask(posted_from=mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:SendMessage)","scheduler",114
@@ -25,6 +24,7 @@
"blink.mojom.WidgetInputHandler reply (hash=3392143105)","mojo",97
"tracing.mojom.ProducerHost message (hash=3013694824)","mojo",82
"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:WriteDataInternal)","scheduler",81
+"RunTask(posted_from=mojo/public/cpp/system/simple_watcher.cc:ArmOrNotify)","mojo",74
"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:CloseInternal)","scheduler",73
"RunTask(posted_from=base/android/task_scheduler/task_runner_android.cc:PostDelayedTask)","scheduler",63
"RunTask(posted_from=base/android/application_status_listener.cc:NotifyApplicationStateChange)","scheduler",54
@@ -37,16 +37,177 @@
"RunTask(posted_from=cc/trees/proxy_impl.cc:ScheduledActionSendBeginMainFrame)","scheduler",47
"RunTask(posted_from=cc/trees/proxy_main.cc:BeginMainFrame)","scheduler",47
"network.mojom.URLLoaderClient message (hash=374770486)","mojo",46
+"network.mojom.URLLoaderFactory message (hash=2397174083)","mojo",46
"viz.mojom.CompositorFrameSink message (hash=1654984935)","mojo",46
"RunTask(posted_from=components/update_client/component.cc:ChangeState)","scheduler",45
-"network.mojom.URLLoaderFactory message (hash=2397174083)","mojo",45
"Choreographer(java_views=OmniboxSuggestionsList)","choreographer",44
"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:CreateEntryInternal)","scheduler",44
"network.mojom.ProxyConfigPollerClient message (hash=2347231843)","mojo",43
"blink.mojom.AssociatedInterfaceProvider message (hash=2648115757)","mojo",37
+"network.mojom.URLLoaderClient message (hash=3734484340)","mojo",35
"network.mojom.URLLoaderNetworkServiceObserver message (hash=3598259070)","mojo",35
"Looper.dispatch: com.android.internal.view.IInputConnectionWrapper$MyHandler(null)","other",34
"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:OpenOrCreateEntryInternal)","scheduler",34
-"network.mojom.URLLoaderClient message (hash=3734484340)","mojo",34
+"network.mojom.URLLoaderClient message (hash=2503424824)","mojo",32
"RunTask(posted_from=components/update_client/component.cc:TransitionState)","scheduler",31
"RunTask(posted_from=net/http/http_stream_factory_job.cc:RunLoop)","scheduler",31
+"RunTask(posted_from=cc/tiles/tile_manager.cc:TaskSetFinished)","scheduler",30
+"RunTask(posted_from=gpu/command_buffer/service/scheduler.cc:TryScheduleSequence)","scheduler",29
+"viz.mojom.CompositorFrameSinkClient message (hash=50871626)","mojo",29
+"network.mojom.URLLoaderFactory message (hash=4026588969)","mojo",28
+"tracing.mojom.ProducerHost message (hash=1567334432)","mojo",27
+"RunTask(posted_from=services/service_manager/public/cpp/interface_binder.h:BindInterface)","scheduler",26
+"RunTask(posted_from=base/memory/memory_pressure_listener.cc:Notify)","scheduler",25
+"RunTask(posted_from=net/http/http_cache.cc:ProcessQueuedTransactions)","scheduler",25
+"Choreographer(java_views=ToolbarLayout,ToolbarPhone.updateLocationBarLayoutForExpansionAnimation)","choreographer",24
+"tracing.mojom.TracingSessionHost message (hash=167101205)","mojo",24
+"tracing.mojom.TracingSessionHost reply (hash=167101205)","mojo",24
+"RunTask(posted_from=components/history/core/browser/history_service.cc:ScheduleTask)","scheduler",23
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:DoomEntryInternal)","scheduler",23
+"RunTask(posted_from=components/omnibox/browser/history_url_provider.cc:ExecuteWithDB)","scheduler",21
+"content.mojom.ChildProcessHost message (hash=3455726814)","mojo",21
+"Looper.dispatch: android.app.ActivityThread$H(null)","other",20
+"RunTask(posted_from=cc/base/unique_notifier.cc:Schedule)","scheduler",20
+"RunTask(posted_from=ipc/ipc_mojo_bootstrap.cc:NotifyEndpointOfError)","scheduler",20
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/binder_map_internal.h:BindInterface)","scheduler",19
+"RunTask(posted_from=content/browser/gpu/gpu_process_host.cc:CallOnIO)","scheduler",18
+"RunTask(posted_from=services/metrics/public/cpp/delegating_ukm_recorder.cc:AddEntry)","scheduler",18
+"blink.mojom.BrowserInterfaceBroker message (hash=2708892102)","mojo",17
+"RunTask(posted_from=components/update_client/update_engine.cc:HandleComponentComplete)","scheduler",16
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:EntryOperationComplete)","scheduler",16
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:ReturnEntryToCallerAsync)","scheduler",16
+"tracing.mojom.ProducerClient message (hash=1884113734)","mojo",16
+"RunTask(posted_from=cc/tiles/tile_manager.cc:RunOnWorkerThread)","scheduler",15
+"RunTask(posted_from=components/update_client/component.cc:DoHandle)","scheduler",15
+"RunTask(posted_from=gpu/ipc/service/gpu_channel_manager.cc:ScheduleWakeUpGpu)","scheduler",15
+"RunTask(posted_from=third_party/blink/renderer/controller/memory_usage_monitor.cc:StartMonitoringIfNeeded)","scheduler",15
+"content.mojom.FrameHost message (hash=3826696652)","mojo",15
+"tracing.mojom.ProducerClient reply (hash=1377057286)","mojo",15
+"RunTask(posted_from=components/crash/content/browser/crash_metrics_reporter_android.cc:NotifyObservers)","scheduler",14
+"RunTask(posted_from=components/update_client/component.cc:EndState)","scheduler",14
+"RunTask(posted_from=components/viz/service/display_embedder/skia_output_surface_dependency_impl.cc:PostTaskToClientThread)","scheduler",14
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:SendMessageWithResponder)","scheduler",14
+"RunTask(posted_from=third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc:PostTaskToMainThread)","scheduler",14
+"viz.mojom.GpuHost message (hash=1989238374)","mojo",14
+"RunTask(posted_from=base/allocator/partition_alloc_support.cc:RunMemoryReclaimer)","scheduler",13
+"RunTask(posted_from=cc/trees/proxy_main.cc:SendCommitRequestToImplThreadIfNeeded)","scheduler",13
+"RunTask(posted_from=components/viz/service/display/display_scheduler.cc:ScheduleBeginFrameDeadline)","scheduler",13
+"RunTask(posted_from=content/browser/webui/web_ui_url_loader_factory.cc:DataAvailable)","scheduler",13
+"RunTask(posted_from=gin/v8_platform.cc:PostJob)","scheduler",13
+"RunTask(posted_from=third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc:DeletePendingRequest)","scheduler",13
+"SingleThreadProxy::BeginMainFrame(java_views=)","ui_thread_begin_main_frame",13
+"blink.mojom.WidgetInputHandler message (hash=1243813610)","mojo",13
+"content.mojom.FrameHost message (hash=171518470)","mojo",13
+"tracing.mojom.ProducerClient message (hash=1377057286)","mojo",13
+"RunTask(posted_from=components/offline_pages/task/task_queue.cc:StartTaskIfAvailable)","scheduler",12
+"RunTask(posted_from=components/page_load_metrics/renderer/page_timing_metrics_sender.cc:EnsureSendTimer)","scheduler",12
+"RunTask(posted_from=content/browser/webui/web_ui_data_source_impl.cc:GetDataResourceBytesOnWorkerThread)","scheduler",12
+"RunTask(posted_from=net/spdy/spdy_session.cc:MaybePostWriteLoop)","scheduler",12
+"RunTask(posted_from=third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc:UpdatePolicyLocked)","scheduler",12
+"page_load_metrics.mojom.PageLoadMetrics message (hash=1589948206)","mojo",12
+"viz.mojom.CompositorFrameSink message (hash=1797539858)","mojo",12
+"viz.mojom.CompositorFrameSinkClient message (hash=713406245)","mojo",12
+"RunTask(posted_from=ipc/ipc_channel_mojo.cc:SendMessage)","scheduler",11
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:OnAssociatedInterfaceRequest)","scheduler",11
+"blink.mojom.LocalMainFrameHost message (hash=2681895571)","mojo",11
+"Looper.dispatch: android.view.ViewRootImpl$ViewRootHandler(android.widget.Editor$Blink@ef6aa9b)","other",10
+"RunTask(posted_from=components/leveldb_proto/internal/proto_database_impl.h:PostTransaction)","scheduler",10
+"RunTask(posted_from=components/leveldb_proto/internal/proto_leveldb_wrapper.cc:LoadEntriesWithFilter)","scheduler",10
+"RunTask(posted_from=gpu/ipc/service/gpu_channel.cc:CreateCommandBuffer)","scheduler",10
+"RunTask(posted_from=cc/trees/layer_tree_host_impl.cc:DidPresentCompositorFrame)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper.cc:PostLaunchOnLauncherThread)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper.cc:StartLaunchOnClientThread)","scheduler",9
+"RunTask(posted_from=content/browser/child_process_launcher_helper_android.cc:LaunchProcessOnLauncherThread)","scheduler",9
+"RunTask(posted_from=content/browser/tracing/background_tracing_manager_impl.cc:ActivateForProcess)","scheduler",9
+"RunTask(posted_from=mojo/core/node_controller.cc:SendBrokerClientInvitation)","scheduler",9
+"blink.mojom.Widget message (hash=1337806005)","mojo",9
+"content.mojom.ChildProcess message (hash=1868925865)","mojo",9
+"device.mojom.DeviceService message (hash=3372813913)","mojo",9
+"device.mojom.PowerMonitor message (hash=1428246654)","mojo",9
+"memory_instrumentation.mojom.CoordinatorConnector message (hash=3189782928)","mojo",9
+"tracing.mojom.PerfettoService message (hash=3821615380)","mojo",9
+"tracing.mojom.TracedProcess reply (hash=2621133919)","mojo",9
+"tracing.mojom.TracingService message (hash=2713683366)","mojo",9
+"ChildProcessConnection.ChildServiceConnection.onServiceConnected","java",8
+"Looper.dispatch: android.net.ConnectivityManager$CallbackHandler(null)","other",8
+"RunTask(posted_from=base/allocator/partition_alloc_support.cc:RunThreadCachePeriodicPurge)","scheduler",8
+"RunTask(posted_from=chrome/browser/data_saver/data_saver.cc:FetchDataSaverOSSettingAsynchronously)","scheduler",8
+"RunTask(posted_from=gpu/command_buffer/service/gr_cache_controller.cc:ScheduleGrContextCleanup)","scheduler",8
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:AddFilter)","scheduler",8
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe)","mojo",8
+"RunTask(posted_from=mojo/public/cpp/bindings/lib/connector.cc:StartReceiving)","scheduler",8
+"RunTask(posted_from=net/cert/multi_threaded_cert_verifier.cc:Start)","scheduler",8
+"RunTask(posted_from=net/disk_cache/simple/simple_entry_impl.cc:PostClientCallback)","scheduler",8
+"RunTask(posted_from=ui/events/gesture_detection/gesture_detector.cc:StartTimeout)","scheduler",8
+"blink.mojom.LocalFrameHost message (hash=3716466459)","mojo",8
+"content.mojom.FrameHost message (hash=4063347880)","mojo",8
+"tracing.mojom.ProducerClient message (hash=3526922743)","mojo",8
+"RunTask(posted_from=base/threading/thread.cc:StopSoon)","scheduler",7
+"RunTask(posted_from=components/crash/content/browser/child_process_crash_observer_android.cc:OnChildExit)","scheduler",7
+"RunTask(posted_from=components/viz/service/display_embedder/skia_output_device.cc:FinishSwapBuffers)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:GetPeakMemoryUsage)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:GetPeakMemoryUsageOnMainThread)","scheduler",7
+"RunTask(posted_from=components/viz/service/gl/gpu_service_impl.cc:StartPeakMemoryMonitor)","scheduler",7
+"RunTask(posted_from=content/browser/browser_child_process_host_impl.cc:RegisterCoordinatorClient)","scheduler",7
+"RunTask(posted_from=content/browser/service_process_host_impl.cc:Launch)","scheduler",7
+"RunTask(posted_from=content/utility/utility_thread_impl.cc:BindServiceInterface)","scheduler",7
+"RunTask(posted_from=third_party/blink/renderer/core/frame/web_frame_widget_impl.cc:DidSwap)","scheduler",7
+"RunTask(posted_from=third_party/blink/renderer/core/script/module_map.cc:DispatchFinishedNotificationAsync)","scheduler",7
+"RunTask(posted_from=ui/gfx/android/android_surface_control_compat.cc:SetOnCommitCb)","scheduler",7
+"RunTask(posted_from=ui/gfx/android/android_surface_control_compat.cc:SetOnCompleteCb)","scheduler",7
+"content.mojom.ChildProcessHost message (hash=38682745)","mojo",7
+"media_session.mojom.MediaSessionObserver message (hash=3966185760)","mojo",7
+"viz.mojom.GpuService reply (hash=3002350734)","mojo",7
+"ChromeApplication.attachBaseContext","java",6
+"RunTask(posted_from=base/files/important_file_writer.cc:WriteNowWithBackgroundDataProducer)","scheduler",6
+"RunTask(posted_from=base/power_monitor/power_monitor.cc:NotifyPowerStateChange)","scheduler",6
+"RunTask(posted_from=base/task/sequenced_task_runner.h:operator())","scheduler",6
+"RunTask(posted_from=cc/trees/proxy_impl.cc:DrawInternal)","scheduler",6
+"RunTask(posted_from=cc/trees/proxy_impl.cc:~ScopedCommitCompletionEvent)","scheduler",6
+"RunTask(posted_from=cc/trees/single_thread_proxy.cc:DidReceiveCompositorFrameAckOnImplThread)","scheduler",6
+"RunTask(posted_from=chrome/browser/image_decoder/image_decoder.cc:RunDecodeCallbackOnTaskRunner)","scheduler",6
+"RunTask(posted_from=chrome/browser/image_decoder/image_decoder.cc:StartWithOptionsImpl)","scheduler",6
+"RunTask(posted_from=components/history/core/browser/history_service.cc:GetFaviconsForURL)","scheduler",6
+"RunTask(posted_from=components/leveldb_proto/internal/proto_leveldb_wrapper.cc:UpdateEntries)","scheduler",6
+"RunTask(posted_from=components/offline_pages/task/task_queue.cc:TaskCompletedCallback)","scheduler",6
+"RunTask(posted_from=components/omnibox/browser/autocomplete_controller.cc:StartStopTimer)","scheduler",6
+"RunTask(posted_from=components/performance_manager/decorators/page_load_tracker_decorator_helper.cc:NotifyPageLoadTrackerDecoratorOnPMSequence)","scheduler",6
+"RunTask(posted_from=components/performance_manager/performance_manager_impl.cc:CreateNodeImpl)","scheduler",6
+"RunTask(posted_from=components/power_scheduler/power_mode_arbiter.cc:OnTaskRunnerAvailable)","scheduler",6
+"RunTask(posted_from=content/browser/service_worker/service_worker_context_core.cc:NotifyClientIsExecutionReady)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:ExposeInterfacesToBrowser)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:GetBackgroundTracingAgentProvider)","scheduler",6
+"RunTask(posted_from=content/child/child_thread_impl.cc:Init)","scheduler",6
+"RunTask(posted_from=content/common/android/cpu_time_metrics_internal.cc:ProcessCpuTimeMetrics)","scheduler",6
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:Init)","scheduler",6
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:OnChannelConnected)","scheduler",6
+"RunTask(posted_from=mojo/core/node_controller.cc:AcceptBrokerClientInvitation)","scheduler",6
+"RunTask(posted_from=mojo/core/node_controller.cc:Create)","scheduler",6
+"RunTask(posted_from=services/device/public/cpp/power_monitor/power_monitor_broadcast_source.cc:Init)","scheduler",6
+"RunTask(posted_from=services/tracing/public/cpp/perfetto/perfetto_traced_process.cc:CreateProducerConnection)","scheduler",6
+"RunTask(posted_from=services/tracing/public/cpp/perfetto/producer_client.cc:Connect)","scheduler",6
+"RunTask(posted_from=third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc:UpdateForInputEventOnCompositorThread)","scheduler",6
+"blink.mojom.PageBroadcast message (hash=1682824955)","mojo",6
+"network.mojom.NetworkService message (hash=121963099)","mojo",6
+"service_manager.mojom.InterfaceProvider message (hash=607834802)","mojo",6
+"tracing.mojom.ProducerClient message (hash=2964700609)","mojo",6
+"tracing.mojom.TracedProcess message (hash=2621133919)","mojo",6
+"Choreographer(java_views=ToolbarLayout,ToolbarPhone.layoutLocationBar,ToolbarPhone.updateLocationBarLayoutForExpansionAnimation)","choreographer",5
+"Looper.dispatch: android.os.Handler(ZM3@26e2110)","other",5
+"RunTask(posted_from=base/files/important_file_writer.cc:ScheduleWrite)","scheduler",5
+"RunTask(posted_from=cc/trees/proxy_main.cc:UpdateBrowserControlsState)","scheduler",5
+"RunTask(posted_from=components/leveldb_proto/internal/proto_database_impl.h:ParseLoadedEntries)","scheduler",5
+"RunTask(posted_from=components/viz/common/gpu/context_cache_controller.cc:PostIdleCallback)","scheduler",5
+"RunTask(posted_from=gin/v8_foreground_task_runner.cc:PostNonNestableTask)","scheduler",5
+"RunTask(posted_from=ipc/ipc_channel_proxy.cc:Pause)","scheduler",5
+"RunTask(posted_from=net/socket/transport_client_socket_pool.cc:StartBackupJobTimer)","scheduler",5
+"RunTask(posted_from=third_party/blink/renderer/core/css/font_face_set.cc:HandlePendingEventsAndPromisesSoon)","scheduler",5
+"RunTask(posted_from=ui/gl/gl_surface_egl_surface_control.cc:CheckPendingPresentationCallbacks)","scheduler",5
+"blink.mojom.LocalFrameHost message (hash=3721282066)","mojo",5
+"blink.mojom.LocalMainFrame message (hash=2603884585)","mojo",5
+"blink.mojom.LocalMainFrameHost message (hash=2936437485)","mojo",5
+"content.mojom.ChildHistogramFetcherFactory message (hash=3650055636)","mojo",5
+"media_session.mojom.MediaSessionObserver message (hash=169447862)","mojo",5
+"network.mojom.DevToolsObserver message (hash=3093371228)","mojo",5
+"viz.mojom.GpuHost message (hash=2261948180)","mojo",5
+"viz.mojom.GpuHost message (hash=4264056690)","mojo",5
diff --git a/test/trace_processor/diff_tests/chrome/index.py b/test/trace_processor/diff_tests/chrome/index.py
deleted file mode 100644
index 67e8ca4..0000000
--- a/test/trace_processor/diff_tests/chrome/index.py
+++ /dev/null
@@ -1,1427 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class DiffTestModule_Chrome(DiffTestModule):
-
- def test_scroll_jank_general_validation(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query=Path('scroll_jank_general_validation_test.sql'),
- out=Path('scroll_jank_general_validation.out'))
-
- def test_scroll_jank(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank.sql');
-
-SELECT
- gesture_scroll_id,
- trace_id,
- jank,
- ts,
- dur,
- jank_budget
-FROM scroll_jank;
-""",
- out=Path('scroll_jank.out'))
-
- def test_event_latency_to_breakdowns(self):
- return DiffTestBlueprint(
- trace=Path('../../data/event_latency_with_args.perfetto-trace'),
- query="""
-SELECT RUN_METRIC('chrome/event_latency_to_breakdowns.sql');
-
-SELECT
- event_latency_ts,
- event_latency_dur,
- event_type,
- GenerationToRendererCompositorNs,
- GenerationToBrowserMainNs,
- BrowserMainToRendererCompositorNs,
- RendererCompositorQueueingDelayNs,
- unknown_stages_seen
-FROM event_latency_to_breakdowns
-ORDER BY event_latency_id
-LIMIT 30;
-""",
- out=Path('event_latency_to_breakdowns.out'))
-
- def test_event_latency_scroll_jank(self):
- return DiffTestBlueprint(
- trace=Path('../../data/event_latency_with_args.perfetto-trace'),
- query="""
-SELECT RUN_METRIC('chrome/event_latency_scroll_jank.sql');
-
-SELECT
- jank,
- next_jank,
- prev_jank,
- gesture_begin_ts,
- gesture_end_ts,
- ts,
- dur,
- event_type,
- next_ts,
- next_dur,
- prev_ts,
- prev_dur
-FROM scroll_event_latency_jank
-ORDER BY jank DESC
-LIMIT 10;
-""",
- out=Path('event_latency_scroll_jank.out'))
-
- def test_event_latency_scroll_jank_cause(self):
- return DiffTestBlueprint(
- trace=Path('../../data/event_latency_with_args.perfetto-trace'),
- query="""
-SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
-
-SELECT
- dur,
- ts,
- event_type,
- next_jank,
- prev_jank,
- next_delta_dur_ns,
- prev_delta_dur_ns,
- cause_of_jank,
- max_delta_dur_ns,
- sub_cause_of_jank
-FROM event_latency_scroll_jank_cause
-ORDER by ts;
-""",
- out=Path('event_latency_scroll_jank_cause.out'))
-
- def test_scroll_flow_event(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
-
-SELECT
- trace_id,
- ts,
- dur,
- jank,
- step,
- ancestor_end,
- maybe_next_ancestor_ts,
- next_ts,
- next_trace_id,
- next_step
-FROM scroll_flow_event
-ORDER BY gesture_scroll_id, trace_id, ts;
-""",
- out=Path('scroll_flow_event.out'))
-
- def test_scroll_flow_event_general_validation(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
-
-SELECT
- -- Each trace_id (in our example trace not true in general) has 8 steps. There
- -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
- (
- SELECT
- COUNT(*)
- FROM (
- SELECT
- trace_id,
- COUNT(*)
- FROM scroll_flow_event
- GROUP BY trace_id
- )
- ) AS total_scroll_updates,
- (
- SELECT COUNT(*) FROM scroll_flow_event
- ) AS total_flow_event_steps,
- (
- SELECT COUNT(*) FROM scroll_flow_event WHERE jank
- ) AS total_janky_flow_event_steps,
- (
- SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event GROUP BY step)
- ) AS number_of_unique_steps;
-""",
- out=Path('scroll_flow_event_general_validation.out'))
-
- def test_scroll_jank_cause(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause.sql');
-
-SELECT
- COUNT(*) AS total,
- SUM(jank) AS total_jank,
- SUM(explained_jank + unexplained_jank) AS sum_explained_and_unexplained,
- SUM(
- CASE WHEN explained_jank THEN
- unexplained_jank
- ELSE
- CASE WHEN jank AND NOT unexplained_jank THEN
- 1
- ELSE
- 0
- END
- END
- ) AS error_rows
-FROM scroll_jank_cause;
-""",
- out=Csv("""
-"total","total_jank","sum_explained_and_unexplained","error_rows"
-139,7,7,0
-"""))
-
- def test_scroll_flow_event_queuing_delay(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql');
-
-SELECT
- trace_id,
- jank,
- step,
- next_step,
- ancestor_end,
- maybe_next_ancestor_ts,
- queuing_time_ns
-FROM scroll_flow_event_queuing_delay
-WHERE trace_id = 2954 OR trace_id = 2956 OR trace_id = 2960
-ORDER BY trace_id, ts;
-""",
- out=Path('scroll_flow_event_queuing_delay.out'))
-
- def test_scroll_flow_event_general_validation_2(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query=Path(
- 'scroll_flow_event_queuing_delay_general_validation_test.sql'),
- out=Path('scroll_flow_event_general_validation.out'))
-
- def test_scroll_jank_cause_queuing_delay(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
- process_name,
- thread_name,
- trace_id,
- jank,
- dur_overlapping_ns,
- metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC;
-""",
- out=Path('scroll_jank_cause_queuing_delay.out'))
-
- def test_scroll_jank_cause_queuing_delay_restricted(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
- process_name,
- thread_name,
- trace_id,
- jank,
- dur_overlapping_ns,
- restricted_metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC;
-""",
- out=Path('scroll_jank_cause_queuing_delay_restricted.out'))
-
- def test_scroll_jank_cause_queuing_delay_general_validation(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
- COUNT(*) AS total,
- (
- SELECT DISTINCT
- (avg_no_jank_dur_overlapping_ns)
- FROM scroll_jank_cause_queuing_delay
- WHERE
- location = "LatencyInfo.Flow"
- AND jank
- ) AS janky_latency_info_non_jank_avg_dur,
- (
- SELECT DISTINCT
- (avg_no_jank_dur_overlapping_ns)
- FROM scroll_jank_cause_queuing_delay
- WHERE
- location = "LatencyInfo.Flow"
- AND NOT jank
- ) AS non_janky_latency_info_non_jank_avg_dur
-FROM (
- SELECT
- trace_id
- FROM scroll_jank_cause_queuing_delay
- GROUP BY trace_id
-);
-""",
- out=Path('scroll_jank_cause_queuing_delay_general_validation.out'))
-
- def test_chrome_thread_slice(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
-
-SELECT
- EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
- dur,
- thread_dur
-FROM chrome_thread_slice
-WHERE
- name = 'LatencyInfo.Flow'
- AND EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') = 2734;
-""",
- out=Csv("""
-"trace_id","dur","thread_dur"
-2734,25000,25000
-2734,1000,2000
-2734,2000,2000
-2734,258000,171000
-2734,1000,1000
-"""))
-
- def test_chrome_input_to_browser_intervals(self):
- return DiffTestBlueprint(
- trace=Path(
- '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
-
-SELECT
- *
-FROM chrome_input_to_browser_intervals
-WHERE window_start_ts >= 60934320005158
- AND window_start_ts <= 60934338798158;
-""",
- out=Path('chrome_input_to_browser_intervals.out'))
-
- def test_chrome_scroll_jank_caused_by_scheduling_test(self):
- return DiffTestBlueprint(
- trace=Path('../../data/fling_with_input_delay.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
- 'dur_causes_jank_ms',
-/* dur_causes_jank_ms = */ '5');
-
-SELECT
- full_name,
- total_duration_ms,
- total_thread_duration_ms,
- count,
- window_start_ts,
- window_end_ts,
- scroll_type
-FROM chrome_scroll_jank_caused_by_scheduling;
-""",
- out=Path('chrome_scroll_jank_caused_by_scheduling_test.out'))
-
- def test_chrome_tasks_delaying_input_processing_test(self):
- return DiffTestBlueprint(
- trace=Path('../../data/fling_with_input_delay.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_tasks_delaying_input_processing.sql',
- 'duration_causing_jank_ms',
- /* duration_causing_jank_ms = */ '8');
-
-SELECT
- full_name,
- duration_ms,
- thread_dur_ms
-FROM chrome_tasks_delaying_input_processing;
-""",
- out=Path('chrome_tasks_delaying_input_processing_test.out'))
-
- def test_long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test(
- self):
- return DiffTestBlueprint(
- trace=Path('../../data/long_task_tracking_trace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_long_tasks_delaying_input_processing.sql');
-
-SELECT
- full_name,
- duration_ms,
- slice_id
-FROM chrome_tasks_delaying_input_processing
-ORDER BY slice_id;
-""",
- out=Path(
- 'long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out'
- ))
-
- def test_experimental_reliable_chrome_tasks_delaying_input_processing_test(
- self):
- return DiffTestBlueprint(
- trace=Path('../../data/fling_with_input_delay.pftrace'),
- query="""
-SELECT RUN_METRIC(
- 'chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql',
- 'duration_causing_jank_ms', '8');
-
-SELECT
- full_name,
- duration_ms,
- thread_dur_ms
-FROM chrome_tasks_delaying_input_processing;
-""",
- out=Path(
- 'experimental_reliable_chrome_tasks_delaying_input_processing_test.out'
- ))
-
- def test_chrome_scroll_inputs_per_frame_test(self):
- return DiffTestBlueprint(
- trace=Path(
- '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_inputs_per_frame.sql');
-
-SELECT
- count_for_frame,
- ts
-FROM chrome_scroll_inputs_per_frame
-WHERE ts = 60934316798158;
-""",
- out=Csv("""
-"count_for_frame","ts"
-4,60934316798158
-"""))
-
- def test_chrome_thread_slice_repeated(self):
- return DiffTestBlueprint(
- trace=Path('../track_event/track_event_counters.textproto'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
-
-SELECT
- name,
- ts,
- dur,
- thread_dur
-FROM chrome_thread_slice;
-""",
- out=Csv("""
-"name","ts","dur","thread_dur"
-"event1_on_t1",1000,100,10000
-"event2_on_t1",2000,200,30000
-"event3_on_t1",2000,200,10000
-"event4_on_t1",4000,0,0
-"float_counter_on_t1",4300,0,"[NULL]"
-"float_counter_on_t1",4500,0,"[NULL]"
-"event1_on_t3",4000,100,5000
-"""))
-
- def test_frame_times_metric(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_rendering_desktop.pftrace'),
- query=Metric('frame_times'),
- out=Path('frame_times_metric.out'))
-
- def test_chrome_dropped_frames_metric(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_rendering_desktop.pftrace'),
- query=Metric('chrome_dropped_frames'),
- out=TextProto(r"""
-[perfetto.protos.chrome_dropped_frames]: {
- dropped_frame: {
- ts: 166479338462000
- process_name: "Renderer"
- pid: 12743
- }
- dropped_frame: {
- ts: 166479355302000
- process_name: "Renderer"
- pid: 12743
- }
-}"""))
-
- def test_chrome_long_latency_metric(self):
- return DiffTestBlueprint(
- trace=Path('../chrome/long_event_latency.textproto'),
- query="""
-SELECT RUN_METRIC('experimental/chrome_long_latency.sql');
-
-SELECT * FROM long_latency_with_process_info;
-""",
- out=Csv("""
-"ts","event_type","process_name","process_id"
-200111000,"FirstGestureScrollUpdate,GestureScrollUpdate","Renderer",1001
-200111000,"GestureScrollUpdate","Renderer",1002
-280111001,"GestureScrollUpdate","Renderer",1001
-"""))
-
- def test_scroll_jank_mojo_simple_watcher(self):
- return DiffTestBlueprint(
- trace=Path('scroll_jank_mojo_simple_watcher.py'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
- trace_id,
- jank,
- dur_overlapping_ns,
- metric_name
-FROM scroll_jank_cause_queuing_delay
-ORDER BY trace_id ASC, ts ASC;
-""",
- out=Path('scroll_jank_mojo_simple_watcher.out'))
-
- def test_scroll_jank_gpu_check(self):
- return DiffTestBlueprint(
- trace=Path('scroll_jank_gpu_check.py'),
- query="""
-SELECT RUN_METRIC('chrome/scroll_jank.sql');
-
-SELECT ts, jank
-FROM scroll_jank
-ORDER BY ts ASC;
-""",
- out=Csv("""
-"ts","jank"
-15000000,0
-30000000,1
-115000000,0
-"""))
-
- def test_touch_jank(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/touch_jank.sql');
-
-SELECT
- touch_id,
- trace_id,
- jank,
- ts,
- dur,
- jank_budget
-FROM touch_jank;
-""",
- out=Path('touch_jank.out'))
-
- def test_touch_flow_event(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/touch_flow_event.sql');
-
-SELECT
- trace_id,
- ts,
- dur,
- jank,
- step,
- ancestor_end,
- maybe_next_ancestor_ts,
- next_ts,
- next_trace_id,
- next_step
-FROM touch_flow_event
-ORDER BY touch_id, trace_id, ts;
-""",
- out=Path('touch_flow_event.out'))
-
- def test_touch_flow_event_queuing_delay(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
-
-SELECT
- trace_id,
- jank,
- step,
- next_step,
- ancestor_end,
- maybe_next_ancestor_ts,
- queuing_time_ns
-FROM touch_flow_event_queuing_delay
-WHERE trace_id = 6915 OR trace_id = 6911 OR trace_id = 6940
-ORDER BY trace_id, ts;
-""",
- out=Path('touch_flow_event_queuing_delay.out'))
-
- def test_touch_jank_synth(self):
- return DiffTestBlueprint(
- trace=Path('touch_jank.py'),
- query="""
-SELECT RUN_METRIC('chrome/touch_jank.sql');
-
-SELECT
- touch_id,
- trace_id,
- jank,
- ts,
- dur,
- jank_budget
-FROM touch_jank;
-""",
- out=Csv("""
-"touch_id","trace_id","jank","ts","dur","jank_budget"
-87654,34577,0,0,10000000,-31333333.350000
-87654,34578,1,16000000,33000000,14666666.650000
-87654,34579,0,55000000,33000000,-8333333.350000
-"""))
-
- def test_touch_flow_event_synth(self):
- return DiffTestBlueprint(
- trace=Path('touch_jank.py'),
- query="""
-SELECT RUN_METRIC('chrome/touch_flow_event.sql');
-
-SELECT
- trace_id,
- ts,
- dur,
- jank,
- step,
- ancestor_end,
- maybe_next_ancestor_ts,
- next_ts,
- next_trace_id,
- next_step
-FROM touch_flow_event
-ORDER BY touch_id, trace_id, ts;
-""",
- out=Path('touch_flow_event_synth.out'))
-
- def test_touch_flow_event_queuing_delay_synth(self):
- return DiffTestBlueprint(
- trace=Path('touch_jank.py'),
- query="""
-SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
-
-SELECT
- trace_id,
- jank,
- step,
- next_step,
- ancestor_end,
- maybe_next_ancestor_ts,
- queuing_time_ns
-FROM touch_flow_event_queuing_delay
-ORDER BY trace_id, ts;
-""",
- out=Path('touch_flow_event_queuing_delay_synth.out'))
-
- def test_memory_snapshot_general_validation(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- (
- SELECT COUNT(*) FROM memory_snapshot
- ) AS total_snapshots,
- (
- SELECT COUNT(*) FROM process
- ) AS total_processes,
- (
- SELECT COUNT(*) FROM process_memory_snapshot
- ) AS total_process_snapshots,
- (
- SELECT COUNT(*) FROM memory_snapshot_node
- ) AS total_nodes,
- (
- SELECT COUNT(*) FROM memory_snapshot_edge
- ) AS total_edges,
- (
- SELECT COUNT(DISTINCT args.id)
- FROM args
- JOIN memory_snapshot_node
- ON args.arg_set_id = memory_snapshot_node.arg_set_id
- ) AS total_node_args,
- (
- SELECT COUNT(*) FROM profiler_smaps
- JOIN memory_snapshot ON timestamp = ts
- ) AS total_smaps;
-""",
- out=Path('memory_snapshot_general_validation.out'))
-
- def test_memory_snapshot_os_dump_events(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- p.upid,
- pid,
- p.name,
- timestamp,
- detail_level,
- pf.value AS private_footprint_kb,
- prs.value AS peak_resident_set_kb,
- EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable') AS is_peak_rss_resettable
-FROM process p
-LEFT JOIN memory_snapshot
-LEFT JOIN (
- SELECT id, upid
- FROM process_counter_track
- WHERE name = 'chrome.private_footprint_kb'
- ) AS pct_pf
- ON p.upid = pct_pf.upid
-LEFT JOIN counter pf ON timestamp = pf.ts AND pct_pf.id = pf.track_id
-LEFT JOIN (
- SELECT id, upid
- FROM process_counter_track
- WHERE name = 'chrome.peak_resident_set_kb'
- ) AS pct_prs
- ON p.upid = pct_prs.upid
-LEFT JOIN counter prs ON timestamp = prs.ts AND pct_prs.id = prs.track_id
-ORDER BY timestamp;
-""",
- out=Path('memory_snapshot_os_dump_events.out'))
-
- def test_memory_snapshot_chrome_dump_events(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- pms.id AS process_snapshot_id,
- upid,
- snapshot_id,
- timestamp,
- detail_level
-FROM memory_snapshot ms
-LEFT JOIN process_memory_snapshot pms
- ON ms.id = pms.snapshot_id;
-""",
- out=Path('memory_snapshot_chrome_dump_events.out'))
-
- def test_memory_snapshot_nodes(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- id,
- process_snapshot_id,
- parent_node_id,
- path,
- size,
- effective_size
-FROM memory_snapshot_node
-LIMIT 20;
-""",
- out=Path('memory_snapshot_nodes.out'))
-
- def test_memory_snapshot_edges(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- id,
- source_node_id,
- target_node_id,
- importance
-FROM memory_snapshot_edge
-LIMIT 20;
-""",
- out=Path('memory_snapshot_edges.out'))
-
- def test_memory_snapshot_node_args(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- node.id AS node_id,
- key,
- value_type,
- int_value,
- string_value
-FROM memory_snapshot_node node
-JOIN args ON node.arg_set_id = args.arg_set_id
-LIMIT 20;
-""",
- out=Path('memory_snapshot_node_args.out'))
-
- def test_memory_snapshot_smaps(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_memory_snapshot.pftrace'),
- query="""
-SELECT
- process.upid,
- process.name,
- smap.ts,
- path,
- size_kb,
- private_dirty_kb,
- swap_kb,
- file_name,
- start_address,
- module_timestamp,
- module_debugid,
- module_debug_path,
- protection_flags,
- private_clean_resident_kb,
- shared_dirty_resident_kb,
- shared_clean_resident_kb,
- locked_kb,
- proportional_resident_kb
-FROM process
-JOIN profiler_smaps smap ON process.upid = smap.upid
-JOIN memory_snapshot ms ON ms.timestamp = smap.ts
-LIMIT 20;
-""",
- out=Path('memory_snapshot_smaps.out'))
-
- def test_combined_rail_modes(self):
- return DiffTestBlueprint(
- trace=Path('combined_rail_modes.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM combined_overall_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","rail_mode"
-1,0,10000,"response"
-2,10000,25000,"animation"
-3,35000,10000,"background"
-"""))
-
- def test_cpu_time_by_combined_rail_mode(self):
- return DiffTestBlueprint(
- trace=Path('cpu_time_by_combined_rail_mode.py'),
- query="""
-SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql');
-SELECT * FROM cpu_time_by_rail_mode;
-""",
- out=Csv("""
-"id","ts","dur","rail_mode","cpu_dur"
-1,0,10000,"response",26000
-2,10000,20000,"animation",20000
-3,30000,5000,"background",8000
-4,35000,10000,"animation",21000
-5,45000,10000,"background",1000
-"""))
-
- def test_actual_power_by_combined_rail_mode(self):
- return DiffTestBlueprint(
- trace=Path('actual_power_by_combined_rail_mode.py'),
- query="""
-SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql');
-SELECT * FROM real_power_by_rail_mode;
-""",
- out=Csv("""
-"id","ts","dur","rail_mode","subsystem","joules","drain_w"
-1,0,10000000,"response","cellular",0.000000,0.000000
-1,0,10000000,"response","cpu_little",0.000140,0.014000
-2,10000000,20000000,"animation","cellular",0.000350,0.017500
-2,10000000,20000000,"animation","cpu_little",0.000140,0.007000
-3,30000000,5000000,"background","cellular",0.000018,0.003500
-3,30000000,5000000,"background","cpu_little",0.000007,0.001400
-4,35000000,10000000,"animation","cellular",0.000021,0.002100
-4,35000000,10000000,"animation","cpu_little",0.000070,0.007000
-5,45000000,10000000,"background","cellular",0.000003,0.000350
-5,45000000,10000000,"background","cpu_little",0.000070,0.007000
-"""))
-
- def test_estimated_power_by_combined_rail_mode(self):
- return DiffTestBlueprint(
- trace=Path('estimated_power_by_combined_rail_mode.py'),
- query="""
-SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql');
-SELECT * FROM power_by_rail_mode;
-""",
- out=Csv("""
-"id","ts","dur","rail_mode","mas","ma"
-1,0,10000000,"response",0.554275,55.427500
-2,10000000,20000000,"animation",0.284850,14.242500
-3,30000000,5000000,"background",0.076233,15.246667
-4,35000000,10000000,"animation",0.536850,53.685000
-5,45000000,10000000,"background",0.071580,7.158000
-"""))
-
- def test_modified_rail_modes(self):
- return DiffTestBlueprint(
- trace=Path('modified_rail_modes.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,333333324,"animation"
-5,3283333324,216666676,"foreground_idle"
-6,3500000000,1000000000,"background"
-"""))
-
- def test_modified_rail_modes_no_vsyncs(self):
- return DiffTestBlueprint(
- trace=Path('modified_rail_modes_no_vsyncs.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,2500000000,"foreground_idle"
-4,3500000000,1000000000,"background"
-"""))
-
- def test_modified_rail_modes_with_input(self):
- return DiffTestBlueprint(
- trace=Path('modified_rail_modes_with_input.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,50000000,"animation"
-5,3000000000,66666674,"response"
-6,3066666674,216666650,"animation"
-7,3283333324,216666676,"foreground_idle"
-8,3500000000,1000000000,"background"
-"""))
-
- def test_modified_rail_modes_long(self):
- return DiffTestBlueprint(
- trace=Path('modified_rail_modes_long.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1,"background"
-"""))
-
- def test_modified_rail_modes_extra_long(self):
- return DiffTestBlueprint(
- trace=Path('modified_rail_modes_extra_long.py'),
- query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
- out=Csv("""
-"id","ts","dur","mode"
-"""))
-
- def test_chrome_processes(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT pid, name, process_type FROM chrome_process;
-""",
- out=Csv("""
-"pid","name","process_type"
-18250,"Renderer","Renderer"
-17547,"Browser","Browser"
-18277,"GPU Process","Gpu"
-17578,"Browser","Browser"
-"""))
-
- def test_chrome_processes_android_systrace(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_android_systrace.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT pid, name, process_type FROM chrome_process;
-""",
- out=Path('chrome_processes_android_systrace.out'))
-
- def test_chrome_threads(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT tid, name, is_main_thread, canonical_name
-FROM chrome_thread
-ORDER BY tid, name;
-""",
- out=Path('chrome_threads.out'))
-
- def test_chrome_threads_android_systrace(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_android_systrace.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT tid, name, is_main_thread, canonical_name
-FROM chrome_thread
-ORDER BY tid, name;
-""",
- out=Path('chrome_threads_android_systrace.out'))
-
- def test_chrome_processes_type(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
- process
-JOIN
- (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
- ON
- process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
- out=Csv("""
-"pid","name","chrome_process_type"
-17547,"Browser","Browser"
-17578,"Browser","Browser"
-18250,"Renderer","Renderer"
-18277,"GPU Process","Gpu"
-"""))
-
- def test_chrome_processes_type_android_systrace(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_android_systrace.pftrace'),
- query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
- process
-JOIN
- (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
- ON
- process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
- out=Path('chrome_processes_type_android_systrace.out'))
-
- def test_track_with_chrome_process(self):
- return DiffTestBlueprint(
- trace=Path('track_with_chrome_process.textproto'),
- query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
- process
-JOIN
- (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
- ON
- process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
- out=Csv("""
-"pid","name","chrome_process_type"
-5,"p5","[NULL]"
-"""))
-
- def test_chrome_histogram_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_histogram_hashes.textproto'),
- query=Metric('chrome_histogram_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_histogram_hashes]: {
- hash: 10
- hash: 20
-}
-"""))
-
- def test_chrome_user_event_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_user_event_hashes.textproto'),
- query=Metric('chrome_user_event_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_user_event_hashes]: {
- action_hash: 10
- action_hash: 20
-}
-
-"""))
-
- def test_chrome_performance_mark_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_performance_mark_hashes.textproto'),
- query=Metric('chrome_performance_mark_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_performance_mark_hashes]: {
- site_hash: 10
- site_hash: 20
- mark_hash: 100
- mark_hash: 200
-}
-"""))
-
- def test_chrome_reliable_range(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-12,"First slice for utid=2","[NULL]",2
-"""))
-
- def test_chrome_reliable_range_cropping(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_cropping.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-10000,"Range of interest packet","[NULL]",2
-"""))
-
- def test_chrome_reliable_range_missing_processes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_missing_processes.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing process data for upid=2",2,1
-"""))
-
- def test_chrome_reliable_range_missing_browser_main(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_missing_browser_main.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing main thread for upid=1",1,1
-"""))
-
- def test_chrome_reliable_range_missing_renderer_main(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_missing_renderer_main.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing main thread for upid=1",1,1
-"""))
-
- def test_chrome_reliable_range_non_chrome_process(self):
- return DiffTestBlueprint(
- # We need a trace with a large number of non-chrome slices, so that the
- # reliable range is affected by their filtering.
- trace=Path('../../data/example_android_trace_30s.pb'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
- "start","reason","debug_limiting_upid","debug_limiting_utid"
- 0,"[NULL]","[NULL]","[NULL]"
- """))
-
- def test_chrome_slice_names(self):
- return DiffTestBlueprint(
- trace=Path('chrome_slice_names.textproto'),
- query=Metric('chrome_slice_names'),
- out=TextProto(r"""
-[perfetto.protos.chrome_slice_names]: {
- chrome_version_code: 123
- slice_name: "Looper.Dispatch: class1"
- slice_name: "name2"
-}
-"""))
-
- def test_chrome_tasks(self):
- return DiffTestBlueprint(
- trace=Path(
- '../../data/chrome_page_load_all_categories_not_extended.pftrace.gz'
- ),
- query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
- out=Path('chrome_tasks.out'))
-
- def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks_test(
- self):
- return DiffTestBlueprint(
- trace=Path('../../data/top_level_java_choreographer_slices'),
- query="""
-SELECT RUN_METRIC(
- 'chrome/chrome_tasks_template.sql',
- 'slice_table_name', 'slice',
- 'function_prefix', ''
-);
-
-SELECT
- full_name,
- task_type
-FROM chrome_tasks
-WHERE category = "toplevel,Java"
-AND ts < 263904000000000
-GROUP BY full_name, task_type;
-""",
- out=Path(
- 'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
- ))
-
- def test_chrome_stack_samples_for_task_test(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_stack_traces_symbolized_trace.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
- 'target_duration_ms', '0.000001',
- 'thread_name', '"CrBrowserMain"',
- 'task_name', '"sendTouchEvent"');
-
-SELECT
- sample.description,
- sample.ts,
- sample.depth
-FROM chrome_stack_samples_for_task sample
-JOIN (
- SELECT
- ts,
- dur
- FROM slice
- WHERE ts = 696373965001470
-) test_slice
-ON sample.ts >= test_slice.ts
- AND sample.ts <= test_slice.ts + test_slice.dur
-ORDER BY sample.ts, sample.depth;
-""",
- out=Path('chrome_stack_samples_for_task_test.out'))
-
- def test_unsymbolized_args(self):
- return DiffTestBlueprint(
- trace=Path('unsymbolized_args.textproto'),
- query=Metric('chrome_unsymbolized_args'),
- out=TextProto(r"""
-[perfetto.protos.chrome_unsymbolized_args]: {
- args {
- module: "/liblib.so"
- build_id: "6275696c642d6964"
- address: 123
- google_lookup_id: "6275696c642d6964"
- }
- args {
- module: "/libmonochrome_64.so"
- build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
- address: 234
- google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
- }
-}"""))
-
- def test_async_trace_1_count_slices(self):
- return DiffTestBlueprint(
- trace=Path('../../data/async-trace-1.json'),
- query="""
-SELECT COUNT(1) FROM slice;
-""",
- out=Csv("""
-"COUNT(1)"
-16
-"""))
-
- def test_async_trace_2_count_slices(self):
- return DiffTestBlueprint(
- trace=Path('../../data/async-trace-2.json'),
- query="""
-SELECT COUNT(1) FROM slice;
-""",
- out=Csv("""
-"COUNT(1)"
-35
-"""))
-
- def test_chrome_args_class_names(self):
- return DiffTestBlueprint(
- trace=Path('chrome_args_class_names.textproto'),
- query=Metric('chrome_args_class_names'),
- out=TextProto(r"""
-
-[perfetto.protos.chrome_args_class_names] {
- class_names_per_version {
- class_name: "abc"
- class_name: "def"
- class_name: "ghi"
- class_name: "jkl"
- }
-}
-"""))
-
- def test_chrome_log_message(self):
- return DiffTestBlueprint(
- trace=Path('chrome_log_message.textproto'),
- query="""
-SELECT utid, tag, msg FROM android_logs;
-""",
- out=Csv("""
-"utid","tag","msg"
-1,"foo.cc:123","log message"
-"""))
-
- def test_chrome_log_message_args(self):
- return DiffTestBlueprint(
- trace=Path('chrome_log_message.textproto'),
- query=Path('chrome_log_message_args_test.sql'),
- out=Csv("""
-"log_message","function_name","file_name","line_number"
-"log message","func","foo.cc",123
-"""))
-
- def test_chrome_missing_processes_default_trace(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT upid, pid, reliable_from
-FROM
- experimental_missing_chrome_processes
-JOIN
- process
- USING(upid)
-ORDER BY upid;
-""",
- out=Csv("""
-"upid","pid","reliable_from"
-"""))
-
- def test_chrome_missing_processes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_missing_processes.textproto'),
- query="""
-SELECT upid, pid, reliable_from
-FROM
- experimental_missing_chrome_processes
-JOIN
- process
- USING(upid)
-ORDER BY upid;
-""",
- out=Csv("""
-"upid","pid","reliable_from"
-2,100,1000000000
-3,1000,"[NULL]"
-"""))
-
- def test_chrome_missing_processes_args(self):
- return DiffTestBlueprint(
- trace=Path('chrome_missing_processes.textproto'),
- query="""
-SELECT arg_set_id, key, int_value
-FROM
- slice
-JOIN
- args
- USING(arg_set_id)
-ORDER BY arg_set_id, key;
-""",
- out=Csv("""
-"arg_set_id","key","int_value"
-2,"chrome_active_processes.pid[0]",10
-2,"chrome_active_processes.pid[1]",100
-2,"chrome_active_processes.pid[2]",1000
-"""))
-
- def test_chrome_missing_processes_2(self):
- return DiffTestBlueprint(
- trace=Path('chrome_missing_processes_extension.textproto'),
- query="""
-SELECT upid, pid, reliable_from
-FROM
- experimental_missing_chrome_processes
-JOIN
- process
- USING(upid)
-ORDER BY upid;
-""",
- out=Csv("""
-"upid","pid","reliable_from"
-2,100,1000000000
-3,1000,"[NULL]"
-"""))
-
- def test_chrome_missing_processes_extension_args(self):
- return DiffTestBlueprint(
- trace=Path('chrome_missing_processes_extension.textproto'),
- query="""
-SELECT arg_set_id, key, int_value
-FROM
- slice
-JOIN
- args
- USING(arg_set_id)
-ORDER BY arg_set_id, key;
-""",
- out=Csv("""
-"arg_set_id","key","int_value"
-2,"active_processes.pid[0]",10
-2,"active_processes.pid[1]",100
-2,"active_processes.pid[2]",1000
-"""))
-
- def test_chrome_custom_navigation_tasks(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_custom_navigation_trace.gz'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-WHERE full_name GLOB 'FrameHost::BeginNavigation*'
- OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
- OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
- OR full_name GLOB 'FrameHost::DidStopLoading*'
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
- out=Csv("""
-"full_name","task_type","count"
-"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
-"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
-"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
-"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
-"""))
-
- def test_proto_content(self):
- return DiffTestBlueprint(
- trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
- query="""
-SELECT path, SUM(total_size) as total_size
-FROM experimental_proto_content as content
-JOIN experimental_proto_path as frame ON content.path_id = frame.id
-GROUP BY path
-ORDER BY total_size DESC, path
-LIMIT 10;
-""",
- out=Path('proto_content.out'))
-
- def test_chrome_scroll_jank_v2(self):
- return DiffTestBlueprint(
- trace=Path('../../data/event_latency_with_args.perfetto-trace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_jank_v2.sql');
-
-SELECT
- scroll_processing_ms,
- scroll_jank_processing_ms,
- scroll_jank_percentage
-FROM chrome_scroll_jank_v2_output;
-""",
- out=Path('chrome_scroll_jank_v2.out'))
diff --git a/test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py b/test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py
index 7e33ac2..c9ba45c 100644
--- a/test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py
+++ b/test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py
@@ -24,51 +24,9 @@
from synth_common import ms_to_ns
trace = synth_common.create_trace()
+from chrome_scroll_helper import ChromeScrollHelper
-class Helper:
-
- def __init__(self, trace, start_id, start_gesture_id):
- self.trace = trace
- self.id = start_id
- self.gesture_id = start_gesture_id
-
- def begin(self, from_ms, dur_ms):
- self.trace.add_input_latency_event_slice(
- "GestureScrollBegin",
- ts=ms_to_ns(from_ms),
- dur=ms_to_ns(dur_ms),
- track=self.id,
- trace_id=self.id,
- gesture_scroll_id=self.gesture_id,
- )
- self.id += 1
-
- def update(self, from_ms, dur_ms, gets_to_gpu=True):
- self.trace.add_input_latency_event_slice(
- "GestureScrollUpdate",
- ts=ms_to_ns(from_ms),
- dur=ms_to_ns(dur_ms),
- track=self.id,
- trace_id=self.id,
- gesture_scroll_id=self.gesture_id,
- gets_to_gpu=gets_to_gpu,
- is_coalesced=False,
- )
- self.id += 1
-
- def end(self, from_ms, dur_ms):
- self.trace.add_input_latency_event_slice(
- "GestureScrollEnd",
- ts=ms_to_ns(from_ms),
- dur=ms_to_ns(dur_ms),
- track=self.id,
- trace_id=self.id,
- gesture_scroll_id=self.gesture_id)
- self.id += 1
- self.gesture_id += 1
-
-
-helper = Helper(trace, start_id=1234, start_gesture_id=5678)
+helper = ChromeScrollHelper(trace, start_id=1234, start_gesture_id=5678)
helper.begin(from_ms=0, dur_ms=10)
helper.update(from_ms=15, dur_ms=10)
diff --git a/test/trace_processor/diff_tests/chrome/tests.py b/test/trace_processor/diff_tests/chrome/tests.py
index ec4181e..c6187aa 100644
--- a/test/trace_processor/diff_tests/chrome/tests.py
+++ b/test/trace_processor/diff_tests/chrome/tests.py
@@ -253,11 +253,11 @@
query="""
SELECT RUN_METRIC('chrome/chrome_tasks.sql');
- SELECT full_name, task_type, count() AS count
+ SELECT full_name as name, task_type, count() AS count
FROM chrome_tasks
GROUP BY full_name, task_type
- ORDER BY count DESC
- LIMIT 50;
+ HAVING count >= 5
+ ORDER BY count DESC, name;
""",
out=Path('chrome_tasks.out'))
@@ -446,6 +446,45 @@
"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
"""))
+ # Chrome custom navigation event names
+ def test_chrome_histograms(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_5672_histograms.pftrace.gz'),
+ query="""
+ SELECT IMPORT('chrome.histograms');
+
+ SELECT
+ name,
+ count() as count
+ FROM chrome_histograms
+ GROUP BY name
+ ORDER BY count DESC, name
+ LIMIT 20;
+ """,
+ out=Csv("""
+ "name","count"
+ "Net.QuicSession.AsyncRead",19207
+ "Net.QuicSession.NumQueuedPacketsBeforeWrite",19193
+ "RendererScheduler.QueueingDuration.NormalPriority",9110
+ "Net.OnTransferSizeUpdated.Experimental.OverridenBy",8525
+ "Compositing.Renderer.AnimationUpdateOnMissingPropertyNode",3489
+ "Net.QuicConnection.WritePacketStatus",3099
+ "Net.QuicSession.PacketWriteTime.Synchronous",3082
+ "Net.QuicSession.SendPacketSize.ForwardSecure",3012
+ "Net.URLLoaderThrottleExecutionTime.WillStartRequest",1789
+ "Net.URLLoaderThrottleExecutionTime.BeforeWillProcessResponse",1773
+ "Net.URLLoaderThrottleExecutionTime.WillProcessResponse",1773
+ "UMA.StackProfiler.SampleInOrder",1534
+ "GPU.SharedImage.ContentConsumed",1037
+ "Gpu.Rasterization.Raster.MSAASampleCountLog2",825
+ "Scheduling.Renderer.DeadlineMode",637
+ "Blink.CullRect.UpdateTime",622
+ "Scheduling.Renderer.BeginImplFrameLatency2",591
+ "Net.QuicSession.CoalesceStreamFrameStatus",551
+ "API.StorageAccess.AllowedRequests2",541
+ "Net.HttpResponseCode",541
+ """))
+
# Trace proto content
def test_proto_content(self):
return DiffTestBlueprint(
@@ -453,6 +492,12 @@
query=Path('proto_content_test.sql'),
out=Path('proto_content.out'))
+ def test_speedometer(self):
+ return DiffTestBlueprint(
+ trace=DataPath('speedometer.perfetto_trace.gz'),
+ query=Path('chrome_speedometer_test.sql'),
+ out=Path('chrome_speedometer.out'))
+
# TODO(mayzner): Uncomment when it works
# def test_proto_content_path(self):
# return DiffTestBlueprint(
diff --git a/test/trace_processor/diff_tests/chrome/tests_general.py b/test/trace_processor/diff_tests/chrome/tests_general.py
deleted file mode 100644
index 99938b8..0000000
--- a/test/trace_processor/diff_tests/chrome/tests_general.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class ChromeGeneral(DiffTestModule):
-
- def test_chrome_histogram_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_histogram_hashes.textproto'),
- query=Metric('chrome_histogram_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_histogram_hashes]: {
- hash: 10
- hash: 20
-}
-"""))
-
- def test_chrome_user_event_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_user_event_hashes.textproto'),
- query=Metric('chrome_user_event_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_user_event_hashes]: {
- action_hash: 10
- action_hash: 20
-}
-
-"""))
-
- def test_chrome_performance_mark_hashes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_performance_mark_hashes.textproto'),
- query=Metric('chrome_performance_mark_hashes'),
- out=TextProto(r"""
-[perfetto.protos.chrome_performance_mark_hashes]: {
- site_hash: 10
- site_hash: 20
- mark_hash: 100
- mark_hash: 200
-}
-"""))
-
- def test_chrome_reliable_range(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-12,"First slice for utid=2","[NULL]",2
-"""))
-
- def test_chrome_reliable_range_cropping(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_cropping.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-10000,"Range of interest packet","[NULL]",2
-"""))
-
- def test_chrome_reliable_range_missing_processes(self):
- return DiffTestBlueprint(
- trace=Path('chrome_reliable_range_missing_processes.textproto'),
- query=Path('chrome_reliable_range_test.sql'),
- out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing process data for upid=2",2,1
-"""))
-
- def test_chrome_slice_names(self):
- return DiffTestBlueprint(
- trace=Path('chrome_slice_names.textproto'),
- query=Metric('chrome_slice_names'),
- out=TextProto(r"""
-[perfetto.protos.chrome_slice_names]: {
- chrome_version_code: 123
- slice_name: "Looper.Dispatch: class1"
- slice_name: "name2"
-}
-"""))
-
- def test_chrome_tasks(self):
- return DiffTestBlueprint(
- trace=DataPath(
- 'chrome_page_load_all_categories_not_extended.pftrace.gz'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
- out=Path('chrome_tasks.out'))
-
- def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks(
- self):
- return DiffTestBlueprint(
- trace=DataPath('top_level_java_choreographer_slices'),
- query="""
-SELECT RUN_METRIC(
- 'chrome/chrome_tasks_template.sql',
- 'slice_table_name', 'slice',
- 'function_prefix', ''
-);
-
-SELECT
- full_name,
- task_type
-FROM chrome_tasks
-WHERE category = "toplevel,Java"
-AND ts < 263904000000000
-GROUP BY full_name, task_type;
-""",
- out=Path(
- 'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
- ))
-
- def test_chrome_stack_samples_for_task(self):
- return DiffTestBlueprint(
- trace=DataPath('chrome_stack_traces_symbolized_trace.pftrace'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
- 'target_duration_ms', '0.000001',
- 'thread_name', '"CrBrowserMain"',
- 'task_name', '"sendTouchEvent"');
-
-SELECT
- sample.description,
- sample.ts,
- sample.depth
-FROM chrome_stack_samples_for_task sample
-JOIN (
- SELECT
- ts,
- dur
- FROM slice
- WHERE ts = 696373965001470
-) test_slice
-ON sample.ts >= test_slice.ts
- AND sample.ts <= test_slice.ts + test_slice.dur
-ORDER BY sample.ts, sample.depth;
-""",
- out=Path('chrome_stack_samples_for_task_test.out'))
-
- def test_chrome_log_message(self):
- return DiffTestBlueprint(
- trace=Path('chrome_log_message.textproto'),
- query="""
-SELECT utid, tag, msg FROM android_logs;
-""",
- out=Csv("""
-"utid","tag","msg"
-1,"foo.cc:123","log message"
-"""))
-
- def test_chrome_log_message_args(self):
- return DiffTestBlueprint(
- trace=Path('chrome_log_message.textproto'),
- query=Path('chrome_log_message_args_test.sql'),
- out=Csv("""
-"log_message","function_name","file_name","line_number"
-"log message","func","foo.cc",123
-"""))
-
- def test_chrome_custom_navigation_tasks(self):
- return DiffTestBlueprint(
- trace=DataPath('chrome_custom_navigation_trace.gz'),
- query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-WHERE full_name GLOB 'FrameHost::BeginNavigation*'
- OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
- OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
- OR full_name GLOB 'FrameHost::DidStopLoading*'
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
- out=Csv("""
-"full_name","task_type","count"
-"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
-"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
-"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
-"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
-"""))
-
- def test_proto_content(self):
- return DiffTestBlueprint(
- trace=DataPath('chrome_scroll_without_vsync.pftrace'),
- query=Path('proto_content_test.sql'),
- out=Path('proto_content.out'))
diff --git a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
index c5fd3bc..75668e9 100644
--- a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -518,3 +518,54 @@
30000000,1
115000000,0
"""))
+
+ def test_chrome_scrolls(self):
+ return DiffTestBlueprint(
+ trace=Path('chrome_scroll_check.py'),
+ query="""
+ SELECT IMPORT('chrome.chrome_scrolls');
+
+ SELECT
+ id,
+ ts,
+ dur,
+ scroll_start_ts,
+ scroll_end_ts
+ FROM chrome_scrolls
+ ORDER by id;
+ """,
+ out=Csv("""
+ "id","ts","dur","scroll_start_ts","scroll_end_ts"
+ 5678,0,55000000,0,45000000
+ 5679,60000000,40000000,60000000,90000000
+ 5680,120000000,70000000,120000000,-1
+ """))
+
+ def test_chrome_scroll_jank_v2(self):
+ return DiffTestBlueprint(
+ trace=DataPath('event_latency_with_args.perfetto-trace'),
+ query=Metric('chrome_scroll_jank_v2'),
+ out=TextProto(r"""
+ [perfetto.protos.chrome_scroll_jank_v2] {
+ scroll_processing_ms: 12374.56
+ scroll_jank_processing_ms: 154.217
+ scroll_jank_percentage: 1.2462422906349802
+ num_scroll_janks: 4
+ scroll_jank_causes_and_durations {
+ cause: "SubmitCompositorFrameToPresentationCompositorFrame"
+ duration_ms: 39.44
+ }
+ scroll_jank_causes_and_durations {
+ cause: "SubmitCompositorFrameToPresentationCompositorFrame"
+ duration_ms: 35.485
+ }
+ scroll_jank_causes_and_durations {
+ cause: "SubmitCompositorFrameToPresentationCompositorFrame"
+ duration_ms: 43.838
+ }
+ scroll_jank_causes_and_durations {
+ cause: "SubmitCompositorFrameToPresentationCompositorFrame"
+ duration_ms: 35.454
+ }
+ }
+ """))
diff --git a/test/trace_processor/diff_tests/cros/tests_general.py b/test/trace_processor/diff_tests/cros/tests_general.py
deleted file mode 100644
index 5f012db..0000000
--- a/test/trace_processor/diff_tests/cros/tests_general.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class CrosGeneral(DiffTestModule):
-
- def test_cros_ec_sensorhub_data(self):
- return DiffTestBlueprint(
- trace=Path('cros_ec_sensorhub_data.textproto'),
- query="""
-SELECT
- t.name,
- c.ts,
- c.value,
- EXTRACT_ARG(c.arg_set_id, 'ec_num') AS ec_num,
- EXTRACT_ARG(c.arg_set_id, 'ec_delta') AS ec_delta,
- EXTRACT_ARG(c.arg_set_id, 'sample_ts') AS sample_ts
-FROM counter c
-JOIN track t
- ON c.track_id = t.id
-WHERE t.name = 'cros_ec.cros_ec_sensorhub_data.0';
-""",
- out=Path('cros_ec_sensorhub_data.out'))
diff --git a/test/trace_processor/diff_tests/functions/tests.py b/test/trace_processor/diff_tests/functions/tests.py
index 7f952ed..e5dc15c 100644
--- a/test/trace_processor/diff_tests/functions/tests.py
+++ b/test/trace_processor/diff_tests/functions/tests.py
@@ -520,3 +520,21 @@
4,3
5,3
"""))
+
+ def test_math_functions(self):
+ return DiffTestBlueprint(
+ trace=TextProto(""),
+ query="""
+ SELECT
+ CAST(EXP(1) * 1000 AS INTEGER) AS a,
+ CAST(LN(1) * 1000 AS INTEGER) AS b,
+ CAST(LN(EXP(1)) * 1000 AS INTEGER) AS c,
+ EXP("asd") AS d,
+ EXP(NULL) AS e,
+ LN("as") AS f,
+ LN(NULL) AS g
+ """,
+ out=Csv("""
+ "a","b","c","d","e","f","g"
+ 2718,0,1000,"[NULL]","[NULL]","[NULL]","[NULL]"
+ """))
diff --git a/test/trace_processor/diff_tests/graphics/android_jank_cuj.out b/test/trace_processor/diff_tests/graphics/android_jank_cuj.out
index 5784a98..22045da 100644
--- a/test/trace_processor/diff_tests/graphics/android_jank_cuj.out
+++ b/test/trace_processor/diff_tests/graphics/android_jank_cuj.out
@@ -16,6 +16,7 @@
apk_version_code: 1
debuggable: false
}
+ pid: 1000
}
ts: 0
dur: 123000000
@@ -196,6 +197,7 @@
apk_version_code: 1
debuggable: false
}
+ pid: 1000
}
ts: 0
dur: 901000010
diff --git a/test/trace_processor/diff_tests/performance/frame_timeline_metric.out b/test/trace_processor/diff_tests/performance/frame_timeline_metric.out
index eac9136..1a034f0 100644
--- a/test/trace_processor/diff_tests/performance/frame_timeline_metric.out
+++ b/test/trace_processor/diff_tests/performance/frame_timeline_metric.out
@@ -6,6 +6,7 @@
process {
process {
name: "process1"
+ pid: 1001
}
total_frames: 2
missed_frames: 2
@@ -27,6 +28,7 @@
process {
process {
name: "process2"
+ pid: 1002
}
total_frames: 2
missed_frames: 1
@@ -48,6 +50,7 @@
process {
process {
name: "process3"
+ pid: 1003
}
total_frames: 2
missed_frames: 2
@@ -69,6 +72,7 @@
process {
process {
name: "process4"
+ pid: 1004
}
total_frames: 5
missed_frames: 4
diff --git a/test/trace_processor/diff_tests/power/tests_power_rails.py b/test/trace_processor/diff_tests/power/tests_power_rails.py
index 869604c..5085cd2 100644
--- a/test/trace_processor/diff_tests/power/tests_power_rails.py
+++ b/test/trace_processor/diff_tests/power/tests_power_rails.py
@@ -58,18 +58,18 @@
return DiffTestBlueprint(
trace=Path('power_rails.textproto'),
query="""
- SELECT ts, value, t.name AS name
+ SELECT ts, extract_arg(arg_set_id,'packet_ts') as packet_ts, value, t.name AS name
FROM counter c JOIN counter_track t ON t.id = c.track_id
ORDER BY ts
LIMIT 20;
""",
out=Csv("""
- "ts","value","name"
- 3000000,333.000000,"power.test_rail_uws"
- 3000000,0.000000,"power.test_rail_uws"
- 3000004,1000.000000,"Testing"
- 3000005,999.000000,"power.test_rail2_uws"
- 5000000,666.000000,"power.test_rail_uws"
+ "ts","packet_ts","value","name"
+ 3000000,3000003,333.000000,"power.test_rail_uws"
+ 3000000,3000005,0.000000,"power.test_rail_uws"
+ 3000004,"[NULL]",1000.000000,"Testing"
+ 3000005,3000005,999.000000,"power.test_rail2_uws"
+ 5000000,3000005,666.000000,"power.test_rail_uws"
"""))
def test_power_rails_well_known_power_rails(self):
diff --git a/test/trace_processor/diff_tests/profiling/heap_graph.textproto b/test/trace_processor/diff_tests/profiling/heap_graph.textproto
index b3decef..a6e314b 100644
--- a/test/trace_processor/diff_tests/profiling/heap_graph.textproto
+++ b/test/trace_processor/diff_tests/profiling/heap_graph.textproto
@@ -21,7 +21,7 @@
pid: 2
rss_anon_kb: 1000
vm_swap_kb: 0
- oom_score_adj: 0
+ oom_score_adj: -800
}
}
}
diff --git a/test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out b/test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out
index 1ee09bf..9a78653 100644
--- a/test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out
+++ b/test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out
@@ -4,6 +4,7 @@
process {
name: "proc1"
uid: 1000
+ pid: 2
}
samples {
ts: 200000000
@@ -28,6 +29,7 @@
process {
name: "proc2"
uid: 1000
+ pid: 3
}
samples {
ts: 1500000000
diff --git a/test/trace_processor/diff_tests/profiling/java_heap_histogram.out b/test/trace_processor/diff_tests/profiling/java_heap_histogram.out
index 206dc7c..c5aaf49 100644
--- a/test/trace_processor/diff_tests/profiling/java_heap_histogram.out
+++ b/test/trace_processor/diff_tests/profiling/java_heap_histogram.out
@@ -3,7 +3,8 @@
upid: 2
process {
name: "system_server"
- uid: 1000
+ uid: 1000,
+ pid: 2
}
samples {
ts: 10
diff --git a/test/trace_processor/diff_tests/profiling/tests.py b/test/trace_processor/diff_tests/profiling/tests.py
index e56fbff..4c5c2e3 100644
--- a/test/trace_processor/diff_tests/profiling/tests.py
+++ b/test/trace_processor/diff_tests/profiling/tests.py
@@ -116,6 +116,7 @@
process {
name: "system_server"
uid: 1000
+ pid: 2
}
mappings {
path: "[anon: libc_malloc]"
diff --git a/test/trace_processor/diff_tests/profiling/tests_metrics.py b/test/trace_processor/diff_tests/profiling/tests_metrics.py
index cb210cf..9a4ff07 100644
--- a/test/trace_processor/diff_tests/profiling/tests_metrics.py
+++ b/test/trace_processor/diff_tests/profiling/tests_metrics.py
@@ -71,6 +71,7 @@
process {
name: "system_server"
uid: 1000
+ pid: 2
}
samples {
ts: 10
@@ -81,6 +82,7 @@
obj_count: 6
reachable_obj_count: 3
anon_rss_and_swap_size: 4096000
+ oom_score_adj: 0
roots {
root_type: "ROOT_JAVA_FRAME"
type_name: "DeobfuscatedA[]"
diff --git a/test/trace_processor/diff_tests/span_join/tests.py b/test/trace_processor/diff_tests/span_join/tests.py
deleted file mode 100644
index e675aff..0000000
--- a/test/trace_processor/diff_tests/span_join/tests.py
+++ /dev/null
@@ -1,411 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class DiffTestModule_SpanJoin(DiffTestModule):
-
- def test_span_outer_join(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_outer_join_test.sql'),
- out=Path('span_outer_join.out'))
-
- def test_span_outer_join_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part,
- t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-500,100,10
-"""))
-
- def test_span_outer_join_unpartitioned_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur"
-"""))
-
- def test_span_outer_join_unpartitioned_left_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur"
-100,400
-500,50
-600,100
-"""))
-
- def test_span_outer_join_unpartitioned_right_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur"
-100,400
-500,50
-600,100
-"""))
-
- def test_span_outer_join_mixed(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_outer_join_mixed_test.sql'),
- out=Path('span_outer_join_mixed.out'))
-
- def test_span_outer_join_mixed_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-"""))
-
- def test_span_outer_join_mixed_left_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-"""))
-
- def test_span_outer_join_mixed_left_empty_rev(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-100,400,0
-100,50,1
-600,100,1
-"""))
-
- def test_span_outer_join_mixed_right_empty(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- b BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part","b"
-100,400,0,"[NULL]"
-100,50,1,"[NULL]"
-600,100,1,"[NULL]"
-"""))
-
- def test_span_outer_join_mixed_right_empty_rev(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- b BIGINT,
- PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part","b"
-"""))
-
- def test_span_outer_join_mixed_2(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_outer_join_mixed_test.sql'),
- out=Path('span_outer_join_mixed.out'))
-
- def test_span_left_join(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_left_join_test.sql'),
- out=Path('span_left_join.out'))
-
- def test_span_left_join_unpartitioned(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_left_join_unpartitioned_test.sql'),
- out=Path('span_left_join_unpartitioned.out'))
-
- def test_span_left_join_left_unpartitioned(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_left_join_left_unpartitioned_test.sql'),
- out=Path('span_left_join_left_unpartitioned.out'))
-
- def test_span_left_join_left_partitioned(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query=Path('span_left_join_left_partitioned_test.sql'),
- out=Path('span_left_join_left_partitioned.out'))
-
- def test_span_left_join_empty_right(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(500, 500, 100);
-
-CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
- t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-500,500,100
-"""))
-
- def test_span_left_join_unordered_android_sched_and_ps(self):
- return DiffTestBlueprint(
- trace=Path('../common/synth_1.py'),
- query="""
-CREATE TABLE t1(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
- ts BIGINT,
- dur BIGINT,
- part BIGINT,
- PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
-INSERT INTO t2(ts, dur, part)
-VALUES (500, 100, 5);
-
-CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
- t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
- out=Csv("""
-"ts","dur","part"
-500,100,10
-"""))
diff --git a/test/trace_processor/diff_tests/startup/android_startup.out b/test/trace_processor/diff_tests/startup/android_startup.out
index 48fa752..18b879c 100644
--- a/test/trace_processor/diff_tests/startup/android_startup.out
+++ b/test/trace_processor/diff_tests/startup/android_startup.out
@@ -42,7 +42,8 @@
package_name: "com.google.android.calendar"
apk_version_code: 123
debuggable: false
- }
+ },
+ pid: 3
}
report_fully_drawn {
dur_ns: 198
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution.out b/test/trace_processor/diff_tests/startup/android_startup_attribution.out
index 8336103..ea96296 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.out
@@ -49,6 +49,10 @@
dur_ns: 499999845
dur_ms: 499.999845
}
+ time_dlopen_thread_main {
+ dur_ns: 2
+ dur_ms: 2e-06
+ }
}
activity_hosting_process_count: 1
process {
@@ -64,6 +68,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
event_timestamps {
intent_received: 100
@@ -110,6 +115,8 @@
name: "dl"
dur_ns: 5
}
+ dlopen_file: "libandroid.so"
+ dlopen_file: "libandroid2.so"
startup_type: "hot"
slow_start_reason: "GC Activity"
slow_start_reason: "Main Thread - Time spent in OpenDexFilesFromOat*"
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution.py b/test/trace_processor/diff_tests/startup/android_startup_attribution.py
index 0d7e784..59301e0 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution.py
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.py
@@ -85,6 +85,15 @@
ts=5, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(nothing)')
trace.add_atrace_end(ts=35, pid=APP_PID, tid=APP_TID)
+# dlopen slices within the startup.
+trace.add_atrace_begin(
+ ts=166, pid=APP_PID, tid=APP_TID, buf='dlopen: libandroid.so')
+trace.add_atrace_end(ts=167, pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_begin(
+ ts=168, pid=APP_PID, tid=APP_TID, buf='dlopen: libandroid2.so')
+trace.add_atrace_end(ts=169, pid=APP_PID, tid=APP_TID)
+
trace.add_atrace_async_end(
ts=LAUNCH_END_TS,
tid=SYSTEM_SERVER_TID,
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
index 80f6f81..4bf3fff 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
@@ -64,6 +64,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
event_timestamps {
intent_received: 100000000000
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
index 52dd550..2f930d3 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
@@ -70,6 +70,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
activities {
name: "com.google.android.calendar.MainActivity"
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
index 254759a..4417091 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
@@ -70,6 +70,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
activities {
name: "com.google.android.calendar.MainActivity"
diff --git a/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out b/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out
index e570b22..ba1311c 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out
@@ -55,6 +55,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
event_timestamps {
intent_received: 110
diff --git a/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out
index cd5dc6c..aa4ec05 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out
@@ -55,6 +55,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
event_timestamps {
intent_received: 110000000000
diff --git a/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out
index 1723d34..2a8307b 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out
@@ -63,6 +63,7 @@
apk_version_code: 123
debuggable: true
}
+ pid: 3
}
event_timestamps {
intent_received: 220
diff --git a/test/trace_processor/diff_tests/startup/android_startup_process_track.out b/test/trace_processor/diff_tests/startup/android_startup_process_track.out
index 41b26e5..561fb0d 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_process_track.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_process_track.out
@@ -51,6 +51,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
event_timestamps {
intent_received: 100
@@ -118,6 +119,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 4
}
event_timestamps {
intent_received: 200
diff --git a/test/trace_processor/diff_tests/startup/android_startup_slow.out b/test/trace_processor/diff_tests/startup/android_startup_slow.out
index 53d53b9..f791fd6 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_slow.out
@@ -43,6 +43,7 @@
apk_version_code: 123
debuggable: false
}
+ pid: 3
}
report_fully_drawn {
dur_ns: 198000000000
diff --git a/test/trace_processor/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
index 1f1ee49..3ef8d14 100644
--- a/test/trace_processor/diff_tests/tables/tests_sched.py
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -79,3 +79,44 @@
81473010341386
81473010352792
"""))
+
+ def test_sched_wakeup(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT * FROM spurious_sched_wakeup
+ ORDER BY ts LIMIT 10
+ """,
+ out=Csv("""
+ "id","type","ts","thread_state_id","irq_context","utid","waker_utid"
+ 0,"spurious_sched_wakeup",1735850782904,395,0,230,1465
+ 1,"spurious_sched_wakeup",1736413914899,852,0,230,1467
+ 2,"spurious_sched_wakeup",1736977755745,1261,0,230,1469
+ 3,"spurious_sched_wakeup",1737046900004,1434,0,1472,1473
+ 4,"spurious_sched_wakeup",1737047159060,1463,0,1474,1472
+ 5,"spurious_sched_wakeup",1737081636170,2721,0,1214,1319
+ 6,"spurious_sched_wakeup",1737108696536,4684,0,501,557
+ 7,"spurious_sched_wakeup",1737153309978,6080,0,11,506
+ 8,"spurious_sched_wakeup",1737165240546,6562,0,565,499
+ 9,"spurious_sched_wakeup",1737211563344,8645,0,178,1195
+ """))
+
+ def test_raw_common_flags(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT * FROM raw WHERE common_flags != 0 ORDER BY ts LIMIT 10
+ """,
+ out=Csv("""
+ "id","type","ts","name","cpu","utid","arg_set_id","common_flags"
+ 3,"ftrace_event",1735489788930,"sched_waking",0,300,4,1
+ 4,"ftrace_event",1735489812571,"sched_waking",0,300,5,1
+ 5,"ftrace_event",1735489833977,"sched_waking",1,305,6,1
+ 8,"ftrace_event",1735489876788,"sched_waking",1,297,9,1
+ 9,"ftrace_event",1735489879097,"sched_waking",0,304,10,1
+ 12,"ftrace_event",1735489933912,"sched_waking",0,428,13,1
+ 14,"ftrace_event",1735489972385,"sched_waking",1,232,15,1
+ 17,"ftrace_event",1735489999987,"sched_waking",1,232,15,1
+ 19,"ftrace_event",1735490039439,"sched_waking",1,298,18,1
+ 20,"ftrace_event",1735490042084,"sched_waking",1,298,19,1
+ """))
diff --git a/third_party/.gitignore b/third_party/.gitignore
new file mode 100644
index 0000000..1642854
--- /dev/null
+++ b/third_party/.gitignore
@@ -0,0 +1,3 @@
+clang-format/
+gn/
+ninja/
diff --git a/tools/check_sql_modules.py b/tools/check_sql_modules.py
index 051f1a9..8037fca 100755
--- a/tools/check_sql_modules.py
+++ b/tools/check_sql_modules.py
@@ -16,32 +16,30 @@
# This tool checks that every SQL object created without prefix
# 'internal_' is documented with proper schema.
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
import os
import sys
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(ROOT_DIR))
-FILE_DIR = ROOT_DIR
-from python.generators.stdlib_docs.stdlib import *
+from python.generators.stdlib_docs.parse import parse_file_to_dict
def main():
-
errors = []
- metrics_sources = os.path.join(FILE_DIR, "src", "trace_processor", "stdlib")
+ metrics_sources = os.path.join(ROOT_DIR, "src", "trace_processor", "stdlib")
for root, _, files in os.walk(metrics_sources, topdown=True):
for f in files:
path = os.path.join(root, f)
- if path.endswith(".sql"):
- with open(path) as f:
- sql = f.read()
- errors += parse_file_to_dict(path, sql)[1]
- sys.stderr.write("\n\n".join(errors))
+ if not path.endswith(".sql"):
+ continue
+ with open(path, 'r') as f:
+ sql = f.read()
+
+ res = parse_file_to_dict(path, sql)
+ errors += res if isinstance(res, list) else []
+ sys.stderr.write("\n".join(errors))
+ sys.stderr.write("\n")
return 0 if not errors else 1
diff --git a/tools/gen_amalgamated b/tools/gen_amalgamated
index c529155..fac7f60 100755
--- a/tools/gen_amalgamated
+++ b/tools/gen_amalgamated
@@ -44,6 +44,7 @@
# line).
gn_args = ' '.join([
'enable_perfetto_ipc=true',
+ 'enable_perfetto_zlib=false',
'is_debug=false',
'is_perfetto_build_generator=true',
'is_perfetto_embedder=true',
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 5317914..b154ee3 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -103,6 +103,8 @@
'//protos/perfetto/trace:perfetto_trace_protos',
'//src/trace_processor:demangle',
'//src/trace_processor:trace_processor_shell',
+ '//src/traced/probes:traced_probes',
+ '//src/traced/service:traced',
]
target_vendor_available = [
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 3121aa8..7461e4d 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -76,16 +76,23 @@
# exported publicly.
default_targets = [
'//src/base:perfetto_base_default_platform',
+ '//src/cloud_trace_processor:cloud_trace_processor',
'//src/ipc:perfetto_ipc',
'//src/ipc/protoc_plugin:ipc_plugin',
'//src/protozero:protozero',
- '//src/protozero/protoc_plugin:protozero_plugin',
'//src/protozero/protoc_plugin:cppgen_plugin',
- '//test:client_api_example',
+ '//src/protozero/protoc_plugin:protozero_plugin',
'//src/tools/proto_filter:proto_filter',
'//src/tools/proto_merger:proto_merger',
+ '//test:client_api_example',
] + public_targets
+# Proto targets are required by internal build rules but don't need to be
+# exported publicly.
+proto_default_targets = [
+ '//protos/perfetto/cloud_trace_processor:lite'
+]
+
# Proto target groups which will be made public.
proto_groups = {
'config': {
@@ -787,6 +794,10 @@
continue
gn.get_target(re.sub('(lite|zero|cpp|ipc)$', 'source_set', target.name))
+ # Discover all the default proto targets so it will be generated next.
+ for target in sorted(proto_default_targets):
+ gn.get_target(target)
+
# Generate targets for the transitive set of proto targets.
labels = [
l for target in sorted(itervalues(gn.proto_libs))
diff --git a/tools/gen_stdlib_docs_json.py b/tools/gen_stdlib_docs_json.py
index c89b7a7..1f2e5be 100755
--- a/tools/gen_stdlib_docs_json.py
+++ b/tools/gen_stdlib_docs_json.py
@@ -23,7 +23,7 @@
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(os.path.join(ROOT_DIR))
-from python.generators.stdlib_docs.stdlib import *
+from python.generators.stdlib_docs.parse import parse_file_to_dict
def main():
@@ -70,7 +70,8 @@
module_name = path.split("/")[0]
import_key = path.split(".sql")[0].replace("/", ".")
- docs = parse_file_to_dict(path, sql)[0]
+ docs = parse_file_to_dict(path, sql)
+ assert isinstance(docs, dict)
if not any(docs.values()):
continue
file_dict = {'import_key': import_key, **docs}
diff --git a/tools/gen_tp_table_docs.py b/tools/gen_tp_table_docs.py
index 97e183e..04339bf 100755
--- a/tools/gen_tp_table_docs.py
+++ b/tools/gen_tp_table_docs.py
@@ -27,6 +27,7 @@
#pylint: disable=wrong-import-position
from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import ColumnFlag
import python.generators.trace_processor_table.util as util
from python.generators.trace_processor_table.util import ParsedTable
from python.generators.trace_processor_table.util import ParsedColumn
@@ -43,6 +44,10 @@
if table.table.tabledoc.skip_id_and_type and is_skippable_col:
return None
+ # Ignore hidden columns in the documentation.
+ if ColumnFlag.HIDDEN in col.column.flags:
+ return None
+
# Our default assumption is the documentation for a column is a plain string
# so just make the comment for the column equal to that.
diff --git a/tools/install-build-deps b/tools/install-build-deps
index 255be95..c72eae9 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -23,6 +23,7 @@
import stat
import sys
import tempfile
+import time
import zipfile
from collections import namedtuple
@@ -60,6 +61,17 @@
'buildtools/emsdk', # Moved to buildtools/{mac,linux64}/emsdk
'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 .
+ 'buildtools/mac/clang-format',
+ 'buildtools/mac/gn',
+ 'buildtools/mac/ninja',
+ 'buildtools/linux64/clang-format',
+ 'buildtools/linux64/gn',
+ 'buildtools/linux64/ninja',
+ 'buildtools/win/clang-format.exe',
+ 'buildtools/win/gn.exe',
+ 'buildtools/win/ninja.exe',
]
# Dependencies required to build code on the host or when targeting desktop OS.
@@ -67,22 +79,22 @@
# GN. From https://chrome-infra-packages.appspot.com/dl/gn/gn/.
# git_revision:0725d7827575b239594fbc8fd5192873a1d62f44 .
Dependency(
- 'buildtools/mac/gn',
+ 'third_party/gn/gn',
'https://storage.googleapis.com/perfetto/gn-mac-1968-0725d782',
'9ced623a664560bba38bbadb9b91158ca4186358c847e17ab7d982b351373c2e',
'darwin', 'x64'),
Dependency(
- 'buildtools/mac/gn',
+ 'third_party/gn/gn',
'https://storage.googleapis.com/perfetto/gn-mac-arm64-1968-0725d782',
'd22336b5210b4dad5e36e8c28ce81187f491822cf4d8fd0a257b30d6bee3fd3f',
'darwin', 'arm64'),
Dependency(
- 'buildtools/linux64/gn',
+ 'third_party/gn/gn',
'https://storage.googleapis.com/perfetto/gn-linux64-1968-0725d782',
'f706aaa0676e3e22f5fc9ca482295d7caee8535d1869f99efa2358177b64f5cd',
'linux', 'x64'),
Dependency(
- 'buildtools/win/gn.exe',
+ 'third_party/gn/gn.exe',
'https://storage.googleapis.com/perfetto/gn-win-1968-0725d782',
'001f777f023c7a6959c778fb3a6b6cfc63f6baef953410ecdeaec350fb12285b',
'windows', 'x64'),
@@ -90,19 +102,19 @@
# clang-format
# From https://chromium.googlesource.com/chromium/src/buildtools/+/refs/heads/master/mac/clang-format.sha1
Dependency(
- 'buildtools/mac/clang-format',
+ 'third_party/clang-format/clang-format',
'https://storage.googleapis.com/chromium-clang-format/62bde1baa7196ad9df969fc1f06b66360b1a927b',
'6df686a937443cbe6efc013467a7ba5f98d3f187eb7765bb7abc6ce47626cf66',
'darwin', 'all'),
# From https://chromium.googlesource.com/chromium/src/buildtools/+/refs/heads/master/linux64/clang-format.sha1
Dependency(
- 'buildtools/linux64/clang-format',
+ 'third_party/clang-format/clang-format',
'https://storage.googleapis.com/chromium-clang-format/1baf0089e895c989a311b6a38ed94d0e8be4c0a7',
'd02a97a87e8c28898033aaf5986967b24dc47ebd5b376e1cd93e5009f22cd75e',
'linux', 'x64'),
# From https://chromium.googlesource.com/chromium/src/buildtools/+/refs/heads/master/win/clang-format.exe.sha1
Dependency(
- 'buildtools/win/clang-format.exe',
+ 'third_party/clang-format/clang-format.exe',
'https://storage.googleapis.com/chromium-clang-format/d4afd4eba27022f5f6d518133aebde57281677c9',
'2ba1b4d3ade90ea80316890b598ab5fc16777572be26afec6ce23117da121b80',
'windows', 'x64'),
@@ -115,17 +127,17 @@
# Ninja
Dependency(
- 'buildtools/mac/ninja',
+ 'third_party/ninja/ninja',
'https://storage.googleapis.com/perfetto/ninja-mac-x64_and_arm64-182',
'36e8b7aaa06911e1334feb664dd731a1cd69a15eb916a231a3d10ff65fca2c73',
'darwin', 'all'),
Dependency(
- 'buildtools/linux64/ninja',
+ 'third_party/ninja/ninja',
'https://storage.googleapis.com/perfetto/ninja-linux64-182',
'54ac6a01362190aaabf4cf276f9c8982cdf11b225438940fdde3339be0f2ecdc',
'linux', 'x64'),
Dependency(
- 'buildtools/win/ninja.exe',
+ 'third_party/ninja/ninja.exe',
'https://storage.googleapis.com/perfetto/ninja-win-182',
'09ced0fcd1a4dec7d1b798a2cf9ce5d20e5d2fbc2337343827f192ce47d0f491',
'windows', 'x64'),
@@ -399,8 +411,22 @@
TEST_DATA_SCRIPT = os.path.join(TOOLS_DIR, 'test_data')
+def CheckCallRetry(*args, **kwargs):
+ """ Like subprocess.check_call, with retries up to 5 times. """
+ MAX_ATTEMPTS = 5
+ for attempt in range(1, MAX_ATTEMPTS + 1):
+ try:
+ return subprocess.check_call(*args, **kwargs)
+ except subprocess.CalledProcessError as error:
+ if attempt == MAX_ATTEMPTS:
+ raise error
+ else:
+ logging.error(error)
+ time.sleep(attempt * 3)
+
+
def DownloadURL(url, out_file):
- subprocess.check_call(['curl', '-L', '-#', '-o', out_file, url])
+ CheckCallRetry(['curl', '-L', '-#', '-o', out_file, url])
def GetArch():
@@ -458,16 +484,23 @@
if not os.path.exists(path):
return
+ third_party_path = os.path.abspath(os.path.join(ROOT_DIR, 'third_party'))
buildtools_path = os.path.abspath(os.path.join(ROOT_DIR, 'buildtools'))
test_path = os.path.abspath(os.path.join(ROOT_DIR, 'test', 'data'))
if (not os.path.abspath(path).startswith(buildtools_path) and
- not os.path.abspath(path).startswith(test_path)):
+ not os.path.abspath(path).startswith(test_path) and
+ not os.path.abspath(path).startswith(third_party_path)):
# Safety check to prevent that some merge confilct ends up doing some
# rm -rf / or similar.
- logging.fatal('Cannot remove %s: outside of buildtools and test/data', path)
+ logging.fatal(
+ 'Cannot remove %s: outside of {buildtools, test/data, third_party}',
+ path)
sys.exit(1)
logging.info('Removing %s' % path)
- shutil.rmtree(path, onerror=del_read_only_for_windows)
+ if os.path.isdir(path):
+ shutil.rmtree(path, onerror=del_read_only_for_windows)
+ else:
+ os.remove(path)
def CheckoutGitRepo(path, git_url, revision, check_only):
@@ -480,10 +513,10 @@
MkdirRecursive(path)
logging.info('Fetching %s @ %s into %s', git_url, revision, path)
subprocess.check_call(['git', 'init', path], cwd=path)
- subprocess.check_call(
- ['git', 'fetch', '--quiet', '--depth', '1', git_url, revision], cwd=path)
+ CheckCallRetry(['git', 'fetch', '--quiet', '--depth', '1', git_url, revision],
+ cwd=path)
subprocess.check_call(['git', 'checkout', revision, '--quiet'], cwd=path)
- subprocess.check_call(
+ CheckCallRetry(
['git', 'submodule', 'update', '--init', '--recursive', '--quiet'],
cwd=path)
assert (IsGitRepoCheckoutOutAtRevision(path, revision))
@@ -538,6 +571,21 @@
dep.source_url, dep.checksum, actual_checksum))
+def CheckDepotToolsIsRecent():
+ gn_py_path = shutil.which('gn.py')
+ if gn_py_path is None:
+ return True # depot_tools doesn't seem to be installed in the PATH.
+ dt_dir = os.path.abspath(os.path.dirname(gn_py_path))
+ cmd = ['git', '-C', dt_dir, 'merge-base', '--is-ancestor', 'a0cf4321', 'HEAD']
+ git_ret = subprocess.call(cmd, stderr=subprocess.DEVNULL)
+ if git_ret == 0:
+ return True
+ print('\033[91mYour depot_tools revision is too old. Please run:\033[0m')
+ print('git -C %s fetch origin && git -C %s checkout -B main -t origin/main' %
+ (dt_dir, dt_dir))
+ return False
+
+
def Main():
parser = argparse.ArgumentParser()
parser.add_argument(
@@ -569,6 +617,9 @@
print('Building the UI on Windows is unsupported')
return 1
+ if not CheckDepotToolsIsRecent():
+ return 1
+
deps = BUILD_DEPS_HOST
if not args.no_toolchain:
deps += BUILD_DEPS_TOOLCHAIN_HOST
diff --git a/tools/record_android_trace b/tools/record_android_trace
index a9db452..013be00 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -351,12 +351,17 @@
class HttpHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
- self.send_header('Access-Control-Allow-Origin', '*')
- return super().end_headers()
+ self.send_header('Access-Control-Allow-Origin', self.server.allow_origin)
+ self.send_header('Cache-Control', 'no-cache')
+ super().end_headers()
def do_GET(self):
- self.server.last_request = self.path
- return super().do_GET()
+ if self.path != '/' + self.server.expected_fname:
+ self.send_error(404, "File not found")
+ return
+
+ self.server.fname_get_completed = True
+ super().do_GET()
def do_POST(self):
self.send_error(404, "File not found")
@@ -382,9 +387,15 @@
help = 'Output file or directory (default: %s)' % default_out_dir_str
parser.add_argument('-o', '--out', default=default_out_dir, help=help)
- help = 'Don\'t open in the browser'
+ help = 'Don\'t open or serve the trace'
parser.add_argument('-n', '--no-open', action='store_true', help=help)
+ help = 'Don\'t open in browser, but still serve trace (good for remote use)'
+ parser.add_argument('--no-open-browser', action='store_true', help=help)
+
+ help = 'The web address used to open trace files'
+ parser.add_argument('--origin', default='https://ui.perfetto.dev', help=help)
+
help = 'Force the use of the sideloaded binaries rather than system daemons'
parser.add_argument('--sideload', action='store_true', help=help)
@@ -627,7 +638,8 @@
if not args.no_open:
prt('\n')
prt('Opening the trace (%s) in the browser' % host_file)
- open_trace_in_browser(host_file)
+ open_browser = not args.no_open_browser
+ open_trace_in_browser(host_file, open_browser, args.origin)
def prt(msg, colors=ANSI.END):
@@ -655,17 +667,24 @@
sys.exit(1)
-def open_trace_in_browser(path):
+def open_trace_in_browser(path, open_browser, origin):
# We reuse the HTTP+RPC port because it's the only one allowed by the CSP.
PORT = 9001
+ path = os.path.abspath(path)
os.chdir(os.path.dirname(path))
fname = os.path.basename(path)
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
- webbrowser.open_new_tab(
- 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
- (PORT, fname))
- while httpd.__dict__.get('last_request') != '/' + fname:
+ address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}'
+ if open_browser:
+ webbrowser.open_new_tab(address)
+ else:
+ print(f'Open URL in browser: {address}')
+
+ httpd.expected_fname = fname
+ httpd.fname_get_completed = None
+ httpd.allow_origin = origin
+ while httpd.fname_get_completed is None:
httpd.handle_request()
diff --git a/tools/run_buildtools_binary.py b/tools/run_buildtools_binary.py
index 6fcfe1a..1ce25a7 100644
--- a/tools/run_buildtools_binary.py
+++ b/tools/run_buildtools_binary.py
@@ -46,7 +46,13 @@
cmd = args[0]
args = args[1:]
- exe_path = os.path.join(ROOT_DIR, 'buildtools', os_dir, cmd) + ext
+
+ # Some binaries have been migrated to third_party/xxx. Look into that path
+ # first (see b/261398524)
+ exe_path = os.path.join(ROOT_DIR, 'third_party', cmd, cmd) + ext
+ if not os.path.exists(exe_path):
+ exe_path = os.path.join(ROOT_DIR, 'buildtools', os_dir, cmd) + ext
+
if sys_name == 'windows':
# execl() behaves oddly on Windows: the spawned process doesn't seem to
# receive CTRL+C. Use subprocess instead.
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 7dc51bd..1bd97da 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -32,7 +32,8 @@
"pako": "^1.0.11",
"protobufjs": "^6.9.0",
"util": "^0.12.3",
- "uuid": "^9.0.0"
+ "uuid": "^9.0.0",
+ "vega-lite": "^5.9.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
@@ -1569,6 +1570,11 @@
"@types/har-format": "*"
}
},
+ "node_modules/@types/clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg=="
+ },
"node_modules/@types/color-convert": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
@@ -1601,6 +1607,12 @@
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ=="
},
+ "node_modules/@types/geojson": {
+ "version": "7946.0.4",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz",
+ "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==",
+ "peer": true
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -2164,7 +2176,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -2173,7 +2184,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -2914,6 +2924,14 @@
"wrap-ansi": "^6.2.0"
}
},
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -2980,6 +2998,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "peer": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -3093,6 +3120,229 @@
"resolved": "src/base/utils",
"link": true
},
+ "node_modules/d3-array": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz",
+ "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==",
+ "peer": true,
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "peer": true,
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "peer": true,
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "peer": true,
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo-projection": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz",
+ "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==",
+ "peer": true,
+ "dependencies": {
+ "commander": "7",
+ "d3-array": "1 - 3",
+ "d3-geo": "1.12.0 - 3"
+ },
+ "bin": {
+ "geo2svg": "bin/geo2svg.js",
+ "geograticule": "bin/geograticule.js",
+ "geoproject": "bin/geoproject.js",
+ "geoquantize": "bin/geoquantize.js",
+ "geostitch": "bin/geostitch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "peer": true,
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "peer": true,
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "peer": true,
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/data-urls": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
@@ -3236,6 +3486,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "peer": true,
+ "dependencies": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3355,14 +3614,12 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "dev": true,
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.2"
@@ -3741,7 +3998,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -4337,8 +4593,7 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-glob": {
"version": "3.2.12",
@@ -4371,8 +4626,7 @@
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
@@ -4562,7 +4816,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
@@ -4995,8 +5248,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
- "optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@@ -5115,6 +5366,15 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
@@ -5277,7 +5537,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -6358,6 +6617,11 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
+ "node_modules/json-stringify-pretty-compact": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz",
+ "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA=="
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -6934,7 +7198,6 @@
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
- "dev": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -8274,7 +8537,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -8388,6 +8650,12 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+ "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==",
+ "peer": true
+ },
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
@@ -8491,6 +8759,12 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "peer": true
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -8523,8 +8797,7 @@
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sane": {
"version": "4.1.0",
@@ -9590,7 +9863,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -9604,7 +9876,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -9886,11 +10157,30 @@
"node": ">=8.0"
}
},
+ "node_modules/topojson-client": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+ "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+ "peer": true,
+ "dependencies": {
+ "commander": "2"
+ },
+ "bin": {
+ "topo2geo": "bin/topo2geo",
+ "topomerge": "bin/topomerge",
+ "topoquantize": "bin/topoquantize"
+ }
+ },
+ "node_modules/topojson-client/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "peer": true
+ },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/trim-newlines": {
"version": "3.0.1",
@@ -9910,8 +10200,7 @@
"node_modules/tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
- "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
- "dev": true
+ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"node_modules/type-check": {
"version": "0.4.0",
@@ -10197,6 +10486,484 @@
"spdx-expression-parse": "^3.0.0"
}
},
+ "node_modules/vega": {
+ "version": "5.25.0",
+ "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz",
+ "integrity": "sha512-lr+uj0mhYlSN3JOKbMNp1RzZBenWp9DxJ7kR3lha58AFNCzzds7pmFa7yXPbtbaGhB7Buh/t6n+Bzk3Y0VnF5g==",
+ "peer": true,
+ "dependencies": {
+ "vega-crossfilter": "~4.1.1",
+ "vega-dataflow": "~5.7.5",
+ "vega-encode": "~4.9.2",
+ "vega-event-selector": "~3.0.1",
+ "vega-expression": "~5.1.0",
+ "vega-force": "~4.2.0",
+ "vega-format": "~1.1.1",
+ "vega-functions": "~5.13.2",
+ "vega-geo": "~4.4.1",
+ "vega-hierarchy": "~4.1.1",
+ "vega-label": "~1.2.1",
+ "vega-loader": "~4.5.1",
+ "vega-parser": "~6.2.0",
+ "vega-projection": "~1.6.0",
+ "vega-regression": "~1.2.0",
+ "vega-runtime": "~6.1.4",
+ "vega-scale": "~7.3.0",
+ "vega-scenegraph": "~4.10.2",
+ "vega-statistics": "~1.9.0",
+ "vega-time": "~2.1.1",
+ "vega-transforms": "~4.10.2",
+ "vega-typings": "~0.24.0",
+ "vega-util": "~1.17.2",
+ "vega-view": "~5.11.1",
+ "vega-view-transforms": "~4.5.9",
+ "vega-voronoi": "~4.2.1",
+ "vega-wordcloud": "~4.1.4"
+ }
+ },
+ "node_modules/vega-canvas": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/vega-canvas/-/vega-canvas-1.2.7.tgz",
+ "integrity": "sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q==",
+ "peer": true
+ },
+ "node_modules/vega-crossfilter": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/vega-crossfilter/-/vega-crossfilter-4.1.1.tgz",
+ "integrity": "sha512-yesvlMcwRwxrtAd9IYjuxWJJuAMI0sl7JvAFfYtuDkkGDtqfLXUcCzHIATqW6igVIE7tWwGxnbfvQLhLNgK44Q==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-dataflow": {
+ "version": "5.7.5",
+ "resolved": "https://registry.npmjs.org/vega-dataflow/-/vega-dataflow-5.7.5.tgz",
+ "integrity": "sha512-EdsIl6gouH67+8B0f22Owr2tKDiMPNNR8lEvJDcxmFw02nXd8juimclpLvjPQriqn6ta+3Dn5txqfD117H04YA==",
+ "peer": true,
+ "dependencies": {
+ "vega-format": "^1.1.1",
+ "vega-loader": "^4.5.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-encode": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/vega-encode/-/vega-encode-4.9.2.tgz",
+ "integrity": "sha512-c3J0LYkgYeXQxwnYkEzL15cCFBYPRaYUon8O2SZ6O4PhH4dfFTXBzSyT8+gh8AhBd572l2yGDfxpEYA6pOqdjg==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-interpolate": "^3.0.1",
+ "vega-dataflow": "^5.7.5",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-event-selector": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/vega-event-selector/-/vega-event-selector-3.0.1.tgz",
+ "integrity": "sha512-K5zd7s5tjr1LiOOkjGpcVls8GsH/f2CWCrWcpKy74gTCp+llCdwz0Enqo013ZlGaRNjfgD/o1caJRt3GSaec4A=="
+ },
+ "node_modules/vega-expression": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.0.tgz",
+ "integrity": "sha512-u8Rzja/cn2PEUkhQN3zUj3REwNewTA92ExrcASNKUJPCciMkHJEjESwFYuI6DWMCq4hQElQ92iosOAtwzsSTqA==",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-expression/node_modules/@types/estree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+ "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
+ },
+ "node_modules/vega-force": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/vega-force/-/vega-force-4.2.0.tgz",
+ "integrity": "sha512-aE2TlP264HXM1r3fl58AvZdKUWBNOGkIvn4EWyqeJdgO2vz46zSU7x7TzPG4ZLuo44cDRU5Ng3I1eQk23Asz6A==",
+ "peer": true,
+ "dependencies": {
+ "d3-force": "^3.0.0",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-format": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/vega-format/-/vega-format-1.1.1.tgz",
+ "integrity": "sha512-Rll7YgpYbsgaAa54AmtEWrxaJqgOh5fXlvM2wewO4trb9vwM53KBv4Q/uBWCLK3LLGeBXIF6gjDt2LFuJAUtkQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-format": "^3.1.0",
+ "d3-time-format": "^4.1.0",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-functions": {
+ "version": "5.13.2",
+ "resolved": "https://registry.npmjs.org/vega-functions/-/vega-functions-5.13.2.tgz",
+ "integrity": "sha512-YE1Xl3Qi28kw3vdXVYgKFMo20ttd3+SdKth1jUNtBDGGdrOpvPxxFhZkVqX+7FhJ5/1UkDoAYs/cZY0nRKiYgA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-color": "^3.1.0",
+ "d3-geo": "^3.1.0",
+ "vega-dataflow": "^5.7.5",
+ "vega-expression": "^5.1.0",
+ "vega-scale": "^7.3.0",
+ "vega-scenegraph": "^4.10.2",
+ "vega-selections": "^5.4.1",
+ "vega-statistics": "^1.8.1",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-geo": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/vega-geo/-/vega-geo-4.4.1.tgz",
+ "integrity": "sha512-s4WeZAL5M3ZUV27/eqSD3v0FyJz3PlP31XNSLFy4AJXHxHUeXT3qLiDHoVQnW5Om+uBCPDtTT1ROx1smGIf2aA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-color": "^3.1.0",
+ "d3-geo": "^3.1.0",
+ "vega-canvas": "^1.2.7",
+ "vega-dataflow": "^5.7.5",
+ "vega-projection": "^1.6.0",
+ "vega-statistics": "^1.8.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-hierarchy": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/vega-hierarchy/-/vega-hierarchy-4.1.1.tgz",
+ "integrity": "sha512-h5mbrDtPKHBBQ9TYbvEb/bCqmGTlUX97+4CENkyH21tJs7naza319B15KRK0NWOHuhbGhFmF8T0696tg+2c8XQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-hierarchy": "^3.1.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-label": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/vega-label/-/vega-label-1.2.1.tgz",
+ "integrity": "sha512-n/ackJ5lc0Xs9PInCaGumYn2awomPjJ87EMVT47xNgk2bHmJoZV1Ve/1PUM6Eh/KauY211wPMrNp/9Im+7Ripg==",
+ "peer": true,
+ "dependencies": {
+ "vega-canvas": "^1.2.6",
+ "vega-dataflow": "^5.7.3",
+ "vega-scenegraph": "^4.9.2",
+ "vega-util": "^1.15.2"
+ }
+ },
+ "node_modules/vega-lite": {
+ "version": "5.9.0",
+ "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.9.0.tgz",
+ "integrity": "sha512-VA3XDlF6nd/t46KDMfq8eNKOJKy9gpJuM+6CIl3jbWqS97jWXRWXp8DpUyDmbV+iq8n4hqNTaoPqDP/e03kifw==",
+ "dependencies": {
+ "@types/clone": "~2.1.1",
+ "clone": "~2.1.2",
+ "fast-deep-equal": "~3.1.3",
+ "fast-json-stable-stringify": "~2.1.0",
+ "json-stringify-pretty-compact": "~3.0.0",
+ "tslib": "~2.5.0",
+ "vega-event-selector": "~3.0.1",
+ "vega-expression": "~5.1.0",
+ "vega-util": "~1.17.2",
+ "yargs": "~17.7.1"
+ },
+ "bin": {
+ "vl2pdf": "bin/vl2pdf",
+ "vl2png": "bin/vl2png",
+ "vl2svg": "bin/vl2svg",
+ "vl2vg": "bin/vl2vg"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "vega": "^5.24.0"
+ }
+ },
+ "node_modules/vega-lite/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vega-lite/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/vega-lite/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/vega-lite/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vega-lite/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vega-loader": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/vega-loader/-/vega-loader-4.5.1.tgz",
+ "integrity": "sha512-qy5x32SaT0YkEujQM2yKqvLGV9XWQ2aEDSugBFTdYzu/1u4bxdUSRDREOlrJ9Km3RWIOgFiCkobPmFxo47SKuA==",
+ "peer": true,
+ "dependencies": {
+ "d3-dsv": "^3.0.1",
+ "node-fetch": "^2.6.7",
+ "topojson-client": "^3.1.0",
+ "vega-format": "^1.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-parser": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/vega-parser/-/vega-parser-6.2.0.tgz",
+ "integrity": "sha512-as+QnX8Qxe9q51L1C2sVBd+YYYctP848+zEvkBT2jlI2g30aZ6Uv7sKsq7QTL6DUbhXQKR0XQtzlanckSFdaOQ==",
+ "peer": true,
+ "dependencies": {
+ "vega-dataflow": "^5.7.5",
+ "vega-event-selector": "^3.0.1",
+ "vega-functions": "^5.13.1",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-projection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/vega-projection/-/vega-projection-1.6.0.tgz",
+ "integrity": "sha512-LGUaO/kpOEYuTlul+x+lBzyuL9qmMwP1yShdUWYLW+zXoeyGbs5OZW+NbPPwLYqJr5lpXDr/vGztFuA/6g2xvQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-geo": "^3.1.0",
+ "d3-geo-projection": "^4.0.0",
+ "vega-scale": "^7.3.0"
+ }
+ },
+ "node_modules/vega-regression": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vega-regression/-/vega-regression-1.2.0.tgz",
+ "integrity": "sha512-6TZoPlhV/280VbxACjRKqlE0Nv48z5g4CSNf1FmGGTWS1rQtElPTranSoVW4d7ET5eVQ6f9QLxNAiALptvEq+g==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.3",
+ "vega-statistics": "^1.9.0",
+ "vega-util": "^1.15.2"
+ }
+ },
+ "node_modules/vega-runtime": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/vega-runtime/-/vega-runtime-6.1.4.tgz",
+ "integrity": "sha512-0dDYXyFLQcxPQ2OQU0WuBVYLRZnm+/CwVu6i6N4idS7R9VXIX5581EkCh3pZ20pQ/+oaA7oJ0pR9rJgJ6rukRQ==",
+ "peer": true,
+ "dependencies": {
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-scale": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/vega-scale/-/vega-scale-7.3.0.tgz",
+ "integrity": "sha512-pMOAI2h+e1z7lsqKG+gMfR6NKN2sTcyjZbdJwntooW0uFHwjLGjMSY7kSd3nSEquF0HQ8qF7zR6gs1eRwlGimw==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-scenegraph": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/vega-scenegraph/-/vega-scenegraph-4.10.2.tgz",
+ "integrity": "sha512-R8m6voDZO5+etwNMcXf45afVM3XAtokMqxuDyddRl9l1YqSJfS+3u8hpolJ50c2q6ZN20BQiJwKT1o0bB7vKkA==",
+ "peer": true,
+ "dependencies": {
+ "d3-path": "^3.1.0",
+ "d3-shape": "^3.2.0",
+ "vega-canvas": "^1.2.7",
+ "vega-loader": "^4.5.1",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-selections": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/vega-selections/-/vega-selections-5.4.1.tgz",
+ "integrity": "sha512-EtYc4DvA+wXqBg9tq+kDomSoVUPCmQfS7hUxy2qskXEed79YTimt3Hcl1e1fW226I4AVDBEqTTKebmKMzbSgAA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "3.2.2",
+ "vega-expression": "^5.0.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-selections/node_modules/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==",
+ "peer": true,
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vega-statistics": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/vega-statistics/-/vega-statistics-1.9.0.tgz",
+ "integrity": "sha512-GAqS7mkatpXcMCQKWtFu1eMUKLUymjInU0O8kXshWaQrVWjPIO2lllZ1VNhdgE0qGj4oOIRRS11kzuijLshGXQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2"
+ }
+ },
+ "node_modules/vega-time": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/vega-time/-/vega-time-2.1.1.tgz",
+ "integrity": "sha512-z1qbgyX0Af2kQSGFbApwBbX2meenGvsoX8Nga8uyWN8VIbiySo/xqizz1KrP6NbB6R+x5egKmkjdnyNThPeEWA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-time": "^3.1.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-transforms": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/vega-transforms/-/vega-transforms-4.10.2.tgz",
+ "integrity": "sha512-sJELfEuYQ238PRG+GOqQch8D69RYnJevYSGLsRGQD2LxNz3j+GlUX6Pid+gUEH5HJy22Q5L0vsTl2ZNhIr4teQ==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-statistics": "^1.8.1",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-typings": {
+ "version": "0.24.1",
+ "resolved": "https://registry.npmjs.org/vega-typings/-/vega-typings-0.24.1.tgz",
+ "integrity": "sha512-WNw6tDxwMsynQ9osJb3RZi3g8GZruxVgXfe8N7nbqvNOgDQkUuVjqTZiwGg5kqjmLqx09lRRlskgp/ov7lEGeg==",
+ "peer": true,
+ "dependencies": {
+ "@types/geojson": "7946.0.4",
+ "vega-event-selector": "^3.0.1",
+ "vega-expression": "^5.0.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-util": {
+ "version": "1.17.2",
+ "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.17.2.tgz",
+ "integrity": "sha512-omNmGiZBdjm/jnHjZlywyYqafscDdHaELHx1q96n5UOz/FlO9JO99P4B3jZg391EFG8dqhWjQilSf2JH6F1mIw=="
+ },
+ "node_modules/vega-view": {
+ "version": "5.11.1",
+ "resolved": "https://registry.npmjs.org/vega-view/-/vega-view-5.11.1.tgz",
+ "integrity": "sha512-RoWxuoEMI7xVQJhPqNeLEHCezudsf3QkVMhH5tCovBqwBADQGqq9iWyax3ZzdyX1+P3eBgm7cnLvpqtN2hU8kA==",
+ "peer": true,
+ "dependencies": {
+ "d3-array": "^3.2.2",
+ "d3-timer": "^3.0.1",
+ "vega-dataflow": "^5.7.5",
+ "vega-format": "^1.1.1",
+ "vega-functions": "^5.13.1",
+ "vega-runtime": "^6.1.4",
+ "vega-scenegraph": "^4.10.2",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-view-transforms": {
+ "version": "4.5.9",
+ "resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-4.5.9.tgz",
+ "integrity": "sha512-NxEq4ZD4QwWGRrl2yDLnBRXM9FgCI+vvYb3ZC2+nVDtkUxOlEIKZsMMw31op5GZpfClWLbjCT3mVvzO2xaTF+g==",
+ "peer": true,
+ "dependencies": {
+ "vega-dataflow": "^5.7.5",
+ "vega-scenegraph": "^4.10.2",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-voronoi": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/vega-voronoi/-/vega-voronoi-4.2.1.tgz",
+ "integrity": "sha512-zzi+fxU/SBad4irdLLsG3yhZgXWZezraGYVQfZFWe8kl7W/EHUk+Eqk/eetn4bDeJ6ltQskX+UXH3OP5Vh0Q0Q==",
+ "peer": true,
+ "dependencies": {
+ "d3-delaunay": "^6.0.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "node_modules/vega-wordcloud": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/vega-wordcloud/-/vega-wordcloud-4.1.4.tgz",
+ "integrity": "sha512-oeZLlnjiusLAU5vhk0IIdT5QEiJE0x6cYoGNq1th+EbwgQp153t4r026fcib9oq15glHFOzf81a8hHXHSJm1Jw==",
+ "peer": true,
+ "dependencies": {
+ "vega-canvas": "^1.2.7",
+ "vega-dataflow": "^5.7.5",
+ "vega-scale": "^7.3.0",
+ "vega-statistics": "^1.8.1",
+ "vega-util": "^1.17.1"
+ }
+ },
"node_modules/vlq": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
@@ -10237,8 +11004,7 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-encoding": {
"version": "1.0.5",
@@ -10271,7 +11037,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
@@ -11706,6 +12471,11 @@
"@types/har-format": "*"
}
},
+ "@types/clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@types/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha512-BZIU34bSYye0j/BFcPraiDZ5ka6MJADjcDVELGf7glr9K+iE8NYVjFslJFVWzskSxkLLyCrSPScE82/UUoBSvg=="
+ },
"@types/color-convert": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
@@ -11738,6 +12508,12 @@
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ=="
},
+ "@types/geojson": {
+ "version": "7946.0.4",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz",
+ "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==",
+ "peer": true
+ },
"@types/graceful-fs": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -12161,14 +12937,12 @@
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -12711,6 +13485,11 @@
"wrap-ansi": "^6.2.0"
}
},
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -12761,6 +13540,12 @@
"delayed-stream": "~1.0.0"
}
},
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "peer": true
+ },
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -12860,6 +13645,157 @@
"custom_utils": {
"version": "file:src/base/utils"
},
+ "d3-array": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.3.tgz",
+ "integrity": "sha512-JRHwbQQ84XuAESWhvIPaUV4/1UYTBOLiOPGWqgFDHZS1D5QN9c57FbH3QpEnQMYiOXNzKUQyGTZf+EVO7RT5TQ==",
+ "peer": true,
+ "requires": {
+ "internmap": "1 - 2"
+ }
+ },
+ "d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "peer": true
+ },
+ "d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "peer": true,
+ "requires": {
+ "delaunator": "5"
+ }
+ },
+ "d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "peer": true
+ },
+ "d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "peer": true,
+ "requires": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ }
+ },
+ "d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "peer": true,
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ },
+ "d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "peer": true
+ },
+ "d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "2.5.0 - 3"
+ }
+ },
+ "d3-geo-projection": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz",
+ "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==",
+ "peer": true,
+ "requires": {
+ "commander": "7",
+ "d3-array": "1 - 3",
+ "d3-geo": "1.12.0 - 3"
+ }
+ },
+ "d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "peer": true
+ },
+ "d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "peer": true,
+ "requires": {
+ "d3-color": "1 - 3"
+ }
+ },
+ "d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "peer": true
+ },
+ "d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "peer": true
+ },
+ "d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "peer": true,
+ "requires": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ }
+ },
+ "d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "peer": true,
+ "requires": {
+ "d3-path": "^3.1.0"
+ }
+ },
+ "d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "peer": true,
+ "requires": {
+ "d3-array": "2 - 3"
+ }
+ },
+ "d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "peer": true,
+ "requires": {
+ "d3-time": "1 - 3"
+ }
+ },
+ "d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "peer": true
+ },
"data-urls": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
@@ -12966,6 +13902,15 @@
"isobject": "^3.0.1"
}
},
+ "delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "peer": true,
+ "requires": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -13057,14 +14002,12 @@
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"encoding": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "dev": true,
"optional": true,
"requires": {
"iconv-lite": "^0.6.2"
@@ -13252,8 +14195,7 @@
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"escape-string-regexp": {
"version": "4.0.0",
@@ -13707,8 +14649,7 @@
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
"version": "3.2.12",
@@ -13737,8 +14678,7 @@
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
- "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
- "dev": true
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fast-levenshtein": {
"version": "2.0.6",
@@ -13890,8 +14830,7 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-intrinsic": {
"version": "1.2.0",
@@ -14221,8 +15160,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
- "optional": true,
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
@@ -14296,6 +15233,12 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "peer": true
+ },
"ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
@@ -14409,8 +15352,7 @@
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"is-generator-fn": {
"version": "2.1.0",
@@ -15246,6 +16188,11 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"dev": true
},
+ "json-stringify-pretty-compact": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz",
+ "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA=="
+ },
"json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -15698,7 +16645,6 @@
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
- "dev": true,
"requires": {
"whatwg-url": "^5.0.0"
}
@@ -16714,8 +17660,7 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
},
"require-main-filename": {
"version": "2.0.0",
@@ -16796,6 +17741,12 @@
"glob": "^7.1.3"
}
},
+ "robust-predicates": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+ "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==",
+ "peer": true
+ },
"rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
@@ -16868,6 +17819,12 @@
"queue-microtask": "^1.2.2"
}
},
+ "rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "peer": true
+ },
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -16886,8 +17843,7 @@
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sane": {
"version": "4.1.0",
@@ -17764,7 +18720,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -17775,7 +18730,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -17997,11 +18951,27 @@
"is-number": "^7.0.0"
}
},
+ "topojson-client": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz",
+ "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==",
+ "peer": true,
+ "requires": {
+ "commander": "2"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "peer": true
+ }
+ }
+ },
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"trim-newlines": {
"version": "3.0.1",
@@ -18018,8 +18988,7 @@
"tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
- "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
- "dev": true
+ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"type-check": {
"version": "0.4.0",
@@ -18244,6 +19213,457 @@
"spdx-expression-parse": "^3.0.0"
}
},
+ "vega": {
+ "version": "5.25.0",
+ "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz",
+ "integrity": "sha512-lr+uj0mhYlSN3JOKbMNp1RzZBenWp9DxJ7kR3lha58AFNCzzds7pmFa7yXPbtbaGhB7Buh/t6n+Bzk3Y0VnF5g==",
+ "peer": true,
+ "requires": {
+ "vega-crossfilter": "~4.1.1",
+ "vega-dataflow": "~5.7.5",
+ "vega-encode": "~4.9.2",
+ "vega-event-selector": "~3.0.1",
+ "vega-expression": "~5.1.0",
+ "vega-force": "~4.2.0",
+ "vega-format": "~1.1.1",
+ "vega-functions": "~5.13.2",
+ "vega-geo": "~4.4.1",
+ "vega-hierarchy": "~4.1.1",
+ "vega-label": "~1.2.1",
+ "vega-loader": "~4.5.1",
+ "vega-parser": "~6.2.0",
+ "vega-projection": "~1.6.0",
+ "vega-regression": "~1.2.0",
+ "vega-runtime": "~6.1.4",
+ "vega-scale": "~7.3.0",
+ "vega-scenegraph": "~4.10.2",
+ "vega-statistics": "~1.9.0",
+ "vega-time": "~2.1.1",
+ "vega-transforms": "~4.10.2",
+ "vega-typings": "~0.24.0",
+ "vega-util": "~1.17.2",
+ "vega-view": "~5.11.1",
+ "vega-view-transforms": "~4.5.9",
+ "vega-voronoi": "~4.2.1",
+ "vega-wordcloud": "~4.1.4"
+ }
+ },
+ "vega-canvas": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/vega-canvas/-/vega-canvas-1.2.7.tgz",
+ "integrity": "sha512-OkJ9CACVcN9R5Pi9uF6MZBF06pO6qFpDYHWSKBJsdHP5o724KrsgR6UvbnXFH82FdsiTOff/HqjuaG8C7FL+9Q==",
+ "peer": true
+ },
+ "vega-crossfilter": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/vega-crossfilter/-/vega-crossfilter-4.1.1.tgz",
+ "integrity": "sha512-yesvlMcwRwxrtAd9IYjuxWJJuAMI0sl7JvAFfYtuDkkGDtqfLXUcCzHIATqW6igVIE7tWwGxnbfvQLhLNgK44Q==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-dataflow": {
+ "version": "5.7.5",
+ "resolved": "https://registry.npmjs.org/vega-dataflow/-/vega-dataflow-5.7.5.tgz",
+ "integrity": "sha512-EdsIl6gouH67+8B0f22Owr2tKDiMPNNR8lEvJDcxmFw02nXd8juimclpLvjPQriqn6ta+3Dn5txqfD117H04YA==",
+ "peer": true,
+ "requires": {
+ "vega-format": "^1.1.1",
+ "vega-loader": "^4.5.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-encode": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/vega-encode/-/vega-encode-4.9.2.tgz",
+ "integrity": "sha512-c3J0LYkgYeXQxwnYkEzL15cCFBYPRaYUon8O2SZ6O4PhH4dfFTXBzSyT8+gh8AhBd572l2yGDfxpEYA6pOqdjg==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-interpolate": "^3.0.1",
+ "vega-dataflow": "^5.7.5",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-event-selector": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/vega-event-selector/-/vega-event-selector-3.0.1.tgz",
+ "integrity": "sha512-K5zd7s5tjr1LiOOkjGpcVls8GsH/f2CWCrWcpKy74gTCp+llCdwz0Enqo013ZlGaRNjfgD/o1caJRt3GSaec4A=="
+ },
+ "vega-expression": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.0.tgz",
+ "integrity": "sha512-u8Rzja/cn2PEUkhQN3zUj3REwNewTA92ExrcASNKUJPCciMkHJEjESwFYuI6DWMCq4hQElQ92iosOAtwzsSTqA==",
+ "requires": {
+ "@types/estree": "^1.0.0",
+ "vega-util": "^1.17.1"
+ },
+ "dependencies": {
+ "@types/estree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
+ "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
+ }
+ }
+ },
+ "vega-force": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/vega-force/-/vega-force-4.2.0.tgz",
+ "integrity": "sha512-aE2TlP264HXM1r3fl58AvZdKUWBNOGkIvn4EWyqeJdgO2vz46zSU7x7TzPG4ZLuo44cDRU5Ng3I1eQk23Asz6A==",
+ "peer": true,
+ "requires": {
+ "d3-force": "^3.0.0",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-format": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/vega-format/-/vega-format-1.1.1.tgz",
+ "integrity": "sha512-Rll7YgpYbsgaAa54AmtEWrxaJqgOh5fXlvM2wewO4trb9vwM53KBv4Q/uBWCLK3LLGeBXIF6gjDt2LFuJAUtkQ==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-format": "^3.1.0",
+ "d3-time-format": "^4.1.0",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-functions": {
+ "version": "5.13.2",
+ "resolved": "https://registry.npmjs.org/vega-functions/-/vega-functions-5.13.2.tgz",
+ "integrity": "sha512-YE1Xl3Qi28kw3vdXVYgKFMo20ttd3+SdKth1jUNtBDGGdrOpvPxxFhZkVqX+7FhJ5/1UkDoAYs/cZY0nRKiYgA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-color": "^3.1.0",
+ "d3-geo": "^3.1.0",
+ "vega-dataflow": "^5.7.5",
+ "vega-expression": "^5.1.0",
+ "vega-scale": "^7.3.0",
+ "vega-scenegraph": "^4.10.2",
+ "vega-selections": "^5.4.1",
+ "vega-statistics": "^1.8.1",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-geo": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/vega-geo/-/vega-geo-4.4.1.tgz",
+ "integrity": "sha512-s4WeZAL5M3ZUV27/eqSD3v0FyJz3PlP31XNSLFy4AJXHxHUeXT3qLiDHoVQnW5Om+uBCPDtTT1ROx1smGIf2aA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-color": "^3.1.0",
+ "d3-geo": "^3.1.0",
+ "vega-canvas": "^1.2.7",
+ "vega-dataflow": "^5.7.5",
+ "vega-projection": "^1.6.0",
+ "vega-statistics": "^1.8.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-hierarchy": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/vega-hierarchy/-/vega-hierarchy-4.1.1.tgz",
+ "integrity": "sha512-h5mbrDtPKHBBQ9TYbvEb/bCqmGTlUX97+4CENkyH21tJs7naza319B15KRK0NWOHuhbGhFmF8T0696tg+2c8XQ==",
+ "peer": true,
+ "requires": {
+ "d3-hierarchy": "^3.1.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-label": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/vega-label/-/vega-label-1.2.1.tgz",
+ "integrity": "sha512-n/ackJ5lc0Xs9PInCaGumYn2awomPjJ87EMVT47xNgk2bHmJoZV1Ve/1PUM6Eh/KauY211wPMrNp/9Im+7Ripg==",
+ "peer": true,
+ "requires": {
+ "vega-canvas": "^1.2.6",
+ "vega-dataflow": "^5.7.3",
+ "vega-scenegraph": "^4.9.2",
+ "vega-util": "^1.15.2"
+ }
+ },
+ "vega-lite": {
+ "version": "5.9.0",
+ "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.9.0.tgz",
+ "integrity": "sha512-VA3XDlF6nd/t46KDMfq8eNKOJKy9gpJuM+6CIl3jbWqS97jWXRWXp8DpUyDmbV+iq8n4hqNTaoPqDP/e03kifw==",
+ "requires": {
+ "@types/clone": "~2.1.1",
+ "clone": "~2.1.2",
+ "fast-deep-equal": "~3.1.3",
+ "fast-json-stable-stringify": "~2.1.0",
+ "json-stringify-pretty-compact": "~3.0.0",
+ "tslib": "~2.5.0",
+ "vega-event-selector": "~3.0.1",
+ "vega-expression": "~5.1.0",
+ "vega-util": "~1.17.2",
+ "yargs": "~17.7.1"
+ },
+ "dependencies": {
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+ }
+ }
+ },
+ "vega-loader": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/vega-loader/-/vega-loader-4.5.1.tgz",
+ "integrity": "sha512-qy5x32SaT0YkEujQM2yKqvLGV9XWQ2aEDSugBFTdYzu/1u4bxdUSRDREOlrJ9Km3RWIOgFiCkobPmFxo47SKuA==",
+ "peer": true,
+ "requires": {
+ "d3-dsv": "^3.0.1",
+ "node-fetch": "^2.6.7",
+ "topojson-client": "^3.1.0",
+ "vega-format": "^1.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-parser": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/vega-parser/-/vega-parser-6.2.0.tgz",
+ "integrity": "sha512-as+QnX8Qxe9q51L1C2sVBd+YYYctP848+zEvkBT2jlI2g30aZ6Uv7sKsq7QTL6DUbhXQKR0XQtzlanckSFdaOQ==",
+ "peer": true,
+ "requires": {
+ "vega-dataflow": "^5.7.5",
+ "vega-event-selector": "^3.0.1",
+ "vega-functions": "^5.13.1",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-projection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/vega-projection/-/vega-projection-1.6.0.tgz",
+ "integrity": "sha512-LGUaO/kpOEYuTlul+x+lBzyuL9qmMwP1yShdUWYLW+zXoeyGbs5OZW+NbPPwLYqJr5lpXDr/vGztFuA/6g2xvQ==",
+ "peer": true,
+ "requires": {
+ "d3-geo": "^3.1.0",
+ "d3-geo-projection": "^4.0.0",
+ "vega-scale": "^7.3.0"
+ }
+ },
+ "vega-regression": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vega-regression/-/vega-regression-1.2.0.tgz",
+ "integrity": "sha512-6TZoPlhV/280VbxACjRKqlE0Nv48z5g4CSNf1FmGGTWS1rQtElPTranSoVW4d7ET5eVQ6f9QLxNAiALptvEq+g==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.3",
+ "vega-statistics": "^1.9.0",
+ "vega-util": "^1.15.2"
+ }
+ },
+ "vega-runtime": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/vega-runtime/-/vega-runtime-6.1.4.tgz",
+ "integrity": "sha512-0dDYXyFLQcxPQ2OQU0WuBVYLRZnm+/CwVu6i6N4idS7R9VXIX5581EkCh3pZ20pQ/+oaA7oJ0pR9rJgJ6rukRQ==",
+ "peer": true,
+ "requires": {
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-scale": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/vega-scale/-/vega-scale-7.3.0.tgz",
+ "integrity": "sha512-pMOAI2h+e1z7lsqKG+gMfR6NKN2sTcyjZbdJwntooW0uFHwjLGjMSY7kSd3nSEquF0HQ8qF7zR6gs1eRwlGimw==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-scenegraph": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/vega-scenegraph/-/vega-scenegraph-4.10.2.tgz",
+ "integrity": "sha512-R8m6voDZO5+etwNMcXf45afVM3XAtokMqxuDyddRl9l1YqSJfS+3u8hpolJ50c2q6ZN20BQiJwKT1o0bB7vKkA==",
+ "peer": true,
+ "requires": {
+ "d3-path": "^3.1.0",
+ "d3-shape": "^3.2.0",
+ "vega-canvas": "^1.2.7",
+ "vega-loader": "^4.5.1",
+ "vega-scale": "^7.3.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-selections": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/vega-selections/-/vega-selections-5.4.1.tgz",
+ "integrity": "sha512-EtYc4DvA+wXqBg9tq+kDomSoVUPCmQfS7hUxy2qskXEed79YTimt3Hcl1e1fW226I4AVDBEqTTKebmKMzbSgAA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "3.2.2",
+ "vega-expression": "^5.0.1",
+ "vega-util": "^1.17.1"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==",
+ "peer": true,
+ "requires": {
+ "internmap": "1 - 2"
+ }
+ }
+ }
+ },
+ "vega-statistics": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/vega-statistics/-/vega-statistics-1.9.0.tgz",
+ "integrity": "sha512-GAqS7mkatpXcMCQKWtFu1eMUKLUymjInU0O8kXshWaQrVWjPIO2lllZ1VNhdgE0qGj4oOIRRS11kzuijLshGXQ==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2"
+ }
+ },
+ "vega-time": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/vega-time/-/vega-time-2.1.1.tgz",
+ "integrity": "sha512-z1qbgyX0Af2kQSGFbApwBbX2meenGvsoX8Nga8uyWN8VIbiySo/xqizz1KrP6NbB6R+x5egKmkjdnyNThPeEWA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-time": "^3.1.0",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-transforms": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/vega-transforms/-/vega-transforms-4.10.2.tgz",
+ "integrity": "sha512-sJELfEuYQ238PRG+GOqQch8D69RYnJevYSGLsRGQD2LxNz3j+GlUX6Pid+gUEH5HJy22Q5L0vsTl2ZNhIr4teQ==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-statistics": "^1.8.1",
+ "vega-time": "^2.1.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-typings": {
+ "version": "0.24.1",
+ "resolved": "https://registry.npmjs.org/vega-typings/-/vega-typings-0.24.1.tgz",
+ "integrity": "sha512-WNw6tDxwMsynQ9osJb3RZi3g8GZruxVgXfe8N7nbqvNOgDQkUuVjqTZiwGg5kqjmLqx09lRRlskgp/ov7lEGeg==",
+ "peer": true,
+ "requires": {
+ "@types/geojson": "7946.0.4",
+ "vega-event-selector": "^3.0.1",
+ "vega-expression": "^5.0.1",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-util": {
+ "version": "1.17.2",
+ "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.17.2.tgz",
+ "integrity": "sha512-omNmGiZBdjm/jnHjZlywyYqafscDdHaELHx1q96n5UOz/FlO9JO99P4B3jZg391EFG8dqhWjQilSf2JH6F1mIw=="
+ },
+ "vega-view": {
+ "version": "5.11.1",
+ "resolved": "https://registry.npmjs.org/vega-view/-/vega-view-5.11.1.tgz",
+ "integrity": "sha512-RoWxuoEMI7xVQJhPqNeLEHCezudsf3QkVMhH5tCovBqwBADQGqq9iWyax3ZzdyX1+P3eBgm7cnLvpqtN2hU8kA==",
+ "peer": true,
+ "requires": {
+ "d3-array": "^3.2.2",
+ "d3-timer": "^3.0.1",
+ "vega-dataflow": "^5.7.5",
+ "vega-format": "^1.1.1",
+ "vega-functions": "^5.13.1",
+ "vega-runtime": "^6.1.4",
+ "vega-scenegraph": "^4.10.2",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-view-transforms": {
+ "version": "4.5.9",
+ "resolved": "https://registry.npmjs.org/vega-view-transforms/-/vega-view-transforms-4.5.9.tgz",
+ "integrity": "sha512-NxEq4ZD4QwWGRrl2yDLnBRXM9FgCI+vvYb3ZC2+nVDtkUxOlEIKZsMMw31op5GZpfClWLbjCT3mVvzO2xaTF+g==",
+ "peer": true,
+ "requires": {
+ "vega-dataflow": "^5.7.5",
+ "vega-scenegraph": "^4.10.2",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-voronoi": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/vega-voronoi/-/vega-voronoi-4.2.1.tgz",
+ "integrity": "sha512-zzi+fxU/SBad4irdLLsG3yhZgXWZezraGYVQfZFWe8kl7W/EHUk+Eqk/eetn4bDeJ6ltQskX+UXH3OP5Vh0Q0Q==",
+ "peer": true,
+ "requires": {
+ "d3-delaunay": "^6.0.2",
+ "vega-dataflow": "^5.7.5",
+ "vega-util": "^1.17.1"
+ }
+ },
+ "vega-wordcloud": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/vega-wordcloud/-/vega-wordcloud-4.1.4.tgz",
+ "integrity": "sha512-oeZLlnjiusLAU5vhk0IIdT5QEiJE0x6cYoGNq1th+EbwgQp153t4r026fcib9oq15glHFOzf81a8hHXHSJm1Jw==",
+ "peer": true,
+ "requires": {
+ "vega-canvas": "^1.2.7",
+ "vega-dataflow": "^5.7.5",
+ "vega-scale": "^7.3.0",
+ "vega-statistics": "^1.8.1",
+ "vega-util": "^1.17.1"
+ }
+ },
"vlq": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
@@ -18280,8 +19700,7 @@
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-encoding": {
"version": "1.0.5",
@@ -18313,7 +19732,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
diff --git a/ui/package.json b/ui/package.json
index 0f4d69b..1257ab7 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -30,7 +30,8 @@
"pako": "^1.0.11",
"protobufjs": "^6.9.0",
"util": "^0.12.3",
- "uuid": "^9.0.0"
+ "uuid": "^9.0.0",
+ "vega-lite": "^5.9.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.0.1",
diff --git a/ui/release/channels.json b/ui/release/channels.json
index fb05de4..09f14f4 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
"channels": [
{
"name": "stable",
- "rev": "1f0f1aa6babb0b3ff273c5102aa677b2604e9a4e"
+ "rev": "1b8de44522f33d371def110325bf31b847c1f5c4"
},
{
"name": "canary",
- "rev": "056cc57893b57b82ed411256c2bd4091641f55fb"
+ "rev": "a34bc46479e2e659532f7e208c6eba5462c26bae"
},
{
"name": "autopush",
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index 0435422..c26ee4f 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -187,10 +187,6 @@
}
}
-.query-table-container {
- width: 100%;
-}
-
@mixin table-font-size {
font-size: 14px;
line-height: 18px;
@@ -270,8 +266,9 @@
font-size: 14px;
border: 0;
thead td {
- position: sticky;
- top: 0;
+ // TODO(stevegolton): Get sticky working again.
+ // position: sticky;
+ // top: 0;
background-color: hsl(214, 22%, 90%);
color: #262f3c;
text-align: center;
@@ -471,17 +468,7 @@
.details-panel-container {
box-shadow: #0000003b 0px 0px 3px 1px;
- position: relative;
- overflow-x: hidden;
- overflow-y: auto;
- flex: 1 1 auto;
- // TODO(hjd): This causes the sticky header to flicker when scrolling.
- // Is will-change necessary in the details panel?
- // will-change: transform;
- display: grid;
- grid-template-columns: 1fr;
- grid-template-rows: 1fr;
- grid-template-areas: "space";
+ overflow: auto;
}
.pinned-panel-container {
@@ -520,9 +507,10 @@
position: relative; // Otherwise canvas covers panel dom.
&.sticky {
- position: sticky;
+ // TODO(stevegolton): Get sticky working again.
+ // position: sticky;
z-index: 3;
- top: 0;
+ // top: 0;
background-color: hsl(215, 22%, 19%);
}
}
@@ -555,6 +543,9 @@
}
header.overview {
+ position: sticky;
+ top: 0;
+ left: 0;
display: flex;
align-content: baseline;
background-color: hsl(213, 22%, 82%);
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index 9ba93b3..50e0d20 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -12,92 +12,87 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-.details-content {
+.handle {
+ background-color: hsl(215, 1%, 95%);
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ border-bottom: none;
+ cursor: row-resize;
+ // Disable user selection since this handle is draggable to resize the
+ // bottom panels.
+ user-select: none;
+ height: 28px;
+ min-height: 28px;
display: grid;
- grid-template-rows: auto 1fr;
+ grid-auto-columns: 1fr 60px;
+ grid-template-areas: "tabs buttons";
- .handle {
- background-color: hsl(215, 1%, 95%);
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-bottom: none;
- cursor: row-resize;
- // Disable user selection since this handle is draggable to resize the
- // bottom panels.
- user-select: none;
- height: 28px;
- min-height: 28px;
- display: grid;
- grid-auto-columns: 1fr 60px;
- grid-template-areas: "tabs buttons";
+ .tabs {
+ display: flex;
+ grid-area: tabs;
+ overflow: hidden;
- .tabs {
- display: flex;
- grid-area: tabs;
+ .tab {
+ font-family: "Roboto Condensed", sans-serif;
+ color: #3c4b5d;
+ padding: 3px 10px 0 10px;
+ margin-top: 3px;
+ font-size: 13px;
+ border-radius: 3px 3px 0 0;
+ background-color: #0000000f;
+ border-right: solid 1px hsla(0, 0%, 75%, 1);
overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ z-index: 5;
+ box-shadow: #0000003b 0px 0px 3px 1px;
- .tab {
- font-family: "Roboto Condensed", sans-serif;
- color: #3c4b5d;
- padding: 3px 10px 0 10px;
- margin-top: 3px;
- font-size: 13px;
- border-radius: 3px 3px 0 0;
- background-color: #0000000f;
- border-right: solid 1px hsla(0, 0%, 75%, 1);
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- z-index: 5;
- box-shadow: #0000003b 0px 0px 3px 1px;
-
- &[active] {
- background-color: white;
- &:hover {
- cursor: default;
- background-color: white;
- }
- }
-
- &:hover {
- cursor: pointer;
- background-color: hsla(0, 0%, 85%, 1);
- }
-
- &:nth-child(1) {
- margin-left: 3px;
- }
- }
- }
-
- i.material-icons {
- font-size: 24px;
- margin-right: 5px;
- margin-top: 1px;
- &:hover {
- cursor: pointer;
- }
- &[disabled] {
- color: rgb(219, 219, 219);
+ &[active] {
+ background-color: white;
&:hover {
cursor: default;
+ background-color: white;
}
}
- }
- .buttons {
- grid-area: buttons;
- text-align: right;
- }
+ &:hover {
+ cursor: pointer;
+ background-color: hsla(0, 0%, 85%, 1);
+ }
- .handle-title {
- font-family: "Roboto Condensed", sans-serif;
- font-weight: 300;
- color: #3c4b5d;
- margin-left: 5px;
- padding: 5px;
- font-size: 13px;
+ &:nth-child(1) {
+ margin-left: 3px;
+ }
}
}
+
+ i.material-icons {
+ font-size: 24px;
+ margin-right: 5px;
+ margin-top: 1px;
+ &:hover {
+ cursor: pointer;
+ }
+ &[disabled] {
+ color: rgb(219, 219, 219);
+ &:hover {
+ cursor: default;
+ }
+ }
+ }
+
+ .buttons {
+ grid-area: buttons;
+ text-align: right;
+ }
+
+ .handle-title {
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 300;
+ color: #3c4b5d;
+ margin-left: 5px;
+ padding: 5px;
+ font-size: 13px;
+ }
}
.details-panel {
@@ -293,6 +288,7 @@
.details-table-multicolumn {
display: flex;
+ user-select: 'text';
}
.flow-link:hover {
@@ -401,16 +397,12 @@
}
.log-panel {
- width: 100%;
- height: 100%;
- display: grid;
- grid-template-rows: auto 1fr;
- font-family: "Roboto Condensed", sans-serif;
- user-select: text;
+ display: contents;
header {
position: sticky;
top: 0;
+ left: 0;
z-index: 1;
background-color: white;
color: #3c4b5d;
@@ -619,14 +611,12 @@
}
.ftrace-panel {
- display: grid;
- grid-template-rows: auto 1fr;
- font-family: "Roboto Condensed", sans-serif;
- user-select: text;
+ display: contents;
.sticky {
position: sticky;
top: 0;
+ left: 0;
z-index: 1;
background-color: white;
color: #3c4b5d;
diff --git a/ui/src/base/bigint_math.ts b/ui/src/base/bigint_math.ts
new file mode 100644
index 0000000..45fab7b
--- /dev/null
+++ b/ui/src/base/bigint_math.ts
@@ -0,0 +1,105 @@
+// 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.
+
+export class BigintMath {
+ static INT64_MAX: bigint = (2n ** 63n) - 1n;
+
+ // Returns the smallest integral power of 2 that is not smaller than n.
+ // If n is less than or equal to 0, returns 1.
+ static bitCeil(n: bigint): bigint {
+ let result = 1n;
+ while (result < n) {
+ result <<= 1n;
+ }
+ return result;
+ };
+
+ // Returns the largest integral power of 2 which is not greater than n.
+ // If n is less than or equal to 0, returns 1.
+ static bitFloor(n: bigint): bigint {
+ let result = 1n;
+ while ((result << 1n) <= n) {
+ result <<= 1n;
+ }
+ return result;
+ };
+
+ // Returns the largest integral value x where 2^x is not greater than n.
+ static log2(n: bigint): number {
+ let result = 1n;
+ let log2 = 0;
+ while ((result << 1n) <= n) {
+ result <<= 1n;
+ ++log2;
+ }
+ return log2;
+ }
+
+ // Returns the integral multiple of step which is closest to n.
+ // If step is less than or equal to 0, returns n.
+ static quant(n: bigint, step: bigint): bigint {
+ step = BigintMath.max(1n, step);
+ const halfStep = step / 2n;
+ return step * ((n + halfStep) / step);
+ }
+
+ // Returns the largest integral multiple of step which is not larger than n.
+ // If step is less than or equal to 0, returns n.
+ static quantFloor(n: bigint, step: bigint): bigint {
+ step = BigintMath.max(1n, step);
+ return step * (n / step);
+ }
+
+ // Returns the smallest integral multiple of step which is not smaller than n.
+ // If step is less than or equal to 0, returns n.
+ static quantCeil(n: bigint, step: bigint): bigint {
+ step = BigintMath.max(1n, step);
+ const remainder = n % step;
+ if (remainder === 0n) {
+ return n;
+ }
+ const quotient = n / step;
+ return (quotient + 1n) * step;
+ }
+
+ // Returns the greater of a and b.
+ static max(a: bigint, b: bigint): bigint {
+ return a > b ? a : b;
+ }
+
+ // Returns the smaller of a and b.
+ static min(a: bigint, b: bigint): bigint {
+ return a < b ? a : b;
+ }
+
+ // Returns the number of 1 bits in n.
+ static popcount(n: bigint): number {
+ if (n < 0n) {
+ throw Error(`Can\'t get popcount of negative number ${n}`);
+ }
+ let count = 0;
+ while (n) {
+ if (n & 1n) {
+ ++count;
+ }
+ n >>= 1n;
+ }
+ return count;
+ }
+
+ // Return the ratio between two bigints as a number.
+ static ratio(dividend: bigint, divisor: bigint): number {
+ return Number(dividend) / Number(divisor);
+ }
+}
diff --git a/ui/src/base/bigint_math_unittest.ts b/ui/src/base/bigint_math_unittest.ts
new file mode 100644
index 0000000..33e7179
--- /dev/null
+++ b/ui/src/base/bigint_math_unittest.ts
@@ -0,0 +1,224 @@
+// 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 {BigintMath as BIM} from './bigint_math';
+
+describe('BigIntMath', () => {
+ describe('bitCeil', () => {
+ it('rounds powers of 2 to themselves', () => {
+ expect(BIM.bitCeil(1n)).toBe(1n);
+ expect(BIM.bitCeil(2n)).toBe(2n);
+ expect(BIM.bitCeil(4n)).toBe(4n);
+ expect(BIM.bitCeil(4294967296n)).toBe(4294967296n);
+ expect(BIM.bitCeil(2305843009213693952n)).toBe(2305843009213693952n);
+ });
+
+ it('rounds non powers of 2 up to nearest power of 2', () => {
+ expect(BIM.bitCeil(3n)).toBe(4n);
+ expect(BIM.bitCeil(11n)).toBe(16n);
+ expect(BIM.bitCeil(33n)).toBe(64n);
+ expect(BIM.bitCeil(63n)).toBe(64n);
+ expect(BIM.bitCeil(1234567890123456789n)).toBe(2305843009213693952n);
+ });
+
+ it('rounds 0 or negative values up to 1', () => {
+ expect(BIM.bitCeil(0n)).toBe(1n);
+ expect(BIM.bitCeil(-123n)).toBe(1n);
+ });
+ });
+
+ describe('bitFloor', () => {
+ it('rounds powers of 2 to themselves', () => {
+ expect(BIM.bitFloor(1n)).toBe(1n);
+ expect(BIM.bitFloor(2n)).toBe(2n);
+ expect(BIM.bitFloor(4n)).toBe(4n);
+ expect(BIM.bitFloor(4294967296n)).toBe(4294967296n);
+ expect(BIM.bitFloor(2305843009213693952n)).toBe(2305843009213693952n);
+ });
+
+ it('rounds non powers of 2 down to nearest power of 2', () => {
+ expect(BIM.bitFloor(3n)).toBe(2n);
+ expect(BIM.bitFloor(11n)).toBe(8n);
+ expect(BIM.bitFloor(33n)).toBe(32n);
+ expect(BIM.bitFloor(63n)).toBe(32n);
+ expect(BIM.bitFloor(1234567890123456789n)).toBe(1152921504606846976n);
+ });
+
+ it('rounds 0 or negative values up to 1', () => {
+ expect(BIM.bitFloor(0n)).toBe(1n);
+ expect(BIM.bitFloor(-123n)).toBe(1n);
+ });
+ });
+
+ describe('log2', () => {
+ it('calcs exact powers of 2', () => {
+ expect(BIM.log2(1n)).toBe(0);
+ expect(BIM.log2(2n)).toBe(1);
+ expect(BIM.log2(4n)).toBe(2);
+ expect(BIM.log2(4294967296n)).toBe(32);
+ expect(BIM.log2(2305843009213693952n)).toBe(61);
+ });
+
+ it('rounds non powers of 2 down to nearest power of 2', () => {
+ expect(BIM.log2(3n)).toBe(1);
+ expect(BIM.log2(11n)).toBe(3);
+ expect(BIM.log2(33n)).toBe(5);
+ expect(BIM.log2(63n)).toBe(5);
+ expect(BIM.log2(1234567890123456789n)).toBe(60);
+ });
+
+ it('returns 0 for 0n negative numbers', () => {
+ expect(BIM.log2(0n)).toBe(0);
+ expect(BIM.log2(-123n)).toBe(0);
+ });
+ });
+
+ describe('quant', () => {
+ it('should round an int to the nearest multiple of a stepsize', () => {
+ expect(BIM.quant(0n, 2n)).toEqual(0n);
+ expect(BIM.quant(1n, 2n)).toEqual(2n);
+ expect(BIM.quant(2n, 2n)).toEqual(2n);
+ expect(BIM.quant(3n, 2n)).toEqual(4n);
+ expect(BIM.quant(4n, 2n)).toEqual(4n);
+
+ expect(BIM.quant(0n, 3n)).toEqual(0n);
+ expect(BIM.quant(1n, 3n)).toEqual(0n);
+ expect(BIM.quant(2n, 3n)).toEqual(3n);
+ expect(BIM.quant(3n, 3n)).toEqual(3n);
+ expect(BIM.quant(4n, 3n)).toEqual(3n);
+ expect(BIM.quant(5n, 3n)).toEqual(6n);
+ expect(BIM.quant(6n, 3n)).toEqual(6n);
+ });
+
+ it('should return value if stepsize is smaller than 1', () => {
+ expect(BIM.quant(123n, 0n)).toEqual(123n);
+ expect(BIM.quant(123n, -10n)).toEqual(123n);
+ });
+ });
+
+ describe('quantFloor', () => {
+ it('should quantize a number to the nearest multiple of a stepsize', () => {
+ expect(BIM.quantFloor(10n, 2n)).toEqual(10n);
+ expect(BIM.quantFloor(11n, 2n)).toEqual(10n);
+ expect(BIM.quantFloor(12n, 2n)).toEqual(12n);
+ expect(BIM.quantFloor(13n, 2n)).toEqual(12n);
+
+ expect(BIM.quantFloor(9n, 4n)).toEqual(8n);
+ expect(BIM.quantFloor(10n, 4n)).toEqual(8n);
+ expect(BIM.quantFloor(11n, 4n)).toEqual(8n);
+ expect(BIM.quantFloor(12n, 4n)).toEqual(12n);
+ expect(BIM.quantFloor(13n, 4n)).toEqual(12n);
+ });
+
+ it('should return value if stepsize is smaller than 1', () => {
+ expect(BIM.quantFloor(123n, 0n)).toEqual(123n);
+ expect(BIM.quantFloor(123n, -10n)).toEqual(123n);
+ });
+ });
+
+ describe('quantCeil', () => {
+ it('should round an int up to the nearest multiple of a stepsize', () => {
+ expect(BIM.quantCeil(10n, 2n)).toEqual(10n);
+ expect(BIM.quantCeil(11n, 2n)).toEqual(12n);
+ expect(BIM.quantCeil(12n, 2n)).toEqual(12n);
+ expect(BIM.quantCeil(13n, 2n)).toEqual(14n);
+
+ expect(BIM.quantCeil(9n, 4n)).toEqual(12n);
+ expect(BIM.quantCeil(10n, 4n)).toEqual(12n);
+ expect(BIM.quantCeil(11n, 4n)).toEqual(12n);
+ expect(BIM.quantCeil(12n, 4n)).toEqual(12n);
+ expect(BIM.quantCeil(13n, 4n)).toEqual(16n);
+ });
+
+ it('should return value if stepsize is smaller than 1', () => {
+ expect(BIM.quantCeil(123n, 0n)).toEqual(123n);
+ expect(BIM.quantCeil(123n, -10n)).toEqual(123n);
+ });
+ });
+
+ describe('quantRound', () => {
+ it('should quantize a number to the nearest multiple of a stepsize', () => {
+ expect(BIM.quant(0n, 2n)).toEqual(0n);
+ expect(BIM.quant(1n, 2n)).toEqual(2n);
+ expect(BIM.quant(2n, 2n)).toEqual(2n);
+ expect(BIM.quant(3n, 2n)).toEqual(4n);
+ expect(BIM.quant(4n, 2n)).toEqual(4n);
+
+ expect(BIM.quant(0n, 3n)).toEqual(0n);
+ expect(BIM.quant(1n, 3n)).toEqual(0n);
+ expect(BIM.quant(2n, 3n)).toEqual(3n);
+ expect(BIM.quant(3n, 3n)).toEqual(3n);
+ expect(BIM.quant(4n, 3n)).toEqual(3n);
+ expect(BIM.quant(5n, 3n)).toEqual(6n);
+ expect(BIM.quant(6n, 3n)).toEqual(6n);
+ });
+
+ it('should return value if stepsize is smaller than 1', () => {
+ expect(BIM.quant(123n, 0n)).toEqual(123n);
+ expect(BIM.quant(123n, -10n)).toEqual(123n);
+ });
+ });
+
+ describe('max', () => {
+ it('should return the greater of two numbers', () => {
+ expect(BIM.max(5n, 8n)).toEqual(8n);
+ expect(BIM.max(3n, 7n)).toEqual(7n);
+ expect(BIM.max(6n, 6n)).toEqual(6n);
+ expect(BIM.max(-7n, -12n)).toEqual(-7n);
+ });
+ });
+
+ describe('min', () => {
+ it('should return the smaller of two numbers', () => {
+ expect(BIM.min(5n, 8n)).toEqual(5n);
+ expect(BIM.min(3n, 7n)).toEqual(3n);
+ expect(BIM.min(6n, 6n)).toEqual(6n);
+ expect(BIM.min(-7n, -12n)).toEqual(-12n);
+ });
+ });
+
+ describe('popcount', () => {
+ it('should return the number of set bits in an integer', () => {
+ expect(BIM.popcount(0n)).toBe(0);
+ expect(BIM.popcount(1n)).toBe(1);
+ expect(BIM.popcount(2n)).toBe(1);
+ expect(BIM.popcount(3n)).toBe(2);
+ expect(BIM.popcount(4n)).toBe(1);
+ expect(BIM.popcount(5n)).toBe(2);
+ expect(BIM.popcount(3462151285050974216n)).toBe(10);
+ });
+
+ it('should throw when presented with a negative integer', () => {
+ expect(() => BIM.popcount(-1n))
+ .toThrowError('Can\'t get popcount of negative number -1');
+ });
+ });
+
+ describe('ratio', () => {
+ it('should return ratio as number', () => {
+ expect(BIM.ratio(0n, 1n)).toBeCloseTo(0);
+ expect(BIM.ratio(1n, 1n)).toBeCloseTo(1);
+ expect(BIM.ratio(1n, 2n)).toBeCloseTo(0.5);
+ expect(BIM.ratio(1n, 100n)).toBeCloseTo(0.01);
+ expect(
+ BIM.ratio(
+ 987654321098765432109876543210n, 123456789012345678901234567890n))
+ .toBeCloseTo(8);
+ expect(
+ BIM.ratio(
+ 123456789012345678901234567890n, 987654321098765432109876543210n))
+ .toBeCloseTo(0.125, 3);
+ });
+ });
+});
diff --git a/ui/src/base/binary_search.ts b/ui/src/base/binary_search.ts
index ef352c2..2b78627 100644
--- a/ui/src/base/binary_search.ts
+++ b/ui/src/base/binary_search.ts
@@ -12,12 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-
-type Numbers = Float64Array|Uint32Array|number[];
+type Numeric = number|bigint;
type Range = [number, number];
-function searchImpl(
- haystack: Numbers, needle: number, i: number, j: number): number {
+function searchImpl<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T, i: number, j: number): number {
if (i === j) return -1;
if (i + 1 === j) {
return (needle >= haystack[i]) ? i : -1;
@@ -32,8 +31,8 @@
}
}
-function searchRangeImpl(
- haystack: Numbers, needle: number, i: number, j: number): Range {
+function searchRangeImpl<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T, i: number, j: number): Range {
if (i === j) return [i, j];
if (i + 1 === j) {
if (haystack[i] <= needle) {
@@ -57,29 +56,33 @@
}
}
-export function search(haystack: Numbers, needle: number): number {
+// Given a sorted array of numeric values |haystack| and a |needle|, return the
+// index of the last element of |haystack| which is less than or equal to
+// |needle|, or -1 if all elements of |haystack| are greater than |needle|.
+export function search<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T): number {
return searchImpl(haystack, needle, 0, haystack.length);
}
-// Given a sorted array of numbers (|haystack|) and a |needle| return the
-// half open range [i, j) of indexes where |haystack| is equal to needle.
-export function searchEq(
- haystack: Numbers, needle: number, optRange?: Range): Range {
+// Given a sorted array of numeric values (|haystack|) return the half open
+// range [i, j) of indices where |haystack| is equal to needle.
+export function searchEq<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T, optRange?: Range): Range {
const range = searchRange(haystack, needle, optRange);
const [i, j] = range;
if (haystack[i] === needle) return range;
return [j, j];
}
-// Given a sorted array of numbers (|haystack|) and a |needle| return the
+// Given a sorted array of numeric values (|haystack|) and a |needle| return the
// smallest half open range [i, j) of indexes which contains |needle|.
-export function searchRange(
- haystack: Numbers, needle: number, optRange?: Range): Range {
+export function searchRange<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T, optRange?: Range): Range {
const [left, right] = optRange ? optRange : [0, haystack.length];
return searchRangeImpl(haystack, needle, left, right);
}
-// Given a sorted array of numbers (|haystack|) and a |needle| return a
+// Given a sorted array of numeric values (|haystack|) and a |needle| return a
// pair of indexes [i, j] such that:
// If there is at least one element in |haystack| smaller than |needle|
// i is the index of the largest such number otherwise -1;
@@ -88,7 +91,8 @@
//
// So we try to get the indexes of the two data points around needle
// or -1 if there is no such datapoint.
-export function searchSegment(haystack: Numbers, needle: number): Range {
+export function searchSegment<T extends Numeric>(
+ haystack: ArrayLike<T>, needle: T): Range {
if (!haystack.length) return [-1, -1];
const left = search(haystack, needle);
diff --git a/ui/src/base/binary_search_unittest.ts b/ui/src/base/binary_search_unittest.ts
index c941e02..917c810 100644
--- a/ui/src/base/binary_search_unittest.ts
+++ b/ui/src/base/binary_search_unittest.ts
@@ -12,21 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {search, searchEq, searchRange, searchSegment} from './binary_search';
+import {
+ search,
+ searchEq,
+ searchRange,
+ searchSegment,
+} from './binary_search';
test('binarySearch', () => {
- expect(search(Float64Array.of(), 100)).toEqual(-1);
- expect(search(Float64Array.of(42), 42)).toEqual(0);
- expect(search(Float64Array.of(42), 43)).toEqual(0);
- expect(search(Float64Array.of(42), 41)).toEqual(-1);
- expect(search(Float64Array.of(42, 43), 42)).toEqual(0);
- expect(search(Float64Array.of(42, 43), 43)).toEqual(1);
- expect(search(Float64Array.of(42, 43), 44)).toEqual(1);
- expect(search(Float64Array.of(42, 43, 44), 41)).toEqual(-1);
- expect(search(Float64Array.of(42, 43, 44), 42)).toEqual(0);
- expect(search(Float64Array.of(42, 43, 44), 43)).toEqual(1);
- expect(search(Float64Array.of(42, 43, 44), 44)).toEqual(2);
- expect(search(Float64Array.of(42, 43, 44), 45)).toEqual(2);
+ expect(search([], 100)).toEqual(-1);
+ expect(search([42], 42)).toEqual(0);
+ expect(search([42], 43)).toEqual(0);
+ expect(search([42], 41)).toEqual(-1);
+ expect(search([42, 43], 42)).toEqual(0);
+ expect(search([42, 43], 43)).toEqual(1);
+ expect(search([42, 43], 44)).toEqual(1);
+ expect(search([42, 43, 44], 41)).toEqual(-1);
+ expect(search([42, 43, 44], 42)).toEqual(0);
+ expect(search([42, 43, 44], 43)).toEqual(1);
+ expect(search([42, 43, 44], 44)).toEqual(2);
+ expect(search([42, 43, 44], 45)).toEqual(2);
});
test('searchEq', () => {
@@ -58,8 +63,9 @@
test('searchSegment', () => {
expect(searchSegment(Float64Array.of(), 100)).toEqual([-1, -1]);
- expect(searchSegment(Float64Array.of(42), 41)).toEqual([-1, 0]);
- expect(searchSegment(Float64Array.of(42), 42)).toEqual([0, -1]);
- expect(searchSegment(Float64Array.of(42), 43)).toEqual([0, -1]);
- expect(searchSegment(Float64Array.of(42, 44), 42)).toEqual([0, 1]);
+ expect(searchSegment([42], 41)).toEqual([-1, 0]);
+ expect(searchSegment([42], 42)).toEqual([0, -1]);
+ expect(searchSegment([42], 43)).toEqual([0, -1]);
+ expect(searchSegment([42, 44], 42)).toEqual([0, 1]);
+ expect(searchSegment([1, 2, 2, 3], 2)).toEqual([2, 3]);
});
diff --git a/ui/src/base/math_utils.ts b/ui/src/base/math_utils.ts
deleted file mode 100644
index f1c1816..0000000
--- a/ui/src/base/math_utils.ts
+++ /dev/null
@@ -1,23 +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.
-
-// Round a number up to the nearest stepsize.
-export function roundUpNearest(val: number, stepsize: number): number {
- return stepsize * Math.ceil(val / stepsize);
-}
-
-// Round a number down to the nearest stepsize.
-export function roundDownNearest(val: number, stepsize: number): number {
- return stepsize * Math.floor(val / stepsize);
-}
diff --git a/ui/src/base/math_utils_unittest.ts b/ui/src/base/math_utils_unittest.ts
deleted file mode 100644
index 169b793..0000000
--- a/ui/src/base/math_utils_unittest.ts
+++ /dev/null
@@ -1,29 +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 {roundDownNearest, roundUpNearest} from './math_utils';
-
-describe('roundUpNearest()', () => {
- it('rounds decimal values up to the right step size', () => {
- expect(roundUpNearest(0.1, 0.5)).toBeCloseTo(0.5);
- expect(roundUpNearest(17.2, 0.5)).toBeCloseTo(17.5);
- });
-});
-
-describe('roundDownNearest()', () => {
- it('rounds decimal values down to the right step size', () => {
- expect(roundDownNearest(0.4, 0.5)).toBeCloseTo(0.0);
- expect(roundDownNearest(17.4, 0.5)).toBeCloseTo(17.0);
- });
-});
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 1e602fc..7b8f462 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -61,7 +61,7 @@
UtidToTrackSortKey,
VisibleState,
} from './state';
-import {toNs} from './time';
+import {TPDuration, TPTime} from './time';
export const DEBUG_SLICE_TRACK_KIND = 'DebugSliceTrack';
@@ -89,8 +89,8 @@
}
export interface PostedScrollToRange {
- timeStart: number;
- timeEnd: number;
+ timeStart: TPTime;
+ timeEnd: TPTime;
viewPercentage?: number;
}
@@ -458,6 +458,15 @@
}
},
+ maybeSetPendingDeeplink(
+ state: StateDraft, args: {ts?: string, dur?: string, tid?: string}) {
+ state.pendingDeeplink = args;
+ },
+
+ clearPendingDeeplink(state: StateDraft, _: {}) {
+ state.pendingDeeplink = undefined;
+ },
+
// TODO(hjd): engine.ready should be a published thing. If it's part
// of the state it interacts badly with permalinks.
setEngineReady(
@@ -552,7 +561,7 @@
addAutomaticNote(
state: StateDraft,
- args: {timestamp: number, color: string, text: string}): void {
+ args: {timestamp: TPTime, color: string, text: string}): void {
const id = generateNextId(state);
state.notes[id] = {
noteType: 'DEFAULT',
@@ -563,7 +572,7 @@
};
},
- addNote(state: StateDraft, args: {timestamp: number, color: string}): void {
+ addNote(state: StateDraft, args: {timestamp: TPTime, color: string}): void {
const id = generateNextId(state);
state.notes[id] = {
noteType: 'DEFAULT',
@@ -606,14 +615,10 @@
},
markArea(state: StateDraft, args: {area: Area, persistent: boolean}): void {
+ const {start, end, tracks} = args.area;
+ assertTrue(start <= end);
const areaId = generateNextId(state);
- assertTrue(args.area.endSec >= args.area.startSec);
- state.areas[areaId] = {
- id: areaId,
- startSec: args.area.startSec,
- endSec: args.area.endSec,
- tracks: args.area.tracks,
- };
+ state.areas[areaId] = {id: areaId, start, end, tracks};
const noteId = args.persistent ? generateNextId(state) : '0';
const color = args.persistent ? randomColor() : '#344596';
state.notes[noteId] = {
@@ -667,7 +672,7 @@
selectCounter(
state: StateDraft,
- args: {leftTs: number, rightTs: number, id: number, trackId: string}):
+ args: {leftTs: TPTime, rightTs: TPTime, id: number, trackId: string}):
void {
state.currentSelection = {
kind: 'COUNTER',
@@ -680,7 +685,7 @@
selectHeapProfile(
state: StateDraft,
- args: {id: number, upid: number, ts: number, type: ProfileType}): void {
+ args: {id: number, upid: number, ts: TPTime, type: ProfileType}): void {
state.currentSelection = {
kind: 'HEAP_PROFILE',
id: args.id,
@@ -690,8 +695,8 @@
};
this.openFlamegraph(state, {
type: args.type,
- startNs: toNs(state.traceTime.startSec),
- endNs: args.ts,
+ start: state.traceTime.start,
+ end: args.ts,
upids: [args.upid],
viewingOption: DEFAULT_VIEWING_OPTION,
});
@@ -700,8 +705,8 @@
selectPerfSamples(state: StateDraft, args: {
id: number,
upid: number,
- leftTs: number,
- rightTs: number,
+ leftTs: TPTime,
+ rightTs: TPTime,
type: ProfileType
}): void {
state.currentSelection = {
@@ -714,8 +719,8 @@
};
this.openFlamegraph(state, {
type: args.type,
- startNs: args.leftTs,
- endNs: args.rightTs,
+ start: args.leftTs,
+ end: args.rightTs,
upids: [args.upid],
viewingOption: PERF_SAMPLES_KEY,
});
@@ -723,16 +728,16 @@
openFlamegraph(state: StateDraft, args: {
upids: number[],
- startNs: number,
- endNs: number,
+ start: TPTime,
+ end: TPTime,
type: ProfileType,
viewingOption: FlamegraphStateViewingOption
}): void {
state.currentFlamegraphState = {
kind: 'FLAMEGRAPH_STATE',
upids: args.upids,
- startNs: args.startNs,
- endNs: args.endNs,
+ start: args.start,
+ end: args.end,
type: args.type,
viewingOption: args.viewingOption,
focusRegex: '',
@@ -740,7 +745,7 @@
},
selectCpuProfileSample(
- state: StateDraft, args: {id: number, utid: number, ts: number}): void {
+ state: StateDraft, args: {id: number, utid: number, ts: TPTime}): void {
state.currentSelection = {
kind: 'CPU_PROFILE_SAMPLE',
id: args.id,
@@ -784,16 +789,33 @@
selectDebugSlice(state: StateDraft, args: {
id: number,
sqlTableName: string,
- startS: number,
- durationS: number,
+ start: TPTime,
+ duration: TPDuration,
trackId: string,
}): void {
state.currentSelection = {
kind: 'DEBUG_SLICE',
id: args.id,
sqlTableName: args.sqlTableName,
- startS: args.startS,
- durationS: args.durationS,
+ start: args.start,
+ duration: args.duration,
+ trackId: args.trackId,
+ };
+ },
+
+ selectTopLevelScrollSlice(state: StateDraft, args: {
+ id: number,
+ sqlTableName: string,
+ start: TPTime,
+ duration: TPTime,
+ trackId: string,
+ }): void {
+ state.currentSelection = {
+ kind: 'TOP_LEVEL_SCROLL',
+ id: args.id,
+ sqlTableName: args.sqlTableName,
+ start: args.start,
+ duration: args.duration,
trackId: args.trackId,
};
},
@@ -893,25 +915,17 @@
},
selectArea(state: StateDraft, args: {area: Area}): void {
+ const {start, end, tracks} = args.area;
+ assertTrue(start <= end);
const areaId = generateNextId(state);
- assertTrue(args.area.endSec >= args.area.startSec);
- state.areas[areaId] = {
- id: areaId,
- startSec: args.area.startSec,
- endSec: args.area.endSec,
- tracks: args.area.tracks,
- };
+ state.areas[areaId] = {id: areaId, start, end, tracks};
state.currentSelection = {kind: 'AREA', areaId};
},
editArea(state: StateDraft, args: {area: Area, areaId: string}): void {
- assertTrue(args.area.endSec >= args.area.startSec);
- state.areas[args.areaId] = {
- id: args.areaId,
- startSec: args.area.startSec,
- endSec: args.area.endSec,
- tracks: args.area.tracks,
- };
+ const {start, end, tracks} = args.area;
+ assertTrue(start <= end);
+ state.areas[args.areaId] = {id: args.areaId, start, end, tracks};
},
reSelectArea(state: StateDraft, args: {areaId: string, noteId: string}):
@@ -1031,11 +1045,11 @@
state.searchIndex = args.index;
},
- setHoverCursorTimestamp(state: StateDraft, args: {ts: number}) {
+ setHoverCursorTimestamp(state: StateDraft, args: {ts: TPTime}) {
state.hoverCursorTimestamp = args.ts;
},
- setHoveredNoteTimestamp(state: StateDraft, args: {ts: number}) {
+ setHoveredNoteTimestamp(state: StateDraft, args: {ts: TPTime}) {
state.hoveredNoteTimestamp = args.ts;
},
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index 6702e72..b613777 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -446,9 +446,13 @@
const state = createEmptyState();
const afterSelectingPerf = produce(state, (draft) => {
- StateActions.selectPerfSamples(
- draft,
- {id: 0, upid: 0, leftTs: 0, rightTs: 0, type: ProfileType.PERF_SAMPLE});
+ StateActions.selectPerfSamples(draft, {
+ id: 0,
+ upid: 0,
+ leftTs: 0n,
+ rightTs: 0n,
+ type: ProfileType.PERF_SAMPLE,
+ });
});
expect(assertExists(afterSelectingPerf.currentFlamegraphState).type)
@@ -460,7 +464,7 @@
const afterSelectingPerf = produce(state, (draft) => {
StateActions.selectHeapProfile(
- draft, {id: 0, upid: 0, ts: 0, type: ProfileType.JAVA_HEAP_GRAPH});
+ draft, {id: 0, upid: 0, ts: 0n, type: ProfileType.JAVA_HEAP_GRAPH});
});
expect(assertExists(afterSelectingPerf.currentFlamegraphState).type)
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index bd1e6b6..6d806a2 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -110,7 +110,7 @@
visibleState: {
...defaultTraceTime,
lastUpdate: 0,
- resolution: 0,
+ resolution: 0n,
},
},
@@ -142,8 +142,8 @@
sidebarVisible: true,
hoveredUtid: -1,
hoveredPid: -1,
- hoverCursorTimestamp: -1,
- hoveredNoteTimestamp: -1,
+ hoverCursorTimestamp: -1n,
+ hoveredNoteTimestamp: -1n,
highlightedSliceId: -1,
focusedFlowIdLeft: -1,
focusedFlowIdRight: -1,
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index 8b6cb46..861601f 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -24,18 +24,20 @@
QueryArgs,
ResetTraceProcessorArgs,
} from './protos';
-import {NUM, NUM_NULL, STR} from './query_result';
+import {LONG, LONG_NULL, NUM, STR} from './query_result';
import {
createQueryResult,
QueryError,
QueryResult,
WritableQueryResult,
} from './query_result';
-import {TimeSpan} from './time';
+import {TPTime, TPTimeSpan} from './time';
import TraceProcessorRpc = perfetto.protos.TraceProcessorRpc;
import TraceProcessorRpcStream = perfetto.protos.TraceProcessorRpcStream;
import TPM = perfetto.protos.TraceProcessorRpc.TraceProcessorMethod;
+import {Span} from '../common/time';
+import {BigintMath} from '../base/bigint_math';
export interface LoadingTracker {
beginLoading(): void;
@@ -410,38 +412,38 @@
return result.firstRow({cnt: NUM}).cnt;
}
- async getTraceTimeBounds(): Promise<TimeSpan> {
+ async getTraceTimeBounds(): Promise<Span<TPTime>> {
const result = await this.query(
`select start_ts as startTs, end_ts as endTs from trace_bounds`);
const bounds = result.firstRow({
- startTs: NUM,
- endTs: NUM,
+ startTs: LONG,
+ endTs: LONG,
});
- return new TimeSpan(bounds.startTs / 1e9, bounds.endTs / 1e9);
+ return new TPTimeSpan(bounds.startTs, bounds.endTs);
}
- async getTracingMetadataTimeBounds(): Promise<TimeSpan> {
+ async getTracingMetadataTimeBounds(): Promise<Span<TPTime>> {
const queryRes = await this.query(`select
name,
int_value as intValue
from metadata
where name = 'tracing_started_ns' or name = 'tracing_disabled_ns'
or name = 'all_data_source_started_ns'`);
- let startBound = -Infinity;
- let endBound = Infinity;
- const it = queryRes.iter({'name': STR, 'intValue': NUM_NULL});
+ let startBound = 0n;
+ let endBound = BigintMath.INT64_MAX;
+ const it = queryRes.iter({'name': STR, 'intValue': LONG_NULL});
for (; it.valid(); it.next()) {
const columnName = it.name;
const timestamp = it.intValue;
if (timestamp === null) continue;
if (columnName === 'tracing_disabled_ns') {
- endBound = Math.min(endBound, timestamp / 1e9);
+ endBound = BigintMath.min(endBound, timestamp);
} else {
- startBound = Math.max(startBound, timestamp / 1e9);
+ startBound = BigintMath.max(startBound, timestamp);
}
}
- return new TimeSpan(startBound, endBound);
+ return new TPTimeSpan(startBound, endBound);
}
getProxy(tag: string): EngineProxy {
diff --git a/ui/src/common/high_precision_time.ts b/ui/src/common/high_precision_time.ts
new file mode 100644
index 0000000..fda88c8
--- /dev/null
+++ b/ui/src/common/high_precision_time.ts
@@ -0,0 +1,279 @@
+// 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 {assertTrue} from '../base/logging';
+import {Span, TPTime} from './time';
+
+export type RoundMode = 'round'|'floor'|'ceil';
+export type Timeish = HighPrecisionTime|TPTime;
+
+// Stores a time as a bigint and an offset which is capable of:
+// - Storing and reproducing "TPTime"s without losing precision.
+// - Storing time with sub-nanosecond precision.
+// This class is immutable - each operation returns a new object.
+export class HighPrecisionTime {
+ // Time in nanoseconds == base + offset
+ // offset is kept in the range 0 <= x < 1 to avoid losing precision
+ readonly base: bigint;
+ readonly offset: number;
+
+ static get ZERO(): HighPrecisionTime {
+ return new HighPrecisionTime(0n);
+ }
+
+ constructor(base: bigint = 0n, offset: number = 0) {
+ // Normalize offset to sit in the range 0.0 <= x < 1.0
+ const offsetFloor = Math.floor(offset);
+ this.base = base + BigInt(offsetFloor);
+ this.offset = offset - offsetFloor;
+ }
+
+ static fromTPTime(timestamp: TPTime): HighPrecisionTime {
+ return new HighPrecisionTime(timestamp, 0);
+ }
+
+ static fromNanos(nanos: number|bigint) {
+ if (typeof nanos === 'number') {
+ return new HighPrecisionTime(0n, nanos);
+ } else if (typeof nanos === 'bigint') {
+ return new HighPrecisionTime(nanos);
+ } else {
+ const value: never = nanos;
+ throw new Error(`Value ${value} is neither a number nor a bigint`);
+ }
+ }
+
+ static fromSeconds(seconds: number) {
+ const nanos = seconds * 1e9;
+ const offset = nanos - Math.floor(nanos);
+ return new HighPrecisionTime(BigInt(Math.floor(nanos)), offset);
+ }
+
+ static max(a: HighPrecisionTime, b: HighPrecisionTime): HighPrecisionTime {
+ return a.gt(b) ? a : b;
+ }
+
+ static min(a: HighPrecisionTime, b: HighPrecisionTime): HighPrecisionTime {
+ return a.lt(b) ? a : b;
+ }
+
+ toTPTime(roundMode: RoundMode = 'floor'): TPTime {
+ switch (roundMode) {
+ case 'round':
+ return this.base + BigInt(Math.round(this.offset));
+ case 'floor':
+ return this.base;
+ case 'ceil':
+ return this.base + BigInt(Math.ceil(this.offset));
+ default:
+ const exhaustiveCheck: never = roundMode;
+ throw new Error(`Unhandled roundMode case: ${exhaustiveCheck}`);
+ }
+ }
+
+ get nanos(): number {
+ // WARNING: Number(bigint) can be surprisingly slow.
+ // WARNING: Precision may be lost here.
+ return Number(this.base) + this.offset;
+ }
+
+ get seconds(): number {
+ // WARNING: Number(bigint) can be surprisingly slow.
+ // WARNING: Precision may be lost here.
+ return (Number(this.base) + this.offset) / 1e9;
+ }
+
+ add(other: HighPrecisionTime): HighPrecisionTime {
+ return new HighPrecisionTime(
+ this.base + other.base, this.offset + other.offset);
+ }
+
+ addNanos(nanos: number|bigint): HighPrecisionTime {
+ return this.add(HighPrecisionTime.fromNanos(nanos));
+ }
+
+ addSeconds(seconds: number): HighPrecisionTime {
+ return new HighPrecisionTime(this.base, this.offset + seconds * 1e9);
+ }
+
+ addTPTime(ts: TPTime): HighPrecisionTime {
+ return new HighPrecisionTime(this.base + ts, this.offset);
+ }
+
+ sub(other: HighPrecisionTime): HighPrecisionTime {
+ return new HighPrecisionTime(
+ this.base - other.base, this.offset - other.offset);
+ }
+
+ subTPTime(ts: TPTime): HighPrecisionTime {
+ return this.addTPTime(-ts);
+ }
+
+ subNanos(nanos: number|bigint): HighPrecisionTime {
+ return this.add(HighPrecisionTime.fromNanos(-nanos));
+ }
+
+ divide(divisor: number): HighPrecisionTime {
+ return this.multiply(1 / divisor);
+ }
+
+ multiply(factor: number): HighPrecisionTime {
+ const factorFloor = Math.floor(factor);
+ const newBase = this.base * BigInt(factorFloor);
+ const additionalBit = Number(this.base) * (factor - factorFloor);
+ const newOffset = factor * this.offset + additionalBit;
+ return new HighPrecisionTime(newBase, newOffset);
+ }
+
+ // Return true if other time is within some epsilon, default 1 femtosecond
+ eq(other: Timeish, epsilon: number = 1e-6): boolean {
+ const x = HighPrecisionTime.fromHPTimeOrTPTime(other);
+ return Math.abs(this.sub(x).nanos) < epsilon;
+ }
+
+ private static fromHPTimeOrTPTime(x: HighPrecisionTime|
+ TPTime): HighPrecisionTime {
+ if (x instanceof HighPrecisionTime) {
+ return x;
+ } else if (typeof x === 'bigint') {
+ return HighPrecisionTime.fromTPTime(x);
+ } else {
+ const y: never = x;
+ throw new Error(`Invalid type ${y}`);
+ }
+ }
+
+ lt(other: Timeish): boolean {
+ const x = HighPrecisionTime.fromHPTimeOrTPTime(other);
+ if (this.base < x.base) {
+ return true;
+ } else if (this.base === x.base) {
+ return this.offset < x.offset;
+ } else {
+ return false;
+ }
+ }
+
+ lte(other: Timeish): boolean {
+ if (this.eq(other)) {
+ return true;
+ } else {
+ return this.lt(other);
+ }
+ }
+
+ gt(other: Timeish): boolean {
+ return !this.lte(other);
+ }
+
+ gte(other: Timeish): boolean {
+ return !this.lt(other);
+ }
+
+ clamp(lower: HighPrecisionTime, upper: HighPrecisionTime): HighPrecisionTime {
+ if (this.lt(lower)) {
+ return lower;
+ } else if (this.gt(upper)) {
+ return upper;
+ } else {
+ return this;
+ }
+ }
+
+ toString(): string {
+ const offsetAsString = this.offset.toString();
+ if (offsetAsString === '0') {
+ return this.base.toString();
+ } else {
+ return `${this.base}${offsetAsString.substring(1)}`;
+ }
+ }
+
+ abs(): HighPrecisionTime {
+ if (this.base >= 0n) {
+ return this;
+ }
+ const newBase = -this.base;
+ const newOffset = -this.offset;
+ return new HighPrecisionTime(newBase, newOffset);
+ }
+}
+
+export class HighPrecisionTimeSpan implements Span<HighPrecisionTime> {
+ readonly start: HighPrecisionTime;
+ readonly end: HighPrecisionTime;
+
+ constructor(start: TPTime|HighPrecisionTime, end: TPTime|HighPrecisionTime) {
+ this.start = (start instanceof HighPrecisionTime) ?
+ start :
+ HighPrecisionTime.fromTPTime(start);
+ this.end = (end instanceof HighPrecisionTime) ?
+ end :
+ HighPrecisionTime.fromTPTime(end);
+ assertTrue(
+ this.start.lte(this.end),
+ `TimeSpan start [${this.start}] cannot be greater than end [${
+ this.end}]`);
+ }
+
+ static fromTpTime(start: TPTime, end: TPTime): HighPrecisionTimeSpan {
+ return new HighPrecisionTimeSpan(
+ HighPrecisionTime.fromTPTime(start),
+ HighPrecisionTime.fromTPTime(end),
+ );
+ }
+
+ static get ZERO(): HighPrecisionTimeSpan {
+ return new HighPrecisionTimeSpan(
+ HighPrecisionTime.ZERO,
+ HighPrecisionTime.ZERO,
+ );
+ }
+
+ get duration(): HighPrecisionTime {
+ return this.end.sub(this.start);
+ }
+
+ get midpoint(): HighPrecisionTime {
+ return this.start.add(this.end).divide(2);
+ }
+
+ equals(other: Span<HighPrecisionTime>): boolean {
+ return this.start.eq(other.start) && this.end.eq(other.end);
+ }
+
+ contains(x: HighPrecisionTime|Span<HighPrecisionTime>): boolean {
+ if (x instanceof HighPrecisionTime) {
+ return this.start.lte(x) && x.lt(this.end);
+ } else {
+ return this.start.lte(x.start) && x.end.lte(this.end);
+ }
+ }
+
+ intersects(x: Span<HighPrecisionTime>): boolean {
+ return !(x.end.lte(this.start) || x.start.gte(this.end));
+ }
+
+ add(time: HighPrecisionTime): Span<HighPrecisionTime> {
+ return new HighPrecisionTimeSpan(this.start.add(time), this.end.add(time));
+ }
+
+ // Move the start and end away from each other a certain amount
+ pad(time: HighPrecisionTime): Span<HighPrecisionTime> {
+ return new HighPrecisionTimeSpan(
+ this.start.sub(time),
+ this.end.add(time),
+ );
+ }
+}
diff --git a/ui/src/common/high_precision_time_unittest.ts b/ui/src/common/high_precision_time_unittest.ts
new file mode 100644
index 0000000..ae25b01
--- /dev/null
+++ b/ui/src/common/high_precision_time_unittest.ts
@@ -0,0 +1,326 @@
+// 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 {
+ HighPrecisionTime as HPTime,
+ HighPrecisionTimeSpan as HPTimeSpan,
+} from './high_precision_time';
+import {TPTime} from './time';
+
+// Quick 'n' dirty function to convert a string to a HPtime
+// Used to make tests more readable
+// E.g. '1.3' -> {base: 1, offset: 0.3}
+// E.g. '-0.3' -> {base: -1, offset: 0.7}
+function mkTime(time: string): HPTime {
+ const array = time.split('.');
+ if (array.length > 2) throw new Error(`Bad time format ${time}`);
+ const [base, fractions] = array;
+ const negative = time.startsWith('-');
+ const numBase = BigInt(base);
+
+ if (fractions) {
+ const numFractions = Number(`0.${fractions}`);
+ if (negative) {
+ return new HPTime(numBase - 1n, 1.0 - numFractions);
+ } else {
+ return new HPTime(numBase, numFractions);
+ }
+ } else {
+ return new HPTime(numBase);
+ }
+}
+
+function mkSpan(t1: string, t2: string): HPTimeSpan {
+ return new HPTimeSpan(mkTime(t1), mkTime(t2));
+}
+
+describe('Time', () => {
+ it('should create a new Time object with the given base and offset', () => {
+ const time = new HPTime(136n, 0.3);
+ expect(time.base).toBe(136n);
+ expect(time.offset).toBeCloseTo(0.3);
+ });
+
+ it('should normalize when offset is >= 1', () => {
+ let time = new HPTime(1n, 2.3);
+ expect(time.base).toBe(3n);
+ expect(time.offset).toBeCloseTo(0.3);
+
+ time = new HPTime(1n, 1);
+ expect(time.base).toBe(2n);
+ expect(time.offset).toBeCloseTo(0);
+ });
+
+ it('should normalize when offset is < 0', () => {
+ const time = new HPTime(1n, -0.4);
+ expect(time.base).toBe(0n);
+ expect(time.offset).toBeCloseTo(0.6);
+ });
+
+ it('should store timestamps without losing precision', () => {
+ let time = HPTime.fromTPTime(123n as TPTime);
+ expect(time.toTPTime()).toBe(123n as TPTime);
+
+ time = HPTime.fromTPTime(1152921504606846976n as TPTime);
+ expect(time.toTPTime()).toBe(1152921504606846976n as TPTime);
+ });
+
+ it('should store and manipulate timestamps without losing precision', () => {
+ let time = HPTime.fromTPTime(123n as TPTime);
+ time = time.addTPTime(456n);
+ expect(time.toTPTime()).toBe(579n);
+
+ time = HPTime.fromTPTime(2315700508990407843n as TPTime);
+ time = time.addTPTime(2315718101717517451n as TPTime);
+ expect(time.toTPTime()).toBe(4631418610707925294n);
+ });
+
+ it('should add time', () => {
+ const time1 = mkTime('1.3');
+ const time2 = mkTime('3.1');
+ const result = time1.add(time2);
+ expect(result.base).toEqual(4n);
+ expect(result.offset).toBeCloseTo(0.4);
+ });
+
+ it('should subtract time', () => {
+ const time1 = mkTime('3.1');
+ const time2 = mkTime('1.3');
+ const result = time1.sub(time2);
+ expect(result.base).toEqual(1n);
+ expect(result.offset).toBeCloseTo(0.8);
+ });
+
+ it('should add nanoseconds', () => {
+ const time = mkTime('1.3');
+ const result = time.addNanos(0.8);
+ expect(result.base).toEqual(2n);
+ expect(result.offset).toBeCloseTo(0.1);
+ });
+
+ it('should add seconds', () => {
+ const time = mkTime('1.3');
+ const result = time.addSeconds(0.008);
+ expect(result.base).toEqual(8000001n);
+ expect(result.offset).toBeCloseTo(0.3);
+ });
+
+ it('should perform gte comparisions', () => {
+ const time = mkTime('1.2');
+ expect(time.gte(mkTime('0.5'))).toBeTruthy();
+ expect(time.gte(mkTime('1.1'))).toBeTruthy();
+ expect(time.gte(mkTime('1.2'))).toBeTruthy();
+ expect(time.gte(mkTime('1.5'))).toBeFalsy();
+ expect(time.gte(mkTime('5.5'))).toBeFalsy();
+ });
+
+ it('should perform gt comparisions', () => {
+ const time = mkTime('1.2');
+ expect(time.gt(mkTime('0.5'))).toBeTruthy();
+ expect(time.gt(mkTime('1.1'))).toBeTruthy();
+ expect(time.gt(mkTime('1.2'))).toBeFalsy();
+ expect(time.gt(mkTime('1.5'))).toBeFalsy();
+ expect(time.gt(mkTime('5.5'))).toBeFalsy();
+ });
+
+ it('should perform lt comparisions', () => {
+ const time = mkTime('1.2');
+ expect(time.lt(mkTime('0.5'))).toBeFalsy();
+ expect(time.lt(mkTime('1.1'))).toBeFalsy();
+ expect(time.lt(mkTime('1.2'))).toBeFalsy();
+ expect(time.lt(mkTime('1.5'))).toBeTruthy();
+ expect(time.lt(mkTime('5.5'))).toBeTruthy();
+ });
+
+ it('should perform lte comparisions', () => {
+ const time = mkTime('1.2');
+ expect(time.lte(mkTime('0.5'))).toBeFalsy();
+ expect(time.lte(mkTime('1.1'))).toBeFalsy();
+ expect(time.lte(mkTime('1.2'))).toBeTruthy();
+ expect(time.lte(mkTime('1.5'))).toBeTruthy();
+ expect(time.lte(mkTime('5.5'))).toBeTruthy();
+ });
+
+ it('should detect equality', () => {
+ const time = new HPTime(1n, 0.2);
+ expect(time.eq(new HPTime(1n, 0.2))).toBeTruthy();
+ expect(time.eq(new HPTime(0n, 1.2))).toBeTruthy();
+ expect(time.eq(new HPTime(-100n, 101.2))).toBeTruthy();
+ expect(time.eq(new HPTime(1n, 0.3))).toBeFalsy();
+ expect(time.eq(new HPTime(2n, 0.2))).toBeFalsy();
+ });
+
+ it('should clamp a time to a range', () => {
+ const time1 = mkTime('1.2');
+ const time2 = mkTime('5.4');
+ const time3 = mkTime('2.8');
+ const lower = mkTime('2.3');
+ const upper = mkTime('4.5');
+ expect(time1.clamp(lower, upper)).toEqual(lower);
+ expect(time2.clamp(lower, upper)).toEqual(upper);
+ expect(time3.clamp(lower, upper)).toEqual(time3);
+ });
+
+ it('should convert to seconds', () => {
+ expect(new HPTime(1n, .2).seconds).toBeCloseTo(0.0000000012);
+ expect(new HPTime(1000000000n, .0).seconds).toBeCloseTo(1);
+ });
+
+ it('should convert to nanos', () => {
+ expect(new HPTime(1n, .2).nanos).toBeCloseTo(1.2);
+ expect(new HPTime(1000000000n, .0).nanos).toBeCloseTo(1e9);
+ });
+
+ it('should convert to timestamps', () => {
+ expect(new HPTime(1n, .2).toTPTime('round')).toBe(1n);
+ expect(new HPTime(1n, .5).toTPTime('round')).toBe(2n);
+ expect(new HPTime(1n, .2).toTPTime('floor')).toBe(1n);
+ expect(new HPTime(1n, .5).toTPTime('floor')).toBe(1n);
+ expect(new HPTime(1n, .2).toTPTime('ceil')).toBe(2n);
+ expect(new HPTime(1n, .5).toTPTime('ceil')).toBe(2n);
+ });
+
+ it('should divide', () => {
+ let result = mkTime('1').divide(2);
+ expect(result.base).toBe(0n);
+ expect(result.offset).toBeCloseTo(0.5);
+
+ result = mkTime('1.6').divide(2);
+ expect(result.base).toBe(0n);
+ expect(result.offset).toBeCloseTo(0.8);
+
+ result = mkTime('-0.5').divide(2);
+ expect(result.base).toBe(-1n);
+ expect(result.offset).toBeCloseTo(0.75);
+
+ result = mkTime('123.1').divide(123);
+ expect(result.base).toBe(1n);
+ expect(result.offset).toBeCloseTo(0.000813, 6);
+ });
+
+ it('should multiply', () => {
+ let result = mkTime('1').multiply(2);
+ expect(result.base).toBe(2n);
+ expect(result.offset).toBeCloseTo(0);
+
+ result = mkTime('1').multiply(2.5);
+ expect(result.base).toBe(2n);
+ expect(result.offset).toBeCloseTo(0.5);
+
+ result = mkTime('-0.5').multiply(2);
+ expect(result.base).toBe(-1n);
+ expect(result.offset).toBeCloseTo(0.0);
+
+ result = mkTime('123.1').multiply(25.5);
+ expect(result.base).toBe(3139n);
+ expect(result.offset).toBeCloseTo(0.05);
+ });
+
+ it('should convert to string', () => {
+ expect(mkTime('1.3').toString()).toBe('1.3');
+ expect(mkTime('12983423847.332533').toString()).toBe('12983423847.332533');
+ expect(new HPTime(234n).toString()).toBe('234');
+ });
+
+ it('should calculate absolute', () => {
+ let result = mkTime('-0.7').abs();
+ expect(result.base).toEqual(0n);
+ expect(result.offset).toBeCloseTo(0.7);
+
+ result = mkTime('-1.3').abs();
+ expect(result.base).toEqual(1n);
+ expect(result.offset).toBeCloseTo(0.3);
+
+ result = mkTime('-100').abs();
+ expect(result.base).toEqual(100n);
+ expect(result.offset).toBeCloseTo(0);
+
+ result = mkTime('34.5345').abs();
+ expect(result.base).toEqual(34n);
+ expect(result.offset).toBeCloseTo(0.5345);
+ });
+});
+
+describe('HighPrecisionTimeSpan', () => {
+ it('can be constructed from HP time', () => {
+ const span = new HPTimeSpan(mkTime('10'), mkTime('20'));
+ expect(span.start).toEqual(mkTime('10'));
+ expect(span.end).toEqual(mkTime('20'));
+ });
+
+ it('can be constructed from integer time', () => {
+ const span = new HPTimeSpan(10n, 20n);
+ expect(span.start).toEqual(mkTime('10'));
+ expect(span.end).toEqual(mkTime('20'));
+ });
+
+ it('throws when start is later than end', () => {
+ expect(() => new HPTimeSpan(mkTime('0.1'), mkTime('0'))).toThrow();
+ expect(() => new HPTimeSpan(mkTime('1124.0001'), mkTime('1124'))).toThrow();
+ });
+
+ it('can calc duration', () => {
+ let dur = mkSpan('10', '20').duration;
+ expect(dur.base).toBe(10n);
+ expect(dur.offset).toBeCloseTo(0);
+
+ dur = mkSpan('10.123', '20.456').duration;
+ expect(dur.base).toBe(10n);
+ expect(dur.offset).toBeCloseTo(0.333);
+ });
+
+ it('can calc midpoint', () => {
+ let mid = mkSpan('10', '20').midpoint;
+ expect(mid.base).toBe(15n);
+ expect(mid.offset).toBeCloseTo(0);
+
+ mid = mkSpan('10.25', '16.75').midpoint;
+ expect(mid.base).toBe(13n);
+ expect(mid.offset).toBeCloseTo(0.5);
+ });
+
+ it('can be compared', () => {
+ expect(mkSpan('0.1', '34.2').equals(mkSpan('0.1', '34.2'))).toBeTruthy();
+ expect(mkSpan('0.1', '34.5').equals(mkSpan('0.1', '34.2'))).toBeFalsy();
+ expect(mkSpan('0.9', '34.2').equals(mkSpan('0.1', '34.2'))).toBeFalsy();
+ });
+
+ it('checks if span contains another span', () => {
+ const x = mkSpan('10', '20');
+
+ expect(x.contains(mkTime('9'))).toBeFalsy();
+ expect(x.contains(mkTime('10'))).toBeTruthy();
+ expect(x.contains(mkTime('15'))).toBeTruthy();
+ expect(x.contains(mkTime('20'))).toBeFalsy();
+ expect(x.contains(mkTime('21'))).toBeFalsy();
+
+ expect(x.contains(mkSpan('12', '18'))).toBeTruthy();
+ expect(x.contains(mkSpan('5', '25'))).toBeFalsy();
+ expect(x.contains(mkSpan('5', '15'))).toBeFalsy();
+ expect(x.contains(mkSpan('15', '25'))).toBeFalsy();
+ expect(x.contains(mkSpan('0', '10'))).toBeFalsy();
+ expect(x.contains(mkSpan('20', '30'))).toBeFalsy();
+ });
+
+ it('checks if span intersects another span', () => {
+ const x = mkSpan('10', '20');
+
+ expect(x.intersects(mkSpan('0', '10'))).toBeFalsy();
+ expect(x.intersects(mkSpan('5', '15'))).toBeTruthy();
+ expect(x.intersects(mkSpan('12', '18'))).toBeTruthy();
+ expect(x.intersects(mkSpan('15', '25'))).toBeTruthy();
+ expect(x.intersects(mkSpan('20', '30'))).toBeFalsy();
+ expect(x.intersects(mkSpan('5', '25'))).toBeTruthy();
+ });
+});
diff --git a/ui/src/common/internal_layout_utils.ts b/ui/src/common/internal_layout_utils.ts
new file mode 100644
index 0000000..478cd02
--- /dev/null
+++ b/ui/src/common/internal_layout_utils.ts
@@ -0,0 +1,50 @@
+// 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.
+
+// Object to facilitate generation of SELECT statement using
+// generateSqlWithInternalLayout.
+//
+// Fields:
+// @columns: a string array list of the columns to be selected from the table.
+// @layoutParams: a config of the timestamp (ts) and duration (dur) fields
+// required by the internal_layout function.
+// @sourceTable: the table in the FROM clause, source of the data.
+// @whereClause: the WHERE clause to filter data from the source table.
+// @orderByClause: the ORDER BY clause for the query data.
+interface GenerateSqlArgs {
+ columns: string[];
+ layoutParams: {ts: string, dur: string};
+ sourceTable: string;
+ whereClause?: string;
+ orderByClause?: string;
+}
+
+// Function to generate a SELECT statement utilizing the internal_layout
+// SQL function as a depth field.
+export function generateSqlWithInternalLayout(sqlArgs: GenerateSqlArgs):
+ string {
+ let sql = `SELECT ` + sqlArgs.columns.toString() + ', internal_layout(' +
+ sqlArgs.layoutParams.ts + ',' + sqlArgs.layoutParams.dur +
+ ') OVER (ORDER BY ' + sqlArgs.layoutParams.ts +
+ ' ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS depth' +
+ ' FROM ' + sqlArgs.sourceTable;
+ if (sqlArgs.whereClause !== undefined) {
+ sql += ' WHERE ' + sqlArgs.whereClause;
+ }
+ if (sqlArgs.orderByClause !== undefined) {
+ sql += ' ORDER BY ' + sqlArgs.orderByClause;
+ }
+ sql += ';';
+ return sql;
+}
diff --git a/ui/src/common/logs.ts b/ui/src/common/logs.ts
index 0fc2fae..04cf065 100644
--- a/ui/src/common/logs.ts
+++ b/ui/src/common/logs.ts
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {TPTime} from './time';
+
export const LogExistsKey = 'log-exists';
export const LogBoundsKey = 'log-bounds';
export const LogEntriesKey = 'log-entries';
@@ -19,16 +21,16 @@
export interface LogExists { exists: boolean; }
export interface LogBounds {
- startTs: number;
- endTs: number;
- firstRowTs: number;
- lastRowTs: number;
- total: number;
+ firstLogTs: TPTime;
+ lastLogTs: TPTime;
+ firstVisibleLogTs: TPTime;
+ lastVisibleLogTs: TPTime;
+ totalVisibleLogs: number;
}
export interface LogEntries {
offset: number;
- timestamps: number[];
+ timestamps: TPTime[];
priorities: number[];
tags: string[];
messages: string[];
diff --git a/ui/src/common/plugin_api.ts b/ui/src/common/plugin_api.ts
index fa8a9fc..0dc66d6 100644
--- a/ui/src/common/plugin_api.ts
+++ b/ui/src/common/plugin_api.ts
@@ -15,9 +15,12 @@
import {EngineProxy} from '../common/engine';
import {TrackControllerFactory} from '../controller/track_controller';
import {TrackCreator} from '../frontend/track';
+import {Selection} from './state';
export {EngineProxy} from '../common/engine';
export {
+ LONG,
+ LONG_NULL,
NUM,
NUM_NULL,
STR,
@@ -66,6 +69,16 @@
// could be registered in dev.perfetto.CounterTrack - a whole
// different plugin.
registerTrack(track: TrackCreator): void;
+
+ // Register custom functionality to specify how the plugin should handle
+ // selection changes for tracks in this plugin.
+ //
+ // Params:
+ // @onDetailsPanelSelectionChange a function that takes a Selection as its
+ // parameter and performs whatever must happen on the details panel when the
+ // selection is invoked.
+ registerOnDetailsPanelSelectionChange(
+ onDetailsPanelSelectionChange: (newSelection?: Selection) => void): void;
}
export interface PluginInfo {
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index aa414bc..5898c5f 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -27,12 +27,14 @@
TrackProvider,
} from './plugin_api';
import {Registry} from './registry';
+import {Selection} from './state';
// Every plugin gets its own PluginContext. This is how we keep track
// what each plugin is doing and how we can blame issues on particular
// plugins.
export class PluginContextImpl implements PluginContext {
readonly pluginId: string;
+ onDetailsPanelSelectionChange?: (newSelection?: Selection) => void;
private trackProviders: TrackProvider[];
constructor(pluginId: string) {
@@ -53,6 +55,11 @@
registerTrackProvider(provider: TrackProvider) {
this.trackProviders.push(provider);
}
+
+ registerOnDetailsPanelSelectionChange(
+ onDetailsPanelSelectionChange: (newSelection?: Selection) => void) {
+ this.onDetailsPanelSelectionChange = onDetailsPanelSelectionChange;
+ }
// ==================================================================
// ==================================================================
@@ -123,6 +130,14 @@
}
return promises;
}
+
+ onDetailsPanelSelectionChange(pluginId: string, newSelection?: Selection) {
+ const pluginContext = this.getPluginContext(pluginId);
+ if (pluginContext === undefined) return;
+ if (pluginContext.onDetailsPanelSelectionChange) {
+ pluginContext.onDetailsPanelSelectionChange(newSelection);
+ }
+ }
}
// TODO(hjd): Sort out the story for global singletons like these:
diff --git a/ui/src/common/query_result.ts b/ui/src/common/query_result.ts
index e8d482d..90fb8f4 100644
--- a/ui/src/common/query_result.ts
+++ b/ui/src/common/query_result.ts
@@ -159,7 +159,7 @@
// One row extracted from an SQL result:
export interface Row {
- [key: string]: ColumnType;
+ [key: string]: ColumnType|undefined;
}
// The methods that any iterator has to implement.
diff --git a/ui/src/common/recordingV2/recording_config_utils.ts b/ui/src/common/recordingV2/recording_config_utils.ts
index 2840b3c..d02da5a 100644
--- a/ui/src/common/recordingV2/recording_config_utils.ts
+++ b/ui/src/common/recordingV2/recording_config_utils.ts
@@ -79,9 +79,8 @@
export function genTraceConfig(
uiCfg: RecordConfig, targetInfo: TargetInfo): TraceConfig {
- const androidApiLevel = (targetInfo.targetType === 'ANDROID') ?
- targetInfo.androidApiLevel :
- undefined;
+ const isAndroid = targetInfo.targetType === 'ANDROID';
+ const androidApiLevel = isAndroid ? targetInfo.androidApiLevel : undefined;
const protoCfg = new TraceConfig();
protoCfg.durationMs = uiCfg.durationMs;
@@ -96,8 +95,8 @@
protoCfg.buffers.push(new BufferConfig());
protoCfg.buffers.push(new BufferConfig());
- protoCfg.buffers[1].sizeKb = slowBufSizeKb;
protoCfg.buffers[0].sizeKb = fastBufSizeKb;
+ protoCfg.buffers[1].sizeKb = slowBufSizeKb;
if (uiCfg.mode === 'STOP_WHEN_FULL') {
protoCfg.buffers[0].fillPolicy = BufferConfig.FillPolicy.DISCARD;
@@ -131,6 +130,14 @@
let procThreadAssociationFtrace = false;
let trackInitialOomScore = false;
+ if (isAndroid) {
+ const ds = new TraceConfig.DataSource();
+ ds.config = new DataSourceConfig();
+ ds.config.targetBuffer = 1;
+ ds.config.name = 'android.packages_list';
+ protoCfg.dataSources.push(ds);
+ }
+
if (uiCfg.cpuSched) {
procThreadAssociationPolling = true;
procThreadAssociationFtrace = true;
diff --git a/ui/src/common/search_data.ts b/ui/src/common/search_data.ts
index 0969c1d..2854d3c 100644
--- a/ui/src/common/search_data.ts
+++ b/ui/src/common/search_data.ts
@@ -13,14 +13,14 @@
// limitations under the License.
export interface SearchSummary {
- tsStarts: Float64Array;
- tsEnds: Float64Array;
+ tsStarts: BigInt64Array;
+ tsEnds: BigInt64Array;
count: Uint8Array;
}
export interface CurrentSearchResults {
sliceIds: Float64Array;
- tsStarts: Float64Array;
+ tsStarts: BigInt64Array;
utids: Float64Array;
trackIds: string[];
sources: string[];
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index d4d07b7..52855b8 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -18,7 +18,10 @@
PivotTree,
TableColumn,
} from '../frontend/pivot_table_types';
+import {TopLevelScrollSelection} from '../tracks/scroll_jank/scroll_track';
+
import {Direction} from './event_set';
+import {TPDuration, TPTime} from './time';
/**
* A plain js object, holding objects of type |Class| keyed by string id.
@@ -39,9 +42,9 @@
}
export interface VisibleState extends Timestamped {
- startSec: number;
- endSec: number;
- resolution: number;
+ start: TPTime;
+ end: TPTime;
+ resolution: TPDuration;
}
export interface AreaSelection {
@@ -59,8 +62,8 @@
export type AreaById = Area&{id: string};
export interface Area {
- startSec: number;
- endSec: number;
+ start: TPTime;
+ end: TPTime;
tracks: string[];
}
@@ -100,7 +103,9 @@
// 28. Add a boolean indicating if non matching log entries are hidden.
// 29. Add ftrace state. <-- Borked, state contains a non-serializable object.
// 30. Convert ftraceFilter.excludedNames from Set<string> to string[].
-export const STATE_VERSION = 30;
+// 31. Convert all timestamps to bigints.
+// 32. Add pendingDeeplink.
+export const STATE_VERSION = 31;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -268,8 +273,8 @@
}
export interface TraceTime {
- startSec: number;
- endSec: number;
+ start: TPTime;
+ end: TPTime;
}
export interface FrontendLocalState {
@@ -284,7 +289,7 @@
export interface Note {
noteType: 'DEFAULT';
id: string;
- timestamp: number;
+ timestamp: TPTime;
color: string;
text: string;
}
@@ -311,14 +316,14 @@
kind: 'DEBUG_SLICE';
id: number;
sqlTableName: string;
- startS: number;
- durationS: number;
+ start: TPTime;
+ duration: TPDuration;
}
export interface CounterSelection {
kind: 'COUNTER';
- leftTs: number;
- rightTs: number;
+ leftTs: TPTime;
+ rightTs: TPTime;
id: number;
}
@@ -326,7 +331,7 @@
kind: 'HEAP_PROFILE';
id: number;
upid: number;
- ts: number;
+ ts: TPTime;
type: ProfileType;
}
@@ -334,16 +339,16 @@
kind: 'PERF_SAMPLES';
id: number;
upid: number;
- leftTs: number;
- rightTs: number;
+ leftTs: TPTime;
+ rightTs: TPTime;
type: ProfileType;
}
export interface FlamegraphState {
kind: 'FLAMEGRAPH_STATE';
upids: number[];
- startNs: number;
- endNs: number;
+ start: TPTime;
+ end: TPTime;
type: ProfileType;
viewingOption: FlamegraphStateViewingOption;
focusRegex: string;
@@ -354,7 +359,7 @@
kind: 'CPU_PROFILE_SAMPLE';
id: number;
utid: number;
- ts: number;
+ ts: TPTime;
}
export interface ChromeSliceSelection {
@@ -377,8 +382,8 @@
export type Selection =
(NoteSelection|SliceSelection|CounterSelection|HeapProfileSelection|
CpuProfileSampleSelection|ChromeSliceSelection|ThreadStateSelection|
- AreaSelection|PerfSamplesSelection|LogSelection|DebugSliceSelection)&
- {trackId?: string};
+ AreaSelection|PerfSamplesSelection|LogSelection|DebugSliceSelection|
+ TopLevelScrollSelection)&{trackId?: string};
export type SelectionKind = Selection['kind']; // 'THREAD_STATE' | 'SLICE' ...
export interface Pagination {
@@ -511,6 +516,12 @@
excludedNames: string[];
}
+export interface PendingDeeplinkState {
+ ts?: string;
+ dur?: string;
+ tid?: string;
+}
+
export interface State {
version: number;
nextId: string;
@@ -570,8 +581,8 @@
// Hovered and focused events
hoveredUtid: number;
hoveredPid: number;
- hoverCursorTimestamp: number;
- hoveredNoteTimestamp: number;
+ hoverCursorTimestamp: TPTime;
+ hoveredNoteTimestamp: TPTime;
highlightedSliceId: number;
focusedFlowIdLeft: number;
focusedFlowIdRight: number;
@@ -605,11 +616,15 @@
// Omnibox info.
omniboxState: OmniboxState;
+
+ // Pending deeplink which will happen when we first finish opening a
+ // trace.
+ pendingDeeplink?: PendingDeeplinkState;
}
export const defaultTraceTime = {
- startSec: 0,
- endSec: 10,
+ start: 0n,
+ end: BigInt(10e9),
};
export declare type RecordMode =
@@ -671,12 +686,13 @@
}
export function getBuiltinChromeCategoryList(): string[] {
- // List of static Chrome categories, last updated at 2023-04-04 from HEAD of
+ // List of static Chrome categories, last updated at 2023-05-30 from HEAD of
// Chromium's //base/trace_event/builtin_categories.h.
return [
'accessibility',
'AccountFetcherService',
'android_webview',
+ 'android_webview.timeline',
'aogh',
'audio',
'base',
diff --git a/ui/src/common/time.ts b/ui/src/common/time.ts
index ea5e9d8..a96778d 100644
--- a/ui/src/common/time.ts
+++ b/ui/src/common/time.ts
@@ -13,8 +13,7 @@
// limitations under the License.
import {assertTrue} from '../base/logging';
-
-const EPSILON = 0.0000000001;
+import {ColumnType} from './query_result';
// TODO(hjd): Combine with timeToCode.
export function timeToString(sec: number) {
@@ -29,6 +28,11 @@
return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`;
}
+export function tpTimeToString(time: TPTime) {
+ // TODO(stevegolton): Write a formatter to format bigint timestamps natively.
+ return timeToString(tpTimeToSeconds(time));
+}
+
export function fromNs(ns: number) {
return ns / 1e9;
}
@@ -52,6 +56,11 @@
return parts.join('.');
}
+export function formatTPTime(time: TPTime) {
+ // TODO(stevegolton): Write a formatter to format bigint timestamps natively.
+ return formatTimestamp(tpTimeToSeconds(time));
+}
+
// TODO(hjd): Rename to formatTimestampWithUnits
// 1000000023ns -> "1s 23ns"
export function timeToCode(sec: number): string {
@@ -77,44 +86,129 @@
return result.slice(0, -1);
}
+export function tpTimeToCode(time: TPTime) {
+ // TODO(stevegolton): Write a formatter to format bigint timestamps natively.
+ return timeToCode(tpTimeToSeconds(time));
+}
+
export function currentDateHourAndMinute(): string {
const date = new Date();
return `${date.toISOString().substr(0, 10)}-${date.getHours()}-${
date.getMinutes()}`;
}
-export class TimeSpan {
- readonly start: number;
- readonly end: number;
+// Aliased "Trace Processor" time and duration types.
+// Note(stevegolton): While it might be nice to type brand these in the future,
+// for now we're going to keep things simple. We do a lot of maths with these
+// timestamps and type branding requires a lot of jumping through hoops to
+// coerse the type back to the correct format.
+export type TPTime = bigint;
+export type TPDuration = bigint;
- constructor(start: number, end: number) {
- assertTrue(start <= end);
+export function tpTimeFromNanos(nanos: number): TPTime {
+ return BigInt(Math.floor(nanos));
+}
+
+export function tpTimeFromSeconds(seconds: number): TPTime {
+ return BigInt(Math.floor(seconds * 1e9));
+}
+
+export function tpTimeToNanos(time: TPTime): number {
+ return Number(time);
+}
+
+export function tpTimeToMillis(time: TPTime): number {
+ return Number(time) / 1e6;
+}
+
+export function tpTimeToSeconds(time: TPTime): number {
+ return Number(time) / 1e9;
+}
+
+// Create a TPTime from an arbitrary SQL value.
+// Throws if the value cannot be reasonably converted to a bigint.
+// Assumes value is in nanoseconds.
+export function tpTimeFromSql(value: ColumnType): TPTime {
+ if (typeof value === 'bigint') {
+ return value;
+ } else if (typeof value === 'number') {
+ return tpTimeFromNanos(value);
+ } else if (value === null) {
+ return 0n;
+ } else {
+ throw Error(`Refusing to create Timestamp from unrelated type ${value}`);
+ }
+}
+
+export function tpDurationToSeconds(dur: TPDuration): number {
+ return tpTimeToSeconds(dur);
+}
+
+export function tpDurationToNanos(dur: TPDuration): number {
+ return tpTimeToSeconds(dur);
+}
+
+export function tpDurationFromNanos(nanos: number): TPDuration {
+ return tpTimeFromNanos(nanos);
+}
+
+export function tpDurationFromSql(nanos: ColumnType): TPDuration {
+ return tpTimeFromSql(nanos);
+}
+
+export interface Span<Unit, Duration = Unit> {
+ get start(): Unit;
+ get end(): Unit;
+ get duration(): Duration;
+ get midpoint(): Unit;
+ contains(span: Unit|Span<Unit, Duration>): boolean;
+ intersects(x: Span<Unit>): boolean;
+ equals(span: Span<Unit, Duration>): boolean;
+ add(offset: Duration): Span<Unit, Duration>;
+ pad(padding: Duration): Span<Unit, Duration>;
+}
+
+export class TPTimeSpan implements Span<TPTime, TPDuration> {
+ readonly start: TPTime;
+ readonly end: TPTime;
+
+ constructor(start: TPTime, end: TPTime) {
+ assertTrue(
+ start <= end,
+ `Span start [${start}] cannot be greater than end [${end}]`);
this.start = start;
this.end = end;
}
- clone() {
- return new TimeSpan(this.start, this.end);
- }
-
- equals(other: TimeSpan): boolean {
- return Math.abs(this.start - other.start) < EPSILON &&
- Math.abs(this.end - other.end) < EPSILON;
- }
-
- get duration() {
+ get duration(): TPDuration {
return this.end - this.start;
}
- isInBounds(sec: number) {
- return this.start <= sec && sec <= this.end;
+ get midpoint(): TPTime {
+ return (this.start + this.end) / 2n;
}
- add(sec: number): TimeSpan {
- return new TimeSpan(this.start + sec, this.end + sec);
+ contains(x: TPTime|Span<TPTime, TPDuration>): boolean {
+ if (typeof x === 'bigint') {
+ return this.start <= x && x < this.end;
+ } else {
+ return this.start <= x.start && x.end <= this.end;
+ }
}
- contains(other: TimeSpan) {
- return this.start <= other.start && other.end <= this.end;
+ intersects(x: Span<TPTime, TPDuration>): boolean {
+ return !(x.end <= this.start || x.start >= this.end);
+ }
+
+ equals(span: Span<TPTime, TPDuration>): boolean {
+ return this.start === span.start && this.end === span.end;
+ }
+
+ add(x: TPTime): Span<TPTime, TPDuration> {
+ return new TPTimeSpan(this.start + x, this.end + x);
+ }
+
+ pad(padding: TPDuration): Span<TPTime, TPDuration> {
+ return new TPTimeSpan(this.start - padding, this.end + padding);
}
}
diff --git a/ui/src/common/time_unittest.ts b/ui/src/common/time_unittest.ts
index 7bfead1..b9e6bd9 100644
--- a/ui/src/common/time_unittest.ts
+++ b/ui/src/common/time_unittest.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TimeSpan, timeToCode} from './time';
+import {timeToCode, TPTime, TPTimeSpan} from './time';
test('seconds to code', () => {
expect(timeToCode(3)).toEqual('3s');
@@ -29,9 +29,67 @@
expect(timeToCode(0)).toEqual('0s');
});
-test('Time span equality', () => {
- expect((new TimeSpan(0, 1)).equals(new TimeSpan(0, 1))).toBe(true);
- expect((new TimeSpan(0, 1)).equals(new TimeSpan(0, 2))).toBe(false);
- expect((new TimeSpan(0, 1)).equals(new TimeSpan(0, 1 + Number.EPSILON)))
- .toBe(true);
+function mkSpan(start: TPTime, end: TPTime) {
+ return new TPTimeSpan(start, end);
+}
+
+describe('TPTimeSpan', () => {
+ it('throws when start is later than end', () => {
+ expect(() => mkSpan(1n, 0n)).toThrow();
+ });
+
+ it('can calc duration', () => {
+ expect(mkSpan(10n, 20n).duration).toBe(10n);
+ });
+
+ it('can calc midpoint', () => {
+ expect(mkSpan(10n, 20n).midpoint).toBe(15n);
+ expect(mkSpan(10n, 19n).midpoint).toBe(14n);
+ expect(mkSpan(10n, 10n).midpoint).toBe(10n);
+ });
+
+ it('can be compared', () => {
+ const x = mkSpan(10n, 20n);
+ expect(x.equals(mkSpan(10n, 20n))).toBeTruthy();
+ expect(x.equals(mkSpan(11n, 20n))).toBeFalsy();
+ expect(x.equals(mkSpan(10n, 19n))).toBeFalsy();
+ });
+
+ it('checks containment', () => {
+ const x = mkSpan(10n, 20n);
+
+ expect(x.contains(9n)).toBeFalsy();
+ expect(x.contains(10n)).toBeTruthy();
+ expect(x.contains(15n)).toBeTruthy();
+ expect(x.contains(20n)).toBeFalsy();
+ expect(x.contains(21n)).toBeFalsy();
+
+ expect(x.contains(mkSpan(12n, 18n))).toBeTruthy();
+ expect(x.contains(mkSpan(5n, 25n))).toBeFalsy();
+ expect(x.contains(mkSpan(5n, 15n))).toBeFalsy();
+ expect(x.contains(mkSpan(15n, 25n))).toBeFalsy();
+ expect(x.contains(mkSpan(0n, 10n))).toBeFalsy();
+ expect(x.contains(mkSpan(20n, 30n))).toBeFalsy();
+ });
+
+ it('checks intersection', () => {
+ const x = mkSpan(10n, 20n);
+
+ expect(x.intersects(mkSpan(0n, 10n))).toBeFalsy();
+ expect(x.intersects(mkSpan(5n, 15n))).toBeTruthy();
+ expect(x.intersects(mkSpan(12n, 18n))).toBeTruthy();
+ expect(x.intersects(mkSpan(15n, 25n))).toBeTruthy();
+ expect(x.intersects(mkSpan(20n, 30n))).toBeFalsy();
+ expect(x.intersects(mkSpan(5n, 25n))).toBeTruthy();
+ });
+
+ it('can add', () => {
+ const x = mkSpan(10n, 20n);
+ expect(x.add(5n)).toEqual(mkSpan(15n, 25n));
+ });
+
+ it('can pad', () => {
+ const x = mkSpan(10n, 20n);
+ expect(x.pad(5n)).toEqual(mkSpan(5n, 25n));
+ });
});
diff --git a/ui/src/common/track_data.ts b/ui/src/common/track_data.ts
index 9af7fff..a49a56d 100644
--- a/ui/src/common/track_data.ts
+++ b/ui/src/common/track_data.ts
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {TPDuration, TPTime} from './time';
+
// TODO(hjd): Refactor into method on TrackController
export const LIMIT = 10000;
export interface TrackData {
- start: number;
- end: number;
- resolution: number;
+ start: TPTime;
+ end: TPTime;
+ resolution: TPDuration;
length: number;
}
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
index f92f4fa..f5c855b 100644
--- a/ui/src/controller/aggregation/aggregation_controller.ts
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -149,7 +149,12 @@
} else if (item instanceof Uint8Array) {
column.data[i] = internString('<Binary blob>');
} else if (typeof item === 'bigint') {
- // TODO(stevegolton) Handle potential loss of precision
+ // TODO(stevegolton) It would be nice to keep bigints as bigints for
+ // the purposes of aggregation, however the aggregation infrastructure
+ // is likely to be significantly reworked when we introduce EventSet,
+ // and the complexity of supporting bigints throughout the aggregation
+ // panels in it's current form is not worth it. Thus, we simply
+ // convert bigints to numbers.
column.data[i] = Number(item);
} else {
column.data[i] = item;
diff --git a/ui/src/controller/aggregation/counter_aggregation_controller.ts b/ui/src/controller/aggregation/counter_aggregation_controller.ts
index dd7f14a..e632f8e 100644
--- a/ui/src/controller/aggregation/counter_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/counter_aggregation_controller.ts
@@ -15,7 +15,7 @@
import {ColumnDef} from '../../common/aggregation_data';
import {Engine} from '../../common/engine';
import {Area, Sorting} from '../../common/state';
-import {toNs} from '../../common/time';
+import {tpDurationToSeconds} from '../../common/time';
import {globals} from '../../frontend/globals';
import {Config, COUNTER_TRACK_KIND} from '../../tracks/counter';
@@ -38,21 +38,22 @@
}
}
if (ids.length === 0) return false;
+ const duration = area.end - area.start;
+ const durationSec = tpDurationToSeconds(duration);
const query = `create view ${this.kind} as select
name,
count(1) as count,
- round(sum(weighted_value)/${
- toNs(area.endSec) - toNs(area.startSec)}, 2) as avg_value,
+ round(sum(weighted_value)/${duration}, 2) as avg_value,
last as last_value,
first as first_value,
max(last) - min(first) as delta_value,
- round((max(last) - min(first))/${area.endSec - area.startSec}, 2) as rate,
+ round((max(last) - min(first))/${durationSec}, 2) as rate,
min(value) as min_value,
max(value) as max_value
from
(select *,
- (min(ts + dur, ${toNs(area.endSec)}) - max(ts,${toNs(area.startSec)}))
+ (min(ts + dur, ${area.end}) - max(ts,${area.start}))
* value as weighted_value,
first_value(value) over
(partition by track_id order by ts) as first,
@@ -61,8 +62,8 @@
range between unbounded preceding and unbounded following) as last
from experimental_counter_dur
where track_id in (${ids})
- and ts + dur >= ${toNs(area.startSec)} and
- ts <= ${toNs(area.endSec)})
+ and ts + dur >= ${area.start} and
+ ts <= ${area.end})
join counter_track
on track_id = counter_track.id
group by track_id`;
diff --git a/ui/src/controller/aggregation/cpu_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
index 94d3950..452ca75 100644
--- a/ui/src/controller/aggregation/cpu_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/cpu_aggregation_controller.ts
@@ -15,7 +15,6 @@
import {ColumnDef} from '../../common/aggregation_data';
import {Engine} from '../../common/engine';
import {Area, Sorting} from '../../common/state';
-import {toNs} from '../../common/time';
import {globals} from '../../frontend/globals';
import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices';
@@ -46,8 +45,8 @@
JOIN thread_state USING(utid)
WHERE cpu IN (${selectedCpus}) AND
state = "Running" AND
- thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
- thread_state.ts < ${toNs(area.endSec)} group by utid`;
+ thread_state.ts + thread_state.dur > ${area.start} AND
+ thread_state.ts < ${area.end} group by utid`;
await engine.query(query);
return true;
diff --git a/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
index b28e496..fc24d1e 100644
--- a/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/cpu_by_process_aggregation_controller.ts
@@ -15,7 +15,6 @@
import {ColumnDef} from '../../common/aggregation_data';
import {Engine} from '../../common/engine';
import {Area, Sorting} from '../../common/state';
-import {toNs} from '../../common/time';
import {globals} from '../../frontend/globals';
import {Config, CPU_SLICE_TRACK_KIND} from '../../tracks/cpu_slices';
@@ -45,8 +44,8 @@
JOIN thread_state USING(utid)
WHERE cpu IN (${selectedCpus}) AND
state = "Running" AND
- thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
- thread_state.ts < ${toNs(area.endSec)} group by upid`;
+ thread_state.ts + thread_state.dur > ${area.start} AND
+ thread_state.ts < ${area.end} group by upid`;
await engine.query(query);
return true;
diff --git a/ui/src/controller/aggregation/frame_aggregation_controller.ts b/ui/src/controller/aggregation/frame_aggregation_controller.ts
index 97e1f86..a22a83d 100644
--- a/ui/src/controller/aggregation/frame_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/frame_aggregation_controller.ts
@@ -15,7 +15,6 @@
import {ColumnDef} from '../../common/aggregation_data';
import {Engine} from '../../common/engine';
import {Area, Sorting} from '../../common/state';
-import {toNs} from '../../common/time';
import {globals} from '../../frontend/globals';
import {
ACTUAL_FRAMES_SLICE_TRACK_KIND,
@@ -48,8 +47,8 @@
MAX(dur) as maxDur
FROM actual_frame_timeline_slice
WHERE track_id IN (${selectedSqlTrackIds}) AND
- ts + dur > ${toNs(area.startSec)} AND
- ts < ${toNs(area.endSec)} group by jank_type`;
+ ts + dur > ${area.start} AND
+ ts < ${area.end} group by jank_type`;
await engine.query(query);
return true;
diff --git a/ui/src/controller/aggregation/slice_aggregation_controller.ts b/ui/src/controller/aggregation/slice_aggregation_controller.ts
index ebb8000..8dcaccc 100644
--- a/ui/src/controller/aggregation/slice_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/slice_aggregation_controller.ts
@@ -15,7 +15,6 @@
import {ColumnDef} from '../../common/aggregation_data';
import {Engine} from '../../common/engine';
import {Area, Sorting} from '../../common/state';
-import {toNs} from '../../common/time';
import {globals} from '../../frontend/globals';
import {
ASYNC_SLICE_TRACK_KIND,
@@ -64,8 +63,8 @@
count(1) as occurrences
FROM slices
WHERE track_id IN (${selectedTrackIds}) AND
- ts + dur > ${toNs(area.startSec)} AND
- ts < ${toNs(area.endSec)} group by name`;
+ ts + dur > ${area.start} AND
+ ts < ${area.end} group by name`;
await engine.query(query);
return true;
diff --git a/ui/src/controller/aggregation/thread_aggregation_controller.ts b/ui/src/controller/aggregation/thread_aggregation_controller.ts
index ddfadae..6e288d5 100644
--- a/ui/src/controller/aggregation/thread_aggregation_controller.ts
+++ b/ui/src/controller/aggregation/thread_aggregation_controller.ts
@@ -17,7 +17,6 @@
import {NUM, NUM_NULL, STR_NULL} from '../../common/query_result';
import {Area, Sorting} from '../../common/state';
import {translateState} from '../../common/thread_state';
-import {toNs} from '../../common/time';
import {globals} from '../../frontend/globals';
import {
Config,
@@ -60,8 +59,8 @@
JOIN thread USING(upid)
JOIN thread_state USING(utid)
WHERE utid IN (${this.utids}) AND
- thread_state.ts + thread_state.dur > ${toNs(area.startSec)} AND
- thread_state.ts < ${toNs(area.endSec)}
+ thread_state.ts + thread_state.dur > ${area.start} AND
+ thread_state.ts < ${area.end}
GROUP BY utid, concat_state
`;
@@ -78,8 +77,8 @@
JOIN thread USING(upid)
JOIN thread_state USING(utid)
WHERE utid IN (${this.utids}) AND thread_state.ts + thread_state.dur > ${
- toNs(area.startSec)} AND
- thread_state.ts < ${toNs(area.endSec)}
+ area.start} AND
+ thread_state.ts < ${area.end}
GROUP BY state, io_wait`;
const result = await engine.query(query);
diff --git a/ui/src/controller/area_selection_handler.ts b/ui/src/controller/area_selection_handler.ts
index b1d1c7e..32dcb11 100644
--- a/ui/src/controller/area_selection_handler.ts
+++ b/ui/src/controller/area_selection_handler.ts
@@ -36,10 +36,10 @@
// where `a ||= b` is formatted to `a || = b`, by inserting a space which
// breaks the operator.
// Therefore, we are using the pattern `a = a || b` instead.
- hasAreaChanged = hasAreaChanged ||
- selectedArea.startSec !== this.previousArea.startSec;
hasAreaChanged =
- hasAreaChanged || selectedArea.endSec !== this.previousArea.endSec;
+ hasAreaChanged || selectedArea.start !== this.previousArea.start;
+ hasAreaChanged =
+ hasAreaChanged || selectedArea.end !== this.previousArea.end;
hasAreaChanged = hasAreaChanged ||
selectedArea.tracks.length !== this.previousArea.tracks.length;
for (let i = 0; i < selectedArea.tracks.length; ++i) {
diff --git a/ui/src/controller/area_selection_handler_unittest.ts b/ui/src/controller/area_selection_handler_unittest.ts
index c5a27c0..caac678 100644
--- a/ui/src/controller/area_selection_handler_unittest.ts
+++ b/ui/src/controller/area_selection_handler_unittest.ts
@@ -20,7 +20,7 @@
test('validAreaAfterUndefinedArea', () => {
const areaId = '0';
- const latestArea: AreaById = {startSec: 0, endSec: 1, tracks: [], id: areaId};
+ const latestArea: AreaById = {start: 0n, end: 1n, tracks: [], id: areaId};
globals.state = createEmptyState();
globals.state.currentSelection = {kind: 'AREA', areaId};
globals.state.areas[areaId] = latestArea;
@@ -35,7 +35,7 @@
test('UndefinedAreaAfterValidArea', () => {
const previousAreaId = '0';
const previous:
- AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
+ AreaById = {start: 0n, end: 1n, tracks: [], id: previousAreaId};
globals.state = createEmptyState();
globals.state.currentSelection = {
kind: 'AREA',
@@ -71,7 +71,7 @@
test('validAreaAfterValidArea', () => {
const previousAreaId = '0';
const previous:
- AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
+ AreaById = {start: 0n, end: 1n, tracks: [], id: previousAreaId};
globals.state = createEmptyState();
globals.state.currentSelection = {
kind: 'AREA',
@@ -82,8 +82,7 @@
areaSelectionHandler.getAreaChange();
const currentAreaId = '1';
- const current:
- AreaById = {startSec: 1, endSec: 2, tracks: [], id: currentAreaId};
+ const current: AreaById = {start: 1n, end: 2n, tracks: [], id: currentAreaId};
globals.state.currentSelection = {
kind: 'AREA',
areaId: currentAreaId,
@@ -98,7 +97,7 @@
test('sameAreaSelected', () => {
const previousAreaId = '0';
const previous:
- AreaById = {startSec: 0, endSec: 1, tracks: [], id: previousAreaId};
+ AreaById = {start: 0n, end: 1n, tracks: [], id: previousAreaId};
globals.state = createEmptyState();
globals.state.currentSelection = {
kind: 'AREA',
@@ -109,8 +108,7 @@
areaSelectionHandler.getAreaChange();
const currentAreaId = '0';
- const current:
- AreaById = {startSec: 0, endSec: 1, tracks: [], id: currentAreaId};
+ const current: AreaById = {start: 0n, end: 1n, tracks: [], id: currentAreaId};
globals.state.currentSelection = {
kind: 'AREA',
areaId: currentAreaId,
@@ -128,7 +126,7 @@
areaSelectionHandler.getAreaChange();
globals.state
- .currentSelection = {kind: 'COUNTER', leftTs: 0, rightTs: 0, id: 1};
+ .currentSelection = {kind: 'COUNTER', leftTs: 0n, rightTs: 0n, id: 1};
const [hasAreaChanged, selectedArea] = areaSelectionHandler.getAreaChange();
expect(hasAreaChanged).toEqual(false);
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index e94531d..31a74c3 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -27,7 +27,7 @@
} from '../common/flamegraph_util';
import {NUM, STR} from '../common/query_result';
import {CallsiteInfo, FlamegraphState, ProfileType} from '../common/state';
-import {toNs} from '../common/time';
+import {tpDurationToSeconds, TPTime} from '../common/time';
import {FlamegraphDetails, globals} from '../frontend/globals';
import {publishFlamegraphDetails} from '../frontend/publish';
import {
@@ -145,8 +145,8 @@
}
globals.dispatch(Actions.openFlamegraph({
upids,
- startNs: toNs(area.startSec),
- endNs: toNs(area.endSec),
+ start: area.start,
+ end: area.end,
type: ProfileType.PERF_SAMPLE,
viewingOption: PERF_SAMPLES_KEY,
}));
@@ -169,8 +169,8 @@
const selectedFlamegraphState = {...selection};
const flamegraphMetadata = await this.getFlamegraphMetadata(
selection.type,
- selectedFlamegraphState.startNs,
- selectedFlamegraphState.endNs,
+ selectedFlamegraphState.start,
+ selectedFlamegraphState.end,
selectedFlamegraphState.upids);
if (flamegraphMetadata !== undefined) {
Object.assign(this.flamegraphDetails, flamegraphMetadata);
@@ -192,7 +192,7 @@
selectedFlamegraphState.expandedCallsite.totalSize;
const key = `${selectedFlamegraphState.upids};${
- selectedFlamegraphState.startNs};${selectedFlamegraphState.endNs}`;
+ selectedFlamegraphState.start};${selectedFlamegraphState.end}`;
try {
const flamegraphData = await this.getFlamegraphData(
@@ -200,15 +200,15 @@
selectedFlamegraphState.viewingOption ?
selectedFlamegraphState.viewingOption :
DEFAULT_VIEWING_OPTION,
- selection.startNs,
- selection.endNs,
+ selection.start,
+ selection.end,
selectedFlamegraphState.upids,
selectedFlamegraphState.type,
selectedFlamegraphState.focusRegex);
if (flamegraphData !== undefined && selection &&
selection.kind === selectedFlamegraphState.kind &&
- selection.startNs === selectedFlamegraphState.startNs &&
- selection.endNs === selectedFlamegraphState.endNs) {
+ selection.start === selectedFlamegraphState.start &&
+ selection.end === selectedFlamegraphState.end) {
const expandedFlamegraphData =
expandCallsites(flamegraphData, expandedId);
this.prepareAndMergeCallsites(
@@ -230,8 +230,8 @@
private shouldRequestData(selection: FlamegraphState) {
return selection.kind === 'FLAMEGRAPH_STATE' &&
(this.lastSelectedFlamegraphState === undefined ||
- (this.lastSelectedFlamegraphState.startNs !== selection.startNs ||
- this.lastSelectedFlamegraphState.endNs !== selection.endNs ||
+ (this.lastSelectedFlamegraphState.start !== selection.start ||
+ this.lastSelectedFlamegraphState.end !== selection.end ||
this.lastSelectedFlamegraphState.type !== selection.type ||
!FlamegraphController.areArraysEqual(
this.lastSelectedFlamegraphState.upids, selection.upids) ||
@@ -267,7 +267,7 @@
}
async getFlamegraphData(
- baseKey: string, viewingOption: string, startNs: number, endNs: number,
+ baseKey: string, viewingOption: string, start: TPTime, end: TPTime,
upids: number[], type: ProfileType,
focusRegex: string): Promise<CallsiteInfo[]> {
let currentData: CallsiteInfo[];
@@ -280,8 +280,8 @@
// Collecting data for drawing flamegraph for selected profile.
// Data needs to be in following format:
// id, name, parent_id, depth, total_size
- const tableName = await this.prepareViewsAndTables(
- startNs, endNs, upids, type, focusRegex);
+ const tableName =
+ await this.prepareViewsAndTables(start, end, upids, type, focusRegex);
currentData = await this.getFlamegraphDataFromTables(
tableName, viewingOption, focusRegex);
this.flamegraphDatasets.set(key, currentData);
@@ -413,7 +413,7 @@
}
private async prepareViewsAndTables(
- startNs: number, endNs: number, upids: number[], type: ProfileType,
+ start: TPTime, end: TPTime, upids: number[], type: ProfileType,
focusRegex: string): Promise<string> {
// Creating unique names for views so we can reuse and not delete them
// for each marker.
@@ -437,8 +437,8 @@
cumulative_alloc_size, cumulative_count, cumulative_alloc_count,
size, alloc_size, count, alloc_count, source_file, line_number
from experimental_flamegraph
- where profile_type = '${flamegraphType}' and ${startNs} <= ts and
- ts <= ${endNs} and ${upidConditional}
+ where profile_type = '${flamegraphType}' and ${start} <= ts and
+ ts <= ${end} and ${upidConditional}
${focusRegexConditional}`);
}
return this.cache.getTableName(
@@ -447,7 +447,7 @@
size, alloc_size, count, alloc_count, source_file, line_number
from experimental_flamegraph
where profile_type = '${flamegraphType}'
- and ts = ${endNs}
+ and ts = ${end}
and upid = ${upids[0]}
${focusRegexConditional}`);
}
@@ -455,7 +455,8 @@
getMinSizeDisplayed(flamegraphData: CallsiteInfo[], rootSize?: number):
number {
const timeState = globals.state.frontendLocalState.visibleState;
- let width = (timeState.endSec - timeState.startSec) / timeState.resolution;
+ const dur = globals.stateVisibleTime().duration;
+ let width = tpDurationToSeconds(dur / timeState.resolution);
// TODO(168048193): Remove screen size hack:
width = Math.max(width, 800);
if (rootSize === undefined) {
@@ -465,11 +466,12 @@
}
async getFlamegraphMetadata(
- type: ProfileType, startNs: number, endNs: number, upids: number[]) {
+ type: ProfileType, start: TPTime, end: TPTime,
+ upids: number[]): Promise<FlamegraphDetails|undefined> {
// Don't do anything if selection of the marker stayed the same.
if ((this.lastSelectedFlamegraphState !== undefined &&
- ((this.lastSelectedFlamegraphState.startNs === startNs &&
- this.lastSelectedFlamegraphState.endNs === endNs &&
+ ((this.lastSelectedFlamegraphState.start === start &&
+ this.lastSelectedFlamegraphState.end === end &&
FlamegraphController.areArraysEqual(
this.lastSelectedFlamegraphState.upids, upids))))) {
return undefined;
@@ -486,7 +488,7 @@
for (let i = 0; it.valid(); ++i, it.next()) {
pids.push(it.pid);
}
- return {startNs, durNs: endNs - startNs, pids, upids, type};
+ return {start, dur: end - start, pids, upids, type};
}
private static areArraysEqual(a: number[], b: number[]) {
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index d89b514..d92090d 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -14,9 +14,8 @@
import {Engine} from '../common/engine';
import {featureFlags} from '../common/feature_flags';
-import {NUM, STR_NULL} from '../common/query_result';
+import {LONG, NUM, STR_NULL} from '../common/query_result';
import {Area} from '../common/state';
-import {fromNs, toNs} from '../common/time';
import {Flow, globals} from '../frontend/globals';
import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish';
import {
@@ -86,8 +85,8 @@
beginSliceName: STR_NULL,
beginSliceChromeCustomName: STR_NULL,
beginSliceCategory: STR_NULL,
- beginSliceStartTs: NUM,
- beginSliceEndTs: NUM,
+ beginSliceStartTs: LONG,
+ beginSliceEndTs: LONG,
beginDepth: NUM,
beginThreadName: STR_NULL,
beginProcessName: STR_NULL,
@@ -96,8 +95,8 @@
endSliceName: STR_NULL,
endSliceChromeCustomName: STR_NULL,
endSliceCategory: STR_NULL,
- endSliceStartTs: NUM,
- endSliceEndTs: NUM,
+ endSliceStartTs: LONG,
+ endSliceEndTs: LONG,
endDepth: NUM,
endThreadName: STR_NULL,
endProcessName: STR_NULL,
@@ -116,8 +115,8 @@
it.beginSliceChromeCustomName;
const beginSliceCategory =
it.beginSliceCategory === null ? 'NULL' : it.beginSliceCategory;
- const beginSliceStartTs = fromNs(it.beginSliceStartTs);
- const beginSliceEndTs = fromNs(it.beginSliceEndTs);
+ const beginSliceStartTs = it.beginSliceStartTs;
+ const beginSliceEndTs = it.beginSliceEndTs;
const beginDepth = it.beginDepth;
const beginThreadName =
it.beginThreadName === null ? 'NULL' : it.beginThreadName;
@@ -133,8 +132,8 @@
it.endSliceChromeCustomName;
const endSliceCategory =
it.endSliceCategory === null ? 'NULL' : it.endSliceCategory;
- const endSliceStartTs = fromNs(it.endSliceStartTs);
- const endSliceEndTs = fromNs(it.endSliceEndTs);
+ const endSliceStartTs = it.endSliceStartTs;
+ const endSliceEndTs = it.endSliceEndTs;
const endDepth = it.endDepth;
const endThreadName =
it.endThreadName === null ? 'NULL' : it.endThreadName;
@@ -241,8 +240,8 @@
const area = globals.state.areas[areaId];
if (this.lastSelectedKind === 'AREA' && this.lastSelectedArea &&
this.lastSelectedArea.tracks.join(',') === area.tracks.join(',') &&
- this.lastSelectedArea.endSec === area.endSec &&
- this.lastSelectedArea.startSec === area.startSec) {
+ this.lastSelectedArea.end === area.end &&
+ this.lastSelectedArea.start === area.start) {
return;
}
@@ -268,8 +267,8 @@
const tracks = `(${trackIds.join(',')})`;
- const startNs = toNs(area.startSec);
- const endNs = toNs(area.endSec);
+ const startNs = area.start;
+ const endNs = area.end;
const query = `
select
diff --git a/ui/src/controller/ftrace_controller.ts b/ui/src/controller/ftrace_controller.ts
index f558a4a..7f4ecd9 100644
--- a/ui/src/controller/ftrace_controller.ts
+++ b/ui/src/controller/ftrace_controller.ts
@@ -13,9 +13,13 @@
// limitations under the License.
import {Engine} from '../common/engine';
-import {NUM, STR, STR_NULL} from '../common/query_result';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
+import {LONG, NUM, STR, STR_NULL} from '../common/query_result';
import {FtraceFilterState, Pagination} from '../common/state';
-import {TimeSpan, toNsCeil, toNsFloor} from '../common/time';
+import {Span} from '../common/time';
import {FtraceEvent, globals} from '../frontend/globals';
import {publishFtracePanelData} from '../frontend/publish';
import {ratelimit} from '../frontend/rate_limiters';
@@ -34,7 +38,7 @@
export class FtraceController extends Controller<'main'> {
private engine: Engine;
- private oldSpan: TimeSpan = new TimeSpan(0, 0);
+ private oldSpan: Span<HighPrecisionTime> = HighPrecisionTimeSpan.ZERO;
private oldFtraceFilter?: FtraceFilterState;
private oldPagination?: Pagination;
@@ -45,7 +49,7 @@
run() {
if (this.shouldUpdate()) {
- this.oldSpan = globals.frontendLocalState.visibleWindowTime.clone();
+ this.oldSpan = globals.frontendLocalState.visibleWindowTime;
this.oldFtraceFilter = globals.state.ftraceFilter;
this.oldPagination = globals.state.ftracePagination;
if (globals.state.ftracePagination.count > 0) {
@@ -66,8 +70,7 @@
private shouldUpdate(): boolean {
// Has the visible window moved?
const visibleWindow = globals.frontendLocalState.visibleWindowTime;
- if (this.oldSpan.start !== visibleWindow.start ||
- this.oldSpan.end !== visibleWindow.end) {
+ if (!this.oldSpan.equals(visibleWindow)) {
return true;
}
@@ -86,11 +89,7 @@
async lookupFtraceEvents(offset: number, count: number): Promise<RetVal> {
const appState = globals.state;
- const frontendState = globals.frontendLocalState;
- const {start, end} = frontendState.visibleWindowTime;
-
- const startNs = toNsFloor(start);
- const endNs = toNsCeil(end);
+ const {start, end} = globals.stateVisibleTime();
const excludeList = appState.ftraceFilter.excludedNames;
const excludeListSql = excludeList.map((s) => `'${s}'`).join(',');
@@ -105,7 +104,7 @@
from ftrace_event
where
ftrace_event.name not in (${excludeListSql}) and
- ts >= ${startNs} and ts <= ${endNs}
+ ts >= ${start} and ts <= ${end}
`);
const {numEvents} = queryRes.firstRow({numEvents: NUM});
@@ -125,14 +124,14 @@
on thread.upid = process.upid
where
ftrace_event.name not in (${excludeListSql}) and
- ts >= ${startNs} and ts <= ${endNs}
+ ts >= ${start} and ts <= ${end}
order by id
limit ${count} offset ${offset};`);
const events: FtraceEvent[] = [];
const it = queryRes.iter(
{
id: NUM,
- ts: NUM,
+ ts: LONG,
name: STR,
cpu: NUM,
thread: STR_NULL,
diff --git a/ui/src/controller/logs_controller.ts b/ui/src/controller/logs_controller.ts
index d8f9b3a..c931ce0 100644
--- a/ui/src/controller/logs_controller.ts
+++ b/ui/src/controller/logs_controller.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../base/bigint_math';
import {Engine} from '../common/engine';
import {
LogBounds,
@@ -20,62 +21,61 @@
LogEntriesKey,
LogExistsKey,
} from '../common/logs';
-import {NUM, STR} from '../common/query_result';
+import {LONG, LONG_NULL, NUM, STR} from '../common/query_result';
import {escapeGlob, escapeQuery} from '../common/query_utils';
import {LogFilteringCriteria} from '../common/state';
-import {fromNs, TimeSpan, toNsCeil, toNsFloor} from '../common/time';
+import {Span} from '../common/time';
+import {
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {globals} from '../frontend/globals';
import {publishTrackData} from '../frontend/publish';
import {Controller} from './controller';
async function updateLogBounds(
- engine: Engine, span: TimeSpan): Promise<LogBounds> {
- const vizStartNs = toNsFloor(span.start);
- const vizEndNs = toNsCeil(span.end);
+ engine: Engine, span: Span<TPTime>): Promise<LogBounds> {
+ const vizStartNs = span.start;
+ const vizEndNs = span.end;
- const countResult = await engine.query(`select
- ifnull(min(ts), 0) as minTs,
- ifnull(max(ts), 0) as maxTs,
- count(ts) as countTs
- from filtered_logs
- where ts >= ${vizStartNs}
- and ts <= ${vizEndNs}`);
+ const vizFilter = `ts between ${vizStartNs} and ${vizEndNs}`;
- const countRow = countResult.firstRow({minTs: NUM, maxTs: NUM, countTs: NUM});
+ const result = await engine.query(`select
+ min(ts) as minTs,
+ max(ts) as maxTs,
+ min(case when ${vizFilter} then ts end) as minVizTs,
+ max(case when ${vizFilter} then ts end) as maxVizTs,
+ count(case when ${vizFilter} then ts end) as countTs
+ from filtered_logs`);
- const firstRowNs = countRow.minTs;
- const lastRowNs = countRow.maxTs;
- const total = countRow.countTs;
+ const data = result.firstRow({
+ minTs: LONG_NULL,
+ maxTs: LONG_NULL,
+ minVizTs: LONG_NULL,
+ maxVizTs: LONG_NULL,
+ countTs: NUM,
+ });
- const minResult = await engine.query(`
- select ifnull(max(ts), 0) as maxTs from filtered_logs where ts < ${
- vizStartNs}`);
- const startNs = minResult.firstRow({maxTs: NUM}).maxTs;
+ const firstLogTs = data.minTs ?? 0n;
+ const lastLogTs = data.maxTs ?? BigintMath.INT64_MAX;
- const maxResult = await engine.query(`
- select ifnull(min(ts), 0) as minTs from filtered_logs where ts > ${
- vizEndNs}`);
- const endNs = maxResult.firstRow({minTs: NUM}).minTs;
-
- const startTs = startNs ? fromNs(startNs) : 0;
- const endTs = endNs ? fromNs(endNs) : Number.MAX_SAFE_INTEGER;
- const firstRowTs = firstRowNs ? fromNs(firstRowNs) : endTs;
- const lastRowTs = lastRowNs ? fromNs(lastRowNs) : startTs;
- return {
- startTs,
- endTs,
- firstRowTs,
- lastRowTs,
- total,
+ const bounds: LogBounds = {
+ firstLogTs,
+ lastLogTs,
+ firstVisibleLogTs: data.minVizTs ?? firstLogTs,
+ lastVisibleLogTs: data.maxVizTs ?? lastLogTs,
+ totalVisibleLogs: data.countTs,
};
+
+ return bounds;
}
async function updateLogEntries(
- engine: Engine, span: TimeSpan, pagination: Pagination):
+ engine: Engine, span: Span<TPTime>, pagination: Pagination):
Promise<LogEntries> {
- const vizStartNs = toNsFloor(span.start);
- const vizEndNs = toNsCeil(span.end);
+ const vizStartNs = span.start;
+ const vizEndNs = span.end;
const vizSqlBounds = `ts >= ${vizStartNs} and ts <= ${vizEndNs}`;
const rowsResult = await engine.query(`
@@ -101,7 +101,7 @@
const processName = [];
const it = rowsResult.iter({
- ts: NUM,
+ ts: LONG,
prio: NUM,
tag: STR,
msg: STR,
@@ -179,7 +179,7 @@
*/
export class LogsController extends Controller<'main'> {
private engine: Engine;
- private span: TimeSpan;
+ private span: Span<TPTime>;
private pagination: Pagination;
private hasLogs = false;
private logFilteringCriteria?: LogFilteringCriteria;
@@ -189,7 +189,7 @@
constructor(args: LogsControllerArgs) {
super('main');
this.engine = args.engine;
- this.span = new TimeSpan(0, 10);
+ this.span = new TPTimeSpan(0n, BigInt(10e9));
this.pagination = new Pagination(0, 0);
this.hasAnyLogs().then((exists) => {
this.hasLogs = exists;
@@ -226,8 +226,7 @@
}
private async updateLogTracks() {
- const traceTime = globals.state.frontendLocalState.visibleState;
- const newSpan = new TimeSpan(traceTime.startSec, traceTime.endSec);
+ const newSpan = globals.stateVisibleTime();
const oldSpan = this.span;
const pagination = globals.state.logsPagination;
diff --git a/ui/src/controller/record_controller_jsdomtest.ts b/ui/src/controller/record_controller_jsdomtest.ts
index f160290..bc7c15b 100644
--- a/ui/src/controller/record_controller_jsdomtest.ts
+++ b/ui/src/controller/record_controller_jsdomtest.ts
@@ -32,7 +32,8 @@
const result =
TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
const sources = assertExists(result.dataSources);
- const srcConfig = assertExists(sources[0].config);
+ // TODO(hjd): This is all bad. Should just match the whole config.
+ const srcConfig = assertExists(sources[1].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
expect(ftraceEvents.includes('raw_syscalls/sys_enter')).toBe(true);
@@ -45,7 +46,7 @@
const result =
TraceConfig.decode(genConfigProto(config, {os: 'S', name: 'Android S'}));
const sources = assertExists(result.dataSources);
- const srcConfig = assertExists(sources[1].config);
+ const srcConfig = assertExists(sources[2].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
expect(ftraceConfig.symbolizeKsyms).toBe(true);
@@ -58,7 +59,7 @@
const result =
TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
const sources = assertExists(result.dataSources);
- const srcConfig = assertExists(sources[1].config);
+ const srcConfig = assertExists(sources[2].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
expect(ftraceConfig.symbolizeKsyms).toBe(false);
@@ -72,7 +73,7 @@
const result =
TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
const sources = assertExists(result.dataSources);
- const srcConfig = assertExists(sources[0].config);
+ const srcConfig = assertExists(sources[1].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
expect(ftraceConfig.symbolizeKsyms).toBe(true);
@@ -86,7 +87,7 @@
const result =
TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
const sources = assertExists(result.dataSources);
- const srcConfig = assertExists(sources[0].config);
+ const srcConfig = assertExists(sources[1].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
expect(ftraceConfig.symbolizeKsyms).toBe(false);
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index d1df094..b2c7e04 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -12,13 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../base/bigint_math';
import {sqliteString} from '../base/string_utils';
import {Engine} from '../common/engine';
-import {NUM, STR} from '../common/query_result';
+import {LONG, NUM, STR} from '../common/query_result';
import {escapeSearchQuery} from '../common/query_utils';
import {CurrentSearchResults, SearchSummary} from '../common/search_data';
-import {TimeSpan} from '../common/time';
-import {toNs} from '../common/time';
+import {Span} from '../common/time';
+import {
+ TPDuration,
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {globals} from '../frontend/globals';
import {publishSearch, publishSearchResult} from '../frontend/publish';
@@ -30,8 +35,8 @@
export class SearchController extends Controller<'main'> {
private engine: Engine;
- private previousSpan: TimeSpan;
- private previousResolution: number;
+ private previousSpan: Span<TPTime>;
+ private previousResolution: TPDuration;
private previousSearch: string;
private updateInProgress: boolean;
private setupInProgress: boolean;
@@ -39,11 +44,11 @@
constructor(args: SearchControllerArgs) {
super('main');
this.engine = args.engine;
- this.previousSpan = new TimeSpan(0, 1);
+ this.previousSpan = new TPTimeSpan(0n, 1n);
this.previousSearch = '';
this.updateInProgress = false;
this.setupInProgress = true;
- this.previousResolution = 1;
+ this.previousResolution = 1n;
this.setup().finally(() => {
this.setupInProgress = false;
this.run();
@@ -70,9 +75,9 @@
omniboxState.mode === 'COMMAND') {
return;
}
- const newSpan = new TimeSpan(visibleState.startSec, visibleState.endSec);
+ const newSpan = globals.stateVisibleTime();
const newSearch = omniboxState.omnibox;
- let newResolution = visibleState.resolution;
+ const newResolution = visibleState.resolution;
if (this.previousSpan.contains(newSpan) &&
this.previousResolution === newResolution &&
newSearch === this.previousSearch) {
@@ -83,20 +88,19 @@
// TODO(hjd): We should restrict this to the start of the trace but
// that is not easily available here.
// N.B. Timestamps can be negative.
- const start = newSpan.start - newSpan.duration;
- const end = newSpan.end + newSpan.duration;
- this.previousSpan = new TimeSpan(start, end);
+ const {start, end} = newSpan.pad(newSpan.duration);
+ this.previousSpan = new TPTimeSpan(start, end);
this.previousResolution = newResolution;
this.previousSearch = newSearch;
if (newSearch === '' || newSearch.length < 4) {
publishSearch({
- tsStarts: new Float64Array(0),
- tsEnds: new Float64Array(0),
+ tsStarts: new BigInt64Array(0),
+ tsEnds: new BigInt64Array(0),
count: new Uint8Array(0),
});
publishSearchResult({
sliceIds: new Float64Array(0),
- tsStarts: new Float64Array(0),
+ tsStarts: new BigInt64Array(0),
utids: new Float64Array(0),
sources: [],
trackIds: [],
@@ -105,25 +109,12 @@
return;
}
- let startNs = toNs(newSpan.start);
- let endNs = toNs(newSpan.end);
-
- // TODO(hjd): We shouldn't need to be so defensive here:
- if (!Number.isFinite(startNs)) {
- startNs = 0;
- }
- if (!Number.isFinite(endNs)) {
- endNs = 1;
- }
- if (!Number.isFinite(newResolution)) {
- newResolution = 1;
- }
-
this.updateInProgress = true;
- const computeSummary = this.update(newSearch, startNs, endNs, newResolution)
- .then((summary) => {
- publishSearch(summary);
- });
+ const computeSummary =
+ this.update(newSearch, newSpan.start, newSpan.end, newResolution)
+ .then((summary) => {
+ publishSearch(summary);
+ });
const computeResults =
this.specificSearch(newSearch).then((searchResults) => {
@@ -140,15 +131,14 @@
onDestroy() {}
private async update(
- search: string, startNs: number, endNs: number,
- resolution: number): Promise<SearchSummary> {
- const quantumNs = Math.round(resolution * 10 * 1e9);
-
+ search: string, startNs: TPTime, endNs: TPTime,
+ resolution: TPDuration): Promise<SearchSummary> {
const searchLiteral = escapeSearchQuery(search);
- startNs = Math.floor(startNs / quantumNs) * quantumNs;
+ const quantumNs = resolution * 10n;
+ startNs = BigintMath.quantFloor(startNs, quantumNs);
- const windowDur = Math.max(endNs - startNs, 1);
+ const windowDur = BigintMath.max(endNs - startNs, 1n);
await this.query(`update search_summary_window set
window_start=${startNs},
window_dur=${windowDur},
@@ -169,8 +159,8 @@
const res = await this.query(`
select
- (quantum_ts * ${quantumNs} + ${startNs})/1e9 as tsStart,
- ((quantum_ts+1) * ${quantumNs} + ${startNs})/1e9 as tsEnd,
+ (quantum_ts * ${quantumNs} + ${startNs}) as tsStart,
+ ((quantum_ts+1) * ${quantumNs} + ${startNs}) as tsEnd,
min(count(*), 255) as count
from (
select
@@ -187,13 +177,13 @@
order by quantum_ts;`);
const numRows = res.numRows();
- const summary = {
- tsStarts: new Float64Array(numRows),
- tsEnds: new Float64Array(numRows),
+ const summary: SearchSummary = {
+ tsStarts: new BigInt64Array(numRows),
+ tsEnds: new BigInt64Array(numRows),
count: new Uint8Array(numRows),
};
- const it = res.iter({tsStart: NUM, tsEnd: NUM, count: NUM});
+ const it = res.iter({tsStart: LONG, tsEnd: LONG, count: NUM});
for (let row = 0; it.valid(); it.next(), ++row) {
summary.tsStarts[row] = it.tsStart;
summary.tsEnds[row] = it.tsEnd;
@@ -269,7 +259,7 @@
const rows = queryRes.numRows();
const searchResults: CurrentSearchResults = {
sliceIds: new Float64Array(rows),
- tsStarts: new Float64Array(rows),
+ tsStarts: new BigInt64Array(rows),
utids: new Float64Array(rows),
trackIds: [],
sources: [],
@@ -277,7 +267,7 @@
};
const it = queryRes.iter(
- {sliceId: NUM, ts: NUM, source: STR, sourceId: NUM, utid: NUM});
+ {sliceId: NUM, ts: LONG, source: STR, sourceId: NUM, utid: NUM});
for (; it.valid(); it.next()) {
let trackId = undefined;
if (it.source === 'cpu') {
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 66cf3d0..b5a3907 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -16,14 +16,23 @@
import {Arg, Args} from '../common/arg_types';
import {Engine} from '../common/engine';
import {
+ LONG,
NUM,
NUM_NULL,
STR,
STR_NULL,
} from '../common/query_result';
import {ChromeSliceSelection} from '../common/state';
-import {fromNs, toNs} from '../common/time';
-import {SliceDetails, ThreadStateDetails} from '../frontend/globals';
+import {
+ tpDurationFromSql,
+ TPTime,
+ tpTimeFromSql,
+} from '../common/time';
+import {
+ CounterDetails,
+ SliceDetails,
+ ThreadStateDetails,
+} from '../frontend/globals';
import {globals} from '../frontend/globals';
import {
publishCounterDetails,
@@ -176,10 +185,10 @@
case 'id':
break;
case 'ts':
- ts = fromNs(Number(v)) - globals.state.traceTime.startSec;
+ ts = tpTimeFromSql(v);
break;
case 'thread_ts':
- threadTs = fromNs(Number(v));
+ threadTs = tpTimeFromSql(v);
break;
case 'absTime':
if (v) absTime = `${v}`;
@@ -188,10 +197,10 @@
name = `${v}`;
break;
case 'dur':
- dur = fromNs(Number(v));
+ dur = tpDurationFromSql(v);
break;
case 'thread_dur':
- threadDur = fromNs(Number(v));
+ threadDur = tpDurationFromSql(v);
break;
case 'category':
case 'cat':
@@ -326,13 +335,13 @@
const selection = globals.state.currentSelection;
if (result.numRows() > 0 && selection) {
const row = result.firstRow({
- ts: NUM,
- dur: NUM,
+ ts: LONG,
+ dur: LONG,
});
- const ts = row.ts;
- const timeFromStart = fromNs(ts) - globals.state.traceTime.startSec;
- const dur = fromNs(row.dur);
- const selected: ThreadStateDetails = {ts: timeFromStart, dur};
+ const selected: ThreadStateDetails = {
+ ts: row.ts,
+ dur: row.dur,
+ };
publishThreadStateDetails(selected);
}
}
@@ -353,8 +362,8 @@
const selection = globals.state.currentSelection;
if (result.numRows() > 0 && selection) {
const row = result.firstRow({
- ts: NUM,
- dur: NUM,
+ ts: LONG,
+ dur: LONG,
priority: NUM,
endState: STR_NULL,
utid: NUM,
@@ -362,15 +371,14 @@
threadStateId: NUM_NULL,
});
const ts = row.ts;
- const timeFromStart = fromNs(ts) - globals.state.traceTime.startSec;
- const dur = fromNs(row.dur);
+ const dur = row.dur;
const priority = row.priority;
const endState = row.endState;
const utid = row.utid;
const cpu = row.cpu;
const threadStateId = row.threadStateId || undefined;
const selected: SliceDetails = {
- ts: timeFromStart,
+ ts,
dur,
priority,
endState,
@@ -391,7 +399,8 @@
}
}
- async counterDetails(ts: number, rightTs: number, id: number) {
+ async counterDetails(ts: TPTime, rightTs: TPTime, id: number):
+ Promise<CounterDetails> {
const counter = await this.args.engine.query(
`SELECT value, track_id as trackId FROM counter WHERE id = ${id}`);
const row = counter.iter({
@@ -407,17 +416,15 @@
IFNULL(value, 0) as value
FROM counter WHERE ts < ${ts} and track_id = ${trackId}`);
const previousValue = previous.firstRow({value: NUM}).value;
- const endTs =
- rightTs !== -1 ? rightTs : toNs(globals.state.traceTime.endSec);
+ const endTs = rightTs !== -1n ? rightTs : globals.state.traceTime.end;
const delta = value - previousValue;
const duration = endTs - ts;
- const startTime = fromNs(ts) - globals.state.traceTime.startSec;
const uiTrackId = globals.state.uiTrackIdByTraceTrackId[trackId];
const name = uiTrackId ? globals.state.tracks[uiTrackId].name : undefined;
- return {startTime, value, delta, duration, name};
+ return {startTime: ts, value, delta, duration, name};
}
- async schedulingDetails(ts: number, utid: number|Long) {
+ async schedulingDetails(ts: TPTime, utid: number|Long) {
// Find the ts of the first wakeup before the current slice.
const wakeResult = await this.args.engine.query(`
select ts, waker_utid as wakerUtid
@@ -430,7 +437,7 @@
return undefined;
}
- const wakeFirstRow = wakeResult.firstRow({ts: NUM, wakerUtid: NUM_NULL});
+ const wakeFirstRow = wakeResult.firstRow({ts: LONG, wakerUtid: NUM_NULL});
const wakeupTs = wakeFirstRow.ts;
const wakerUtid = wakeFirstRow.wakerUtid;
if (wakerUtid === null) {
@@ -449,7 +456,7 @@
// If this is the first sched slice for this utid or if the wakeup found
// was after the previous slice then we know the wakeup was for this slice.
if (prevSchedResult.numRows() !== 0 &&
- wakeupTs < prevSchedResult.firstRow({ts: NUM}).ts) {
+ wakeupTs < prevSchedResult.firstRow({ts: LONG}).ts) {
return undefined;
}
@@ -468,7 +475,7 @@
}
const wakerRow = wakerResult.firstRow({cpu: NUM});
- return {wakeupTs: fromNs(wakeupTs), wakerUtid, wakerCpu: wakerRow.cpu};
+ return {wakeupTs, wakerUtid, wakerCpu: wakerRow.cpu};
}
async computeThreadDetails(utid: number):
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index b704e87..49301e3 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../base/bigint_math';
import {assertExists, assertTrue} from '../base/logging';
import {
Actions,
@@ -20,15 +21,35 @@
import {cacheTrace} from '../common/cache_manager';
import {Engine} from '../common/engine';
import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../common/feature_flags';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
import {HttpRpcEngine} from '../common/http_rpc_engine';
import {
getEnabledMetatracingCategories,
isMetatracingEnabled,
} from '../common/metatracing';
-import {NUM, NUM_NULL, QueryError, STR, STR_NULL} from '../common/query_result';
+import {
+ LONG,
+ NUM,
+ NUM_NULL,
+ QueryError,
+ STR,
+ STR_NULL,
+} from '../common/query_result';
import {onSelectionChanged} from '../common/selection_observer';
-import {defaultTraceTime, EngineMode, ProfileType} from '../common/state';
-import {TimeSpan, toNs, toNsCeil, toNsFloor} from '../common/time';
+import {
+ defaultTraceTime,
+ EngineMode,
+ PendingDeeplinkState,
+ ProfileType,
+} from '../common/state';
+import {Span} from '../common/time';
+import {
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {resetEngineWorker, WasmEngineProxy} from '../common/wasm_engine_proxy';
import {BottomTabList} from '../frontend/bottom_tab';
import {
@@ -39,6 +60,7 @@
} from '../frontend/globals';
import {showModal} from '../frontend/modal';
import {
+ clearOverviewData,
publishFtraceCounters,
publishMetricError,
publishOverviewData,
@@ -415,11 +437,11 @@
const traceUuid = await this.cacheCurrentTrace();
const traceTime = await this.engine.getTraceTimeBounds();
- const startSec = traceTime.start;
- const endSec = traceTime.end;
+ const start = traceTime.start;
+ const end = traceTime.end;
const traceTimeState = {
- startSec,
- endSec,
+ start,
+ end,
};
const shownJsonWarning =
@@ -452,16 +474,16 @@
Actions.setTraceTime(traceTimeState),
];
- const [startVisibleTime, endVisibleTime] =
- await computeVisibleTime(startSec, endSec, isJsonTrace, this.engine);
+ const visibleTimeSpan = await computeVisibleTime(
+ traceTime.start, traceTime.end, isJsonTrace, this.engine);
// We don't know the resolution at this point. However this will be
// replaced in 50ms so a guess is fine.
- const resolution = (endVisibleTime - startVisibleTime) / 1000;
+ const resolution = visibleTimeSpan.duration.divide(1000).toTPTime();
actions.push(Actions.setVisibleTraceTime({
- startSec: startVisibleTime,
- endSec: endVisibleTime,
+ start: visibleTimeSpan.start.toTPTime(),
+ end: visibleTimeSpan.end.toTPTime(),
lastUpdate: Date.now() / 1000,
- resolution,
+ resolution: BigintMath.max(resolution, 1n),
}));
globals.dispatchMultiple(actions);
@@ -506,6 +528,12 @@
await this.selectPerfSample();
}
+ const pendingDeeplink = globals.state.pendingDeeplink;
+ if (pendingDeeplink !== undefined) {
+ globals.dispatch(Actions.clearPendingDeeplink({}));
+ await this.selectPendingDeeplink(pendingDeeplink);
+ }
+
// If the trace was shared via a permalink, it might already have a
// selection. Emit onSelectionChanged to ensure that the components (like
// current selection details) react to it.
@@ -513,6 +541,8 @@
onSelectionChanged(globals.state.currentSelection, undefined);
}
+ globals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
+
// Trace Processor doesn't support the reliable range feature for JSON
// traces.
if (!isJsonTrace && ENABLE_CHROME_RELIABLE_RANGE_ANNOTATION_FLAG.get()) {
@@ -539,8 +569,8 @@
if (profile.numRows() !== 1) return;
const row = profile.firstRow({upid: NUM});
const upid = row.upid;
- const leftTs = toNs(globals.state.traceTime.startSec);
- const rightTs = toNs(globals.state.traceTime.endSec);
+ const leftTs = globals.state.traceTime.start;
+ const rightTs = globals.state.traceTime.end;
globals.dispatch(Actions.selectPerfSamples(
{id: 0, upid, leftTs, rightTs, type: ProfileType.PERF_SAMPLE}));
}
@@ -559,13 +589,59 @@
order by ts limit 1`;
const profile = await assertExists(this.engine).query(query);
if (profile.numRows() !== 1) return;
- const row = profile.firstRow({ts: NUM, type: STR, upid: NUM});
+ const row = profile.firstRow({ts: LONG, type: STR, upid: NUM});
const ts = row.ts;
const type = profileType(row.type);
const upid = row.upid;
globals.dispatch(Actions.selectHeapProfile({id: 0, upid, ts, type}));
}
+ private async selectPendingDeeplink(link: PendingDeeplinkState) {
+ const conditions = [];
+ const {ts, dur} = link;
+
+ if (ts !== undefined) {
+ conditions.push(`ts = ${ts}`);
+ }
+ if (dur !== undefined) {
+ conditions.push(`dur = ${dur}`);
+ }
+
+ if (conditions.length === 0) {
+ return;
+ }
+
+ const query = `
+ select
+ id,
+ track_id as traceProcessorTrackId,
+ type
+ from slice
+ where ${conditions.join(' and ')}
+ ;`;
+
+ const result = await assertExists(this.engine).query(query);
+ if (result.numRows() > 0) {
+ const row = result.firstRow({
+ id: NUM,
+ traceProcessorTrackId: NUM,
+ type: STR,
+ });
+
+ const id = row.traceProcessorTrackId;
+ const trackId = globals.state.uiTrackIdByTraceTrackId[id];
+ if (trackId === undefined) {
+ return;
+ }
+ globals.makeSelection(Actions.selectChromeSlice({
+ id: row.id,
+ trackId,
+ table: '',
+ scroll: true,
+ }));
+ }
+ }
+
private async listTracks() {
this.updateStatus('Loading tracks');
const engine = assertExists<Engine>(this.engine);
@@ -609,31 +685,32 @@
publishThreads(threads);
}
- private async loadTimelineOverview(traceTime: TimeSpan) {
+ private async loadTimelineOverview(trace: Span<TPTime>) {
+ clearOverviewData();
+
const engine = assertExists<Engine>(this.engine);
- const numSteps = 100;
- const stepSec = traceTime.duration / numSteps;
+ const stepSize = BigintMath.max(1n, trace.duration / 100n);
let hasSchedOverview = false;
- for (let step = 0; step < numSteps; step++) {
+ for (let start = trace.start; start < trace.end; start += stepSize) {
+ const progress = start - trace.start;
+ const ratio = Number(progress) / Number(trace.duration);
this.updateStatus(
- 'Loading overview ' +
- `${Math.round((step + 1) / numSteps * 1000) / 10}%`);
- const startSec = traceTime.start + step * stepSec;
- const startNs = toNsFloor(startSec);
- const endSec = startSec + stepSec;
- const endNs = toNsCeil(endSec);
+ 'Loading overview ' +
+ `${Math.round(ratio * 100)}%`);
+ const end = start + stepSize;
// Sched overview.
const schedResult = await engine.query(
- `select sum(dur)/${stepSec}/1e9 as load, cpu from sched ` +
- `where ts >= ${startNs} and ts < ${endNs} and utid != 0 ` +
- 'group by cpu order by cpu');
+ `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] = {startSec, endSec, load};
+ schedData[cpu] = {start, end, load};
hasSchedOverview = true;
}
publishOverviewData(schedData);
@@ -644,16 +721,15 @@
}
// Slices overview.
- const traceStartNs = toNs(traceTime.start);
- const stepSecNs = toNs(stepSec);
const sliceResult = await engine.query(`select
bucket,
upid,
- ifnull(sum(utid_sum) / cast(${stepSecNs} as float), 0) as load
+ ifnull(sum(utid_sum) / cast(${stepSize} as float), 0) as load
from thread
inner join (
select
- ifnull(cast((ts - ${traceStartNs})/${stepSecNs} as int), 0) as bucket,
+ ifnull(cast((ts - ${trace.start})/${
+ stepSize} as int), 0) as bucket,
sum(dur) as utid_sum,
utid
from slice
@@ -664,21 +740,21 @@
group by bucket, upid`);
const slicesData: {[key: string]: QuantizedLoad[]} = {};
- const it = sliceResult.iter({bucket: NUM, upid: NUM, load: NUM});
+ const it = sliceResult.iter({bucket: LONG, upid: NUM, load: NUM});
for (; it.valid(); it.next()) {
const bucket = it.bucket;
const upid = it.upid;
const load = it.load;
- const startSec = traceTime.start + stepSec * bucket;
- const endSec = startSec + stepSec;
+ const start = trace.start + stepSize * bucket;
+ const end = start + stepSize;
const upidStr = upid.toString();
let loadArray = slicesData[upidStr];
if (loadArray === undefined) {
loadArray = slicesData[upidStr] = [];
}
- loadArray.push({startSec, endSec, load});
+ loadArray.push({start, end, load});
}
publishOverviewData(slicesData);
}
@@ -889,48 +965,48 @@
}
}
-async function computeTraceReliableRangeStart(engine: Engine): Promise<number> {
+async function computeTraceReliableRangeStart(engine: Engine): Promise<TPTime> {
const result =
await engine.query(`SELECT RUN_METRIC('chrome/chrome_reliable_range.sql');
SELECT start FROM chrome_reliable_range`);
- const bounds = result.firstRow({start: NUM});
- return bounds.start / 1e9;
+ const bounds = result.firstRow({start: LONG});
+ return bounds.start;
}
async function computeVisibleTime(
- traceStartSec: number,
- traceEndSec: number,
- isJsonTrace: boolean,
- engine: Engine): Promise<[number, number]> {
+ traceStart: TPTime, traceEnd: TPTime, isJsonTrace: boolean, engine: Engine):
+ Promise<Span<HighPrecisionTime>> {
// if we have non-default visible state, update the visible time to it
- const previousVisibleState = globals.state.frontendLocalState.visibleState;
- if (!(previousVisibleState.startSec === defaultTraceTime.startSec &&
- previousVisibleState.endSec === defaultTraceTime.endSec) &&
- (previousVisibleState.startSec >= traceStartSec &&
- previousVisibleState.endSec <= traceEndSec)) {
- return [previousVisibleState.startSec, previousVisibleState.endSec];
+ const previousVisibleState = globals.stateVisibleTime();
+ const defaultTraceSpan =
+ new TPTimeSpan(defaultTraceTime.start, defaultTraceTime.end);
+ if (!(previousVisibleState.start === defaultTraceSpan.start &&
+ previousVisibleState.end === defaultTraceSpan.end) &&
+ (previousVisibleState.start >= traceStart &&
+ previousVisibleState.end <= traceEnd)) {
+ return HighPrecisionTimeSpan.fromTpTime(
+ previousVisibleState.start, previousVisibleState.end);
}
// initialise visible time to the trace time bounds
- let visibleStartSec = traceStartSec;
- let visibleEndSec = traceEndSec;
+ let visibleStartSec = traceStart;
+ let visibleEndSec = traceEnd;
// compare start and end with metadata computed by the trace processor
const mdTime = await engine.getTracingMetadataTimeBounds();
// make sure the bounds hold
- if (Math.max(visibleStartSec, mdTime.start) <
- Math.min(visibleEndSec, mdTime.end)) {
- visibleStartSec =
- Math.max(visibleStartSec, mdTime.start);
- visibleEndSec = Math.min(visibleEndSec, mdTime.end);
+ if (BigintMath.max(visibleStartSec, mdTime.start) <
+ BigintMath.min(visibleEndSec, mdTime.end)) {
+ visibleStartSec = BigintMath.max(visibleStartSec, mdTime.start);
+ visibleEndSec = BigintMath.min(visibleEndSec, mdTime.end);
}
// Trace Processor doesn't support the reliable range feature for JSON
// traces.
if (!isJsonTrace && ENABLE_CHROME_RELIABLE_RANGE_ZOOM_FLAG.get()) {
const reliableRangeStart = await computeTraceReliableRangeStart(engine);
- visibleStartSec = Math.max(visibleStartSec, reliableRangeStart);
+ visibleStartSec = BigintMath.max(visibleStartSec, reliableRangeStart);
}
- return [visibleStartSec, visibleEndSec];
+ return HighPrecisionTimeSpan.fromTpTime(visibleStartSec, visibleEndSec);
}
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index 46d1f1f..7934a07 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -12,11 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertExists, assertTrue} from '../base/logging';
+import {BigintMath} from '../base/bigint_math';
+import {assertExists} from '../base/logging';
import {Engine} from '../common/engine';
import {Registry} from '../common/registry';
import {TraceTime, TrackState} from '../common/state';
-import {fromNs, toNs} from '../common/time';
+import {
+ TPDuration,
+ TPTime,
+ tpTimeFromSeconds,
+ TPTimeSpan,
+} from '../common/time';
import {LIMIT, TrackData} from '../common/track_data';
import {globals} from '../frontend/globals';
import {publishTrackData} from '../frontend/publish';
@@ -28,10 +34,6 @@
type TrackConfigWithNamespace = TrackConfig&{namespace: string};
-// Allow to override via devtools for testing (note, needs to be done in the
-// controller-thread).
-(self as {} as {quantPx: number}).quantPx = 1;
-
// TrackController is a base class overridden by track implementations (e.g.,
// sched slices, nestable slices, counters).
export abstract class TrackController<
@@ -55,10 +57,6 @@
this.engine = args.engine;
}
- protected pxSize(): number {
- return (self as {} as {quantPx: number}).quantPx;
- }
-
// Can be overriden by the track implementation to allow one time setup work
// to be performed before the first onBoundsChange invcation.
async onSetup() {}
@@ -70,7 +68,7 @@
// Must be overridden by the track implementation. Is invoked when the track
// frontend runs out of cached data. The derived track controller is expected
// to publish new track data in response to this call.
- abstract onBoundsChange(start: number, end: number, resolution: number):
+ abstract onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data>;
get trackState(): TrackState {
@@ -129,6 +127,7 @@
}
shouldRequestData(traceTime: TraceTime): boolean {
+ const tspan = new TPTimeSpan(traceTime.start, traceTime.end);
if (this.data === undefined) return true;
if (this.shouldReload()) return true;
@@ -137,15 +136,14 @@
if (atLimit) {
// We request more data than the window, so add window duration to find
// the previous window.
- const prevWindowStart =
- this.data.start + (traceTime.startSec - traceTime.endSec);
- return traceTime.startSec !== prevWindowStart;
+ const prevWindowStart = this.data.start + tspan.duration;
+ return tspan.start !== prevWindowStart;
}
// Otherwise request more data only when out of range of current data or
// resolution has changed.
- const inRange = traceTime.startSec >= this.data.start &&
- traceTime.endSec <= this.data.end;
+ const inRange =
+ tspan.start >= this.data.start && tspan.end <= this.data.end;
return !inRange ||
this.data.resolution !==
globals.state.frontendLocalState.visibleState.resolution;
@@ -156,14 +154,13 @@
// data. Returns the bucket size (in ns) if caching should happen and
// undefined otherwise.
// Subclasses should call this in their setup function
- cachedBucketSizeNs(numRows: number): number|undefined {
+ calcCachedBucketSize(numRows: number): TPDuration|undefined {
// Ensure that we're not caching when the table size isn't even that big.
if (numRows < TrackController.MIN_TABLE_SIZE_TO_CACHE) {
return undefined;
}
- const bounds = globals.state.traceTime;
- const traceDurNs = toNs(bounds.endSec - bounds.startSec);
+ const traceDuration = globals.stateTraceTimeTP().duration;
// For large traces, going through the raw table in the most zoomed-out
// states can be very expensive as this can involve going through O(millions
@@ -193,50 +190,46 @@
// 4k monitors have 3840 horizontal pixels so use that for a worst case
// approximation of the window width.
- const approxWidthPx = 3840;
+ const approxWidthPx = 3840n;
// Compute the outermost bucket size. This acts as a starting point for
// computing the cached size.
- const outermostResolutionLevel =
- Math.ceil(Math.log2(traceDurNs / approxWidthPx));
- const outermostBucketNs = Math.pow(2, outermostResolutionLevel);
+ const outermostBucketSize =
+ BigintMath.bitCeil(traceDuration / approxWidthPx);
+ const outermostResolutionLevel = BigintMath.log2(outermostBucketSize);
// This constant decides how many resolution levels down from our outermost
// bucket computation we want to be able to use the cached table.
// We've chosen 7 as it seems to be empircally seems to be a good fit for
// trace data.
- const resolutionLevelsCovered = 7;
+ const resolutionLevelsCovered = 7n;
// If we've got less resolution levels in the trace than the number of
// resolution levels we want to go down, bail out because this cached
// table is really not going to be used enough.
if (outermostResolutionLevel < resolutionLevelsCovered) {
- return Number.MAX_SAFE_INTEGER;
+ return BigintMath.INT64_MAX;
}
// Another way to look at moving down resolution levels is to consider how
// many sub-intervals we are splitting the bucket into.
- const bucketSubIntervals = Math.pow(2, resolutionLevelsCovered);
+ const bucketSubIntervals = 1n << resolutionLevelsCovered;
// Calculate the smallest bucket we want our table to be able to handle by
// dividing the outermsot bucket by the number of subintervals we should
// divide by.
- const cachedBucketSizeNs = outermostBucketNs / bucketSubIntervals;
+ const cachedBucketSize = outermostBucketSize / bucketSubIntervals;
- // Our logic above should make sure this is an integer but double check that
- // here as an assertion before returning.
- assertTrue(Number.isInteger(cachedBucketSizeNs));
-
- return cachedBucketSizeNs;
+ return cachedBucketSize;
}
run() {
const visibleState = globals.state.frontendLocalState.visibleState;
- if (visibleState === undefined || visibleState.resolution === undefined ||
- visibleState.resolution === Infinity) {
+ if (visibleState === undefined) {
return;
}
- const dur = visibleState.endSec - visibleState.startSec;
+ const visibleTimeSpan = globals.stateVisibleTime();
+ const dur = visibleTimeSpan.duration;
if (globals.state.visibleTracks.includes(this.trackId) &&
this.shouldRequestData(visibleState)) {
if (this.requestingData) {
@@ -253,16 +246,14 @@
.then(() => {
this.isSetup = true;
let resolution = visibleState.resolution;
- // TODO(hjd): We shouldn't have to be so defensive here.
- if (Math.log2(toNs(resolution)) % 1 !== 0) {
- // resolution is in pixels per second so 1000 means
- // 1px = 1ms.
- resolution =
- fromNs(Math.pow(2, Math.floor(Math.log2(toNs(1000)))));
+
+ if (BigintMath.popcount(resolution) !== 1) {
+ resolution = BigintMath.bitFloor(tpTimeFromSeconds(1000));
}
+
return this.onBoundsChange(
- visibleState.startSec - dur,
- visibleState.endSec + dur,
+ visibleTimeSpan.start - dur,
+ visibleTimeSpan.end + dur,
resolution);
})
.then((data) => {
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 0653552..50b4719 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -25,6 +25,7 @@
import {featureFlags, PERF_SAMPLE_FLAG} from '../common/feature_flags';
import {pluginManager} from '../common/plugins';
import {
+ LONG_NULL,
NUM,
NUM_NULL,
STR,
@@ -61,6 +62,12 @@
PROCESS_SCHEDULING_TRACK_KIND,
} from '../tracks/process_scheduling';
import {PROCESS_SUMMARY_TRACK} from '../tracks/process_summary';
+import {
+ ENABLE_SCROLL_JANK_PLUGIN_V2,
+ INPUT_LATENCY_TRACK,
+} from '../tracks/scroll_jank';
+import {addLatenciesTrack} from '../tracks/scroll_jank/event_latency_track';
+import {addTopLevelScrollTrack} from '../tracks/scroll_jank/scroll_track';
import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state';
const TRACKS_V2_FLAG = featureFlags.register({
@@ -191,14 +198,42 @@
return 'Unknown';
}
+ async guessCpuSizes(): Promise<Map<number, string>> {
+ const cpuToSize = new Map<number, string>();
+ await this.engine.query(`
+ SELECT IMPORT('common.cpus');
+ `);
+ const result = await this.engine.query(`
+ SELECT cpu, GUESS_CPU_SIZE(cpu) as size FROM cpu_counter_track;
+ `);
+
+ const it = result.iter({
+ cpu: NUM,
+ size: STR_NULL,
+ });
+
+ for (; it.valid(); it.next()) {
+ const size = it.size;
+ if (size !== null) {
+ cpuToSize.set(it.cpu, size);
+ }
+ }
+
+ return cpuToSize;
+ }
+
async addCpuSchedulingTracks(): Promise<void> {
const cpus = await this.engine.getCpus();
+ const cpuToSize = await this.guessCpuSizes();
+
for (const cpu of cpus) {
+ const size = cpuToSize.get(cpu);
+ const name = size === undefined ? `Cpu ${cpu}` : `Cpu ${cpu} (${size})`;
this.tracksToAdd.push({
engineId: this.engineId,
kind: CPU_SLICE_TRACK_KIND,
trackSortKey: PrimaryTrackSortKey.ORDINARY_TRACK,
- name: `Cpu ${cpu}`,
+ name,
trackGroup: SCROLLING_TRACK_GROUP,
config: {
cpu,
@@ -207,6 +242,25 @@
}
}
+ async addScrollJankTracks(engine: Engine): Promise<void> {
+ const topLevelScrolls = addTopLevelScrollTrack(engine);
+ const topLevelScrollsResult = await topLevelScrolls;
+ let originalLength = this.tracksToAdd.length;
+ this.tracksToAdd.length += topLevelScrollsResult.tracksToAdd.length;
+ for (let i = 0; i < topLevelScrollsResult.tracksToAdd.length; ++i) {
+ this.tracksToAdd[i + originalLength] =
+ topLevelScrollsResult.tracksToAdd[i];
+ }
+
+ originalLength = this.tracksToAdd.length;
+ const eventLatencies = addLatenciesTrack(engine);
+ const eventLatencyResult = await eventLatencies;
+ this.tracksToAdd.length += eventLatencyResult.tracksToAdd.length;
+ for (let i = 0; i < eventLatencyResult.tracksToAdd.length; ++i) {
+ this.tracksToAdd[i + originalLength] = eventLatencyResult.tracksToAdd[i];
+ }
+ }
+
async addCpuFreqTracks(engine: EngineProxy): Promise<void> {
const cpus = await this.engine.getCpus();
@@ -309,6 +363,7 @@
});
const parentIdToGroupId = new Map<number, string>();
+ let scrollJankRendered = false;
for (; it.valid(); it.next()) {
const kind = ASYNC_SLICE_TRACK_KIND;
@@ -353,6 +408,13 @@
}
}
+ if (ENABLE_SCROLL_JANK_PLUGIN_V2.get() && !scrollJankRendered &&
+ name.includes(INPUT_LATENCY_TRACK)) {
+ // This ensures that the scroll jank tracks render above the tracks
+ // for GestureScrollUpdate.
+ await this.addScrollJankTracks(this.engine);
+ scrollJankRendered = true;
+ }
const track = {
engineId: this.engineId,
kind,
@@ -959,9 +1021,9 @@
upid: NUM_NULL,
tid: NUM_NULL,
threadName: STR_NULL,
- startTs: NUM_NULL,
+ startTs: LONG_NULL,
trackId: NUM,
- endTs: NUM_NULL,
+ endTs: LONG_NULL,
});
for (; it.valid(); it.next()) {
const utid = it.utid;
@@ -1276,8 +1338,8 @@
upid: NUM,
pid: NUM_NULL,
processName: STR_NULL,
- startTs: NUM_NULL,
- endTs: NUM_NULL,
+ startTs: LONG_NULL,
+ endTs: LONG_NULL,
});
for (let i = 0; it.valid(); ++i, it.next()) {
const pid = it.pid;
diff --git a/ui/src/controller/validators.ts b/ui/src/controller/validators.ts
index c35c69d..cc072b7 100644
--- a/ui/src/controller/validators.ts
+++ b/ui/src/controller/validators.ts
@@ -85,6 +85,28 @@
}
}
+class OptionalValidator<T> implements Validator<T|undefined> {
+ private inner: Validator<T>;
+
+ constructor(inner: Validator<T>) {
+ this.inner = inner;
+ }
+
+ validate(input: unknown, context: ValidatorContext): T|undefined {
+ if (input === undefined) {
+ return undefined;
+ }
+ try {
+ return this.inner.validate(input, context);
+ } catch (e) {
+ if (e instanceof ValidationError) {
+ context.invalidKeys.push(renderPath(context.path));
+ return undefined;
+ }
+ throw e;
+ }
+ }
+}
class StringValidator extends PrimitiveValidator<string> {
predicate(input: unknown): input is string {
@@ -244,14 +266,25 @@
export const requiredStr = new StringValidator('', true);
+export const optStr = new OptionalValidator<string>(requiredStr);
+
export function num(defaultValue = 0): NumberValidator {
return new NumberValidator(defaultValue, false);
}
+export const requiredNum = new NumberValidator(0, true);
+
+export const optNum = new OptionalValidator<number>(requiredNum);
+
export function bool(defaultValue = false): BooleanValidator {
return new BooleanValidator(defaultValue, false);
}
+export const requiredBool = new BooleanValidator(false, true);
+
+export const optBool = new OptionalValidator<boolean>(requiredBool);
+
+
export function record<T extends Record<string, Validator<unknown>>>(
validators: T): RecordValidator<T> {
return new RecordValidator<T>(validators);
diff --git a/ui/src/controller/validators_unittest.ts b/ui/src/controller/validators_unittest.ts
index eec9aa5..fc0fe51 100644
--- a/ui/src/controller/validators_unittest.ts
+++ b/ui/src/controller/validators_unittest.ts
@@ -16,6 +16,7 @@
arrayOf,
num,
oneOf,
+ optStr,
record,
requiredStr,
runValidator,
@@ -107,3 +108,27 @@
expect(result.extraKeys).toContain('deeply.nested.array[1].extra3');
expect(result.invalidKeys).toContain('deeply.nested.array[0].x');
});
+
+
+describe('optStr', () => {
+ test('it validates undefined', () => {
+ const validation = runValidator(optStr, undefined);
+ expect(validation.result).toEqual(undefined);
+ expect(validation.invalidKeys).toEqual([]);
+ expect(validation.extraKeys).toEqual([]);
+ });
+
+ test('it validates string', () => {
+ const validation = runValidator(optStr, 'foo');
+ expect(validation.result).toEqual('foo');
+ expect(validation.invalidKeys).toEqual([]);
+ expect(validation.extraKeys).toEqual([]);
+ });
+
+ test('it reports numbers', () => {
+ const validation = runValidator(optStr, 42);
+ expect(validation.result).toEqual(undefined);
+ expect(validation.invalidKeys).toEqual(['']);
+ expect(validation.extraKeys).toEqual([]);
+ });
+});
diff --git a/ui/src/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts
index ddcb6ba..6bd6422 100644
--- a/ui/src/frontend/aggregation_panel.ts
+++ b/ui/src/frontend/aggregation_panel.ts
@@ -22,6 +22,7 @@
} from '../common/aggregation_data';
import {colorForState, textColorForState} from '../common/colorizer';
import {translateState} from '../common/thread_state';
+import {tpTimeToMillis} from '../common/time';
import {globals} from './globals';
import {Panel} from './panel';
@@ -111,7 +112,8 @@
const selection = globals.state.currentSelection;
if (selection === null || selection.kind !== 'AREA') return undefined;
const selectedArea = globals.state.areas[selection.areaId];
- const rangeDurationMs = (selectedArea.endSec - selectedArea.startSec) * 1e3;
+ const rangeDurationMs =
+ tpTimeToMillis(selectedArea.end - selectedArea.start);
return m('.time-range', `Selected range: ${rangeDurationMs.toFixed(6)} ms`);
}
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index ac3cad0..88badd1 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -20,9 +20,14 @@
colorToStr,
UNEXPECTED_PINK_COLOR,
} from '../common/colorizer';
-import {NUM} from '../common/query_result';
+import {LONG, NUM} from '../common/query_result';
import {Selection, SelectionKind} from '../common/state';
-import {fromNs, toNs} from '../common/time';
+import {
+ TPDuration,
+ tpDurationFromNanos,
+ TPTime,
+ tpTimeFromNanos,
+} from '../common/time';
import {checkerboardExcept} from './checkerboard';
import {globals} from './globals';
@@ -45,7 +50,7 @@
// Exposed and standalone to allow for testing without making this
// visible to subclasses.
function filterVisibleSlices<S extends Slice>(
- slices: S[], startS: number, endS: number): S[] {
+ slices: S[], start: TPTime, end: TPTime): S[] {
// Here we aim to reduce the number of slices we have to draw
// by ignoring those that are not visible. A slice is visible iff:
// slice.start + slice.duration >= start && slice.start <= end
@@ -89,7 +94,7 @@
// For all slice in slices: slice.startS > endS (e.g. all slices are to the
// right). Since the slices are sorted by startS we can check this easily:
const maybeFirstSlice: S|undefined = slices[0];
- if (maybeFirstSlice && maybeFirstSlice.startS > endS) {
+ if (maybeFirstSlice && maybeFirstSlice.start > end) {
return [];
}
// It's not possible to easily check the analogous edge case where all slices
@@ -108,15 +113,15 @@
let endIdx = slices.length;
for (; startIdx < endIdx; ++startIdx) {
const slice = slices[startIdx];
- const sliceEndS = slice.startS + slice.durationS;
- if (sliceEndS >= startS && slice.startS <= endS) {
+ const sliceEndS = slice.start + slice.duration;
+ if (sliceEndS >= start && slice.start <= end) {
break;
}
}
for (; startIdx < endIdx; --endIdx) {
const slice = slices[endIdx - 1];
- const sliceEndS = slice.startS + slice.durationS;
- if (sliceEndS >= startS && slice.startS <= endS) {
+ const sliceEndS = slice.start + slice.duration;
+ if (sliceEndS >= start && slice.start <= end) {
break;
}
}
@@ -181,8 +186,8 @@
private cache: TrackCache<Array<CastInternal<T['slice']>>> =
new TrackCache(5);
- private readonly tableName: string;
- private maxDurNs = 0;
+ protected readonly tableName: string;
+ private maxDurNs: TPDuration = 0n;
private sqlState: 'UNINITIALIZED'|'INITIALIZING'|'QUERY_PENDING'|
'QUERY_DONE' = 'UNINITIALIZED';
private extraSqlColumns: string[];
@@ -272,13 +277,15 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO(hjd): fonts and colors should come from the CSS and not hardcoded
// here.
- const timeScale = globals.frontendLocalState.timeScale;
- const vizTime = globals.frontendLocalState.visibleWindowTime;
+ const {
+ visibleTimeScale: timeScale,
+ visibleWindowTime: vizTime,
+ } = globals.frontendLocalState;
{
- const windowSizePx = Math.max(1, timeScale.endPx - timeScale.startPx);
- const rawStartNs = toNs(vizTime.start);
- const rawEndNs = toNs(vizTime.end);
+ const windowSizePx = Math.max(1, timeScale.pxSpan.delta);
+ const rawStartNs = vizTime.start.toTPTime();
+ const rawEndNs = vizTime.end.toTPTime();
const rawSlicesKey = CacheKey.create(rawStartNs, rawEndNs, windowSizePx);
// If the visible time range is outside the cached area, requests
@@ -298,7 +305,8 @@
// Filter only the visible slices. |this.slices| will have more slices than
// needed because maybeRequestData() over-fetches to handle small pan/zooms.
// We don't want to waste time drawing slices that are off screen.
- const vizSlices = this.getVisibleSlicesInternal(vizTime.start, vizTime.end);
+ const vizSlices = this.getVisibleSlicesInternal(
+ vizTime.start.toTPTime('floor'), vizTime.end.toTPTime('ceil'));
let selection = globals.state.currentSelection;
@@ -321,15 +329,15 @@
// pxEnd is the last visible pixel in the visible viewport. Drawing
// anything < 0 or > pxEnd doesn't produce any visible effect as it goes
// beyond the visible portion of the canvas.
- const pxEnd = Math.floor(timeScale.timeToPx(vizTime.end));
+ const pxEnd = Math.floor(timeScale.hpTimeToPx(vizTime.end));
for (const slice of vizSlices) {
// Compute the basic geometry for any visible slice, even if only
// partially visible. This might end up with a negative x if the
// slice starts before the visible time or with a width that overflows
// pxEnd.
- slice.x = timeScale.timeToPx(slice.startS);
- slice.w = timeScale.deltaTimeToPx(slice.durationS);
+ slice.x = timeScale.tpTimeToPx(slice.start);
+ slice.w = timeScale.durationToPx(slice.duration);
if (slice.flags & SLICE_FLAGS_INSTANT) {
// In the case of an instant slice, set the slice geometry on the
// bounding box that will contain the chevron.
@@ -429,10 +437,10 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(vizTime.start),
- timeScale.timeToPx(vizTime.end),
- timeScale.timeToPx(fromNs(this.slicesKey.startNs)),
- timeScale.timeToPx(fromNs(this.slicesKey.endNs)));
+ timeScale.hpTimeToPx(vizTime.start),
+ timeScale.hpTimeToPx(vizTime.end),
+ timeScale.tpTimeToPx(this.slicesKey.start),
+ timeScale.tpTimeToPx(this.slicesKey.end));
// TODO(hjd): Remove this.
// The only thing this does is drawing the sched latency arrow. We should
@@ -478,7 +486,7 @@
const queryRes = await this.engine.query(`select
ifnull(max(dur), 0) as maxDur, count(1) as rowCount
from ${this.tableName}`);
- const row = queryRes.firstRow({maxDur: NUM, rowCount: NUM});
+ const row = queryRes.firstRow({maxDur: LONG, rowCount: NUM});
this.maxDurNs = row.maxDur;
this.sqlState = 'QUERY_DONE';
} else if (
@@ -506,18 +514,18 @@
}
this.sqlState = 'QUERY_PENDING';
- const bucketNs = slicesKey.bucketNs;
+ const bucketNs = slicesKey.bucketSize;
let queryTsq;
let queryTsqEnd;
// When we're zoomed into the level of single ns there is no point
// doing quantization (indeed it causes bad artifacts) so instead
// we use ts / ts+dur directly.
- if (bucketNs === 1) {
+ if (bucketNs === 1n) {
queryTsq = 'ts';
queryTsqEnd = 'ts + dur';
} else {
- queryTsq = `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
- queryTsqEnd = `(ts + dur + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+ queryTsq = `(ts + ${bucketNs / 2n}) / ${bucketNs} * ${bucketNs}`;
+ queryTsqEnd = `(ts + dur + ${bucketNs / 2n}) / ${bucketNs} * ${bucketNs}`;
}
const extraCols = this.extraSqlColumns.join(',');
@@ -557,8 +565,8 @@
${extraCols ? ',' + extraCols : ''}
from ${this.tableName}
where
- ts >= ${slicesKey.startNs - this.maxDurNs /* - durNs */} and
- ts <= ${slicesKey.endNs /* + durNs */}
+ ts >= ${slicesKey.start - this.maxDurNs /* - durNs */} and
+ ts <= ${slicesKey.end /* + durNs */}
group by ${maybeGroupByDepth} tsq
order by tsq),
q2 as (
@@ -623,8 +631,8 @@
return {
id: row.id,
- startS: fromNs(startNsQ),
- durationS: fromNs(endNsQ - startNsQ),
+ start: tpTimeFromNanos(startNsQ),
+ duration: tpDurationFromNanos(endNsQ - startNsQ),
flags,
depth: row.depth,
title: '',
@@ -701,10 +709,10 @@
return true;
}
- private getVisibleSlicesInternal(startS: number, endS: number):
+ private getVisibleSlicesInternal(start: TPTime, end: TPTime):
Array<CastInternal<T['slice']>> {
return filterVisibleSlices<CastInternal<T['slice']>>(
- this.slices, startS, endS);
+ this.slices, start, end);
}
private updateSliceAndTrackHeight() {
@@ -785,7 +793,7 @@
return this.computedTrackHeight;
}
- getSliceRect(_tStart: number, _tEnd: number, _depth: number): SliceRect
+ getSliceRect(_tStart: TPTime, _tEnd: TPTime, _depth: number): SliceRect
|undefined {
// TODO(hjd): Implement this as part of updating flow events.
return undefined;
diff --git a/ui/src/frontend/base_slice_track_unittest.ts b/ui/src/frontend/base_slice_track_unittest.ts
index 7dd109d..e9202a2 100644
--- a/ui/src/frontend/base_slice_track_unittest.ts
+++ b/ui/src/frontend/base_slice_track_unittest.ts
@@ -19,11 +19,11 @@
} from './base_slice_track';
import {Slice} from './slice';
-function slice(startS: number, durationS: number): Slice {
+function slice(start: number, duration: number): Slice {
return {
id: 42,
- startS,
- durationS,
+ start: BigInt(start),
+ duration: BigInt(duration),
depth: 0,
flags: 0,
title: '',
@@ -36,24 +36,24 @@
const s = slice;
test('filterVisibleSlices', () => {
- expect(filterVisibleSlices([], 0, 100)).toEqual([]);
- expect(filterVisibleSlices([s(10, 80)], 0, 100)).toEqual([s(10, 80)]);
- expect(filterVisibleSlices([s(0, 20)], 10, 100)).toEqual([s(0, 20)]);
- expect(filterVisibleSlices([s(0, 10)], 10, 100)).toEqual([s(0, 10)]);
- expect(filterVisibleSlices([s(100, 10)], 10, 100)).toEqual([s(100, 10)]);
- expect(filterVisibleSlices([s(10, 0)], 10, 100)).toEqual([s(10, 0)]);
- expect(filterVisibleSlices([s(100, 0)], 10, 100)).toEqual([s(100, 0)]);
- expect(filterVisibleSlices([s(0, 5)], 10, 90)).toEqual([]);
- expect(filterVisibleSlices([s(95, 5)], 10, 90)).toEqual([]);
- expect(filterVisibleSlices([s(0, 5), s(95, 5)], 10, 90)).toEqual([]);
+ expect(filterVisibleSlices([], 0n, 100n)).toEqual([]);
+ expect(filterVisibleSlices([s(10, 80)], 0n, 100n)).toEqual([s(10, 80)]);
+ expect(filterVisibleSlices([s(0, 20)], 10n, 100n)).toEqual([s(0, 20)]);
+ expect(filterVisibleSlices([s(0, 10)], 10n, 100n)).toEqual([s(0, 10)]);
+ expect(filterVisibleSlices([s(100, 10)], 10n, 100n)).toEqual([s(100, 10)]);
+ expect(filterVisibleSlices([s(10, 0)], 10n, 100n)).toEqual([s(10, 0)]);
+ expect(filterVisibleSlices([s(100, 0)], 10n, 100n)).toEqual([s(100, 0)]);
+ expect(filterVisibleSlices([s(0, 5)], 10n, 90n)).toEqual([]);
+ expect(filterVisibleSlices([s(95, 5)], 10n, 90n)).toEqual([]);
+ expect(filterVisibleSlices([s(0, 5), s(95, 5)], 10n, 90n)).toEqual([]);
expect(filterVisibleSlices(
[
s(0, 5),
s(50, 0),
s(95, 5),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toEqual([
s(50, 0),
]);
@@ -63,8 +63,8 @@
s(1, 9),
s(6, 3),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toContainEqual(s(1, 9));
expect(filterVisibleSlices(
[
@@ -73,16 +73,16 @@
s(6, 3),
s(50, 0),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toContainEqual(s(1, 9));
expect(filterVisibleSlices(
[
s(85, 10),
s(100, 10),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toEqual([
s(85, 10),
]);
@@ -91,8 +91,8 @@
s(0, 100),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toEqual([
s(0, 100),
]);
@@ -109,7 +109,7 @@
s(8, 1),
s(9, 1),
],
- 10,
- 90))
+ 10n,
+ 90n))
.toContainEqual(s(5, 10));
});
diff --git a/ui/src/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index 6c982cf..58efd07 100644
--- a/ui/src/frontend/chrome_slice_panel.ts
+++ b/ui/src/frontend/chrome_slice_panel.ts
@@ -17,7 +17,9 @@
import {sqliteString} from '../base/string_utils';
import {Actions} from '../common/actions';
import {Arg, ArgsTree, isArgTreeArray, isArgTreeMap} from '../common/arg_types';
-import {timeToCode} from '../common/time';
+import {EngineProxy} from '../common/engine';
+import {runQuery} from '../common/queries';
+import {TPDuration, tpDurationToSeconds, tpTimeToCode} from '../common/time';
import {FlowPoint, globals, SliceDetails} from './globals';
import {PanelSize} from './panel';
@@ -54,6 +56,29 @@
),
},
{
+ name: 'Binder call names',
+ shouldDisplay: () => true,
+ getAction: (slice: SliceDetails) => {
+ const engine = getEngine();
+ if (engine === undefined) return;
+ runQuery(`SELECT IMPORT('android.binder');`, engine)
+ .then(
+ () => runQueryInNewTab(
+ `
+ SELECT s.ts, s.dur, tx.aidl_name AS name, s.id
+ FROM android_sync_binder_metrics_by_txn tx
+ JOIN slice s ON tx.binder_txn_id = s.id
+ JOIN thread_track ON s.track_id = thread_track.id
+ JOIN thread USING (utid)
+ JOIN process USING (upid)
+ WHERE aidl_name IS NOT NULL
+ AND pid = ${slice.pid}
+ AND tid = ${slice.tid}`,
+ `Binder names (${slice.processName}:${slice.tid})`,
+ ));
+ },
+ },
+ {
name: 'Lock graph',
shouldDisplay: (slice: SliceDetails) => slice.id !== undefined,
getAction: (slice: SliceDetails) => runQueryInNewTab(
@@ -110,6 +135,15 @@
});
}
+function getEngine(): EngineProxy|undefined {
+ const engineId = globals.getCurrentEngine()?.id;
+ if (engineId === undefined) {
+ return undefined;
+ }
+ const engine = globals.engines.get(engineId)?.getProxy('SlicePanel');
+ return engine;
+}
+
// Table row contents is one of two things:
// 1. Key-value pair
interface TableRow {
@@ -261,7 +295,9 @@
!sliceInfo.category || sliceInfo.category === '[NULL]' ?
'N/A' :
sliceInfo.category);
- defaultBuilder.add('Start time', timeToCode(sliceInfo.ts));
+ defaultBuilder.add(
+ 'Start time',
+ tpTimeToCode(sliceInfo.ts - globals.state.traceTime.start));
if (sliceInfo.absTime !== undefined) {
defaultBuilder.add('Absolute Time', sliceInfo.absTime);
}
@@ -271,9 +307,11 @@
sliceInfo.threadDur !== undefined) {
// If we have valid thread duration, also display a percentage of
// |threadDur| compared to |dur|.
- const threadDurFractionSuffix = sliceInfo.threadDur === -1 ?
+ const ratio = tpDurationToSeconds(sliceInfo.threadDur) /
+ tpDurationToSeconds(sliceInfo.dur);
+ const threadDurFractionSuffix = sliceInfo.threadDur === -1n ?
'' :
- ` (${(sliceInfo.threadDur / sliceInfo.dur * 100).toFixed(2)}%)`;
+ ` (${(ratio * 100).toFixed(2)}%)`;
defaultBuilder.add(
'Thread duration',
this.computeDuration(sliceInfo.threadTs, sliceInfo.threadDur) +
@@ -313,7 +351,7 @@
}
private fillFlowPanel(
- name: string, flows: {flow: FlowPoint, dur: number}[],
+ name: string, flows: {flow: FlowPoint, dur: TPDuration}[],
includeProcessName: boolean, result: Map<string, TableBuilder>) {
if (flows.length === 0) return;
@@ -327,7 +365,7 @@
flow.sliceName :
flow.sliceChromeCustomName,
});
- builder.add('Delay', timeToCode(dur));
+ builder.add('Delay', tpTimeToCode(dur));
builder.add(
'Thread',
includeProcessName ? `${flow.threadName} (${flow.processName})` :
diff --git a/ui/src/frontend/clipboard.ts b/ui/src/frontend/clipboard.ts
index c7a27d7..38d0099 100644
--- a/ui/src/frontend/clipboard.ts
+++ b/ui/src/frontend/clipboard.ts
@@ -43,7 +43,7 @@
const line = [];
for (const col of resp.columns) {
const value = row[col];
- line.push(value === null ? 'NULL' : value.toString());
+ line.push(value === null ? 'NULL' : `${value}`);
}
lines.push(line);
}
diff --git a/ui/src/frontend/counter_panel.ts b/ui/src/frontend/counter_panel.ts
index 99d3841..4237773 100644
--- a/ui/src/frontend/counter_panel.ts
+++ b/ui/src/frontend/counter_panel.ts
@@ -14,8 +14,7 @@
import m from 'mithril';
-import {fromNs, timeToCode} from '../common/time';
-
+import {tpTimeToCode} from '../common/time';
import {globals} from './globals';
import {Panel} from './panel';
@@ -37,7 +36,11 @@
m('tr', m('th', `Name`), m('td', `${counterInfo.name}`)),
m('tr',
m('th', `Start time`),
- m('td', `${timeToCode(counterInfo.startTime)}`)),
+ m('td',
+ `${
+ tpTimeToCode(
+ counterInfo.startTime -
+ globals.state.traceTime.start)}`)),
m('tr',
m('th', `Value`),
m('td', `${counterInfo.value.toLocaleString()}`)),
@@ -46,7 +49,7 @@
m('td', `${counterInfo.delta.toLocaleString()}`)),
m('tr',
m('th', `Duration`),
- m('td', `${timeToCode(fromNs(counterInfo.duration))}`)),
+ m('td', `${tpTimeToCode(counterInfo.duration)}`)),
])],
));
} else {
diff --git a/ui/src/frontend/debug.ts b/ui/src/frontend/debug.ts
index fae7a83..7e9c9e5 100644
--- a/ui/src/frontend/debug.ts
+++ b/ui/src/frontend/debug.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {produce} from 'immer';
import m from 'mithril';
import {Actions} from '../common/actions';
@@ -19,12 +20,14 @@
import {globals} from './globals';
+
declare global {
interface Window {
m: typeof m;
getSchema: typeof getSchema;
globals: typeof globals;
Actions: typeof Actions;
+ produce: typeof produce;
}
}
@@ -33,4 +36,5 @@
window.m = m;
window.globals = globals;
window.Actions = Actions;
+ window.produce = produce;
}
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 6e5a985..ad04665 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -17,9 +17,12 @@
import {Actions} from '../common/actions';
import {isEmptyData} from '../common/aggregation_data';
import {LogExists, LogExistsKey} from '../common/logs';
+import {pluginManager} from '../common/plugins';
import {addSelectionChangeObserver} from '../common/selection_observer';
import {Selection} from '../common/state';
import {DebugSliceDetailsTab} from '../tracks/debug/details_tab';
+import {SCROLL_JANK_PLUGIN_ID} from '../tracks/scroll_jank';
+import {TOP_LEVEL_SCROLL_KIND} from '../tracks/scroll_jank/scroll_track';
import {AggregationPanel} from './aggregation_panel';
import {ChromeSliceDetailsPanel} from './chrome_slice_panel';
@@ -36,7 +39,7 @@
import {globals} from './globals';
import {LogPanel} from './logs_panel';
import {NotesEditorTab} from './notes_panel';
-import {AnyAttrsVnode, PanelContainer} from './panel_container';
+import {AnyAttrsVnode} from './panel_container';
import {PivotTable} from './pivot_table';
import {SliceDetailsPanel} from './slice_details_panel';
import {ThreadStateTab} from './thread_state_tab';
@@ -45,6 +48,8 @@
const DOWN_ICON = 'keyboard_arrow_down';
const DRAG_HANDLE_HEIGHT_PX = 28;
+export const CURRENT_SELECTION_TAG = 'current_selection';
+
function getDetailsHeight() {
// This needs to be a function instead of a const to ensure the CSS constants
// have been initialized by the time we perform this calculation;
@@ -147,7 +152,7 @@
onclick: () => {
this.isClosed = false;
this.isFullscreen = true;
- this.resize(this.fullscreenHeight);
+ this.resize(this.fullscreenHeight - DRAG_HANDLE_HEIGHT_PX);
globals.rafScheduler.scheduleFullRedraw();
},
title: 'Open fullscreen',
@@ -167,7 +172,7 @@
this.isFullscreen = false;
this.isClosed = true;
this.previousHeight = this.height;
- this.resize(DRAG_HANDLE_HEIGHT_PX);
+ this.resize(0);
}
globals.rafScheduler.scheduleFullRedraw();
},
@@ -178,7 +183,7 @@
}
function handleSelectionChange(newSelection?: Selection, _?: Selection): void {
- const currentSelectionTag = 'current_selection';
+ const currentSelectionTag = CURRENT_SELECTION_TAG;
const bottomTabList = globals.bottomTabList;
if (!bottomTabList) return;
if (newSelection === undefined) {
@@ -225,6 +230,10 @@
},
});
break;
+ case TOP_LEVEL_SCROLL_KIND:
+ pluginManager.onDetailsPanelSelectionChange(
+ SCROLL_JANK_PLUGIN_ID, newSelection);
+ break;
default:
bottomTabList.closeTabByTag(currentSelectionTag);
}
@@ -381,27 +390,27 @@
}
const panel = currentTabDetails?.vnode;
- const panels = panel ? [panel] : [];
- return m(
- '.details-content',
- {
- style: {
- height: `${this.detailsHeight}px`,
- display: detailsPanels.length > 0 ? null : 'none',
- },
+ if (!panel) {
+ return null;
+ }
+
+ return [
+ m(DragHandle, {
+ resize: (height: number) => {
+ this.detailsHeight = Math.max(height, 0);
},
- m(DragHandle, {
- resize: (height: number) => {
- this.detailsHeight = Math.max(height, DRAG_HANDLE_HEIGHT_PX);
- },
- height: this.detailsHeight,
- tabs: detailsPanels.map((tab) => {
- return {key: tab.key, name: tab.name};
- }),
- currentTabKey: currentTabDetails?.key,
+ height: this.detailsHeight,
+ tabs: detailsPanels.map((tab) => {
+ return {key: tab.key, name: tab.name};
}),
- m('.details-panel-container.x-scrollable',
- m(PanelContainer, {doesScroll: true, panels, kind: 'DETAILS'})));
+ currentTabKey: currentTabDetails?.key,
+ }),
+ m('.details-panel-container',
+ {
+ style: {height: `${this.detailsHeight}px`},
+ },
+ panel),
+ ];
}
}
diff --git a/ui/src/frontend/drag/border_drag_strategy.ts b/ui/src/frontend/drag/border_drag_strategy.ts
index df450fc..e046293 100644
--- a/ui/src/frontend/drag/border_drag_strategy.ts
+++ b/ui/src/frontend/drag/border_drag_strategy.ts
@@ -12,28 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {TimeScale} from '../time_scale';
-
import {DragStrategy} from './drag_strategy';
export class BorderDragStrategy extends DragStrategy {
private moveStart = false;
- constructor(timeScale: TimeScale, private pixelBounds: [number, number]) {
- super(timeScale);
+ constructor(map: TimeScale, private pixelBounds: [number, number]) {
+ super(map);
}
onDrag(x: number) {
- let tStart =
- this.timeScale.pxToTime(this.moveStart ? x : this.pixelBounds[0]);
- let tEnd =
- this.timeScale.pxToTime(!this.moveStart ? x : this.pixelBounds[1]);
- if (tStart > tEnd) {
+ let tStart = this.map.pxToHpTime(this.moveStart ? x : this.pixelBounds[0]);
+ let tEnd = this.map.pxToHpTime(!this.moveStart ? x : this.pixelBounds[1]);
+ if (tStart.gt(tEnd)) {
this.moveStart = !this.moveStart;
[tEnd, tStart] = [tStart, tEnd];
}
super.updateGlobals(tStart, tEnd);
- this.pixelBounds =
- [this.timeScale.timeToPx(tStart), this.timeScale.timeToPx(tEnd)];
+ this.pixelBounds = [
+ this.map.hpTimeToPx(tStart),
+ this.map.hpTimeToPx(tEnd),
+ ];
}
onDragStart(x: number) {
diff --git a/ui/src/frontend/drag/drag_strategy.ts b/ui/src/frontend/drag/drag_strategy.ts
index 2896849..afb83e1 100644
--- a/ui/src/frontend/drag/drag_strategy.ts
+++ b/ui/src/frontend/drag/drag_strategy.ts
@@ -11,19 +11,22 @@
// 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 {TimeSpan} from '../../common/time';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../../common/high_precision_time';
import {globals} from '../globals';
import {TimeScale} from '../time_scale';
export abstract class DragStrategy {
- constructor(protected timeScale: TimeScale) {}
+ constructor(protected map: TimeScale) {}
abstract onDrag(x: number): void;
abstract onDragStart(x: number): void;
- protected updateGlobals(tStart: number, tEnd: number) {
- const vizTime = new TimeSpan(tStart, tEnd);
+ protected updateGlobals(tStart: HighPrecisionTime, tEnd: HighPrecisionTime) {
+ const vizTime = new HighPrecisionTimeSpan(tStart, tEnd);
globals.frontendLocalState.updateVisibleTime(vizTime);
globals.rafScheduler.scheduleRedraw();
}
diff --git a/ui/src/frontend/drag/inner_drag_strategy.ts b/ui/src/frontend/drag/inner_drag_strategy.ts
index 2af1b39..7be7f7b 100644
--- a/ui/src/frontend/drag/inner_drag_strategy.ts
+++ b/ui/src/frontend/drag/inner_drag_strategy.ts
@@ -23,8 +23,8 @@
onDrag(x: number) {
const move = x - this.dragStartPx;
- const tStart = this.timeScale.pxToTime(this.pixelBounds[0] + move);
- const tEnd = this.timeScale.pxToTime(this.pixelBounds[1] + move);
+ const tStart = this.map.pxToHpTime(this.pixelBounds[0] + move);
+ const tEnd = this.map.pxToHpTime(this.pixelBounds[1] + move);
super.updateGlobals(tStart, tEnd);
}
diff --git a/ui/src/frontend/drag/outer_drag_strategy.ts b/ui/src/frontend/drag/outer_drag_strategy.ts
index 648b50d..f8269fc 100644
--- a/ui/src/frontend/drag/outer_drag_strategy.ts
+++ b/ui/src/frontend/drag/outer_drag_strategy.ts
@@ -11,16 +11,19 @@
// 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 {
+ HighPrecisionTime,
+} from '../../common/high_precision_time';
import {DragStrategy} from './drag_strategy';
export class OuterDragStrategy extends DragStrategy {
private dragStartPx = 0;
onDrag(x: number) {
- const dragBeginTime = this.timeScale.pxToTime(this.dragStartPx);
- const dragEndTime = this.timeScale.pxToTime(x);
- const tStart = Math.min(dragBeginTime, dragEndTime);
- const tEnd = Math.max(dragBeginTime, dragEndTime);
+ const dragBeginTime = this.map.pxToHpTime(this.dragStartPx);
+ const dragEndTime = this.map.pxToHpTime(x);
+ const tStart = HighPrecisionTime.min(dragBeginTime, dragEndTime);
+ const tEnd = HighPrecisionTime.max(dragBeginTime, dragEndTime);
super.updateGlobals(tStart, tEnd);
}
diff --git a/ui/src/frontend/drag_gesture_handler.ts b/ui/src/frontend/drag_gesture_handler.ts
index 30c0a38..4c547aa 100644
--- a/ui/src/frontend/drag_gesture_handler.ts
+++ b/ui/src/frontend/drag_gesture_handler.ts
@@ -33,8 +33,6 @@
document.body.addEventListener('mousemove', this.boundOnMouseMove);
document.body.addEventListener('mouseup', this.boundOnMouseUp);
this.pendingMouseDownEvent = e;
- // Prevent interactions with other DragGestureHandlers and event listeners
- e.stopPropagation();
}
// We don't start the drag gesture on mouse down, instead we wait until
@@ -60,17 +58,15 @@
this.onDrag(
e.clientX - this.clientRect!.left, e.clientY - this.clientRect!.top);
}
- e.stopPropagation();
}
- private onMouseUp(e: MouseEvent) {
+ private onMouseUp(_e: MouseEvent) {
this._isDragging = false;
document.body.removeEventListener('mousemove', this.boundOnMouseMove);
document.body.removeEventListener('mouseup', this.boundOnMouseUp);
if (!this.pendingMouseDownEvent) {
this.onDragFinished();
}
- e.stopPropagation();
}
get isDragging() {
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index b8246b3..0ac5f79 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -28,10 +28,9 @@
FlamegraphStateViewingOption,
ProfileType,
} from '../common/state';
-import {timeToCode} from '../common/time';
+import {tpTimeToCode} from '../common/time';
import {profileType} from '../controller/flamegraph_controller';
-import {PerfettoMouseEvent} from './events';
import {Flamegraph, NodeRendering} from './flamegraph';
import {globals} from './globals';
import {Modal, ModalDefinition} from './modal';
@@ -41,6 +40,7 @@
import {getCurrentTrace} from './sidebar';
import {convertTraceToPprofAndDownload} from './trace_converter';
import {Button} from './widgets/button';
+import {findRef} from './widgets/utils';
interface FlamegraphDetailsPanelAttrs {}
@@ -64,23 +64,24 @@
export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> {
private profileType?: ProfileType = undefined;
- private ts = 0;
+ private ts = 0n;
private pids: number[] = [];
private flamegraph: Flamegraph = new Flamegraph([]);
private focusRegex = '';
private updateFocusRegexDebounced = debounce(() => {
this.updateFocusRegex();
}, 20);
+ private canvas?: HTMLCanvasElement;
view() {
const flamegraphDetails = globals.flamegraphDetails;
if (flamegraphDetails && flamegraphDetails.type !== undefined &&
- flamegraphDetails.startNs !== undefined &&
- flamegraphDetails.durNs !== undefined &&
+ flamegraphDetails.start !== undefined &&
+ flamegraphDetails.dur !== undefined &&
flamegraphDetails.pids !== undefined &&
flamegraphDetails.upids !== undefined) {
this.profileType = profileType(flamegraphDetails.type);
- this.ts = flamegraphDetails.startNs + flamegraphDetails.durNs;
+ this.ts = flamegraphDetails.start + flamegraphDetails.dur;
this.pids = flamegraphDetails.pids;
if (flamegraphDetails.flamegraph) {
this.flamegraph.updateDataIfChanged(
@@ -91,25 +92,6 @@
0;
return m(
'.details-panel',
- {
- onclick: (e: PerfettoMouseEvent) => {
- if (this.flamegraph !== undefined) {
- this.onMouseClick({y: e.layerY, x: e.layerX});
- }
- return false;
- },
- onmousemove: (e: PerfettoMouseEvent) => {
- if (this.flamegraph !== undefined) {
- this.onMouseMove({y: e.layerY, x: e.layerX});
- globals.rafScheduler.scheduleRedraw();
- }
- },
- onmouseout: () => {
- if (this.flamegraph !== undefined) {
- this.onMouseOut();
- }
- },
- },
this.maybeShowModal(flamegraphDetails.graphIncomplete),
m('.details-panel-heading.flamegraph-profile',
{onclick: (e: MouseEvent) => e.stopPropagation()},
@@ -126,7 +108,7 @@
toSelectedCallsite(
flamegraphDetails.expandedCallsite)}`),
m('div.time',
- `Snapshot time: ${timeToCode(flamegraphDetails.durNs)}`),
+ `Snapshot time: ${tpTimeToCode(flamegraphDetails.dur)}`),
m('input[type=text][placeholder=Focus]', {
oninput: (e: Event) => {
const target = (e.target as HTMLInputElement);
@@ -146,7 +128,20 @@
}),
]),
]),
- m(`div[style=height:${height}px]`),
+ m(`canvas[ref=canvas]`, {
+ style: `height:${height}px; width:100%`,
+ onmousemove: (e: MouseEvent) => {
+ const {offsetX, offsetY} = e;
+ this.onMouseMove({x: offsetX, y: offsetY});
+ },
+ onmouseout: () => {
+ this.onMouseOut();
+ },
+ onclick: (e: MouseEvent) => {
+ const {offsetX, offsetY} = e;
+ this.onMouseClick({x: offsetX, y: offsetY});
+ },
+ }),
);
} else {
return m(
@@ -256,7 +251,53 @@
this.nodeRendering(), flamegraphData, data.expandedCallsite);
}
- renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
+ oncreate({dom}: m.CVnodeDOM<FlamegraphDetailsPanelAttrs>) {
+ this.canvas = FlamegraphDetailsPanel.findCanvasElement(dom);
+ // TODO(stevegolton): If we truely want to be standalone, then we shouldn't
+ // rely on someone else calling the rafScheduler when the window is resized,
+ // but it's good enough for now as we know the ViewerPage will do it.
+ globals.rafScheduler.addRedrawCallback(this.rafRedrawCallback);
+ }
+
+ onupdate({dom}: m.CVnodeDOM<FlamegraphDetailsPanelAttrs>) {
+ this.canvas = FlamegraphDetailsPanel.findCanvasElement(dom);
+ }
+
+ onremove(_vnode: m.CVnodeDOM<FlamegraphDetailsPanelAttrs>) {
+ globals.rafScheduler.removeRedrawCallback(this.rafRedrawCallback);
+ }
+
+ private static findCanvasElement(dom: Element): HTMLCanvasElement|undefined {
+ const canvas = findRef(dom, 'canvas');
+ if (canvas && canvas instanceof HTMLCanvasElement) {
+ return canvas;
+ } else {
+ return undefined;
+ }
+ }
+
+ private rafRedrawCallback = () => {
+ if (this.canvas) {
+ const canvas = this.canvas;
+ canvas.width = canvas.offsetWidth * devicePixelRatio;
+ canvas.height = canvas.offsetHeight * devicePixelRatio;
+ const ctx = canvas.getContext('2d');
+ if (ctx) {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.save();
+ ctx.scale(devicePixelRatio, devicePixelRatio);
+ const {offsetWidth: width, offsetHeight: height} = canvas;
+ this.renderLocalCanvas(ctx, {width, height});
+ ctx.restore();
+ }
+ }
+ };
+
+ renderCanvas() {
+ // No-op
+ }
+
+ private renderLocalCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
this.changeFlamegraphData();
const current = globals.state.currentFlamegraphState;
if (current === null) return;
@@ -265,22 +306,24 @@
current.viewingOption === ALLOC_SPACE_MEMORY_ALLOCATED_KEY ?
'B' :
'';
- this.flamegraph.draw(ctx, size.width, size.height, 0, HEADER_HEIGHT, unit);
+ this.flamegraph.draw(ctx, size.width, size.height, 0, 0, unit);
}
- onMouseClick({x, y}: {x: number, y: number}): boolean {
+ private onMouseClick({x, y}: {x: number, y: number}): boolean {
const expandedCallsite = this.flamegraph.onMouseClick({x, y});
globals.dispatch(Actions.expandFlamegraphState({expandedCallsite}));
return true;
}
- onMouseMove({x, y}: {x: number, y: number}): boolean {
+ private onMouseMove({x, y}: {x: number, y: number}): boolean {
this.flamegraph.onMouseMove({x, y});
+ globals.rafScheduler.scheduleFullRedraw();
return true;
}
- onMouseOut() {
+ private onMouseOut() {
this.flamegraph.onMouseOut();
+ globals.rafScheduler.scheduleFullRedraw();
}
private static selectViewingOptions(profileType: ProfileType) {
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index 755c75d..ee2f146 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -15,7 +15,7 @@
import m from 'mithril';
import {Actions} from '../common/actions';
-import {timeToCode} from '../common/time';
+import {tpTimeToCode} from '../common/time';
import {Flow, globals} from './globals';
import {BLANK_CHECKBOX, CHECKBOX} from './icons';
@@ -95,7 +95,7 @@
const data = [
m('td.flow-link', args, outgoing ? 'Outgoing' : 'Incoming'),
- m('td.flow-link', args, timeToCode(flow.dur)),
+ m('td.flow-link', args, tpTimeToCode(flow.dur)),
m('td.flow-link', args, otherEnd.sliceId.toString()),
m('td.flow-link', args, otherEnd.sliceName),
m('td.flow-link', args, flow.begin.threadName),
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index 909dcb7..c511758 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {TPTime} from '../common/time';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {ALL_CATEGORIES, getFlowCategories} from './flow_events_panel';
import {Flow, FlowPoint, globals} from './globals';
@@ -139,8 +140,8 @@
};
}
- private getXCoordinate(ts: number): number {
- return globals.frontendLocalState.timeScale.timeToPx(ts);
+ private getXCoordinate(ts: TPTime): number {
+ return globals.frontendLocalState.visibleTimeScale.tpTimeToPx(ts);
}
private getSliceRect(args: FlowEventsRendererArgs, point: FlowPoint):
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index 34663d8..bfbd130 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -14,6 +14,10 @@
import {assertTrue} from '../base/logging';
import {Actions} from '../common/actions';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
import {HttpRpcState} from '../common/http_rpc_engine';
import {
Area,
@@ -21,11 +25,15 @@
Timestamped,
VisibleState,
} from '../common/state';
-import {TimeSpan} from '../common/time';
+import {Span, TPDuration} from '../common/time';
+import {
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {globals} from './globals';
import {ratelimit} from './rate_limiters';
-import {TimeScale} from './time_scale';
+import {PxSpan, TimeScale} from './time_scale';
interface Range {
start?: number;
@@ -42,10 +50,6 @@
return current;
}
-function capBetween(t: number, start: number, end: number) {
- return Math.min(Math.max(t, start), end);
-}
-
// Calculate the space a scrollbar takes up so that we can subtract it from
// the canvas width.
function calculateScrollbarWidth() {
@@ -60,13 +64,97 @@
return width;
}
+export class TimeWindow {
+ private readonly MIN_DURATION_NS = 10;
+ private _start: HighPrecisionTime = new HighPrecisionTime();
+ private _durationNanos: number = 10e9;
+
+ private get _end(): HighPrecisionTime {
+ return this._start.addNanos(this._durationNanos);
+ }
+
+ update(span: Span<HighPrecisionTime>) {
+ this._start = span.start;
+ this._durationNanos = Math.max(this.MIN_DURATION_NS, span.duration.nanos);
+ this.preventClip();
+ }
+
+ // Pan the window by certain number of seconds
+ pan(offset: HighPrecisionTime) {
+ this._start = this._start.add(offset);
+ this.preventClip();
+ }
+
+ // Zoom in or out a bit centered on a specific offset from the root
+ // Offset represents the center of the zoom as a normalized value between 0
+ // and 1 where 0 is the start of the time window and 1 is the end
+ zoom(ratio: number, offset: number) {
+ const traceDuration = globals.stateTraceTime().duration;
+ const minDuration = Math.min(this.MIN_DURATION_NS, traceDuration.nanos);
+ const newDurationNanos = Math.max(this._durationNanos * ratio, minDuration);
+ // Delta between new and old duration
+ // +ve if new duration is shorter than old duration
+ const durationDeltaNanos = this._durationNanos - newDurationNanos;
+ // If offset is 0, don't move the start at all
+ // If offset if 1, move the start by the amount the duration has changed
+ // If new duration is shorter - move start to right
+ // If new duration is longer - move start to left
+ this._start = this._start.addNanos(durationDeltaNanos * offset);
+ this._durationNanos = newDurationNanos;
+ this.preventClip();
+ }
+
+ createTimeScale(startPx: number, endPx: number): TimeScale {
+ return new TimeScale(
+ this._start, this._durationNanos, new PxSpan(startPx, endPx));
+ }
+
+ // Get timespan covering entire range of the window
+ get timeSpan(): HighPrecisionTimeSpan {
+ return new HighPrecisionTimeSpan(this._start, this._end);
+ }
+
+ get timestampSpan(): Span<TPTime, TPDuration> {
+ return new TPTimeSpan(this.earliest, this.latest);
+ }
+
+ get earliest(): TPTime {
+ return this._start.toTPTime('floor');
+ }
+
+ get latest(): TPTime {
+ return this._start.addNanos(this._durationNanos).toTPTime('ceil');
+ }
+
+ // Limit the zoom and pan
+ private preventClip() {
+ const traceTimeSpan = globals.stateTraceTime();
+ const traceDurationNanos = traceTimeSpan.duration.nanos;
+
+ if (this._durationNanos > traceDurationNanos) {
+ this._start = traceTimeSpan.start;
+ this._durationNanos = traceDurationNanos;
+ }
+
+ if (this._start.lt(traceTimeSpan.start)) {
+ this._start = traceTimeSpan.start;
+ }
+
+ const end = this._start.addNanos(this._durationNanos);
+ if (end.gt(traceTimeSpan.end)) {
+ this._start = traceTimeSpan.end.subNanos(this._durationNanos);
+ }
+ }
+}
+
/**
* State that is shared between several frontend components, but not the
* controller. This state is updated at 60fps.
*/
export class FrontendLocalState {
- visibleWindowTime = new TimeSpan(0, 10);
- timeScale = new TimeScale(this.visibleWindowTime, [0, 0]);
+ visibleWindow = new TimeWindow();
+ startPx: number = 0;
+ endPx: number = 0;
showPanningHint = false;
showCookieConsent = false;
visibleTracks = new Set<string>();
@@ -82,9 +170,9 @@
private _visibleState: VisibleState = {
lastUpdate: 0,
- startSec: 0,
- endSec: 10,
- resolution: 1,
+ start: 0n,
+ end: BigInt(10e9),
+ resolution: 1n,
};
private _selectedArea?: Area;
@@ -125,6 +213,16 @@
}
}
+ zoomVisibleWindow(ratio: number, centerPoint: number) {
+ this.visibleWindow.zoom(ratio, centerPoint);
+ this.kickUpdateLocalState();
+ }
+
+ panVisibleWindow(delta: HighPrecisionTime) {
+ this.visibleWindow.pan(delta);
+ this.kickUpdateLocalState();
+ }
+
mergeState(state: FrontendState): void {
// This is unfortunately subtle. This class mutates this._visibleState.
// Since we may not mutate |state| (in order to make immer's immutable
@@ -137,19 +235,22 @@
this._visibleState = chooseLatest(this._visibleState, state.visibleState);
const visibleStateWasUpdated = previousVisibleState !== this._visibleState;
if (visibleStateWasUpdated) {
- this.updateLocalTime(
- new TimeSpan(this._visibleState.startSec, this._visibleState.endSec));
+ this.updateLocalTime(new HighPrecisionTimeSpan(
+ HighPrecisionTime.fromTPTime(this._visibleState.start),
+ HighPrecisionTime.fromTPTime(this._visibleState.end),
+ ));
}
}
+ // Set the highlight box to draw
selectArea(
- startSec: number, endSec: number,
+ start: TPTime, end: TPTime,
tracks = this._selectedArea ? this._selectedArea.tracks : []) {
assertTrue(
- endSec >= startSec,
- `Impossible select area: startSec [${startSec}] >= endSec [${endSec}]`);
+ end >= start,
+ `Impossible select area: start [${start}] >= end [${end}]`);
this.showPanningHint = true;
- this._selectedArea = {startSec, endSec, tracks};
+ this._selectedArea = {start, end, tracks},
globals.rafScheduler.scheduleFullRedraw();
}
@@ -166,12 +267,11 @@
globals.dispatch(Actions.setVisibleTraceTime(this._visibleState));
}, 50);
- private updateLocalTime(ts: TimeSpan) {
- const traceTime = globals.state.traceTime;
- const startSec = capBetween(ts.start, traceTime.startSec, traceTime.endSec);
- const endSec = capBetween(ts.end, traceTime.startSec, traceTime.endSec);
- this.visibleWindowTime = new TimeSpan(startSec, endSec);
- this.timeScale.setTimeBounds(this.visibleWindowTime);
+ private updateLocalTime(ts: Span<HighPrecisionTime>) {
+ const traceBounds = globals.stateTraceTime();
+ const start = ts.start.clamp(traceBounds.start, traceBounds.end);
+ const end = ts.end.clamp(traceBounds.start, traceBounds.end);
+ this.visibleWindow.update(new HighPrecisionTimeSpan(start, end));
this.updateResolution();
}
@@ -181,17 +281,17 @@
this.ratelimitedUpdateVisible();
}
- updateVisibleTime(ts: TimeSpan) {
- this.updateLocalTime(ts);
+ private kickUpdateLocalState() {
this._visibleState.lastUpdate = Date.now() / 1000;
- this._visibleState.startSec = this.visibleWindowTime.start;
- this._visibleState.endSec = this.visibleWindowTime.end;
+ this._visibleState.start = this.visibleWindowTime.start.toTPTime();
+ this._visibleState.end = this.visibleWindowTime.end.toTPTime();
this._visibleState.resolution = globals.getCurResolution();
this.ratelimitedUpdateVisible();
}
- getVisibleStateBounds(): [number, number] {
- return [this.visibleWindowTime.start, this.visibleWindowTime.end];
+ updateVisibleTime(ts: Span<HighPrecisionTime>) {
+ this.updateLocalTime(ts);
+ this.kickUpdateLocalState();
}
// Whenever start/end px of the timeScale is changed, update
@@ -202,7 +302,33 @@
pxStart = Math.max(0, pxStart);
pxEnd = Math.max(0, pxEnd);
if (pxStart === pxEnd) pxEnd = pxStart + 1;
- this.timeScale.setLimitsPx(pxStart, pxEnd);
+ this.startPx = pxStart;
+ this.endPx = pxEnd;
this.updateResolution();
}
+
+ // Get the time scale for the visible window
+ get visibleTimeScale(): TimeScale {
+ return this.visibleWindow.createTimeScale(this.startPx, this.endPx);
+ }
+
+ // Produces a TimeScale object for this time window provided start and end px
+ getTimeScale(startPx: number, endPx: number): TimeScale {
+ return this.visibleWindow.createTimeScale(startPx, endPx);
+ }
+
+ // Get the bounds of the window in pixels
+ get windowSpan(): PxSpan {
+ return new PxSpan(this.startPx, this.endPx);
+ }
+
+ // Get the bounds of the visible time window as a time span
+ get visibleWindowTime(): Span<HighPrecisionTime> {
+ return this.visibleWindow.timeSpan;
+ }
+
+ // Get the visible time span as
+ get visibleTimeSpan(): Span<TPTime, TPDuration> {
+ return this.visibleWindow.timestampSpan;
+ }
}
diff --git a/ui/src/frontend/ftrace_panel.ts b/ui/src/frontend/ftrace_panel.ts
index 8de83cb..f66cddf 100644
--- a/ui/src/frontend/ftrace_panel.ts
+++ b/ui/src/frontend/ftrace_panel.ts
@@ -18,7 +18,7 @@
import {assertExists} from '../base/logging';
import {Actions} from '../common/actions';
import {colorForString} from '../common/colorizer';
-import {formatTimestamp} from '../common/time';
+import {formatTPTime, TPTime} from '../common/time';
import {globals} from './globals';
import {Panel} from './panel';
@@ -68,7 +68,7 @@
}
private scrollContainer(dom: Element): HTMLElement {
- const el = dom.parentElement!.parentElement!.parentElement;
+ const el = dom.parentElement;
return assertExists(el);
}
@@ -117,12 +117,12 @@
this.recomputeVisibleRowsAndUpdate(scrollContainer);
};
- onRowOver(ts: number) {
+ onRowOver(ts: TPTime) {
globals.dispatch(Actions.setHoverCursorTimestamp({ts}));
}
onRowOut() {
- globals.dispatch(Actions.setHoverCursorTimestamp({ts: -1}));
+ globals.dispatch(Actions.setHoverCursorTimestamp({ts: -1n}));
}
private renderRowsLabel() {
@@ -188,8 +188,7 @@
for (let i = 0; i < events.length; i++) {
const {ts, name, cpu, process, args} = events[i];
- const timestamp =
- formatTimestamp(ts / 1e9 - globals.state.traceTime.startSec);
+ const timestamp = formatTPTime(ts - globals.state.traceTime.start);
const rank = i + offset;
@@ -204,7 +203,7 @@
`.row`,
{
style: {top: `${(rank + 1.0) * ROW_H}px`},
- onmouseover: this.onRowOver.bind(this, ts / 1e9),
+ onmouseover: this.onRowOver.bind(this, ts),
onmouseout: this.onRowOut.bind(this),
},
m('.cell', timestamp),
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 0ca10a0..1518987 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../base/bigint_math';
import {assertExists} from '../base/logging';
import {Actions, DeferredAction} from '../common/actions';
import {AggregateData} from '../common/aggregation_data';
@@ -22,10 +23,19 @@
} from '../common/conversion_jobs';
import {createEmptyState} from '../common/empty_state';
import {Engine} from '../common/engine';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
import {MetricResult} from '../common/metric_data';
import {CurrentSearchResults, SearchSummary} from '../common/search_data';
import {CallsiteInfo, EngineConfig, ProfileType, State} from '../common/state';
-import {fromNs, toNs} from '../common/time';
+import {Span, tpTimeFromSeconds} from '../common/time';
+import {
+ TPDuration,
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {Analytics, initAnalytics} from './analytics';
import {BottomTabList} from './bottom_tab';
@@ -33,6 +43,7 @@
import {RafScheduler} from './raf_scheduler';
import {Router} from './router';
import {ServiceWorkerController} from './service_worker_controller';
+import {PxSpan, TimeScale} from './time_scale';
type Dispatch = (action: DeferredAction) => void;
type TrackDataStore = Map<string, {}>;
@@ -41,18 +52,18 @@
type Description = Map<string, string>;
export interface SliceDetails {
- ts?: number;
+ ts?: TPTime;
absTime?: string;
- dur?: number;
- threadTs?: number;
- threadDur?: number;
+ dur?: TPDuration;
+ threadTs?: TPTime;
+ threadDur?: TPDuration;
priority?: number;
endState?: string|null;
cpu?: number;
id?: number;
threadStateId?: number;
utid?: number;
- wakeupTs?: number;
+ wakeupTs?: TPTime;
wakerUtid?: number;
wakerCpu?: number;
category?: string;
@@ -75,8 +86,8 @@
sliceName: string;
sliceCategory: string;
sliceId: number;
- sliceStartTs: number;
- sliceEndTs: number;
+ sliceStartTs: TPTime;
+ sliceEndTs: TPTime;
// Thread and process info. Only set in sliceSelected not in areaSelected as
// the latter doesn't display per-flow info and it'd be a waste to join
// additional tables for undisplayed info in that case. Nothing precludes
@@ -97,30 +108,30 @@
begin: FlowPoint;
end: FlowPoint;
- dur: number;
+ dur: TPDuration;
category?: string;
name?: string;
}
export interface CounterDetails {
- startTime?: number;
+ startTime?: TPTime;
value?: number;
delta?: number;
- duration?: number;
+ duration?: TPDuration;
name?: string;
}
export interface ThreadStateDetails {
- ts?: number;
- dur?: number;
+ ts?: TPTime;
+ dur?: TPDuration;
}
export interface FlamegraphDetails {
type?: ProfileType;
id?: number;
- startNs?: number;
- durNs?: number;
+ start?: TPTime;
+ dur?: TPDuration;
pids?: number[];
upids?: number[];
flamegraph?: CallsiteInfo[];
@@ -137,14 +148,14 @@
export interface CpuProfileDetails {
id?: number;
- ts?: number;
+ ts?: TPTime;
utid?: number;
stack?: CallsiteInfo[];
}
export interface QuantizedLoad {
- startSec: number;
- endSec: number;
+ start: TPTime;
+ end: TPTime;
load: number;
}
type OverviewStore = Map<string, QuantizedLoad[]>;
@@ -161,7 +172,7 @@
export interface FtraceEvent {
id: number;
- ts: number;
+ ts: TPTime;
name: string;
cpu: number;
thread: string|null;
@@ -244,15 +255,15 @@
private _currentSearchResults: CurrentSearchResults = {
sliceIds: new Float64Array(0),
- tsStarts: new Float64Array(0),
+ tsStarts: new BigInt64Array(0),
utids: new Float64Array(0),
trackIds: [],
sources: [],
totalResults: 0,
};
searchSummary: SearchSummary = {
- tsStarts: new Float64Array(0),
- tsEnds: new Float64Array(0),
+ tsStarts: new BigInt64Array(0),
+ tsEnds: new BigInt64Array(0),
count: new Uint8Array(0),
};
@@ -530,7 +541,7 @@
this.aggregateDataStore.set(kind, data);
}
- getCurResolution() {
+ getCurResolution(): TPDuration {
// Truncate the resolution to the closest power of 2 (in nanosecond space).
// We choose to work in ns space because resolution is consumed be track
// controllers for quantization and they rely on resolution to be a power
@@ -541,24 +552,16 @@
// levels. Logic: each zoom level represents a delta of 0.1 * (visible
// window span). Therefore, zooming out by six levels is 1.1^6 ~= 2.
// Similarily, zooming in six levels is 0.9^6 ~= 0.5.
- const pxToSec = this.frontendLocalState.timeScale.deltaPxToDuration(1);
+ const timeScale = this.frontendLocalState.visibleTimeScale;
// TODO(b/186265930): Remove once fixed:
- if (!isFinite(pxToSec)) {
- // Resolution is in pixels per second so 1000 means 1px = 1ms.
- console.error(`b/186265930: Bad pxToSec suppressed ${pxToSec}`);
- return fromNs(Math.pow(2, Math.floor(Math.log2(toNs(1000)))));
+ if (timeScale.pxSpan.delta === 0) {
+ console.error(`b/186265930: Bad pxToSec suppressed`);
+ return BigintMath.bitFloor(tpTimeFromSeconds(1000));
}
- const pxToNs = Math.max(toNs(pxToSec), 1);
- const resolution = fromNs(Math.pow(2, Math.floor(Math.log2(pxToNs))));
- const log2 = Math.log2(toNs(resolution));
- if (log2 % 1 !== 0) {
- throw new Error(`Resolution should be a power of two.
- pxToSec: ${pxToSec},
- pxToNs: ${pxToNs},
- resolution: ${resolution},
- log2: ${Math.log2(toNs(resolution))}`);
- }
- return resolution;
+
+ const timePerPx = timeScale.pxDeltaToDuration(this.quantPx);
+
+ return BigintMath.bitFloor(timePerPx.toTPTime('floor'));
}
getCurrentEngine(): EngineConfig|undefined {
@@ -600,7 +603,7 @@
this._metricResult = undefined;
this._currentSearchResults = {
sliceIds: new Float64Array(0),
- tsStarts: new Float64Array(0),
+ tsStarts: new BigInt64Array(0),
utids: new Float64Array(0),
trackIds: [],
sources: [],
@@ -637,6 +640,41 @@
shutdown() {
this._rafScheduler!.shutdown();
}
+
+ // Get a timescale that covers the entire trace
+ getTraceTimeScale(pxSpan: PxSpan): TimeScale {
+ const {start, end} = this.state.traceTime;
+ const traceTime = HighPrecisionTimeSpan.fromTpTime(start, end);
+ return TimeScale.fromHPTimeSpan(traceTime, pxSpan);
+ }
+
+ // Get the trace time bounds
+ stateTraceTime(): Span<HighPrecisionTime> {
+ const {start, end} = this.state.traceTime;
+ return HighPrecisionTimeSpan.fromTpTime(start, end);
+ }
+
+ stateTraceTimeTP(): Span<TPTime> {
+ const {start, end} = this.state.traceTime;
+ return new TPTimeSpan(start, end);
+ }
+
+ // Get the state version of the visible time bounds
+ stateVisibleTime(): Span<TPTime> {
+ const {start, end} = this.state.frontendLocalState.visibleState;
+ return new TPTimeSpan(start, end);
+ }
+
+ // How many pixels to use for one quanta of horizontal resolution
+ get quantPx(): number {
+ const quantPx = (self as {} as {quantPx: number | undefined}).quantPx;
+ if (quantPx) {
+ return quantPx;
+ } else {
+ // Default to 1px per quanta if not defined
+ return 1;
+ }
+ }
}
export const globals = new Globals();
diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts
index 1c9dbfe..6581c81 100644
--- a/ui/src/frontend/gridline_helper.ts
+++ b/ui/src/frontend/gridline_helper.ts
@@ -13,49 +13,91 @@
// limitations under the License.
import {assertTrue} from '../base/logging';
-import {roundDownNearest} from '../base/math_utils';
+import {Span, tpDurationToSeconds} from '../common/time';
+import {TPDuration, TPTime, TPTimeSpan} from '../common/time';
+
import {TRACK_BORDER_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
import {globals} from './globals';
import {TimeScale} from './time_scale';
-// Returns the optimal step size (in seconds) and tick pattern of ticks within
-// the step. The returned step size has two properties: (1) It is 1, 2, or 5,
-// multiplied by some integer power of 10. (2) It is maximised given the
-// constraint: |range| / stepSize <= |maxNumberOfSteps|.
-export function getStepSize(
- range: number, maxNumberOfSteps: number): [number, string] {
- // First, get the largest possible power of 10 that is smaller than the
- // desired step size, and use it as our initial step size.
- // For example, if the range is 2345ms and the desired steps is 10, then the
- // minimum step size is 234.5ms so the step size will initialise to 100.
- const minStepSize = range / maxNumberOfSteps;
- const zeros = Math.floor(Math.log10(minStepSize));
- const initialStepSize = Math.pow(10, zeros);
+const micros = 1000n;
+const millis = 1000n * micros;
+const seconds = 1000n * millis;
+const minutes = 60n * seconds;
+const hours = 60n * minutes;
+const days = 24n * hours;
- // We know that |initialStepSize| is a power of 10, and
- // initialStepSize <= desiredStepSize <= 10 * initialStepSize. There are four
- // possible candidates for final step size: 1, 2, 5 or 10 * initialStepSize.
- // For our example above, this would result in a step size of 500ms, as both
- // 100ms and 200ms are smaller than the minimum step size of 234.5ms.
- // We pick the candidate that minimizes the step size without letting the
- // number of steps exceed |maxNumberOfSteps|. The factor we pick to also
- // determines the pattern of ticks. This pattern is represented using a string
- // where:
- // | = Major tick
- // : = Medium tick
- // . = Minor tick
- const stepSizeMultipliers: [number, string][] =
- [[1, '|....:....'], [2, '|.:.'], [5, '|....'], [10, '|....:....']];
+// These patterns cover the entire range of 0 - 2^63-1 nanoseconds
+const patterns: [bigint, string][] = [
+ [1n, '|'],
+ [2n, '|:'],
+ [5n, '|....'],
+ [10n, '|....:....'],
+ [20n, '|.:.'],
+ [50n, '|....'],
+ [100n, '|....:....'],
+ [200n, '|.:.'],
+ [500n, '|....'],
+ [1n * micros, '|....:....'],
+ [2n * micros, '|.:.'],
+ [5n * micros, '|....'],
+ [10n * micros, '|....:....'],
+ [20n * micros, '|.:.'],
+ [50n * micros, '|....'],
+ [100n * micros, '|....:....'],
+ [200n * micros, '|.:.'],
+ [500n * micros, '|....'],
+ [1n * millis, '|....:....'],
+ [2n * millis, '|.:.'],
+ [5n * millis, '|....'],
+ [10n * millis, '|....:....'],
+ [20n * millis, '|.:.'],
+ [50n * millis, '|....'],
+ [100n * millis, '|....:....'],
+ [200n * millis, '|.:.'],
+ [500n * millis, '|....'],
+ [1n * seconds, '|....:....'],
+ [2n * seconds, '|.:.'],
+ [5n * seconds, '|....'],
+ [10n * seconds, '|....:....'],
+ [30n * seconds, '|.:.:.'],
+ [1n * minutes, '|.....'],
+ [2n * minutes, '|.:.'],
+ [5n * minutes, '|.....'],
+ [10n * minutes, '|....:....'],
+ [30n * minutes, '|.:.:.'],
+ [1n * hours, '|.....'],
+ [2n * hours, '|.:.'],
+ [6n * hours, '|.....'],
+ [12n * hours, '|.....:.....'],
+ [1n * days, '|.:.'],
+ [2n * days, '|.:.'],
+ [5n * days, '|....'],
+ [10n * days, '|....:....'],
+ [20n * days, '|.:.'],
+ [50n * days, '|....'],
+ [100n * days, '|....:....'],
+ [200n * days, '|.:.'],
+ [500n * days, '|....'],
+ [1000n * days, '|....:....'],
+ [2000n * days, '|.:.'],
+ [5000n * days, '|....'],
+ [10000n * days, '|....:....'],
+ [20000n * days, '|.:.'],
+ [50000n * days, '|....'],
+ [100000n * days, '|....:....'],
+ [200000n * days, '|.:.'],
+];
- for (const [multiplier, pattern] of stepSizeMultipliers) {
- const newStepSize = multiplier * initialStepSize;
- const numberOfNewSteps = range / newStepSize;
- if (numberOfNewSteps <= maxNumberOfSteps) {
- return [newStepSize, pattern];
+// Returns the optimal step size and pattern of ticks within the step.
+export function getPattern(minPatternSize: bigint): [TPDuration, string] {
+ for (const [size, pattern] of patterns) {
+ if (size >= minPatternSize) {
+ return [size, pattern];
}
}
- throw new Error('Something has gone horribly wrong with maths');
+ throw new Error('Pattern not defined for this minsize');
}
function tickPatternToArray(pattern: string): TickType[] {
@@ -75,21 +117,23 @@
});
}
-// Assuming a number only has one non-zero decimal digit, find the number of
-// decimal places required to accurately print that number. I.e. the parameter
-// we should pass to number.toFixed(x). To account for floating point
-// innaccuracies when representing numbers in base-10, we only take the first
-// nonzero fractional digit into account. E.g.
+// Get the number of decimal places we would have to print a time to for a given
+// min step size. For example, if we know the min step size is 0.1 and all
+// values are going to be aligned to integral multiples of 0.1, there's no
+// point printing these values with more than 1 decimal place.
+// Note: It's assumed that stepSize only has one significant figure.
+// E.g. 0.3 and 0.00002 are fine, but 0.123 will be treated as if it were 0.1.
+// Some examples: (seconds -> decimal places)
// 1.0 -> 0
// 0.5 -> 1
// 0.009 -> 3
// 0.00007 -> 5
// 30000 -> 0
// 0.30000000000000004 -> 1
-export function guessDecimalPlaces(val: number): number {
- const neglog10 = -Math.floor(Math.log10(val));
- const clamped = Math.max(0, neglog10);
- return clamped;
+export function guessDecimalPlaces(stepSize: TPDuration): number {
+ const stepSizeSeconds = tpDurationToSeconds(stepSize);
+ const decimalPlaces = -Math.floor(Math.log10(stepSizeSeconds));
+ return Math.max(0, decimalPlaces);
}
export enum TickType {
@@ -100,55 +144,58 @@
export interface Tick {
type: TickType;
- time: number;
- position: number;
+ time: TPTime;
}
const MIN_PX_PER_STEP = 80;
+export function getMaxMajorTicks(width: number) {
+ return Math.max(1, Math.floor(width / MIN_PX_PER_STEP));
+}
+
+function roundDownNearest(time: TPTime, stepSize: TPDuration): TPTime {
+ return stepSize * (time / stepSize);
+}
// An iterable which generates a series of ticks for a given timescale.
export class TickGenerator implements Iterable<Tick> {
private _tickPattern: TickType[];
- private _patternSize: number;
+ private _patternSize: TPDuration;
+ private _timeSpan: Span<TPTime>;
+ private _offset: TPTime;
- constructor(private scale: TimeScale, {minLabelPx = MIN_PX_PER_STEP} = {}) {
- assertTrue(minLabelPx > 0, 'minLabelPx cannot be lte 0');
- assertTrue(scale.widthPx > 0, 'widthPx cannot be lte 0');
- assertTrue(
- scale.timeSpan.duration > 0, 'timeSpan.duration cannot be lte 0');
+ constructor(
+ timeSpan: Span<TPTime>, maxMajorTicks: number, offset: TPTime = 0n) {
+ assertTrue(timeSpan.duration > 0n, 'timeSpan.duration cannot be lte 0');
+ assertTrue(maxMajorTicks > 0, 'maxMajorTicks cannot be lte 0');
- const desiredSteps = scale.widthPx / minLabelPx;
- const [size, pattern] = getStepSize(scale.timeSpan.duration, desiredSteps);
+ this._timeSpan = timeSpan.add(-offset);
+ this._offset = offset;
+ const minStepSize =
+ BigInt(Math.floor(Number(timeSpan.duration) / maxMajorTicks));
+ const [size, pattern] = getPattern(minStepSize);
this._patternSize = size;
this._tickPattern = tickPatternToArray(pattern);
}
// Returns an iterable, so this object can be iterated over directly using the
// `for x of y` notation. The use of a generator here is just to make things
- // more elegant than creating an array of ticks and building an iterator for
- // it.
+ // more elegant compared to creating an array of ticks and building an
+ // iterator for it.
* [Symbol.iterator](): Generator<Tick> {
- const span = this.scale.timeSpan;
- const stepSize = this._patternSize / this._tickPattern.length;
- const start = roundDownNearest(span.start, this._patternSize);
- const timeAtStep = (i: number) => start + (i * stepSize);
+ const stepSize = this._patternSize / BigInt(this._tickPattern.length);
+ const start = roundDownNearest(this._timeSpan.start, this._patternSize);
+ const end = this._timeSpan.end;
+ let patternIndex = 0;
- // Iterating using steps instead of
- // for (let s = start; s < span.end; s += stepSize) because if start is much
- // larger than stepSize we can enter an infinite loop due to floating
- // point precision errors.
- for (let i = 0; timeAtStep(i) < span.end; i++) {
- const time = timeAtStep(i);
- if (time >= span.start) {
- const position = Math.floor(this.scale.timeToPx(time));
- const type = this._tickPattern[i % this._tickPattern.length];
- yield {type, time, position};
+ for (let time = start; time < end; time += stepSize, patternIndex++) {
+ if (time >= this._timeSpan.start) {
+ patternIndex = patternIndex % this._tickPattern.length;
+ const type = this._tickPattern[patternIndex];
+ yield {type, time: time + this._offset};
}
}
}
- // The number of decimal places labels should be printed with, assuming labels
- // are only printed on major ticks.
get digits(): number {
return guessDecimalPlaces(this._patternSize);
}
@@ -157,9 +204,7 @@
// Gets the timescale associated with the current visible window.
export function timeScaleForVisibleWindow(
startPx: number, endPx: number): TimeScale {
- const span = globals.frontendLocalState.visibleWindowTime;
- const spanRelative = span.add(-globals.state.traceTime.startSec);
- return new TimeScale(spanRelative, [startPx, endPx]);
+ return globals.frontendLocalState.getTimeScale(startPx, endPx);
}
export function drawGridLines(
@@ -169,13 +214,18 @@
ctx.strokeStyle = TRACK_BORDER_COLOR;
ctx.lineWidth = 1;
- const timeScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, width);
- if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
- for (const {type, position} of new TickGenerator(timeScale)) {
+ const {earliest, latest} = globals.frontendLocalState.visibleWindow;
+ const span = new TPTimeSpan(earliest, latest);
+ if (width > TRACK_SHELL_WIDTH && span.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(width - TRACK_SHELL_WIDTH);
+ const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, width);
+ for (const {type, time} of new TickGenerator(
+ span, maxMajorTicks, globals.state.traceTime.start)) {
+ const px = Math.floor(map.tpTimeToPx(time));
if (type === TickType.MAJOR) {
ctx.beginPath();
- ctx.moveTo(position + 0.5, 0);
- ctx.lineTo(position + 0.5, height);
+ ctx.moveTo(px + 0.5, 0);
+ ctx.lineTo(px + 0.5, height);
ctx.stroke();
}
}
diff --git a/ui/src/frontend/gridline_helper_unittest.ts b/ui/src/frontend/gridline_helper_unittest.ts
index 3b6dcac..2454680 100644
--- a/ui/src/frontend/gridline_helper_unittest.ts
+++ b/ui/src/frontend/gridline_helper_unittest.ts
@@ -12,303 +12,93 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TimeSpan} from '../common/time';
+import {TPTimeSpan} from '../common/time';
-import {getStepSize, Tick, TickGenerator, TickType} from './gridline_helper';
-import {TimeScale} from './time_scale';
-
-const pattern1 = '|....:....';
-const pattern2 = '|.:.';
-const pattern5 = '|....';
-const timeScale = new TimeScale(new TimeSpan(0, 1), [1, 2]);
+import {getPattern, TickGenerator, TickType} from './gridline_helper';
test('gridline helper to have sensible step sizes', () => {
- expect(getStepSize(10, 14)).toEqual([1, pattern1]);
- expect(getStepSize(30, 14)).toEqual([5, pattern5]);
- expect(getStepSize(60, 14)).toEqual([5, pattern5]);
- expect(getStepSize(100, 14)).toEqual([10, pattern1]);
+ expect(getPattern(1n)).toEqual([1n, '|']);
+ expect(getPattern(2n)).toEqual([2n, '|:']);
+ expect(getPattern(3n)).toEqual([5n, '|....']);
+ expect(getPattern(4n)).toEqual([5n, '|....']);
+ expect(getPattern(5n)).toEqual([5n, '|....']);
+ expect(getPattern(7n)).toEqual([10n, '|....:....']);
- expect(getStepSize(10, 21)).toEqual([0.5, pattern5]);
- expect(getStepSize(30, 21)).toEqual([2, pattern2]);
- expect(getStepSize(60, 21)).toEqual([5, pattern5]);
- expect(getStepSize(100, 21)).toEqual([5, pattern5]);
+ expect(getPattern(10n)).toEqual([10n, '|....:....']);
+ expect(getPattern(20n)).toEqual([20n, '|.:.']);
+ expect(getPattern(50n)).toEqual([50n, '|....']);
- expect(getStepSize(10, 3)).toEqual([5, pattern5]);
- expect(getStepSize(30, 3)).toEqual([10, pattern1]);
- expect(getStepSize(60, 3)).toEqual([20, pattern2]);
- expect(getStepSize(100, 3)).toEqual([50, pattern5]);
-
- expect(getStepSize(800, 4)).toEqual([200, pattern2]);
+ expect(getPattern(100n)).toEqual([100n, '|....:....']);
});
-test('gridline helper to scale to very small and very large values', () => {
- expect(getStepSize(.01, 14)).toEqual([.001, pattern1]);
- expect(getStepSize(10000, 14)).toEqual([1000, pattern1]);
-});
+describe('TickGenerator', () => {
+ it('can generate ticks with span starting at origin', () => {
+ const tickGen = new TickGenerator(new TPTimeSpan(0n, 10n), 1);
+ const expected = [
+ {type: TickType.MAJOR, time: 0n},
+ {type: TickType.MINOR, time: 1n},
+ {type: TickType.MINOR, time: 2n},
+ {type: TickType.MINOR, time: 3n},
+ {type: TickType.MINOR, time: 4n},
+ {type: TickType.MEDIUM, time: 5n},
+ {type: TickType.MINOR, time: 6n},
+ {type: TickType.MINOR, time: 7n},
+ {type: TickType.MINOR, time: 8n},
+ {type: TickType.MINOR, time: 9n},
+ ];
+ const actual = Array.from(tickGen!);
+ expect(actual).toStrictEqual(expected);
+ expect(tickGen!.digits).toEqual(8);
+ });
-test('gridline helper to always return a reasonable number of steps', () => {
- for (let i = 1; i <= 1000; i++) {
- const [stepSize, _] = getStepSize(i, 14);
- expect(Math.round(i / stepSize)).toBeGreaterThanOrEqual(6);
- expect(Math.round(i / stepSize)).toBeLessThanOrEqual(14);
- }
-});
+ it('can generate ticks when span has an offset', () => {
+ const tickGen = new TickGenerator(new TPTimeSpan(10n, 20n), 1);
+ const expected = [
+ {type: TickType.MAJOR, time: 10n},
+ {type: TickType.MINOR, time: 11n},
+ {type: TickType.MINOR, time: 12n},
+ {type: TickType.MINOR, time: 13n},
+ {type: TickType.MINOR, time: 14n},
+ {type: TickType.MEDIUM, time: 15n},
+ {type: TickType.MINOR, time: 16n},
+ {type: TickType.MINOR, time: 17n},
+ {type: TickType.MINOR, time: 18n},
+ {type: TickType.MINOR, time: 19n},
+ ];
+ const actual = Array.from(tickGen!);
+ expect(actual).toStrictEqual(expected);
+ expect(tickGen!.digits).toEqual(8);
+ });
-describe('TickGenerator with range 0.0-1.0 and room for 2 labels', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.0, 1.0);
- const timeScale = new TimeScale(timeSpan, [0, 200]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 0.0},
- {type: TickType.MINOR, time: 0.1},
- {type: TickType.MINOR, time: 0.2},
- {type: TickType.MINOR, time: 0.3},
- {type: TickType.MINOR, time: 0.4},
- {type: TickType.MAJOR, time: 0.5},
- {type: TickType.MINOR, time: 0.6},
- {type: TickType.MINOR, time: 0.7},
- {type: TickType.MINOR, time: 0.8},
- {type: TickType.MINOR, time: 0.9},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 1 decimal place for labels', () => {
- expect(tickGen!.digits).toEqual(1);
- });
-});
-
-describe('TickGenerator with range 0.3-1.3 and room for 2 labels', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.3, 1.3);
- const timeScale = new TimeScale(timeSpan, [0, 200]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
- () => {
- const expected = [
- {type: TickType.MINOR, time: 0.3},
- {type: TickType.MINOR, time: 0.4},
- {type: TickType.MAJOR, time: 0.5},
- {type: TickType.MINOR, time: 0.6},
- {type: TickType.MINOR, time: 0.7},
- {type: TickType.MINOR, time: 0.8},
- {type: TickType.MINOR, time: 0.9},
- {type: TickType.MAJOR, time: 1.0},
- {type: TickType.MINOR, time: 1.1},
- {type: TickType.MINOR, time: 1.2},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 1 decimal place for labels', () => {
- expect(tickGen!.digits).toEqual(1);
- });
-});
-
-describe('TickGenerator with range 0.0-0.2 and room for 1 label', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.0, 0.2);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should produce major ticks at 0.2s and minor ticks at 0.1s starting at 0',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 0.0},
- {type: TickType.MINOR, time: 0.05},
- {type: TickType.MEDIUM, time: 0.1},
- {type: TickType.MINOR, time: 0.15},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 1 decimal place for labels', () => {
- expect(tickGen!.digits).toEqual(1);
- });
-});
-
-describe('TickGenerator with range 0.0-0.1 and room for 1 label', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.0, 0.1);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should produce major ticks at 0.1s & minor ticks at 0.02s starting at 0',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 0.0},
- {type: TickType.MINOR, time: 0.01},
- {type: TickType.MINOR, time: 0.02},
- {type: TickType.MINOR, time: 0.03},
- {type: TickType.MINOR, time: 0.04},
- {type: TickType.MEDIUM, time: 0.05},
- {type: TickType.MINOR, time: 0.06},
- {type: TickType.MINOR, time: 0.07},
- {type: TickType.MINOR, time: 0.08},
- {type: TickType.MINOR, time: 0.09},
- ];
- const actual = Array.from(tickGen!);
- expect(tickGen!.digits).toEqual(1);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 1 decimal place for labels', () => {
- expect(tickGen!.digits).toEqual(1);
- });
-});
-
-describe('TickGenerator with a very small timespan', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.0, 1e-9);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should generate minor ticks at 2e-10s and one major tick at the start',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 0.0},
- {type: TickType.MINOR, time: 1e-10},
- {type: TickType.MINOR, time: 2e-10},
- {type: TickType.MINOR, time: 3e-10},
- {type: TickType.MINOR, time: 4e-10},
- {type: TickType.MEDIUM, time: 5e-10},
- {type: TickType.MINOR, time: 6e-10},
- {type: TickType.MINOR, time: 7e-10},
- {type: TickType.MINOR, time: 8e-10},
- {type: TickType.MINOR, time: 9e-10},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 9 decimal places for labels', () => {
- expect(tickGen!.digits).toEqual(9);
- });
-});
-
-describe('TickGenerator with a very large timespan', () => {
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(0.0, 1e9);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
- });
- it('should generate minor ticks at 2e8 and one major tick at the start',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 0.0},
- {type: TickType.MINOR, time: 1e8},
- {type: TickType.MINOR, time: 2e8},
- {type: TickType.MINOR, time: 3e8},
- {type: TickType.MINOR, time: 4e8},
- {type: TickType.MEDIUM, time: 5e8},
- {type: TickType.MINOR, time: 6e8},
- {type: TickType.MINOR, time: 7e8},
- {type: TickType.MINOR, time: 8e8},
- {type: TickType.MINOR, time: 9e8},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 0 decimal places for labels', () => {
+ it('can generate ticks when span is large', () => {
+ const tickGen =
+ new TickGenerator(new TPTimeSpan(1000000000n, 2000000000n), 1);
+ const expected = [
+ {type: TickType.MAJOR, time: 1000000000n},
+ {type: TickType.MINOR, time: 1100000000n},
+ {type: TickType.MINOR, time: 1200000000n},
+ {type: TickType.MINOR, time: 1300000000n},
+ {type: TickType.MINOR, time: 1400000000n},
+ {type: TickType.MEDIUM, time: 1500000000n},
+ {type: TickType.MINOR, time: 1600000000n},
+ {type: TickType.MINOR, time: 1700000000n},
+ {type: TickType.MINOR, time: 1800000000n},
+ {type: TickType.MINOR, time: 1900000000n},
+ ];
+ const actual = Array.from(tickGen!);
+ expect(actual).toStrictEqual(expected);
expect(tickGen!.digits).toEqual(0);
});
-});
-describe('TickGenerator where the timespan has a dynamic range of 1e12', () => {
- // This is the equivalent of zooming in to the nanosecond level, 1000 seconds
- // into a trace Note: this is about the limit of what this generator can
- // handle.
- let tickGen: TickGenerator|undefined = undefined;
- beforeAll(() => {
- const timeSpan = new TimeSpan(1000, 1000.000000001);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+ it('throws an error when timespan duration is 0', () => {
+ expect(() => {
+ new TickGenerator(new TPTimeSpan(0n, 0n), 1);
+ }).toThrow(Error);
});
- it('should generate minor ticks at 1e-10s and one major tick at the start',
- () => {
- const expected = [
- {type: TickType.MAJOR, time: 1000.0000000000},
- {type: TickType.MINOR, time: 1000.0000000001},
- {type: TickType.MINOR, time: 1000.0000000002},
- {type: TickType.MINOR, time: 1000.0000000003},
- {type: TickType.MINOR, time: 1000.0000000004},
- {type: TickType.MEDIUM, time: 1000.0000000005},
- {type: TickType.MINOR, time: 1000.0000000006},
- {type: TickType.MINOR, time: 1000.0000000007},
- {type: TickType.MINOR, time: 1000.0000000008},
- {type: TickType.MINOR, time: 1000.0000000009},
- ];
- const actual = Array.from(tickGen!);
- expectTicksEqual(actual, expected);
- });
- it('should tell us to use 9 decimal places for labels', () => {
- expect(tickGen!.digits).toEqual(9);
+
+ it('throws an error when max ticks is 0', () => {
+ expect(() => {
+ new TickGenerator(new TPTimeSpan(0n, 1n), 0);
+ }).toThrow(Error);
});
});
-
-describe(
- 'TickGenerator where the timespan has a ridiculously huge dynamic range',
- () => {
- // We don't expect this to work, just wanna make sure it doesn't crash or
- // get stuck
- it('should not crash or get stuck in an infinite loop', () => {
- const timeSpan = new TimeSpan(1000, 1000.000000000001);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- new TickGenerator(timeScale);
- });
- });
-
-describe(
- 'TickGenerator where the timespan has a ridiculously huge dynamic range',
- () => {
- // We don't expect this to work, just wanna make sure it doesn't crash or
- // get stuck
- it('should not crash or get stuck in an infinite loop', () => {
- const timeSpan = new TimeSpan(1000, 1000.000000000001);
- const timeScale = new TimeScale(timeSpan, [0, 100]);
- new TickGenerator(timeScale);
- });
- });
-
-test('TickGenerator constructed with a 0 width throws an error', () => {
- expect(() => {
- const timeScale = new TimeScale(new TimeSpan(0.0, 1.0), [0, 0]);
- new TickGenerator(timeScale);
- }).toThrow(Error);
-});
-
-test(
- 'TickGenerator constructed with desiredPxPerStep of 0 throws an error',
- () => {
- expect(() => {
- new TickGenerator(timeScale, {minLabelPx: 0});
- }).toThrow(Error);
- });
-
-test('TickGenerator constructed with a 0 duration throws an error', () => {
- expect(() => {
- const timeScale = new TimeScale(new TimeSpan(0.0, 0.0), [0, 1]);
- new TickGenerator(timeScale);
- }).toThrow(Error);
-});
-
-function expectTicksEqual(actual: Tick[], expected: any[]) {
- // TODO(stevegolton) We could write a custom matcher for this; this approach
- // produces cryptic error messages.
- expect(actual.length).toEqual(expected.length);
- for (let i = 0; i < actual.length; ++i) {
- const ex = expected[i];
- const ac = actual[i];
- expect(ac.type).toEqual(ex.type);
- expect(ac.time).toBeCloseTo(ex.time, 9);
- }
-}
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index cef61e8..053316f 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -142,12 +142,6 @@
}));
}
-function initGlobalsFromQueryString() {
- const queryString = window.location.search;
- globals.embeddedMode = queryString.includes('mode=embedded');
- globals.hideSidebar = queryString.includes('hideSidebar=true');
-}
-
function setupContentSecurityPolicy() {
// Note: self and sha-xxx must be quoted, urls data: and blob: must not.
const policy = {
@@ -252,9 +246,10 @@
maybeOpenTraceFromRoute(route);
};
- // This must be called before calling `globals.initialize` so that the
- // `embeddedMode` global is set.
- initGlobalsFromQueryString();
+ // These need to be set before globals.initialize.
+ const route = Router.parseUrl(window.location.href);
+ globals.embeddedMode = route.args.mode === 'embedded';
+ globals.hideSidebar = route.args.hideSidebar === true;
globals.initialize(dispatch, router);
globals.serviceWorkerController.install();
@@ -311,7 +306,6 @@
}
}
-
function onCssLoaded() {
initCssConstants();
// Clear all the contents of the initial page (e.g. the <pre> error message)
@@ -343,6 +337,14 @@
// accidentially clober the state of an open trace processor instance
// otherwise.
CheckHttpRpcConnection().then(() => {
+ const route = Router.parseUrl(window.location.href);
+
+ globals.dispatch(Actions.maybeSetPendingDeeplink({
+ ts: route.args.ts,
+ tid: route.args.tid,
+ dur: route.args.dur,
+ }));
+
if (!globals.embeddedMode) {
installFileDropHandler();
}
@@ -359,7 +361,7 @@
// Handles the initial ?local_cache_key=123 or ?s=permalink or ?url=...
// cases.
- maybeOpenTraceFromRoute(Router.parseUrl(window.location.href));
+ maybeOpenTraceFromRoute(route);
});
}
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 7da04de..f25bf52 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -14,6 +14,7 @@
import {Actions} from '../common/actions';
import {Area} from '../common/state';
+import {TPTime} from '../common/time';
import {Flow, globals} from './globals';
import {toggleHelp} from './help_modal';
@@ -23,7 +24,8 @@
} from './scroll_helper';
import {executeSearch} from './search_handler';
-const INSTANT_FOCUS_DURATION_S = 1 / 1e9; // 1 ns.
+const INSTANT_FOCUS_DURATION = 1n;
+const INCOMPLETE_SLICE_DURATION = 30_000n;
type Direction = 'Forward'|'Backward';
// Handles all key events than are not handled by the
@@ -55,8 +57,8 @@
if (selection !== null && selection.kind === 'AREA') {
const area = globals.state.areas[selection.areaId];
const coversEntireTimeRange =
- globals.state.traceTime.startSec === area.startSec &&
- globals.state.traceTime.endSec === area.endSec;
+ globals.state.traceTime.start === area.start &&
+ globals.state.traceTime.end === area.end;
if (!coversEntireTimeRange) {
// If the current selection is an area which does not cover the entire
// time range, preserve the list of selected tracks and expand the time
@@ -71,10 +73,11 @@
// If the current selection is not an area, select all.
tracksToSelect = Object.keys(globals.state.tracks);
}
+ const {start, end} = globals.state.traceTime;
globals.dispatch(Actions.selectArea({
area: {
- startSec: globals.state.traceTime.startSec,
- endSec: globals.state.traceTime.endSec,
+ start,
+ end,
tracks: tracksToSelect,
},
}));
@@ -201,29 +204,29 @@
}
}
-function findTimeRangeOfSelection(): {startTs: number, endTs: number} {
+function findTimeRangeOfSelection(): {startTs: TPTime, endTs: TPTime} {
const selection = globals.state.currentSelection;
- let startTs = -1;
- let endTs = -1;
+ let startTs = -1n;
+ let endTs = -1n;
if (selection === null) {
return {startTs, endTs};
} else if (selection.kind === 'SLICE' || selection.kind === 'CHROME_SLICE') {
const slice = globals.sliceDetails;
if (slice.ts && slice.dur !== undefined && slice.dur > 0) {
- startTs = slice.ts + globals.state.traceTime.startSec;
+ startTs = slice.ts;
endTs = startTs + slice.dur;
} else if (slice.ts) {
- startTs = slice.ts + globals.state.traceTime.startSec;
+ startTs = slice.ts;
// This will handle either:
// a)slice.dur === -1 -> unfinished slice
// b)slice.dur === 0 -> instant event
- endTs = slice.dur === -1 ? globals.state.traceTime.endSec :
- startTs + INSTANT_FOCUS_DURATION_S;
+ endTs = slice.dur === -1n ? startTs + INCOMPLETE_SLICE_DURATION :
+ startTs + INSTANT_FOCUS_DURATION;
}
} else if (selection.kind === 'THREAD_STATE') {
const threadState = globals.threadStateDetails;
if (threadState.ts && threadState.dur) {
- startTs = threadState.ts + globals.state.traceTime.startSec;
+ startTs = threadState.ts;
endTs = startTs + threadState.dur;
}
} else if (selection.kind === 'COUNTER') {
@@ -232,8 +235,8 @@
} else if (selection.kind === 'AREA') {
const selectedArea = globals.state.areas[selection.areaId];
if (selectedArea) {
- startTs = selectedArea.startSec;
- endTs = selectedArea.endSec;
+ startTs = selectedArea.start;
+ endTs = selectedArea.end;
}
} else if (selection.kind === 'NOTE') {
const selectedNote = globals.state.notes[selection.id];
@@ -241,16 +244,18 @@
// above in the AREA case.
if (selectedNote && selectedNote.noteType === 'DEFAULT') {
startTs = selectedNote.timestamp;
- endTs = selectedNote.timestamp + INSTANT_FOCUS_DURATION_S;
+ endTs = selectedNote.timestamp + INSTANT_FOCUS_DURATION;
}
} else if (selection.kind === 'LOG') {
// TODO(hjd): Make focus selection work for logs.
- } else if (selection.kind === 'DEBUG_SLICE') {
- startTs = selection.startS;
- if (selection.durationS > 0) {
- endTs = startTs + selection.durationS;
+ } else if (
+ selection.kind === 'DEBUG_SLICE' ||
+ selection.kind === 'TOP_LEVEL_SCROLL') {
+ startTs = selection.start;
+ if (selection.duration > 0) {
+ endTs = startTs + selection.duration;
} else {
- endTs = startTs + INSTANT_FOCUS_DURATION_S;
+ endTs = startTs + INSTANT_FOCUS_DURATION;
}
}
@@ -260,12 +265,12 @@
function lockSliceSpan(persistent = false) {
const range = findTimeRangeOfSelection();
- if (range.startTs !== -1 && range.endTs !== -1 &&
+ if (range.startTs !== -1n && range.endTs !== -1n &&
globals.state.currentSelection !== null) {
const tracks = globals.state.currentSelection.trackId ?
[globals.state.currentSelection.trackId] :
[];
- const area: Area = {startSec: range.startTs, endSec: range.endTs, tracks};
+ const area: Area = {start: range.startTs, end: range.endTs, tracks};
globals.dispatch(Actions.markArea({area, persistent}));
}
}
@@ -275,7 +280,7 @@
if (selection === null) return;
const range = findTimeRangeOfSelection();
- if (range.startTs !== -1 && range.endTs !== -1) {
+ if (range.startTs !== -1n && range.endTs !== -1n) {
focusHorizontalRange(range.startTs, range.endTs);
}
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index 18ed325..8c50589 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -16,14 +16,14 @@
import {assertExists} from '../base/logging';
import {Actions} from '../common/actions';
+import {HighPrecisionTimeSpan} from '../common/high_precision_time';
import {
LogBounds,
LogBoundsKey,
LogEntries,
LogEntriesKey,
} from '../common/logs';
-import {formatTimestamp} from '../common/time';
-import {TimeSpan} from '../common/time';
+import {formatTPTime, TPTime} from '../common/time';
import {SELECTED_LOG_ROWS_COLOR} from './css_constants';
import {globals} from './globals';
@@ -58,16 +58,19 @@
}
oncreate({dom}: m.CVnodeDOM) {
- this.scrollContainer = assertExists(
- dom.parentElement!.parentElement!.parentElement as HTMLElement);
+ this.scrollContainer = assertExists(dom.parentElement as HTMLElement);
this.scrollContainer.addEventListener(
'scroll', this.onScroll.bind(this), {passive: true});
+ // TODO(stevegolton): Type assersions are a source of bugs.
+ // Let's try to find another way of doing this.
this.bounds = globals.trackDataStore.get(LogBoundsKey) as LogBounds;
this.entries = globals.trackDataStore.get(LogEntriesKey) as LogEntries;
this.recomputeVisibleRowsAndUpdate();
}
onbeforeupdate(_: m.CVnodeDOM) {
+ // TODO(stevegolton): Type assersions are a source of bugs.
+ // Let's try to find another way of doing this.
this.bounds = globals.trackDataStore.get(LogBoundsKey) as LogBounds;
this.entries = globals.trackDataStore.get(LogEntriesKey) as LogEntries;
this.recomputeVisibleRowsAndUpdate();
@@ -79,12 +82,12 @@
globals.rafScheduler.scheduleFullRedraw();
}
- onRowOver(ts: number) {
+ onRowOver(ts: TPTime) {
globals.dispatch(Actions.setHoverCursorTimestamp({ts}));
}
onRowOut() {
- globals.dispatch(Actions.setHoverCursorTimestamp({ts: -1}));
+ globals.dispatch(Actions.setHoverCursorTimestamp({ts: -1n}));
}
private totalRows():
@@ -92,17 +95,19 @@
if (!this.bounds) {
return {isStale: false, total: 0, offset: 0, count: 0};
}
- const {total, startTs, endTs, firstRowTs, lastRowTs} = this.bounds;
+ const {
+ totalVisibleLogs,
+ firstVisibleLogTs,
+ lastVisibleLogTs,
+ } = this.bounds;
const vis = globals.frontendLocalState.visibleWindowTime;
- const leftSpan = new TimeSpan(startTs, firstRowTs);
- const rightSpan = new TimeSpan(lastRowTs, endTs);
- const isStaleLeft = !leftSpan.isInBounds(vis.start);
- const isStaleRight = !rightSpan.isInBounds(vis.end);
- const isStale = isStaleLeft || isStaleRight;
- const offset = Math.min(this.visibleRowOffset, total);
- const visCount = Math.min(total - offset, this.visibleRowCount);
- return {isStale, total, count: visCount, offset};
+ const visibleLogSpan =
+ new HighPrecisionTimeSpan(firstVisibleLogTs, lastVisibleLogTs);
+ const isStale = !vis.contains(visibleLogSpan);
+ const offset = Math.min(this.visibleRowOffset, totalVisibleLogs);
+ const visCount = Math.min(totalVisibleLogs - offset, this.visibleRowCount);
+ return {isStale, total: totalVisibleLogs, count: visCount, offset};
}
view(_: m.CVnode<{}>) {
@@ -146,11 +151,10 @@
{
'class': isStale ? 'stale' : '',
style,
- 'onmouseover': this.onRowOver.bind(this, ts / 1e9),
+ 'onmouseover': this.onRowOver.bind(this, ts),
'onmouseout': this.onRowOut.bind(this),
},
- m('.cell',
- formatTimestamp(ts / 1e9 - globals.state.traceTime.startSec)),
+ m('.cell', formatTPTime(ts - globals.state.traceTime.start)),
m('.cell', priorityLetter || '?'),
m('.cell', tags[i]),
hasProcessNames ? m('.cell.with-process', processNames[i]) :
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index d3eec07..27d9ec8 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -17,7 +17,9 @@
import {Actions} from '../common/actions';
import {randomColor} from '../common/colorizer';
import {AreaNote, Note} from '../common/state';
-import {timeToString} from '../common/time';
+import {
+ tpTimeToString,
+} from '../common/time';
import {
BottomTab,
@@ -28,6 +30,7 @@
import {PerfettoMouseEvent} from './events';
import {globals} from './globals';
import {
+ getMaxMajorTicks,
TickGenerator,
TickType,
timeScaleForVisibleWindow,
@@ -46,7 +49,7 @@
function getStartTimestamp(note: Note|AreaNote) {
if (note.noteType === 'AREA') {
- return globals.state.areas[note.areaId].startSec;
+ return globals.state.areas[note.areaId].start;
} else {
return note.timestamp;
}
@@ -66,7 +69,7 @@
});
dom.addEventListener('mouseout', () => {
this.hoveredX = null;
- globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1}));
+ globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1n}));
}, {passive: true});
}
@@ -110,15 +113,27 @@
}
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
- const timeScale = globals.frontendLocalState.timeScale;
let aNoteIsHovered = false;
ctx.fillStyle = '#999';
ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
- const relScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
- if (relScale.timeSpan.duration > 0 && relScale.widthPx > 0) {
- for (const {type, position} of new TickGenerator(relScale)) {
- if (type === TickType.MAJOR) ctx.fillRect(position, 0, 1, size.height);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
+ ctx.clip();
+
+ const span = globals.frontendLocalState.visibleWindow.timestampSpan;
+ const {visibleTimeScale} = globals.frontendLocalState;
+ if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
+ const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+ for (const {type, time} of new TickGenerator(
+ span, maxMajorTicks, globals.state.traceTime.start)) {
+ const px = Math.floor(map.tpTimeToPx(time));
+ if (type === TickType.MAJOR) {
+ ctx.fillRect(px, 0, 1, size.height);
+ }
}
}
@@ -129,11 +144,10 @@
const timestamp = getStartTimestamp(note);
// TODO(hjd): We should still render area selection marks in viewport is
// *within* the area (e.g. both lhs and rhs are out of bounds).
- if ((note.noteType !== 'AREA' && !timeScale.timeInBounds(timestamp)) ||
+ if ((note.noteType !== 'AREA' && !span.contains(timestamp)) ||
(note.noteType === 'AREA' &&
- !timeScale.timeInBounds(globals.state.areas[note.areaId].endSec) &&
- !timeScale.timeInBounds(
- globals.state.areas[note.areaId].startSec))) {
+ !span.contains(globals.state.areas[note.areaId].end) &&
+ !span.contains(globals.state.areas[note.areaId].start))) {
continue;
}
const currentIsHovered =
@@ -144,7 +158,7 @@
const isSelected = selection !== null &&
((selection.kind === 'NOTE' && selection.id === note.id) ||
(selection.kind === 'AREA' && selection.noteId === note.id));
- const x = timeScale.timeToPx(timestamp);
+ const x = visibleTimeScale.tpTimeToPx(timestamp);
const left = Math.floor(x + TRACK_SHELL_WIDTH);
// Draw flag or marker.
@@ -153,7 +167,8 @@
this.drawAreaMarker(
ctx,
left,
- Math.floor(timeScale.timeToPx(area.endSec) + TRACK_SHELL_WIDTH),
+ Math.floor(
+ visibleTimeScale.tpTimeToPx(area.end) + TRACK_SHELL_WIDTH),
note.color,
isSelected);
} else {
@@ -175,19 +190,21 @@
// A real note is hovered so we don't need to see the preview line.
// TODO(hjd): Change cursor to pointer here.
if (aNoteIsHovered) {
- globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1}));
+ globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1n}));
}
// View preview note flag when hovering on notes panel.
if (!aNoteIsHovered && this.hoveredX !== null) {
- const timestamp = timeScale.pxToTime(this.hoveredX);
- if (timeScale.timeInBounds(timestamp)) {
+ const timestamp = visibleTimeScale.pxToHpTime(this.hoveredX).toTPTime();
+ if (span.contains(timestamp)) {
globals.dispatch(Actions.setHoveredNoteTimestamp({ts: timestamp}));
- const x = timeScale.timeToPx(timestamp);
+ const x = visibleTimeScale.tpTimeToPx(timestamp);
const left = Math.floor(x + TRACK_SHELL_WIDTH);
this.drawFlag(ctx, left, size.height, '#aaa', /* fill */ true);
}
}
+
+ ctx.restore();
}
private drawAreaMarker(
@@ -197,7 +214,7 @@
ctx.strokeStyle = color;
const topOffset = 10;
// Don't draw in the track shell section.
- if (x >= globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH) {
+ if (x >= globals.frontendLocalState.windowSpan.start + TRACK_SHELL_WIDTH) {
// Draw left triangle.
ctx.beginPath();
ctx.moveTo(x, topOffset);
@@ -218,7 +235,7 @@
// Start line after track shell section, join triangles.
const startDraw = Math.max(
- x, globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH);
+ x, globals.frontendLocalState.windowSpan.start + TRACK_SHELL_WIDTH);
ctx.beginPath();
ctx.moveTo(startDraw, topOffset);
ctx.lineTo(xEnd, topOffset);
@@ -250,8 +267,8 @@
private onClick(x: number, _: number) {
if (x < 0) return;
- const timeScale = globals.frontendLocalState.timeScale;
- const timestamp = timeScale.pxToTime(x);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const timestamp = visibleTimeScale.pxToHpTime(x).toTPTime();
for (const note of Object.values(globals.state.notes)) {
if (this.hoveredX && this.mouseOverNote(this.hoveredX, note)) {
if (note.noteType === 'AREA') {
@@ -268,13 +285,13 @@
}
private mouseOverNote(x: number, note: AreaNote|Note): boolean {
- const timeScale = globals.frontendLocalState.timeScale;
- const noteX = timeScale.timeToPx(getStartTimestamp(note));
+ const timeScale = globals.frontendLocalState.visibleTimeScale;
+ const noteX = timeScale.tpTimeToPx(getStartTimestamp(note));
if (note.noteType === 'AREA') {
const noteArea = globals.state.areas[note.areaId];
return (noteX <= x && x < noteX + AREA_TRIANGLE_WIDTH) ||
- (timeScale.timeToPx(noteArea.endSec) > x &&
- x > timeScale.timeToPx(noteArea.endSec) - AREA_TRIANGLE_WIDTH);
+ (timeScale.tpTimeToPx(noteArea.end) > x &&
+ x > timeScale.tpTimeToPx(noteArea.end) - AREA_TRIANGLE_WIDTH);
} else {
const width = FLAG_WIDTH;
return noteX <= x && x < noteX + width;
@@ -308,13 +325,12 @@
if (note === undefined) {
return m('.', `No Note with id ${this.config.id}`);
}
- const startTime =
- getStartTimestamp(note) - globals.state.traceTime.startSec;
+ const startTime = getStartTimestamp(note) - globals.state.traceTime.start;
return m(
'.notes-editor-panel',
m('.notes-editor-panel-heading-bar',
m('.notes-editor-panel-heading',
- `Annotation at ${timeToString(startTime)}`),
+ `Annotation at ${tpTimeToString(startTime)}`),
m('input[type=text]', {
onkeydown: (e: Event) => {
e.stopImmediatePropagation();
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 76b4515..dc8a45d 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -14,9 +14,12 @@
import m from 'mithril';
-import {assertExists} from '../base/logging';
import {hueForCpu} from '../common/colorizer';
-import {TimeSpan} from '../common/time';
+import {
+ Span,
+ TPTime,
+ tpTimeToSeconds,
+} from '../common/time';
import {
OVERVIEW_TIMELINE_NON_VISIBLE_COLOR,
@@ -29,9 +32,9 @@
import {OuterDragStrategy} from './drag/outer_drag_strategy';
import {DragGestureHandler} from './drag_gesture_handler';
import {globals} from './globals';
-import {TickGenerator, TickType} from './gridline_helper';
+import {getMaxMajorTicks, TickGenerator, TickType} from './gridline_helper';
import {Panel, PanelSize} from './panel';
-import {TimeScale} from './time_scale';
+import {PxSpan, TimeScale} from './time_scale';
export class OverviewTimelinePanel extends Panel {
private static HANDLE_SIZE_PX = 5;
@@ -39,7 +42,7 @@
private width = 0;
private gesture?: DragGestureHandler;
private timeScale?: TimeScale;
- private totTime = new TimeSpan(0, 0);
+ private traceTime?: Span<TPTime>;
private dragStrategy?: DragStrategy;
private readonly boundOnMouseMove = this.onMouseMove.bind(this);
@@ -47,11 +50,10 @@
// https://github.com/Microsoft/TypeScript/issues/1373
onupdate({dom}: m.CVnodeDOM) {
this.width = dom.getBoundingClientRect().width;
- this.totTime = new TimeSpan(
- globals.state.traceTime.startSec, globals.state.traceTime.endSec);
- this.timeScale = new TimeScale(
- this.totTime, [TRACK_SHELL_WIDTH, assertExists(this.width)]);
-
+ this.traceTime = globals.stateTraceTimeTP();
+ const traceTime = globals.stateTraceTime();
+ const pxSpan = new PxSpan(TRACK_SHELL_WIDTH, this.width);
+ this.timeScale = TimeScale.fromHPTimeSpan(traceTime, pxSpan);
if (this.gesture === undefined) {
this.gesture = new DragGestureHandler(
dom as HTMLElement,
@@ -78,26 +80,27 @@
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
if (this.width === undefined) return;
+ if (this.traceTime === undefined) return;
if (this.timeScale === undefined) return;
const headerHeight = 20;
const tracksHeight = size.height - headerHeight;
- const timeSpan = new TimeSpan(0, this.totTime.duration);
- const timeScale = new TimeScale(timeSpan, [TRACK_SHELL_WIDTH, this.width]);
-
- if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
- const tickGen = new TickGenerator(timeScale);
+ if (size.width > TRACK_SHELL_WIDTH && this.traceTime.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(this.width - TRACK_SHELL_WIDTH);
+ const tickGen = new TickGenerator(
+ this.traceTime, maxMajorTicks, globals.state.traceTime.start);
// Draw time labels on the top header.
ctx.font = '10px Roboto Condensed';
ctx.fillStyle = '#999';
- for (const {type, time, position} of tickGen) {
- const xPos = Math.round(position);
+ for (const {type, time} of tickGen) {
+ const xPos = Math.floor(this.timeScale.tpTimeToPx(time));
if (xPos <= 0) continue;
if (xPos > this.width) break;
if (type === TickType.MAJOR) {
ctx.fillRect(xPos - 1, 0, 1, headerHeight - 5);
- ctx.fillText(time.toFixed(tickGen.digits) + ' s', xPos + 5, 18);
+ const sec = tpTimeToSeconds(time - globals.state.traceTime.start);
+ ctx.fillText(sec.toFixed(tickGen.digits) + ' s', xPos + 5, 18);
} else if (type == TickType.MEDIUM) {
ctx.fillRect(xPos - 1, 0, 1, 8);
} else if (type == TickType.MINOR) {
@@ -114,8 +117,8 @@
for (const key of globals.overviewStore.keys()) {
const loads = globals.overviewStore.get(key)!;
for (let i = 0; i < loads.length; i++) {
- const xStart = Math.floor(this.timeScale.timeToPx(loads[i].startSec));
- const xEnd = Math.ceil(this.timeScale.timeToPx(loads[i].endSec));
+ const xStart = Math.floor(this.timeScale.tpTimeToPx(loads[i].start));
+ const xEnd = Math.ceil(this.timeScale.tpTimeToPx(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}%)`;
@@ -210,10 +213,10 @@
}
private static extractBounds(timeScale: TimeScale): [number, number] {
- const vizTime = globals.frontendLocalState.getVisibleStateBounds();
+ const vizTime = globals.frontendLocalState.visibleWindowTime;
return [
- Math.floor(timeScale.timeToPx(vizTime[0])),
- Math.ceil(timeScale.timeToPx(vizTime[1])),
+ Math.floor(timeScale.hpTimeToPx(vizTime.start)),
+ Math.ceil(timeScale.hpTimeToPx(vizTime.end)),
];
}
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index 4c6576f..b7841d7 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -135,11 +135,13 @@
return;
}
+ const {visibleTimeScale} = globals.frontendLocalState;
+
// The Y value is given from the top of the pan and zoom region, we want it
// from the top of the panel container. The parent offset corrects that.
const panels = this.getPanelsInRegion(
- globals.frontendLocalState.timeScale.timeToPx(area.startSec),
- globals.frontendLocalState.timeScale.timeToPx(area.endSec),
+ visibleTimeScale.tpTimeToPx(area.start),
+ visibleTimeScale.tpTimeToPx(area.end),
globals.frontendLocalState.areaY.start + TOPBAR_HEIGHT,
globals.frontendLocalState.areaY.end + TOPBAR_HEIGHT);
// Get the track ids from the panels.
@@ -160,7 +162,7 @@
}
}
}
- globals.frontendLocalState.selectArea(area.startSec, area.endSec, tracks);
+ globals.frontendLocalState.selectArea(area.start, area.end, tracks);
}
constructor(vnode: m.CVnode<Attrs>) {
@@ -449,8 +451,9 @@
return;
}
- const startX = globals.frontendLocalState.timeScale.timeToPx(area.startSec);
- const endX = globals.frontendLocalState.timeScale.timeToPx(area.endSec);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const startX = visibleTimeScale.tpTimeToPx(area.start);
+ const endX = visibleTimeScale.tpTimeToPx(area.end);
// To align with where to draw on the canvas subtract the first panel Y.
selectedTracksMinY -= this.panelContainerTop;
selectedTracksMaxY -= this.panelContainerTop;
diff --git a/ui/src/frontend/pivot_table.ts b/ui/src/frontend/pivot_table.ts
index 530868f..bf56ad2 100644
--- a/ui/src/frontend/pivot_table.ts
+++ b/ui/src/frontend/pivot_table.ts
@@ -27,7 +27,7 @@
PivotTableResult,
SortDirection,
} from '../common/state';
-import {fromNs, timeToCode} from '../common/time';
+import {tpTimeToCode} from '../common/time';
import {globals} from './globals';
import {Panel} from './panel';
@@ -198,8 +198,8 @@
renderCell(column: TableColumn, value: ColumnType): string {
if (column.kind === 'regular' &&
(column.column === 'dur' || column.column === 'thread_dur')) {
- if (typeof value === 'number') {
- return timeToCode(fromNs(value));
+ if (typeof value === 'bigint') {
+ return tpTimeToCode(value);
}
}
return `${value}`;
diff --git a/ui/src/frontend/pivot_table_query_generator.ts b/ui/src/frontend/pivot_table_query_generator.ts
index 0c61f56..dffa6e4 100644
--- a/ui/src/frontend/pivot_table_query_generator.ts
+++ b/ui/src/frontend/pivot_table_query_generator.ts
@@ -20,7 +20,6 @@
PivotTableQuery,
PivotTableState,
} from '../common/state';
-import {toNs} from '../common/time';
import {
getSelectedTrackIds,
} from '../controller/aggregation/slice_aggregation_controller';
@@ -100,8 +99,8 @@
export function areaFilter(area: Area): string {
return `
- ts + dur > ${toNs(area.startSec)}
- and ts < ${toNs(area.endSec)}
+ ts + dur > ${area.start}
+ and ts < ${area.end}
and track_id in (${getSelectedTrackIds(area).join(', ')})
`;
}
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
index 7632e53..0cc5604 100644
--- a/ui/src/frontend/publish.ts
+++ b/ui/src/frontend/publish.ts
@@ -54,6 +54,11 @@
globals.rafScheduler.scheduleRedraw();
}
+export function clearOverviewData() {
+ globals.overviewStore.clear();
+ globals.rafScheduler.scheduleRedraw();
+}
+
export function publishTrackData(args: {id: string, data: {}}) {
globals.setTrackData(args.id, args.data);
if ([LogExistsKey, LogBoundsKey, LogEntriesKey].includes(args.id)) {
diff --git a/ui/src/frontend/query_table.ts b/ui/src/frontend/query_table.ts
index c27ecc2..be864fc 100644
--- a/ui/src/frontend/query_table.ts
+++ b/ui/src/frontend/query_table.ts
@@ -14,22 +14,19 @@
import m from 'mithril';
+import {BigintMath} from '../base/bigint_math';
import {Actions} from '../common/actions';
import {QueryResponse} from '../common/queries';
-import {ColumnType, Row} from '../common/query_result';
-import {fromNs} from '../common/time';
-import {Anchor} from './anchor';
+import {Row} from '../common/query_result';
+import {Anchor} from './anchor';
import {copyToClipboard, queryResponseToClipboard} from './clipboard';
import {downloadData} from './download_utils';
import {globals} from './globals';
import {Panel} from './panel';
import {Router} from './router';
-import {
- focusHorizontalRange,
- verticalScrollToTrack,
-} from './scroll_helper';
+import {reveal} from './scroll_helper';
import {Button} from './widgets/button';
interface QueryTableRowAttrs {
@@ -37,98 +34,126 @@
columns: string[];
}
-// Convert column value to number if it's a bigint or a number, otherwise throw
-function colToNumber(colValue: ColumnType): number {
- if (typeof colValue === 'bigint') {
- return Number(colValue);
- } else if (typeof colValue === 'number') {
- return colValue;
+type Numeric = bigint|number;
+
+function isIntegral(x: Row[string]): x is Numeric {
+ return typeof x === 'bigint' ||
+ (typeof x === 'number' && Number.isInteger(x));
+}
+
+function hasTs(row: Row): row is Row&{ts: Numeric} {
+ return ('ts' in row && isIntegral(row.ts));
+}
+
+function hasDur(row: Row): row is Row&{dur: Numeric} {
+ return ('dur' in row && isIntegral(row.dur));
+}
+
+function hasTrackId(row: Row): row is Row&{track_id: Numeric} {
+ return ('track_id' in row && isIntegral(row.track_id));
+}
+
+function hasType(row: Row): row is Row&{type: string} {
+ return ('type' in row && typeof row.type === 'string');
+}
+
+function hasId(row: Row): row is Row&{id: Numeric} {
+ return ('id' in row && isIntegral(row.id));
+}
+
+function hasSliceId(row: Row): row is Row&{slice_id: Numeric} {
+ return ('slice_id' in row && isIntegral(row.slice_id));
+}
+
+// These are properties that a row should have in order to be "slice-like",
+// insofar as it represents a time range and a track id which can be revealed
+// or zoomed-into on the timeline.
+type Sliceish = {
+ ts: Numeric,
+ dur: Numeric,
+ track_id: Numeric
+};
+
+export function isSliceish(row: Row): row is Row&Sliceish {
+ return hasTs(row) && hasDur(row) && hasTrackId(row);
+}
+
+// Attempts to extract a slice ID from a row, or undefined if none can be found
+export function getSliceId(row: Row): number|undefined {
+ if (hasType(row) && row.type.includes('slice')) {
+ if (hasId(row)) {
+ return Number(row.id);
+ }
} else {
- throw Error('Value is not a number or a bigint');
+ if (hasSliceId(row)) {
+ return Number(row.slice_id);
+ }
}
+ return undefined;
}
class QueryTableRow implements m.ClassComponent<QueryTableRowAttrs> {
- static columnsContainsSliceLocation(columns: string[]) {
- const requiredColumns = ['ts', 'dur', 'track_id'];
- for (const col of requiredColumns) {
- if (!columns.includes(col)) return false;
- }
- return true;
- }
-
- static rowOnClickHandler(
- event: Event, row: Row, nextTab: 'CurrentSelection'|'QueryResults') {
- // TODO(dproy): Make click handler work from analyze page.
- if (Router.parseUrl(window.location.href).page !== '/viewer') return;
- // If the click bubbles up to the pan and zoom handler that will deselect
- // the slice.
- event.stopPropagation();
-
- const sliceStart = fromNs(colToNumber(row.ts));
- // row.dur can be negative. Clamp to 1ns.
- const sliceDur = fromNs(Math.max(colToNumber(row.dur), 1));
- const sliceEnd = sliceStart + sliceDur;
- const trackId: number = colToNumber(row.track_id);
- const uiTrackId = globals.state.uiTrackIdByTraceTrackId[trackId];
- if (uiTrackId === undefined) return;
- verticalScrollToTrack(uiTrackId, true);
- // TODO(stevegolton) Soon this function will only accept Bigints
- focusHorizontalRange(sliceStart, sliceEnd);
-
- let sliceId: number|undefined;
- if (row.type?.toString().includes('slice')) {
- sliceId = colToNumber(row.id);
- } else {
- sliceId = colToNumber(row.slice_id);
- }
- if (sliceId !== undefined) {
- globals.makeSelection(
- Actions.selectChromeSlice(
- {id: sliceId, trackId: uiTrackId, table: 'slice'}),
- nextTab === 'QueryResults' ? globals.state.currentTab :
- 'current_selection');
- }
- }
-
view(vnode: m.Vnode<QueryTableRowAttrs>) {
- const cells = [];
const {row, columns} = vnode.attrs;
- for (const col of columns) {
- const value = row[col];
- if (value instanceof Uint8Array) {
- cells.push(
- m('td',
- m(Anchor,
- {
- onclick: () => downloadData(`${col}.blob`, value),
- },
- `Blob (${value.length} bytes)`)));
- } else if (typeof value === 'bigint') {
- cells.push(m('td', value.toString()));
- } else {
- cells.push(m('td', value));
+ const cells = columns.map((col) => this.renderCell(col, row[col]));
+
+ // TODO(dproy): Make click handler work from analyze page.
+ if (Router.parseUrl(window.location.href).page === '/viewer' &&
+ isSliceish(row)) {
+ return m(
+ 'tr',
+ {
+ onclick: () => this.highlightSlice(row, globals.state.currentTab),
+ // TODO(altimin): Consider improving the logic here (e.g. delay?) to
+ // account for cases when dblclick fires late.
+ ondblclick: () => this.highlightSlice(row),
+ clickable: true,
+ },
+ cells);
+ } else {
+ return m('tr', cells);
+ }
+ }
+
+ private renderCell(name: string, value: Row[string]) {
+ if (value instanceof Uint8Array) {
+ return m('td', this.renderBlob(name, value));
+ } else {
+ return m('td', `${value}`);
+ }
+ }
+
+ private renderBlob(name: string, value: Uint8Array) {
+ return m(
+ Anchor,
+ {
+ onclick: () => downloadData(`${name}.blob`, value),
+ },
+ `Blob (${value.length} bytes)`);
+ }
+
+ private highlightSlice(row: Row&Sliceish, nextTab?: string) {
+ const trackId = Number(row.track_id);
+ const sliceStart = BigInt(row.ts);
+ // row.dur can be negative. Clamp to 1ns.
+ const sliceDur = BigintMath.max(BigInt(row.dur), 1n);
+ const uiTrackId = globals.state.uiTrackIdByTraceTrackId[trackId];
+ if (uiTrackId !== undefined) {
+ reveal(uiTrackId, sliceStart, sliceStart + sliceDur, true);
+ const sliceId = getSliceId(row);
+ if (sliceId !== undefined) {
+ this.selectSlice(sliceId, uiTrackId, nextTab);
}
}
- const containsSliceLocation =
- QueryTableRow.columnsContainsSliceLocation(columns);
- const maybeOnClick = containsSliceLocation ?
- (e: Event) => QueryTableRow.rowOnClickHandler(e, row, 'QueryResults') :
- null;
- const maybeOnDblClick = containsSliceLocation ?
- (e: Event) =>
- QueryTableRow.rowOnClickHandler(e, row, 'CurrentSelection') :
- null;
- return m(
- 'tr',
- {
- 'onclick': maybeOnClick,
- // TODO(altimin): Consider improving the logic here (e.g. delay?) to
- // account for cases when dblclick fires late.
- 'ondblclick': maybeOnDblClick,
- 'clickable': containsSliceLocation,
- },
- cells);
+ }
+
+ private selectSlice(sliceId: number, uiTrackId: string, nextTab?: string) {
+ const action = Actions.selectChromeSlice({
+ id: sliceId,
+ trackId: uiTrackId,
+ table: 'slice',
+ });
+ globals.makeSelection(action, nextTab);
}
}
@@ -158,9 +183,7 @@
if (resp.error) {
return m('.query-error', `SQL error: ${resp.error}`);
} else {
- return m(
- '.query-table-container.x-scrollable',
- m('table.query-table', m('thead', tableHeader), m('tbody', rows)));
+ return m('table.query-table', m('thead', tableHeader), m('tbody', rows));
}
}
}
@@ -211,7 +234,7 @@
const headers = [m('header.overview', ...header)];
if (resp === undefined) {
- return m('div', ...headers);
+ return headers;
}
if (resp.statementWithOutputCount > 1) {
@@ -222,7 +245,7 @@
`statement are displayed in the table below.`));
}
- return m('div', ...headers, m(QueryTableContent, {resp}));
+ return [...headers, m(QueryTableContent, {resp})];
}
renderCanvas() {}
diff --git a/ui/src/frontend/query_table_unittest.ts b/ui/src/frontend/query_table_unittest.ts
new file mode 100644
index 0000000..fc351a6
--- /dev/null
+++ b/ui/src/frontend/query_table_unittest.ts
@@ -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.
+
+import {getSliceId, isSliceish} from './query_table';
+
+describe('getSliceId', () => {
+ test('get slice_id if present when no other clues are available', () => {
+ expect(getSliceId({})).toBe(undefined);
+ expect(getSliceId({id: 123})).toBe(undefined);
+ expect(getSliceId({slice_id: 456})).toBe(456);
+ expect(getSliceId({id: 123, slice_id: 456})).toBe(456);
+
+ expect(getSliceId({type: 'foo'})).toBe(undefined);
+ expect(getSliceId({type: 'foo', id: 123})).toBe(undefined);
+ expect(getSliceId({type: 'foo', slice_id: 456})).toBe(456);
+ expect(getSliceId({type: 'foo', id: 123, slice_id: 456})).toBe(456);
+ });
+
+ test('get id if present when row looks like a slice', () => {
+ expect(getSliceId({type: 'slice'})).toBe(undefined);
+ expect(getSliceId({type: 'slice', id: 123})).toBe(123);
+ expect(getSliceId({type: 'slice', slice_id: 456})).toBe(undefined);
+ expect(getSliceId({type: 'slice', id: 123, slice_id: 456})).toBe(123);
+ });
+});
+
+test('isSliceish', () => {
+ expect(isSliceish({})).toBeFalsy();
+ expect(isSliceish({ts: 123, dur: 456})).toBeFalsy();
+ expect(isSliceish({ts: 123, dur: 456, track_id: 798})).toBeTruthy();
+ expect(isSliceish({ts: 123n, dur: 456n})).toBeFalsy();
+ expect(isSliceish({ts: 123n, dur: 456n, track_id: 798n})).toBeTruthy();
+ expect(isSliceish({ts: 123.4, dur: 456.7, track_id: 798.9})).toBeFalsy();
+ expect(isSliceish({ts: '123', dur: '456', track_id: '789'})).toBeFalsy();
+});
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index a693b2e..1057d80 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -13,20 +13,24 @@
// limitations under the License.
import m from 'mithril';
+
import {assertExists, assertTrue} from '../base/logging';
+import {
+ oneOf,
+ optBool,
+ optStr,
+ record,
+ runValidator,
+ ValidatedType,
+} from '../controller/validators';
+
import {PageAttrs} from './pages';
export const ROUTE_PREFIX = '#!';
const DEFAULT_ROUTE = '/';
-// A broken down representation of a route.
-// For instance: #!/record/gpu?local_cache_key=a0b1
-// becomes: {page: '/record', subpage: '/gpu', args: {local_cache_key: 'a0b1'}}
-export interface Route {
- page: string;
- subpage: string;
- args: RouteArgs;
-}
+const modes = ['embedded', undefined] as const;
+type Mode = typeof modes[number];
// The set of args that can be set on the route via #!/page?a=1&b2.
// Route args are orthogonal to pages (i.e. should NOT make sense only in a
@@ -44,18 +48,46 @@
// This is client-only. All the routing logic in the Perfetto UI uses only
// this.
-// This must be a type literial to avoid having to duplicate the
-// index type logic of Params.
-export type RouteArgs = {
+const routeArgs = record({
// The local_cache_key is special and is persisted across navigations.
- local_cache_key?: string;
+ local_cache_key: optStr,
// These are transient and are really set only on startup.
- openFromAndroidBugTool?: string;
- s?: string; // For permalinks.
- p?: string; // DEPRECATED: for #!/record?p=cpu subpages (b/191255021).
- url?: string; // For fetching traces from Cloud Storage.
-};
+
+ // Are we loading a trace via ABT.
+ openFromAndroidBugTool: optBool,
+
+ // For permalink hash.
+ s: optStr,
+
+ // DEPRECATED: for #!/record?p=cpu subpages (b/191255021).
+ p: optStr,
+
+ // For fetching traces from Cloud Storage.
+ url: optStr,
+
+ // For the 'mode' of the UI. For example when the mode is 'embedded'
+ // some features are disabled.
+ mode: oneOf<Mode>(modes, undefined),
+
+ // Should we hide the sidebar?
+ hideSidebar: optBool,
+
+ // Deep link support
+ ts: optStr,
+ dur: optStr,
+ tid: optStr,
+});
+type RouteArgs = ValidatedType<typeof routeArgs>;
+
+// A broken down representation of a route.
+// For instance: #!/record/gpu?local_cache_key=a0b1
+// becomes: {page: '/record', subpage: '/gpu', args: {local_cache_key: 'a0b1'}}
+export interface Route {
+ page: string;
+ subpage: string;
+ args: RouteArgs;
+}
export interface RoutesMap {
[key: string]: m.Component<PageAttrs>;
@@ -168,16 +200,50 @@
const argsStart = hash.indexOf('?');
const argsStr = argsStart < 0 ? '' : hash.substring(argsStart + 1);
- const args = argsStr ? m.parseQueryString(hash.substring(argsStart)) : {};
+ const rawArgs =
+ argsStr ? m.parseQueryString(hash.substring(argsStart)) : {};
+
+ const args = runValidator(routeArgs, rawArgs).result;
+
+ // Javascript sadly distinguishes between foo[bar] === undefined
+ // and foo[bar] is not set at all. Here we need the second case to
+ // avoid making the URL ugly.
+ for (const key of Object.keys(args)) {
+ if ((args as any)[key] === undefined) {
+ delete (args as any)[key];
+ }
+ }
return {page, subpage, args};
}
+ private static parseSearchParams(url: string): RouteArgs {
+ const query = (new URL(url)).search;
+ const rawArgs = m.parseQueryString(query);
+ const args = runValidator(routeArgs, rawArgs).result;
+
+ // Javascript sadly distinguishes between foo[bar] === undefined
+ // and foo[bar] is not set at all. Here we need the second case to
+ // avoid making the URL ugly.
+ for (const key of Object.keys(args)) {
+ if ((args as any)[key] === undefined) {
+ delete (args as any)[key];
+ }
+ }
+
+ return args;
+ }
+
// Like parseFragment() but takes a full URL.
static parseUrl(url: string): Route {
+ const searchArgs = Router.parseSearchParams(url);
+
const hashPos = url.indexOf('#');
const fragment = hashPos < 0 ? '' : url.substring(hashPos);
- return Router.parseFragment(fragment);
+ const route = Router.parseFragment(fragment);
+ route.args = Object.assign({}, searchArgs, route.args);
+
+ return route;
}
// Throws if EVENT_LIMIT onhashchange events occur within WINDOW_MS.
diff --git a/ui/src/frontend/router_unittest.ts b/ui/src/frontend/router_unittest.ts
index 612347c..19dac36 100644
--- a/ui/src/frontend/router_unittest.ts
+++ b/ui/src/frontend/router_unittest.ts
@@ -18,89 +18,138 @@
view() {},
};
-beforeEach(() => {
- window.location.hash = '';
-});
-
-test('Default route must be defined', () => {
- expect(() => new Router({'/a': mockComponent})).toThrow();
-});
-
-test('Resolves empty route to default component', () => {
- const router = new Router({'/': mockComponent});
- window.location.hash = '';
- expect(router.resolve().tag).toBe(mockComponent);
-});
-
-test('Resolves subpage route to component of main page', () => {
- const nonDefaultComponent = {view() {}};
- const router = new Router({
- '/': mockComponent,
- '/a': nonDefaultComponent,
+describe('Router#resolve', () => {
+ beforeEach(() => {
+ window.location.hash = '';
});
- window.location.hash = '#!/a/subpage';
- expect(router.resolve().tag).toBe(nonDefaultComponent);
- expect(router.resolve().attrs.subpage).toBe('/subpage');
-});
-test('Pass empty subpage if not found in URL', () => {
- const nonDefaultComponent = {view() {}};
- const router = new Router({
- '/': mockComponent,
- '/a': nonDefaultComponent,
+ test('Default route must be defined', () => {
+ expect(() => new Router({'/a': mockComponent})).toThrow();
});
- window.location.hash = '#!/a';
- expect(router.resolve().tag).toBe(nonDefaultComponent);
- expect(router.resolve().attrs.subpage).toBe('');
+
+ test('Resolves empty route to default component', () => {
+ const router = new Router({'/': mockComponent});
+ window.location.hash = '';
+ expect(router.resolve().tag).toBe(mockComponent);
+ });
+
+ test('Resolves subpage route to component of main page', () => {
+ const nonDefaultComponent = {view() {}};
+ const router = new Router({
+ '/': mockComponent,
+ '/a': nonDefaultComponent,
+ });
+ window.location.hash = '#!/a/subpage';
+ expect(router.resolve().tag).toBe(nonDefaultComponent);
+ expect(router.resolve().attrs.subpage).toBe('/subpage');
+ });
+
+ test('Pass empty subpage if not found in URL', () => {
+ const nonDefaultComponent = {view() {}};
+ const router = new Router({
+ '/': mockComponent,
+ '/a': nonDefaultComponent,
+ });
+ window.location.hash = '#!/a';
+ expect(router.resolve().tag).toBe(nonDefaultComponent);
+ expect(router.resolve().attrs.subpage).toBe('');
+ });
});
-test('Args parsing', () => {
- const url = 'http://localhost/#!/foo?p=123&s=42&url=a?b?c';
- const args = Router.parseUrl(url).args;
- expect(args.p).toBe('123');
- expect(args.s).toBe('42');
- expect(args.url).toBe('a?b?c');
+describe('Router.parseUrl', () => {
+ // Can parse arguments from the search string.
+ test('Search parsing', () => {
+ const url = 'http://localhost?p=123&s=42&url=a?b?c';
+ const args = Router.parseUrl(url).args;
+ expect(args.p).toBe('123');
+ expect(args.s).toBe('42');
+ expect(args.url).toBe('a?b?c');
+ });
+
+ // Or from the fragment string.
+ test('Fragment parsing', () => {
+ const url = 'http://localhost/#!/foo?p=123&s=42&url=a?b?c';
+ const args = Router.parseUrl(url).args;
+ expect(args.p).toBe('123');
+ expect(args.s).toBe('42');
+ expect(args.url).toBe('a?b?c');
+ });
+
+ // Or both in which case fragment overrides the search.
+ test('Fragment parsing', () => {
+ const url =
+ 'http://localhost/?p=1&s=2&hideSidebar=true#!/foo?s=3&url=4&hideSidebar=false';
+ const args = Router.parseUrl(url).args;
+ expect(args.p).toBe('1');
+ expect(args.s).toBe('3');
+ expect(args.url).toBe('4');
+ expect(args.hideSidebar).toBe(false);
+ });
});
-test('empty route broken into empty components', () => {
- const {page, subpage, args} = Router.parseFragment('');
- expect(page).toBe('');
- expect(subpage).toBe('');
- expect(args).toEqual({});
-});
+describe('Router.parseFragment', () => {
+ test('empty route broken into empty components', () => {
+ const {page, subpage, args} = Router.parseFragment('');
+ expect(page).toBe('');
+ expect(subpage).toBe('');
+ expect(args.mode).toBe(undefined);
+ });
-test('invalid route broken into empty components', () => {
- const {page, subpage, args} = Router.parseFragment('/bla');
- expect(page).toBe('');
- expect(subpage).toBe('');
- expect(args).toEqual({});
-});
+ test('by default args are undefined', () => {
+ // This prevents the url from becoming messy.
+ const {args} = Router.parseFragment('');
+ expect(args).toEqual({});
+ });
-test('simple route has page defined', () => {
- const {page, subpage, args} = Router.parseFragment('#!/record');
- expect(page).toBe('/record');
- expect(subpage).toBe('');
- expect(args).toEqual({});
-});
+ test('invalid route broken into empty components', () => {
+ const {page, subpage} = Router.parseFragment('/bla');
+ expect(page).toBe('');
+ expect(subpage).toBe('');
+ });
-test('simple route has both components defined', () => {
- const {page, subpage, args} = Router.parseFragment('#!/record/memory');
- expect(page).toBe('/record');
- expect(subpage).toBe('/memory');
- expect(args).toEqual({});
-});
+ test('simple route has page defined', () => {
+ const {page, subpage} = Router.parseFragment('#!/record');
+ expect(page).toBe('/record');
+ expect(subpage).toBe('');
+ });
-test('route broken at first slash', () => {
- const {page, subpage, args} = Router.parseFragment('#!/record/memory/stuff');
- expect(page).toBe('/record');
- expect(subpage).toBe('/memory/stuff');
- expect(args).toEqual({});
-});
+ test('simple route has both components defined', () => {
+ const {page, subpage} = Router.parseFragment('#!/record/memory');
+ expect(page).toBe('/record');
+ expect(subpage).toBe('/memory');
+ });
-test('parameters separated from route', () => {
- const {page, subpage, args} =
- Router.parseFragment('#!/record/memory?url=http://localhost:1234/aaaa');
- expect(page).toBe('/record');
- expect(subpage).toBe('/memory');
- expect(args).toEqual({url: 'http://localhost:1234/aaaa'});
+ test('route broken at first slash', () => {
+ const {page, subpage} = Router.parseFragment('#!/record/memory/stuff');
+ expect(page).toBe('/record');
+ expect(subpage).toBe('/memory/stuff');
+ });
+
+ test('parameters separated from route', () => {
+ const {page, subpage, args} =
+ Router.parseFragment('#!/record/memory?url=http://localhost:1234/aaaa');
+ expect(page).toBe('/record');
+ expect(subpage).toBe('/memory');
+ expect(args.url).toEqual('http://localhost:1234/aaaa');
+ });
+
+ test('openFromAndroidBugTool can be false', () => {
+ const {args} = Router.parseFragment('#!/?openFromAndroidBugTool=false');
+ expect(args.openFromAndroidBugTool).toEqual(false);
+ });
+
+ test('openFromAndroidBugTool can be true', () => {
+ const {args} = Router.parseFragment('#!/?openFromAndroidBugTool=true');
+ expect(args.openFromAndroidBugTool).toEqual(true);
+ });
+
+ test('bad modes are coerced to default', () => {
+ const {args} = Router.parseFragment('#!/?mode=1234');
+ expect(args.mode).toEqual(undefined);
+ });
+
+ test('bad hideSidebar is coerced to default', () => {
+ const {args} = Router.parseFragment('#!/?hideSidebar=helloworld!');
+ expect(args.hideSidebar).toEqual(undefined);
+ });
});
diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts
index 18a9a78..387913f 100644
--- a/ui/src/frontend/scroll_helper.ts
+++ b/ui/src/frontend/scroll_helper.ts
@@ -13,23 +13,28 @@
// limitations under the License.
import {Actions} from '../common/actions';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
import {getContainingTrackId} from '../common/state';
-import {fromNs, TimeSpan, toNs} from '../common/time';
+import {TPTime} from '../common/time';
import {globals} from './globals';
-const INCOMPLETE_SLICE_TIME_S = 0.00003;
// Given a timestamp, if |ts| is not currently in view move the view to
// center |ts|, keeping the same zoom level.
-export function horizontalScrollToTs(ts: number) {
- const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start);
- const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
- const currentViewNs = endNs - startNs;
- if (ts < startNs || ts > endNs) {
+export function horizontalScrollToTs(ts: TPTime) {
+ const time = HighPrecisionTime.fromTPTime(ts);
+ const visibleWindow = globals.frontendLocalState.visibleWindowTime;
+ if (!visibleWindow.contains(time)) {
// TODO(hjd): This is an ugly jump, we should do a smooth pan instead.
- globals.frontendLocalState.updateVisibleTime(new TimeSpan(
- fromNs(ts - currentViewNs / 2), fromNs(ts + currentViewNs / 2)));
+ const halfDuration = visibleWindow.duration.divide(2);
+ const newStart = time.sub(halfDuration);
+ const newWindow = new HighPrecisionTimeSpan(
+ newStart, newStart.add(visibleWindow.duration));
+ globals.frontendLocalState.updateVisibleTime(newWindow);
}
}
@@ -46,16 +51,11 @@
// to cover 1/5 of the viewport.
// - Otherwise, preserve the zoom range.
export function focusHorizontalRange(
- startTs: number, endTs: number, viewPercentage?: number) {
- const visibleDur = globals.frontendLocalState.visibleWindowTime.end -
- globals.frontendLocalState.visibleWindowTime.start;
- let selectDur = endTs - startTs;
- // TODO(altimin): We go from `ts` and `dur` to `startTs` and `endTs` and back
- // to `dur`. We should fix that.
- if (toNs(selectDur) === -1) { // Unfinished slice
- selectDur = INCOMPLETE_SLICE_TIME_S;
- endTs = startTs;
- }
+ start: TPTime, end: TPTime, viewPercentage?: number) {
+ console.log('focusHorizontalRange', start, end);
+ const visible = globals.frontendLocalState.visibleWindowTime;
+ const trace = globals.stateTraceTime();
+ const select = HighPrecisionTimeSpan.fromTpTime(start, end);
if (viewPercentage !== undefined) {
if (viewPercentage <= 0.0 || viewPercentage > 1.0) {
@@ -67,51 +67,43 @@
viewPercentage = 0.5;
}
const paddingPercentage = 1.0 - viewPercentage;
- const paddingTime = selectDur * paddingPercentage;
- const halfPaddingTime = paddingTime / 2;
- globals.frontendLocalState.updateVisibleTime(
- new TimeSpan(startTs - halfPaddingTime, endTs + halfPaddingTime));
+ const paddingTime = select.duration.multiply(paddingPercentage);
+ const halfPaddingTime = paddingTime.divide(2);
+ globals.frontendLocalState.updateVisibleTime(select.pad(halfPaddingTime));
return;
}
-
// If the range is too large to fit on the current zoom level, resize.
- if (selectDur > 0.5 * visibleDur) {
- globals.frontendLocalState.updateVisibleTime(
- new TimeSpan(startTs - (selectDur * 2), endTs + (selectDur * 2)));
+ if (select.duration.gt(visible.duration.multiply(0.5))) {
+ const paddedRange = select.pad(select.duration.multiply(2));
+ globals.frontendLocalState.updateVisibleTime(paddedRange);
return;
}
- const midpointTs = (endTs + startTs) / 2;
// Calculate the new visible window preserving the zoom level.
- let newStartTs = midpointTs - visibleDur / 2;
- let newEndTs = midpointTs + visibleDur / 2;
+ let newStart = select.midpoint.sub(visible.duration.divide(2));
+ let newEnd = select.midpoint.add(visible.duration.divide(2));
// Adjust the new visible window if it intersects with the trace boundaries.
// It's needed to make the "update the zoom level if visible window doesn't
// change" logic reliable.
- if (newEndTs > globals.state.traceTime.endSec) {
- newStartTs = globals.state.traceTime.endSec - visibleDur;
- newEndTs = globals.state.traceTime.endSec;
+ if (newEnd.gt(trace.end)) {
+ newStart = trace.end.sub(visible.duration);
+ newEnd = trace.end;
}
- if (newStartTs < globals.state.traceTime.startSec) {
- newStartTs = globals.state.traceTime.startSec;
- newEndTs = globals.state.traceTime.startSec + visibleDur;
+ if (newStart.lt(trace.start)) {
+ newStart = trace.start;
+ newEnd = trace.start.add(visible.duration);
}
- const newStartNs = toNs(newStartTs);
- const newEndNs = toNs(newEndTs);
-
- const viewStartNs = toNs(globals.frontendLocalState.visibleWindowTime.start);
- const viewEndNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
+ const view = new HighPrecisionTimeSpan(newStart, newEnd);
// If preserving the zoom doesn't change the visible window, update the zoom
// level.
- if (newStartNs === viewStartNs && newEndNs === viewEndNs) {
- globals.frontendLocalState.updateVisibleTime(
- new TimeSpan(startTs - (selectDur * 2), endTs + (selectDur * 2)));
- return;
+ if (view.start.eq(visible.start) && view.end.eq(visible.end)) {
+ const padded = select.pad(select.duration.multiply(2));
+ globals.frontendLocalState.updateVisibleTime(padded);
+ } else {
+ globals.frontendLocalState.updateVisibleTime(view);
}
- globals.frontendLocalState.updateVisibleTime(
- new TimeSpan(newStartTs, newEndTs));
}
// Given a track id, find a track with that id and scroll it into view. If the
@@ -155,9 +147,16 @@
// Scroll vertically and horizontally to reach track (|trackId|) at |ts|.
export function scrollToTrackAndTs(
- trackId: string|number|undefined, ts: number, openGroup = false) {
+ trackId: string|number|undefined, ts: TPTime, openGroup = false) {
if (trackId !== undefined) {
verticalScrollToTrack(trackId, openGroup);
}
horizontalScrollToTs(ts);
}
+
+// Scroll vertically and horizontally to a track and time range
+export function reveal(
+ trackId: string|number, start: TPTime, end: TPTime, openGroup = false) {
+ verticalScrollToTrack(trackId, openGroup);
+ focusHorizontalRange(start, end);
+}
diff --git a/ui/src/frontend/search_handler.ts b/ui/src/frontend/search_handler.ts
index 1622a7e..190dab9 100644
--- a/ui/src/frontend/search_handler.ts
+++ b/ui/src/frontend/search_handler.ts
@@ -14,8 +14,6 @@
import {searchSegment} from '../base/binary_search';
import {Actions} from '../common/actions';
-import {toNs} from '../common/time';
-
import {globals} from './globals';
function setToPrevious(current: number) {
@@ -34,8 +32,9 @@
export function executeSearch(reverse = false) {
const index = globals.state.searchIndex;
- const startNs = toNs(globals.frontendLocalState.visibleWindowTime.start);
- const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
+ const vizWindow = globals.stateTraceTimeTP();
+ const startNs = vizWindow.start;
+ const endNs = vizWindow.end;
const currentTs = globals.currentSearchResults.tsStarts[index];
// If the value of |globals.currentSearchResults.totalResults| is 0,
diff --git a/ui/src/frontend/slice.ts b/ui/src/frontend/slice.ts
index 5b660ef..7587a27 100644
--- a/ui/src/frontend/slice.ts
+++ b/ui/src/frontend/slice.ts
@@ -13,13 +13,14 @@
// limitations under the License.
import {Color} from '../common/colorizer';
+import {TPDuration, TPTime} from '../common/time';
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 startS: number;
- readonly durationS: number;
+ readonly start: TPTime;
+ readonly duration: TPDuration;
readonly depth: number;
readonly flags: number;
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index 9b018dd..13e3dd5 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -16,7 +16,7 @@
import {Actions} from '../common/actions';
import {translateState} from '../common/thread_state';
-import {timeToCode, toNs} from '../common/time';
+import {tpTimeToCode} from '../common/time';
import {globals, SliceDetails, ThreadDesc} from './globals';
import {scrollToTrackAndTs} from './scroll_helper';
import {SlicePanel} from './slice_panel';
@@ -60,9 +60,8 @@
if (!threadInfo) {
return null;
}
- const timestamp = timeToCode(
- sliceInfo.wakeupTs! - globals.state.traceTime.startSec,
- );
+ const timestamp =
+ tpTimeToCode(sliceInfo.wakeupTs! - globals.state.traceTime.start);
return m(
'.slice-details-wakeup-text',
m('', `Wakeup @ ${timestamp} on CPU ${sliceInfo.wakerCpu} by`),
@@ -76,9 +75,7 @@
return null;
}
- const latency = timeToCode(
- sliceInfo.ts - (sliceInfo.wakeupTs - globals.state.traceTime.startSec),
- );
+ const latency = tpTimeToCode(sliceInfo.ts - sliceInfo.wakeupTs);
return m(
'.slice-details-latency-text',
m('', `Scheduling latency: ${latency}`),
@@ -111,7 +108,10 @@
{onclick: () => this.goToThread(), title: 'Go to thread'},
'call_made'))),
m('tr', m('th', `Cmdline`), m('td', threadInfo.cmdline)),
- m('tr', m('th', `Start time`), m('td', `${timeToCode(sliceInfo.ts)}`)),
+ m('tr',
+ m('th', `Start time`),
+ m('td',
+ `${tpTimeToCode(sliceInfo.ts - globals.state.traceTime.start)}`)),
m('tr',
m('th', `Duration`),
m('td', this.computeDuration(sliceInfo.ts, sliceInfo.dur))),
@@ -172,8 +172,7 @@
trackId: trackId.toString(),
}));
- scrollToTrackAndTs(
- trackId, toNs(sliceInfo.ts + globals.state.traceTime.startSec), true);
+ scrollToTrackAndTs(trackId, sliceInfo.ts, true);
}
}
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 17b4aeb..9d6f542 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {timeToCode, toNs} from '../common/time';
+import {TPDuration, TPTime, tpTimeToCode} from '../common/time';
import {globals, SliceDetails} from './globals';
import {Panel} from './panel';
@@ -34,10 +34,9 @@
}
export abstract class SlicePanel extends Panel {
- protected computeDuration(ts: number, dur: number): string {
- return toNs(dur) === -1 ?
- `${globals.state.traceTime.endSec - ts} (Did not end)` :
- timeToCode(dur);
+ protected computeDuration(ts: TPTime, dur: TPDuration): string {
+ return dur === -1n ? `${globals.state.traceTime.end - ts} (Did not end)` :
+ tpTimeToCode(dur);
}
protected getProcessThreadDetails(sliceInfo: SliceDetails) {
diff --git a/ui/src/frontend/sql_types.ts b/ui/src/frontend/sql_types.ts
index f7135fa..b7e2df2 100644
--- a/ui/src/frontend/sql_types.ts
+++ b/ui/src/frontend/sql_types.ts
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {ColumnType} from 'src/common/query_result';
-import {fromNs, toNs} from '../common/time';
+import {TPTime} from '../common/time';
+
import {globals} from './globals';
// Type-safe aliases for various flavours of ints Trace Processor exposes
@@ -26,37 +26,19 @@
// Timestamp (in nanoseconds) in the same time domain as Trace Processor is
// exposing.
-export type TPTimestamp = bigint&{
+export type TPTimestamp = TPTime&{
__type: 'TPTimestamp'
}
-// Create a timestamp from a bigint in nanos.
-// Use this when we know the type is a bigint.
-export function timestampFromNanos(nanos: bigint) {
- return nanos as TPTimestamp;
-}
-
-// Create a timestamp from an arbitrary SQL value.
-// Throws if the value cannot be reasonably converted to a timestamp.
-// Assumes the input will be in units of nanoseconds.
-export function timestampFromSqlNanos(nanos: ColumnType): TPTimestamp {
- if (typeof nanos === 'bigint') {
- return nanos as TPTimestamp;
- } else if (typeof nanos === 'number') {
- // Note - this will throw if the number is something which cannot be
- // represented by an integer - i.e. decimals, infinity, or NaN.
- return BigInt(nanos) as TPTimestamp;
- } else {
- throw Error('Refusing to create TPTimestamp from unrelated type');
- }
+export function asTPTimestamp(v: bigint): TPTimestamp;
+export function asTPTimestamp(v?: bigint): TPTimestamp|undefined;
+export function asTPTimestamp(v?: bigint): TPTimestamp|undefined {
+ return v as (TPTimestamp | undefined);
}
// TODO: unify this with common/time.ts.
-// TODO(stevegolton): Return a bigint, or a new TPDuration object rather than
-// convert to number which could lose precision.
-export function toTraceTime(ts: TPTimestamp): number {
- const traceStartNs = toNs(globals.state.traceTime.startSec);
- return fromNs(Number(ts - BigInt(traceStartNs)));
+export function toTraceTime(ts: TPTimestamp): TPTime {
+ return ts - globals.state.traceTime.start;
}
// Unique id for a process, id into |process| table.
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index c7031f2..0bc4082 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -16,7 +16,11 @@
import {EngineProxy} from '../common/engine';
import {LONG, NUM, NUM_NULL, STR_NULL} from '../common/query_result';
import {translateState} from '../common/thread_state';
-import {fromNs, timeToCode} from '../common/time';
+import {
+ TPDuration,
+ TPTime,
+ tpTimeToCode,
+} from '../common/time';
import {copyToClipboard} from './clipboard';
import {globals} from './globals';
@@ -26,9 +30,6 @@
asUtid,
SchedSqlId,
ThreadStateSqlId,
- timestampFromNanos,
- toTraceTime,
- TPTimestamp,
} from './sql_types';
import {
constraintsToQueryFragment,
@@ -50,10 +51,10 @@
threadStateSqlId: ThreadStateSqlId;
// Id of the corresponding entry in the |sched| table.
schedSqlId?: SchedSqlId;
- // Timestamp of the beginning of this thread state in nanoseconds.
- ts: TPTimestamp;
+ // Timestamp of the the beginning of this thread state in nanoseconds.
+ ts: TPTime;
// Duration of this thread state in nanoseconds.
- dur: number;
+ dur: TPDuration;
// CPU id if this thread state corresponds to a thread running on the CPU.
cpu?: number;
// Human-readable name of this thread state.
@@ -90,7 +91,7 @@
threadStateSqlId: NUM,
schedSqlId: NUM_NULL,
ts: LONG,
- dur: NUM,
+ dur: LONG,
cpu: NUM_NULL,
state: STR_NULL,
blockedFunction: STR_NULL,
@@ -110,7 +111,7 @@
result.push({
threadStateSqlId: it.threadStateSqlId as ThreadStateSqlId,
schedSqlId: fromNumNull(it.schedSqlId) as (SchedSqlId | undefined),
- ts: timestampFromNanos(it.ts),
+ ts: it.ts,
dur: it.dur,
cpu: fromNumNull(it.cpu),
state: translateState(it.state || undefined, ioWait),
@@ -137,7 +138,7 @@
return result[0];
}
-export function goToSchedSlice(cpu: number, id: SchedSqlId, ts: TPTimestamp) {
+export function goToSchedSlice(cpu: number, id: SchedSqlId, ts: TPTime) {
let trackId: string|undefined;
for (const track of Object.values(globals.state.tracks)) {
if (track.kind === 'CpuSliceTrack' &&
@@ -149,15 +150,12 @@
return;
}
globals.makeSelection(Actions.selectSlice({id, trackId}));
- // TODO(stevegolton): scrollToTrackAndTs() should take a TPTimestamp
- scrollToTrackAndTs(trackId, Number(ts));
+ scrollToTrackAndTs(trackId, ts);
}
function stateToValue(
- state: string,
- cpu: number|undefined,
- id: SchedSqlId|undefined,
- ts: TPTimestamp): Value|null {
+ state: string, cpu: number|undefined, id: SchedSqlId|undefined, ts: TPTime):
+ Value|null {
if (!state) {
return null;
}
@@ -177,8 +175,9 @@
export function threadStateToDict(state: ThreadState): Dict {
const result: {[name: string]: Value|null} = {};
- result['Start time'] = value(timeToCode(toTraceTime(state.ts)));
- result['Duration'] = value(timeToCode(fromNs(state.dur)));
+ result['Start time'] =
+ value(tpTimeToCode(state.ts - globals.state.traceTime.start));
+ result['Duration'] = value(tpTimeToCode(state.dur));
result['State'] =
stateToValue(state.state, state.cpu, state.schedSqlId, state.ts);
result['Blocked function'] = maybeValue(state.blockedFunction);
diff --git a/ui/src/frontend/tickmark_panel.ts b/ui/src/frontend/tickmark_panel.ts
index 00612eb..6076188 100644
--- a/ui/src/frontend/tickmark_panel.ts
+++ b/ui/src/frontend/tickmark_panel.ts
@@ -14,11 +14,12 @@
import m from 'mithril';
-import {fromNs} from '../common/time';
+import {TPTimeSpan} from '../common/time';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {globals} from './globals';
import {
+ getMaxMajorTicks,
TickGenerator,
TickType,
timeScaleForVisibleWindow,
@@ -32,14 +33,26 @@
}
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
ctx.fillStyle = '#999';
ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
- const relScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
- if (relScale.timeSpan.duration > 0 && relScale.widthPx > 0) {
- for (const {type, position} of new TickGenerator(relScale)) {
- if (type === TickType.MAJOR) ctx.fillRect(position, 0, 1, size.height);
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
+ ctx.clip();
+
+ const visibleSpan = globals.frontendLocalState.visibleWindow.timestampSpan;
+ if (size.width > TRACK_SHELL_WIDTH && visibleSpan.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
+ const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+ for (const {type, time} of new TickGenerator(
+ visibleSpan, maxMajorTicks, globals.state.traceTime.start)) {
+ const px = Math.floor(map.tpTimeToPx(time));
+ if (type === TickType.MAJOR) {
+ ctx.fillRect(px, 0, 1, size.height);
+ }
}
}
@@ -47,12 +60,13 @@
for (let i = 0; i < data.tsStarts.length; i++) {
const tStart = data.tsStarts[i];
const tEnd = data.tsEnds[i];
- if (tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end) {
+ const segmentSpan = new TPTimeSpan(tStart, tEnd);
+ if (!visibleSpan.intersects(segmentSpan)) {
continue;
}
const rectStart =
- Math.max(timeScale.timeToPx(tStart), 0) + TRACK_SHELL_WIDTH;
- const rectEnd = timeScale.timeToPx(tEnd) + TRACK_SHELL_WIDTH;
+ Math.max(visibleTimeScale.tpTimeToPx(tStart), 0) + TRACK_SHELL_WIDTH;
+ const rectEnd = visibleTimeScale.tpTimeToPx(tEnd) + TRACK_SHELL_WIDTH;
ctx.fillStyle = '#ffe263';
ctx.fillRect(
Math.floor(rectStart),
@@ -61,16 +75,20 @@
size.height);
}
const index = globals.state.searchIndex;
- const startSec = fromNs(globals.currentSearchResults.tsStarts[index]);
- const triangleStart =
- Math.max(timeScale.timeToPx(startSec), 0) + TRACK_SHELL_WIDTH;
- ctx.fillStyle = '#000';
- ctx.beginPath();
- ctx.moveTo(triangleStart, size.height);
- ctx.lineTo(triangleStart - 3, 0);
- ctx.lineTo(triangleStart + 3, 0);
- ctx.lineTo(triangleStart, size.height);
- ctx.fill();
- ctx.closePath();
+ if (index !== -1 && index <= globals.currentSearchResults.tsStarts.length) {
+ const start = globals.currentSearchResults.tsStarts[index];
+ const triangleStart =
+ Math.max(visibleTimeScale.tpTimeToPx(start), 0) + TRACK_SHELL_WIDTH;
+ ctx.fillStyle = '#000';
+ ctx.beginPath();
+ ctx.moveTo(triangleStart, size.height);
+ ctx.lineTo(triangleStart - 3, 0);
+ ctx.lineTo(triangleStart + 3, 0);
+ ctx.lineTo(triangleStart, size.height);
+ ctx.fill();
+ ctx.closePath();
+ }
+
+ ctx.restore();
}
}
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index 3f6dc64..4819d54 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -14,11 +14,15 @@
import m from 'mithril';
-import {timeToString} from '../common/time';
+import {
+ tpTimeToSeconds,
+ tpTimeToString,
+} from '../common/time';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {globals} from './globals';
import {
+ getMaxMajorTicks,
TickGenerator,
TickType,
timeScaleForVisibleWindow,
@@ -35,21 +39,33 @@
ctx.font = '10px Roboto Condensed';
ctx.textAlign = 'left';
- const startTime = timeToString(globals.state.traceTime.startSec);
+ const startTime = tpTimeToString(globals.state.traceTime.start);
ctx.fillText(startTime + ' +', 6, 11);
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
+ ctx.clip();
+
// Draw time axis.
- const timeScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
- if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
- const tickGen = new TickGenerator(timeScale);
- for (const {type, time, position} of tickGen) {
+ const span = globals.frontendLocalState.visibleWindow.timestampSpan;
+ if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
+ const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+ const tickGen =
+ new TickGenerator(span, maxMajorTicks, globals.state.traceTime.start);
+ for (const {type, time} of tickGen) {
+ const position = Math.floor(map.tpTimeToPx(time));
+ const sec = tpTimeToSeconds(time - globals.state.traceTime.start);
if (type === TickType.MAJOR) {
ctx.fillRect(position, 0, 1, size.height);
- ctx.fillText(time.toFixed(tickGen.digits) + ' s', position + 5, 10);
+ ctx.fillText(sec.toFixed(tickGen.digits) + ' s', position + 5, 10);
}
}
}
+ ctx.restore();
+
ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
}
}
diff --git a/ui/src/frontend/time_scale.ts b/ui/src/frontend/time_scale.ts
index 6f0307a..6d31a99 100644
--- a/ui/src/frontend/time_scale.ts
+++ b/ui/src/frontend/time_scale.ts
@@ -12,95 +12,93 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {assertFalse, assertTrue} from '../base/logging';
-import {TimeSpan} from '../common/time';
+import {assertTrue} from '../base/logging';
+import {
+ HighPrecisionTime,
+ HighPrecisionTimeSpan,
+} from '../common/high_precision_time';
+import {Span} from '../common/time';
+import {
+ TPDuration,
+ TPTime,
+} from '../common/time';
-const MAX_ZOOM_SPAN_SEC = 1e-8; // 10 ns.
-
-/**
- * Defines a mapping between number and seconds for the entire application.
- * Linearly scales time values from boundsMs to pixel values in boundsPx and
- * back.
- */
export class TimeScale {
- private timeBounds: TimeSpan;
- private _startPx: number;
- private _endPx: number;
- private secPerPx = 0;
+ private _start: HighPrecisionTime;
+ private _durationNanos: number;
+ readonly pxSpan: PxSpan;
+ private _nanosPerPx = 0;
+ private _startSec: number;
- constructor(timeBounds: TimeSpan, boundsPx: [number, number]) {
- this.timeBounds = timeBounds;
- this._startPx = boundsPx[0];
- this._endPx = boundsPx[1];
- this.updateSlope();
+ static fromHPTimeSpan(span: Span<HighPrecisionTime>, pxSpan: PxSpan) {
+ return new TimeScale(span.start, span.duration.nanos, pxSpan);
}
- private updateSlope() {
- this.secPerPx = this.timeBounds.duration / (this._endPx - this._startPx);
+ constructor(start: HighPrecisionTime, durationNanos: number, pxSpan: PxSpan) {
+ this.pxSpan = pxSpan;
+ this._start = start;
+ this._durationNanos = durationNanos;
+ if (durationNanos <= 0 || pxSpan.delta <= 0) {
+ this._nanosPerPx = 1;
+ } else {
+ this._nanosPerPx = durationNanos / (pxSpan.delta);
+ }
+ this._startSec = this._start.seconds;
}
- deltaTimeToPx(time: number): number {
- return Math.round(time / this.secPerPx);
+ get timeSpan(): Span<HighPrecisionTime> {
+ const end = this._start.addNanos(this._durationNanos);
+ return new HighPrecisionTimeSpan(this._start, end);
}
- timeToPx(time: number): number {
- return this._startPx + (time - this.timeBounds.start) / this.secPerPx;
+ tpTimeToPx(ts: TPTime): number {
+ // WARNING: Number(bigint) can be surprisingly slow. Avoid in hotpath.
+ const timeOffsetNanos = Number(ts - this._start.base) - this._start.offset;
+ return this.pxSpan.start + timeOffsetNanos / this._nanosPerPx;
}
- pxToTime(px: number): number {
- return this.timeBounds.start + (px - this._startPx) * this.secPerPx;
+ secondsToPx(seconds: number): number {
+ const timeOffset = (seconds - this._startSec) * 1e9;
+ return this.pxSpan.start + timeOffset / this._nanosPerPx;
}
- deltaPxToDuration(px: number): number {
- return px * this.secPerPx;
+ hpTimeToPx(time: HighPrecisionTime): number {
+ const timeOffsetNanos = time.sub(this._start).nanos;
+ return this.pxSpan.start + timeOffsetNanos / this._nanosPerPx;
}
- setTimeBounds(timeBounds: TimeSpan) {
- this.timeBounds = timeBounds;
- this.updateSlope();
+ // Convert pixels to a high precision time object, which can be futher
+ // converted to other time formats.
+ pxToHpTime(px: number): HighPrecisionTime {
+ const offsetNanos = (px - this.pxSpan.start) * this._nanosPerPx;
+ return this._start.addNanos(offsetNanos);
}
- setLimitsPx(pxStart: number, pxEnd: number) {
- assertFalse(pxStart === pxEnd);
- assertTrue(pxStart >= 0 && pxEnd >= 0);
- this._startPx = pxStart;
- this._endPx = pxEnd;
- this.updateSlope();
+ durationToPx(dur: TPDuration): number {
+ // WARNING: Number(bigint) can be surprisingly slow. Avoid in hotpath.
+ return Number(dur) / this._nanosPerPx;
}
- timeInBounds(time: number): boolean {
- return this.timeBounds.isInBounds(time);
- }
-
- get startPx(): number {
- return this._startPx;
- }
-
- get endPx(): number {
- return this._endPx;
- }
-
- get widthPx(): number {
- return this._endPx - this._startPx;
- }
-
- get timeSpan(): TimeSpan {
- return this.timeBounds;
+ pxDeltaToDuration(pxDelta: number): HighPrecisionTime {
+ const time = pxDelta * this._nanosPerPx;
+ return HighPrecisionTime.fromNanos(time);
}
}
-export function computeZoom(
- scale: TimeScale, span: TimeSpan, zoomFactor: number, zoomPx: number):
- TimeSpan {
- const startPx = scale.startPx;
- const endPx = scale.endPx;
- const deltaPx = endPx - startPx;
- const deltaTime = span.end - span.start;
- const newDeltaTime = Math.max(deltaTime * zoomFactor, MAX_ZOOM_SPAN_SEC);
- const clampedZoomPx = Math.max(startPx, Math.min(endPx, zoomPx));
- const zoomTime = scale.pxToTime(clampedZoomPx);
- const r = (clampedZoomPx - startPx) / deltaPx;
- const newStartTime = zoomTime - newDeltaTime * r;
- const newEndTime = newStartTime + newDeltaTime;
- return new TimeSpan(newStartTime, newEndTime);
+export class PxSpan {
+ constructor(private _start: number, private _end: number) {
+ assertTrue(_start <= _end, 'PxSpan start > end');
+ }
+
+ get start(): number {
+ return this._start;
+ }
+
+ get end(): number {
+ return this._end;
+ }
+
+ get delta(): number {
+ return this._end - this._start;
+ }
}
diff --git a/ui/src/frontend/time_scale_unittest.ts b/ui/src/frontend/time_scale_unittest.ts
index 7a1be03..f7046de 100644
--- a/ui/src/frontend/time_scale_unittest.ts
+++ b/ui/src/frontend/time_scale_unittest.ts
@@ -12,66 +12,62 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {TimeSpan} from '../common/time';
+import {HighPrecisionTime} from '../common/high_precision_time';
-import {computeZoom, TimeScale} from './time_scale';
+import {PxSpan, TimeScale} from './time_scale';
-test('time scale to work', () => {
- const scale = new TimeScale(new TimeSpan(0, 100), [200, 1000]);
+describe('TimeScale', () => {
+ const ts =
+ new TimeScale(new HighPrecisionTime(40n), 100, new PxSpan(200, 1000));
- expect(scale.timeToPx(0)).toEqual(200);
- expect(scale.timeToPx(100)).toEqual(1000);
- expect(scale.timeToPx(50)).toEqual(600);
+ it('converts timescales to pixels', () => {
+ expect(ts.tpTimeToPx(40n)).toEqual(200);
+ expect(ts.tpTimeToPx(140n)).toEqual(1000);
+ expect(ts.tpTimeToPx(90n)).toEqual(600);
- expect(scale.pxToTime(200)).toEqual(0);
- expect(scale.pxToTime(1000)).toEqual(100);
- expect(scale.pxToTime(600)).toEqual(50);
+ expect(ts.tpTimeToPx(240n)).toEqual(1800);
+ expect(ts.tpTimeToPx(-60n)).toEqual(-600);
+ });
- expect(scale.deltaPxToDuration(400)).toEqual(50);
+ it('converts pixels to HPTime objects', () => {
+ let result = ts.pxToHpTime(200);
+ expect(result.base).toEqual(40n);
+ expect(result.offset).toBeCloseTo(0);
- expect(scale.timeInBounds(50)).toEqual(true);
- expect(scale.timeInBounds(0)).toEqual(true);
- expect(scale.timeInBounds(100)).toEqual(true);
- expect(scale.timeInBounds(-1)).toEqual(false);
- expect(scale.timeInBounds(101)).toEqual(false);
-});
+ result = ts.pxToHpTime(1000);
+ expect(result.base).toEqual(140n);
+ expect(result.offset).toBeCloseTo(0);
+ result = ts.pxToHpTime(600);
+ expect(result.base).toEqual(90n);
+ expect(result.offset).toBeCloseTo(0);
-test('time scale to be updatable', () => {
- const scale = new TimeScale(new TimeSpan(0, 100), [100, 1000]);
+ result = ts.pxToHpTime(1800);
+ expect(result.base).toEqual(240n);
+ expect(result.offset).toBeCloseTo(0);
- expect(scale.timeToPx(0)).toEqual(100);
+ result = ts.pxToHpTime(-600);
+ expect(result.base).toEqual(-60n);
+ expect(result.offset).toBeCloseTo(0);
+ });
- scale.setLimitsPx(200, 1000);
- expect(scale.timeToPx(0)).toEqual(200);
- expect(scale.timeToPx(100)).toEqual(1000);
+ it('converts durations to pixels', () => {
+ expect(ts.durationToPx(0n)).toEqual(0);
+ expect(ts.durationToPx(1n)).toEqual(8);
+ expect(ts.durationToPx(1000n)).toEqual(8000);
+ });
- scale.setTimeBounds(new TimeSpan(0, 200));
- expect(scale.timeToPx(0)).toEqual(200);
- expect(scale.timeToPx(100)).toEqual(600);
- expect(scale.timeToPx(200)).toEqual(1000);
-});
+ it('converts pxDeltaToDurations to HPTime durations', () => {
+ let result = ts.pxDeltaToDuration(0);
+ expect(result.base).toEqual(0n);
+ expect(result.offset).toBeCloseTo(0);
-test('it zooms', () => {
- const span = new TimeSpan(0, 20);
- const scale = new TimeScale(span, [0, 100]);
- const newSpan = computeZoom(scale, span, 0.5, 50);
- expect(newSpan.start).toEqual(5);
- expect(newSpan.end).toEqual(15);
-});
+ result = ts.pxDeltaToDuration(1);
+ expect(result.base).toEqual(0n);
+ expect(result.offset).toBeCloseTo(0.125);
-test('it zooms an offset scale and span', () => {
- const span = new TimeSpan(1000, 1020);
- const scale = new TimeScale(span, [200, 300]);
- const newSpan = computeZoom(scale, span, 0.5, 250);
- expect(newSpan.start).toEqual(1005);
- expect(newSpan.end).toEqual(1015);
-});
-
-test('it clamps zoom in', () => {
- const span = new TimeSpan(1000, 1040);
- const scale = new TimeScale(span, [200, 300]);
- const newSpan = computeZoom(scale, span, 0.0000000001, 225);
- expect((newSpan.end - newSpan.start) / 2 + newSpan.start).toBeCloseTo(1010);
- expect(newSpan.end - newSpan.start).toBeCloseTo(1e-8);
+ result = ts.pxDeltaToDuration(100);
+ expect(result.base).toEqual(12n);
+ expect(result.offset).toBeCloseTo(0.5);
+ });
});
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index 20f1c53..5711e6a 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -13,9 +13,13 @@
// limitations under the License.
import m from 'mithril';
+import {BigintMath} from '../base/bigint_math';
-import {timeToString} from '../common/time';
-import {TimeSpan} from '../common/time';
+import {Span, tpTimeToString} from '../common/time';
+import {
+ TPTime,
+ TPTimeSpan,
+} from '../common/time';
import {
BACKGROUND_COLOR,
@@ -24,6 +28,7 @@
} from './css_constants';
import {globals} from './globals';
import {
+ getMaxMajorTicks,
TickGenerator,
TickType,
timeScaleForVisibleWindow,
@@ -48,7 +53,7 @@
ctx.fillStyle = FOREGROUND_COLOR;
const xLeft = Math.floor(target.x);
- const xRight = Math.ceil(target.x + target.width);
+ const xRight = Math.floor(target.x + target.width);
const yMid = Math.floor(target.height / 2 + target.y);
const xWidth = xRight - xLeft;
@@ -130,11 +135,21 @@
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
ctx.fillStyle = '#999';
ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
- const scale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
- if (scale.timeSpan.duration > 0 && scale.widthPx > 0) {
- for (const {position, type} of new TickGenerator(scale)) {
+
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(TRACK_SHELL_WIDTH, 0, size.width - TRACK_SHELL_WIDTH, size.height);
+ ctx.clip();
+
+ const span = globals.frontendLocalState.visibleWindow.timestampSpan;
+ if (size.width > TRACK_SHELL_WIDTH && span.duration > 0n) {
+ const maxMajorTicks = getMaxMajorTicks(size.width - TRACK_SHELL_WIDTH);
+ const map = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+ for (const {type, time} of new TickGenerator(
+ span, maxMajorTicks, globals.state.traceTime.start)) {
+ const px = Math.floor(map.tpTimeToPx(time));
if (type === TickType.MAJOR) {
- ctx.fillRect(position, 0, 1, size.height);
+ ctx.fillRect(px, 0, 1, size.height);
}
}
}
@@ -142,17 +157,17 @@
const localArea = globals.frontendLocalState.selectedArea;
const selection = globals.state.currentSelection;
if (localArea !== undefined) {
- const start = Math.min(localArea.startSec, localArea.endSec);
- const end = Math.max(localArea.startSec, localArea.endSec);
- this.renderSpan(ctx, size, new TimeSpan(start, end));
+ const start = BigintMath.min(localArea.start, localArea.end);
+ const end = BigintMath.max(localArea.start, localArea.end);
+ this.renderSpan(ctx, size, new TPTimeSpan(start, end));
} else if (selection !== null && selection.kind === 'AREA') {
const selectedArea = globals.state.areas[selection.areaId];
- const start = Math.min(selectedArea.startSec, selectedArea.endSec);
- const end = Math.max(selectedArea.startSec, selectedArea.endSec);
- this.renderSpan(ctx, size, new TimeSpan(start, end));
+ const start = BigintMath.min(selectedArea.start, selectedArea.end);
+ const end = BigintMath.max(selectedArea.start, selectedArea.end);
+ this.renderSpan(ctx, size, new TPTimeSpan(start, end));
}
- if (globals.state.hoverCursorTimestamp !== -1) {
+ if (globals.state.hoverCursorTimestamp !== -1n) {
this.renderHover(ctx, size, globals.state.hoverCursorTimestamp);
}
@@ -162,27 +177,29 @@
if (note.noteType === 'AREA' && !noteIsSelected) {
const selectedArea = globals.state.areas[note.areaId];
this.renderSpan(
- ctx,
- size,
- new TimeSpan(selectedArea.startSec, selectedArea.endSec));
+ ctx, size, new TPTimeSpan(selectedArea.start, selectedArea.end));
}
}
+
+ ctx.restore();
}
- renderHover(ctx: CanvasRenderingContext2D, size: PanelSize, ts: number) {
- const timeScale = globals.frontendLocalState.timeScale;
- const xPos = TRACK_SHELL_WIDTH + Math.floor(timeScale.timeToPx(ts));
- const offsetTime = timeToString(ts - globals.state.traceTime.startSec);
- const timeFromStart = timeToString(ts);
+ renderHover(ctx: CanvasRenderingContext2D, size: PanelSize, ts: TPTime) {
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const xPos =
+ TRACK_SHELL_WIDTH + Math.floor(visibleTimeScale.tpTimeToPx(ts));
+ const offsetTime = tpTimeToString(ts - globals.state.traceTime.start);
+ const timeFromStart = tpTimeToString(ts);
const label = `${offsetTime} (${timeFromStart})`;
drawIBar(ctx, xPos, this.bounds(size), label);
}
- renderSpan(ctx: CanvasRenderingContext2D, size: PanelSize, span: TimeSpan) {
- const timeScale = globals.frontendLocalState.timeScale;
- const xLeft = timeScale.timeToPx(span.start);
- const xRight = timeScale.timeToPx(span.end);
- const label = timeToString(span.duration);
+ renderSpan(
+ ctx: CanvasRenderingContext2D, size: PanelSize, span: Span<TPTime>) {
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const xLeft = visibleTimeScale.tpTimeToPx(span.start);
+ const xRight = visibleTimeScale.tpTimeToPx(span.end);
+ const label = tpTimeToString(span.duration);
drawHBar(
ctx,
{
diff --git a/ui/src/frontend/trace_converter.ts b/ui/src/frontend/trace_converter.ts
index ca4ced3..0f5ca69 100644
--- a/ui/src/frontend/trace_converter.ts
+++ b/ui/src/frontend/trace_converter.ts
@@ -17,6 +17,7 @@
ConversionJobName,
ConversionJobStatus,
} from '../common/conversion_jobs';
+import {TPTime} from '../common/time';
import {download} from './clipboard';
import {maybeShowErrorDialog} from './error_dialog';
@@ -106,7 +107,7 @@
}
export function convertTraceToPprofAndDownload(
- trace: Blob, pid: number, ts: number) {
+ trace: Blob, pid: number, ts: TPTime) {
makeWorkerAndPost({
kind: 'ConvertTraceToPprof',
trace,
diff --git a/ui/src/frontend/trace_url_handler.ts b/ui/src/frontend/trace_url_handler.ts
index 8b398fb..5e329f5 100644
--- a/ui/src/frontend/trace_url_handler.ts
+++ b/ui/src/frontend/trace_url_handler.ts
@@ -23,7 +23,6 @@
import {Route, Router} from './router';
import {taskTracker} from './task_tracker';
-
export function maybeOpenTraceFromRoute(route: Route) {
if (route.args.s) {
// /?s=xxxx for permalinks.
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index 2d5ab81..9b59059 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -17,6 +17,7 @@
import {assertExists} from '../base/logging';
import {Engine} from '../common/engine';
import {TrackState} from '../common/state';
+import {TPTime} from '../common/time';
import {TrackData} from '../common/track_data';
import {checkerboard} from './checkerboard';
@@ -130,9 +131,11 @@
render(ctx: CanvasRenderingContext2D) {
globals.frontendLocalState.addVisibleTrack(this.trackState.id);
if (this.data() === undefined && !this.frontendOnly) {
- const {visibleWindowTime, timeScale} = globals.frontendLocalState;
- const startPx = Math.floor(timeScale.timeToPx(visibleWindowTime.start));
- const endPx = Math.ceil(timeScale.timeToPx(visibleWindowTime.end));
+ const {visibleWindowTime, visibleTimeScale} = globals.frontendLocalState;
+ const startPx =
+ Math.floor(visibleTimeScale.hpTimeToPx(visibleWindowTime.start));
+ const endPx =
+ Math.ceil(visibleTimeScale.hpTimeToPx(visibleWindowTime.end));
checkerboard(ctx, this.getHeight(), startPx, endPx);
} else {
this.renderCanvas(ctx);
@@ -175,7 +178,7 @@
y -= 10;
// Ensure the box is on screen:
- const endPx = globals.frontendLocalState.timeScale.endPx;
+ const endPx = globals.frontendLocalState.visibleTimeScale.pxSpan.end;
if (x + width > endPx) {
x -= x + width - endPx;
}
@@ -205,7 +208,7 @@
// 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(_tStart: number, _tEnd: number, _depth: number): SliceRect
+ getSliceRect(_tStart: TPTime, _tEnd: TPTime, _depth: number): SliceRect
|undefined {
return undefined;
}
diff --git a/ui/src/frontend/track_cache.ts b/ui/src/frontend/track_cache.ts
index 049cbf4..61f17b1 100644
--- a/ui/src/frontend/track_cache.ts
+++ b/ui/src/frontend/track_cache.ts
@@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../base/bigint_math';
+
import {assertTrue} from '../base/logging';
+import {TPDuration, TPTime} from '../common/time';
export const BUCKETS_PER_PIXEL = 2;
@@ -53,32 +56,34 @@
// non-normal window at a higher resolution. Normalization is used to
// avoid re-fetching data on tiny zooms/moves/resizes.
export class CacheKey {
- readonly startNs: number;
- readonly endNs: number;
- readonly bucketNs: number;
+ readonly start: TPTime;
+ readonly end: TPTime;
+ readonly bucketSize: TPDuration;
readonly windowSizePx: number;
- static create(startNs: number, endNs: number, windowSizePx: number):
+ static create(startNs: TPTime, endNs: TPTime, windowSizePx: number):
CacheKey {
- const bucketNs = (endNs - startNs) / (windowSizePx * BUCKETS_PER_PIXEL);
+ const bucketNs = (endNs - startNs) /
+ BigInt(Math.round(windowSizePx * BUCKETS_PER_PIXEL));
return new CacheKey(startNs, endNs, bucketNs, windowSizePx);
}
private constructor(
- startNs: number, endNs: number, bucketNs: number, windowSizePx: number) {
- this.startNs = startNs;
- this.endNs = endNs;
- this.bucketNs = bucketNs;
+ startNs: TPTime, endNs: TPTime, bucketNs: TPDuration,
+ windowSizePx: number) {
+ this.start = startNs;
+ this.end = endNs;
+ this.bucketSize = bucketNs;
this.windowSizePx = windowSizePx;
}
static zero(): CacheKey {
- return new CacheKey(0, 0, 0, 100);
+ return new CacheKey(0n, 0n, 0n, 100);
}
- get normalizedBucketNs(): number {
+ get normalizedBucketNs(): bigint {
// Round bucketNs down to the nearest smaller power of 2 (minimum 1):
- return Math.max(1, Math.pow(2, Math.floor(Math.log2(this.bucketNs))));
+ return BigintMath.max(1n, BigintMath.bitFloor(this.bucketSize));
}
get normalizedWindowSizePx(): number {
@@ -88,9 +93,9 @@
normalize(): CacheKey {
const windowSizePx = this.normalizedWindowSizePx;
const bucketNs = this.normalizedBucketNs;
- const windowNs = windowSizePx * BUCKETS_PER_PIXEL * bucketNs;
- const startNs = Math.floor(this.startNs / windowNs) * windowNs;
- const endNs = Math.ceil(this.endNs / windowNs) * windowNs;
+ const windowNs = BigInt(windowSizePx * BUCKETS_PER_PIXEL) * bucketNs;
+ const startNs = BigintMath.quantFloor(this.start, windowNs);
+ const endNs = BigintMath.quantCeil(this.end, windowNs);
return new CacheKey(startNs, endNs, bucketNs, windowSizePx);
}
@@ -100,8 +105,8 @@
isCoveredBy(other: CacheKey): boolean {
let r = true;
- r = r && other.startNs <= this.startNs;
- r = r && other.endNs >= this.endNs;
+ r = r && other.start <= this.start;
+ r = r && other.end >= this.end;
r = r && other.normalizedBucketNs === this.normalizedBucketNs;
r = r && other.normalizedWindowSizePx === this.normalizedWindowSizePx;
return r;
@@ -110,9 +115,9 @@
// toString is 'load bearing' in that it's used to key e.g. caches
// with CacheKey's.
toString() {
- const start = this.startNs;
- const end = this.endNs;
- const bucket = this.bucketNs;
+ const start = this.start;
+ const end = this.end;
+ const bucket = this.bucketSize;
const size = this.windowSizePx;
return `CacheKey<${start}, ${end}, ${bucket}, ${size}>`;
}
diff --git a/ui/src/frontend/track_cache_unittest.ts b/ui/src/frontend/track_cache_unittest.ts
index 546406a..fc39a7a 100644
--- a/ui/src/frontend/track_cache_unittest.ts
+++ b/ui/src/frontend/track_cache_unittest.ts
@@ -15,46 +15,46 @@
import {CacheKey, TrackCache} from './track_cache';
test('cacheKeys', () => {
- const k = CacheKey.create(201, 302, 123);
+ const k = CacheKey.create(201n, 302n, 123);
const n = k.normalize();
const n2 = n.normalize();
expect(k.isNormalized()).toEqual(false);
expect(n.isNormalized()).toEqual(true);
expect(n2.isNormalized()).toEqual(true);
expect(n).toEqual(n2);
- expect(n.startNs).toBeLessThanOrEqual(k.startNs);
- expect(n.endNs).toBeGreaterThanOrEqual(k.startNs);
- expect(n.bucketNs).toBeGreaterThanOrEqual(k.bucketNs);
+ expect(n.start).toBeLessThanOrEqual(k.start);
+ expect(n.end).toBeGreaterThanOrEqual(k.start);
+ expect(n.bucketSize).toBeGreaterThanOrEqual(k.bucketSize);
expect(Math.abs(n.windowSizePx - k.windowSizePx)).toBeLessThanOrEqual(200);
});
test('cache', () => {
- const k1 = (CacheKey.create(1000, 1100, 100)).normalize();
- const k2 = (CacheKey.create(2000, 2100, 100)).normalize();
- const k3 = (CacheKey.create(3000, 3100, 100)).normalize();
- const k4 = (CacheKey.create(4000, 4100, 100)).normalize();
- const k5 = (CacheKey.create(5000, 5100, 100)).normalize();
- const k6 = (CacheKey.create(6000, 6100, 100)).normalize();
- const k7 = (CacheKey.create(7000, 7100, 100)).normalize();
+ const key1 = (CacheKey.create(1000n, 1100n, 100)).normalize();
+ const key2 = (CacheKey.create(2000n, 2100n, 100)).normalize();
+ const key3 = (CacheKey.create(3000n, 3100n, 100)).normalize();
+ const key4 = (CacheKey.create(4000n, 4100n, 100)).normalize();
+ const key5 = (CacheKey.create(5000n, 5100n, 100)).normalize();
+ const key6 = (CacheKey.create(6000n, 6100n, 100)).normalize();
+ const key7 = (CacheKey.create(7000n, 7100n, 100)).normalize();
const cache = new TrackCache<string>(5);
- cache.insert(k1, 'v1');
- expect(cache.lookup(k1)).toEqual('v1');
+ cache.insert(key1, 'v1');
+ expect(cache.lookup(key1)).toEqual('v1');
- cache.insert(k2, 'v2');
- cache.insert(k3, 'v3');
- cache.insert(k4, 'v4');
- cache.insert(k5, 'v5');
+ cache.insert(key2, 'v2');
+ cache.insert(key3, 'v3');
+ cache.insert(key4, 'v4');
+ cache.insert(key5, 'v5');
- // Should push k1/v1 out of the cache:
- cache.insert(k6, 'v6');
- expect(cache.lookup(k1)).toEqual(undefined);
+ // Should push key1/v1 out of the cache:
+ cache.insert(key6, 'v6');
+ expect(cache.lookup(key1)).toEqual(undefined);
- // Access k2 then add one more entry:
- expect(cache.lookup(k2)).toEqual('v2');
- cache.insert(k7, 'v7');
+ // Access key2 then add one more entry:
+ expect(cache.lookup(key2)).toEqual('v2');
+ cache.insert(key7, 'v7');
- // k2/v2 should still be present but k3/v3 should be discarded:
- expect(cache.lookup(k2)).toEqual('v2');
- expect(cache.lookup(k3)).toEqual(undefined);
+ // key2/v2 should still be present but key3/v3 should be discarded:
+ expect(cache.lookup(key2)).toEqual('v2');
+ expect(cache.lookup(key3)).toEqual(undefined);
});
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index c9d109f..dbab7f7 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -187,18 +187,17 @@
}
highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
- const localState = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
const selection = globals.state.currentSelection;
if (!selection || selection.kind !== 'AREA') return;
const selectedArea = globals.state.areas[selection.areaId];
+ const selectedAreaDuration = selectedArea.end - selectedArea.start;
if (selectedArea.tracks.includes(this.trackGroupId)) {
ctx.fillStyle = 'rgba(131, 152, 230, 0.3)';
ctx.fillRect(
- localState.timeScale.timeToPx(selectedArea.startSec) +
- this.shellWidth,
+ visibleTimeScale.tpTimeToPx(selectedArea.start) + this.shellWidth,
0,
- localState.timeScale.deltaTimeToPx(
- selectedArea.endSec - selectedArea.startSec),
+ visibleTimeScale.durationToPx(selectedAreaDuration),
size.height);
}
}
@@ -227,20 +226,20 @@
this.highlightIfTrackSelected(ctx, size);
- const localState = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
// Draw vertical line when hovering on the notes panel.
- if (globals.state.hoveredNoteTimestamp !== -1) {
+ if (globals.state.hoveredNoteTimestamp !== -1n) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.state.hoveredNoteTimestamp,
size.height,
`#aaa`);
}
- if (globals.state.hoverCursorTimestamp !== -1) {
+ if (globals.state.hoverCursorTimestamp !== -1n) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.state.hoverCursorTimestamp,
size.height,
`#344596`);
@@ -251,7 +250,7 @@
globals.sliceDetails.wakeupTs !== undefined) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.sliceDetails.wakeupTs,
size.height,
`black`);
@@ -265,21 +264,21 @@
'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
- globals.state.areas[note.areaId].startSec,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].start,
size.height,
transparentNoteColor,
1);
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
- globals.state.areas[note.areaId].endSec,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].end,
size.height,
transparentNoteColor,
1);
} else if (note.noteType === 'DEFAULT') {
drawVerticalLineAtTime(
- ctx, localState.timeScale, note.timestamp, size.height, note.color);
+ ctx, visibleTimeScale, note.timestamp, size.height, note.color);
}
}
}
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 1ab998f..b03d18a 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -17,6 +17,7 @@
import {Actions} from '../common/actions';
import {TrackState} from '../common/state';
+import {TPTime} from '../common/time';
import {SELECTION_FILL_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
import {PerfettoMouseEvent} from './events';
@@ -95,7 +96,6 @@
`.track-shell[draggable=true]`,
{
class: `${highlightClass} ${dragClass} ${dropClass}`,
- onmousedown: this.onmousedown.bind(this),
ondragstart: this.ondragstart.bind(this),
ondragend: this.ondragend.bind(this),
ondragover: this.ondragover.bind(this),
@@ -145,12 +145,6 @@
''));
}
- onmousedown(e: MouseEvent) {
- // Prevent that the click is intercepted by the PanAndZoomHandler and that
- // we start panning while dragging.
- e.stopPropagation();
- }
-
ondragstart(e: DragEvent) {
const dataTransfer = e.dataTransfer;
if (dataTransfer === null) return;
@@ -158,7 +152,6 @@
globals.rafScheduler.scheduleFullRedraw();
dataTransfer.setData('perfetto/track', `${this.attrs!.trackState.id}`);
dataTransfer.setDragImage(new Image(), 0, 0);
- e.stopImmediatePropagation();
}
ondragend() {
@@ -370,20 +363,20 @@
}
highlightIfTrackSelected(ctx: CanvasRenderingContext2D, size: PanelSize) {
- const localState = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
const selection = globals.state.currentSelection;
const trackState = this.trackState;
if (!selection || selection.kind !== 'AREA' || trackState === undefined) {
return;
}
const selectedArea = globals.state.areas[selection.areaId];
+ const selectedAreaDuration = selectedArea.end - selectedArea.start;
if (selectedArea.tracks.includes(trackState.id)) {
- const timeScale = localState.timeScale;
ctx.fillStyle = SELECTION_FILL_COLOR;
ctx.fillRect(
- timeScale.timeToPx(selectedArea.startSec) + TRACK_SHELL_WIDTH,
+ visibleTimeScale.tpTimeToPx(selectedArea.start) + TRACK_SHELL_WIDTH,
0,
- timeScale.deltaTimeToPx(selectedArea.endSec - selectedArea.startSec),
+ visibleTimeScale.durationToPx(selectedAreaDuration),
size.height);
}
}
@@ -404,20 +397,20 @@
this.highlightIfTrackSelected(ctx, size);
- const localState = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
// Draw vertical line when hovering on the notes panel.
- if (globals.state.hoveredNoteTimestamp !== -1) {
+ if (globals.state.hoveredNoteTimestamp !== -1n) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.state.hoveredNoteTimestamp,
size.height,
`#aaa`);
}
- if (globals.state.hoverCursorTimestamp !== -1) {
+ if (globals.state.hoverCursorTimestamp !== -1n) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.state.hoverCursorTimestamp,
size.height,
`#344596`);
@@ -428,7 +421,7 @@
globals.sliceDetails.wakeupTs !== undefined) {
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
+ visibleTimeScale,
globals.sliceDetails.wakeupTs,
size.height,
`black`);
@@ -442,26 +435,26 @@
'rgba(' + hex.rgb(note.color.substr(1)).toString() + ', 0.65)';
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
- globals.state.areas[note.areaId].startSec,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].start,
size.height,
transparentNoteColor,
1);
drawVerticalLineAtTime(
ctx,
- localState.timeScale,
- globals.state.areas[note.areaId].endSec,
+ visibleTimeScale,
+ globals.state.areas[note.areaId].end,
size.height,
transparentNoteColor,
1);
} else if (note.noteType === 'DEFAULT') {
drawVerticalLineAtTime(
- ctx, localState.timeScale, note.timestamp, size.height, note.color);
+ ctx, visibleTimeScale, note.timestamp, size.height, note.color);
}
}
}
- getSliceRect(tStart: number, tDur: number, depth: number): SliceRect
+ getSliceRect(tStart: TPTime, tDur: TPTime, depth: number): SliceRect
|undefined {
if (this.track === undefined) {
return undefined;
diff --git a/ui/src/frontend/vertical_line_helper.ts b/ui/src/frontend/vertical_line_helper.ts
index b1e2cfc..353d166 100644
--- a/ui/src/frontend/vertical_line_helper.ts
+++ b/ui/src/frontend/vertical_line_helper.ts
@@ -12,18 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {TPTime} from '../common/time';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {TimeScale} from './time_scale';
-export function drawVerticalLineAtTime(ctx: CanvasRenderingContext2D,
- timeScale: TimeScale,
- time: number,
- height: number,
- color: string,
- lineWidth = 2) {
- const xPos = TRACK_SHELL_WIDTH + Math.floor(timeScale.timeToPx(time));
- drawVerticalLine(ctx, xPos, height, color, lineWidth);
- }
+export function drawVerticalLineAtTime(
+ ctx: CanvasRenderingContext2D,
+ timeScale: TimeScale,
+ time: TPTime,
+ height: number,
+ color: string,
+ lineWidth = 2) {
+ const xPos = TRACK_SHELL_WIDTH + Math.floor(timeScale.tpTimeToPx(time));
+ drawVerticalLine(ctx, xPos, height, color, lineWidth);
+}
function drawVerticalLine(ctx: CanvasRenderingContext2D,
xPos: number,
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index d3651e7..0f8414c 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -13,10 +13,10 @@
// limitations under the License.
import m from 'mithril';
+import {BigintMath} from '../base/bigint_math';
import {Actions} from '../common/actions';
import {featureFlags} from '../common/feature_flags';
-import {TimeSpan} from '../common/time';
import {TRACK_SHELL_WIDTH} from './css_constants';
import {DetailsPanel} from './details_panel';
@@ -28,7 +28,6 @@
import {AnyAttrsVnode, PanelContainer} from './panel_container';
import {TickmarkPanel} from './tickmark_panel';
import {TimeAxisPanel} from './time_axis_panel';
-import {computeZoom} from './time_scale';
import {TimeSelectionPanel} from './time_selection_panel';
import {DISMISSED_PANNING_HINT_KEY} from './topbar';
import {TrackGroupPanel} from './track_group_panel';
@@ -53,8 +52,9 @@
const area = globals.frontendLocalState.selectedArea ?
globals.frontendLocalState.selectedArea :
globals.state.areas[selection.areaId];
- const start = globals.frontendLocalState.timeScale.timeToPx(area.startSec);
- const end = globals.frontendLocalState.timeScale.timeToPx(area.endSec);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const start = visibleTimeScale.tpTimeToPx(area.start);
+ const end = visibleTimeScale.tpTimeToPx(area.end);
const startDrag = mousePos - TRACK_SHELL_WIDTH;
const startDistance = Math.abs(start - startDrag);
const endDistance = Math.abs(end - startDrag);
@@ -119,21 +119,14 @@
element: panZoomEl,
contentOffsetX: SIDEBAR_WIDTH,
onPanned: (pannedPx: number) => {
+ const {
+ visibleTimeScale,
+ } = globals.frontendLocalState;
+
this.keepCurrentSelection = true;
- const traceTime = globals.state.traceTime;
- const vizTime = globals.frontendLocalState.visibleWindowTime;
- const origDelta = vizTime.duration;
- const tDelta = frontendLocalState.timeScale.deltaPxToDuration(pannedPx);
- let tStart = vizTime.start + tDelta;
- let tEnd = vizTime.end + tDelta;
- if (tStart < traceTime.startSec) {
- tStart = traceTime.startSec;
- tEnd = tStart + origDelta;
- } else if (tEnd > traceTime.endSec) {
- tEnd = traceTime.endSec;
- tStart = tEnd - origDelta;
- }
- frontendLocalState.updateVisibleTime(new TimeSpan(tStart, tEnd));
+ const tDelta = visibleTimeScale.pxDeltaToDuration(pannedPx);
+ frontendLocalState.panVisibleWindow(tDelta);
+
// If the user has panned they no longer need the hint.
localStorage.setItem(DISMISSED_PANNING_HINT_KEY, 'true');
globals.rafScheduler.scheduleRedraw();
@@ -141,11 +134,10 @@
onZoomed: (zoomedPositionPx: number, zoomRatio: number) => {
// TODO(hjd): Avoid hardcoding TRACK_SHELL_WIDTH.
// TODO(hjd): Improve support for zooming in overview timeline.
- const span = frontendLocalState.visibleWindowTime;
- const scale = frontendLocalState.timeScale;
const zoomPx = zoomedPositionPx - TRACK_SHELL_WIDTH;
- const newSpan = computeZoom(scale, span, 1 - zoomRatio, zoomPx);
- frontendLocalState.updateVisibleTime(newSpan);
+ const rect = vnode.dom.getBoundingClientRect();
+ const centerPoint = zoomPx / (rect.width - TRACK_SHELL_WIDTH);
+ frontendLocalState.zoomVisibleWindow(1 - zoomRatio, centerPoint);
globals.rafScheduler.scheduleRedraw();
},
editSelection: (currentPx: number) => {
@@ -159,7 +151,7 @@
currentY: number,
editing: boolean) => {
const traceTime = globals.state.traceTime;
- const scale = frontendLocalState.timeScale;
+ const {visibleTimeScale} = frontendLocalState;
this.keepCurrentSelection = true;
if (editing) {
const selection = globals.state.currentSelection;
@@ -167,33 +159,40 @@
const area = globals.frontendLocalState.selectedArea ?
globals.frontendLocalState.selectedArea :
globals.state.areas[selection.areaId];
- let newTime = scale.pxToTime(currentX - TRACK_SHELL_WIDTH);
+ let newTime =
+ visibleTimeScale.pxToHpTime(currentX - TRACK_SHELL_WIDTH)
+ .toTPTime();
// Have to check again for when one boundary crosses over the other.
const curBoundary = onTimeRangeBoundary(prevX);
if (curBoundary == null) return;
- const keepTime =
- curBoundary === 'START' ? area.endSec : area.startSec;
+ const keepTime = curBoundary === 'START' ? area.end : area.start;
// Don't drag selection outside of current screen.
if (newTime < keepTime) {
- newTime = Math.max(newTime, scale.pxToTime(scale.startPx));
+ newTime = BigintMath.max(
+ newTime, visibleTimeScale.timeSpan.start.toTPTime());
} else {
- newTime = Math.min(newTime, scale.pxToTime(scale.endPx));
+ newTime = BigintMath.max(
+ newTime, visibleTimeScale.timeSpan.end.toTPTime());
}
// When editing the time range we always use the saved tracks,
// since these will not change.
frontendLocalState.selectArea(
- Math.max(Math.min(keepTime, newTime), traceTime.startSec),
- Math.min(Math.max(keepTime, newTime), traceTime.endSec),
+ BigintMath.max(
+ BigintMath.min(keepTime, newTime), traceTime.start),
+ BigintMath.min(
+ BigintMath.max(keepTime, newTime), traceTime.end),
globals.state.areas[selection.areaId].tracks);
}
} else {
let startPx = Math.min(dragStartX, currentX) - TRACK_SHELL_WIDTH;
let endPx = Math.max(dragStartX, currentX) - TRACK_SHELL_WIDTH;
if (startPx < 0 && endPx < 0) return;
- startPx = Math.max(startPx, scale.startPx);
- endPx = Math.min(endPx, scale.endPx);
+ startPx = Math.max(startPx, visibleTimeScale.pxSpan.start);
+ endPx = Math.min(endPx, visibleTimeScale.pxSpan.end);
frontendLocalState.selectArea(
- scale.pxToTime(startPx), scale.pxToTime(endPx));
+ visibleTimeScale.pxToHpTime(startPx).toTPTime('floor'),
+ visibleTimeScale.pxToHpTime(endPx).toTPTime('ceil'),
+ );
frontendLocalState.areaY.start = dragStartY;
frontendLocalState.areaY.end = currentY;
}
diff --git a/ui/src/frontend/widgets/button.ts b/ui/src/frontend/widgets/button.ts
index 6c90c2e..ac86e13 100644
--- a/ui/src/frontend/widgets/button.ts
+++ b/ui/src/frontend/widgets/button.ts
@@ -15,6 +15,7 @@
import m from 'mithril';
import {classNames} from '../classnames';
import {Icon} from './icon';
+import {Popup} from './popup';
interface CommonAttrs {
// Always show the button as if the "active" pseudo class were applied, which
@@ -37,6 +38,9 @@
rightIcon?: string;
// List of space separated class names forwarded to the icon.
className?: string;
+ // Allow clicking this button to close parent popups.
+ // Defaults to false.
+ dismissPopup?: boolean;
// Remaining attributes forwarded to the underlying HTML <button>.
[htmlAttrs: string]: any;
}
@@ -66,6 +70,7 @@
disabled = false,
rightIcon,
className,
+ dismissPopup = false,
...htmlAttrs
} = attrs;
@@ -75,6 +80,7 @@
compact && 'pf-compact',
minimal && 'pf-minimal',
(icon && !label) && 'pf-icon-only',
+ dismissPopup && Popup.DISMISS_POPUP_GROUP_CLASS,
className,
);
diff --git a/ui/src/frontend/widgets/duration.ts b/ui/src/frontend/widgets/duration.ts
index 6f36c95..cd18e02 100644
--- a/ui/src/frontend/widgets/duration.ts
+++ b/ui/src/frontend/widgets/duration.ts
@@ -14,14 +14,14 @@
import m from 'mithril';
-import {fromNs, timeToCode} from '../../common/time';
+import {TPDuration, tpTimeToCode} from '../../common/time';
interface DurationAttrs {
- dur: number;
+ dur: TPDuration;
}
export class Duration implements m.ClassComponent<DurationAttrs> {
view(vnode: m.Vnode<DurationAttrs>) {
- return timeToCode(fromNs(vnode.attrs.dur));
+ return tpTimeToCode(vnode.attrs.dur);
}
}
diff --git a/ui/src/frontend/widgets/menu.ts b/ui/src/frontend/widgets/menu.ts
index 582e6fb..b521dab 100644
--- a/ui/src/frontend/widgets/menu.ts
+++ b/ui/src/frontend/widgets/menu.ts
@@ -68,6 +68,7 @@
...rest,
}),
showArrow: false,
+ createNewGroup: false,
},
children,
);
@@ -86,7 +87,7 @@
const classes = classNames(
active && 'pf-active',
- !disabled && closePopupOnClick && 'pf-close-parent-popup-on-click',
+ !disabled && closePopupOnClick && Popup.DISMISS_POPUP_GROUP_CLASS,
);
return m(
@@ -131,6 +132,14 @@
// Whether we should show the little arrow pointing to the trigger.
// Defaults to true.
showArrow?: boolean;
+ // Whether this popup should form a new popup group.
+ // When nesting popups, grouping controls how popups are closed.
+ // When closing popups via the Escape key, each group is closed one by one,
+ // starting at the topmost group in the stack.
+ // When using a magic button to close groups (see DISMISS_POPUP_GROUP_CLASS),
+ // only the group in which the button lives and it's children will be closed.
+ // Defaults to true.
+ createNewGroup?: boolean;
}
// A combination of a Popup and a Menu component.
@@ -146,7 +155,6 @@
{
trigger,
position: popupPosition,
- closeOnContentClick: true,
...popupAttrs,
},
m(Menu, children));
diff --git a/ui/src/frontend/widgets/popup.ts b/ui/src/frontend/widgets/popup.ts
index ca6feb8..b258a11 100644
--- a/ui/src/frontend/widgets/popup.ts
+++ b/ui/src/frontend/widgets/popup.ts
@@ -65,14 +65,19 @@
isOpen?: boolean;
// Called when the popup isOpen state should be changed in controlled mode.
onChange?: OnChangeCallback;
- // Close the popup if clicked on.
- // Defaults to false.
- closeOnContentClick?: boolean;
// Space delimited class names applied to the popup div.
className?: string;
// Whether to show a little arrow pointing to our trigger element.
// Defaults to true.
showArrow?: boolean;
+ // Whether this popup should form a new popup group.
+ // When nesting popups, grouping controls how popups are closed.
+ // When closing popups via the Escape key, each group is closed one by one,
+ // starting at the topmost group in the stack.
+ // When using a magic button to close groups (see DISMISS_POPUP_GROUP_CLASS),
+ // only the group in which the button lives and it's children will be closed.
+ // Defaults to true.
+ createNewGroup?: boolean;
}
// A popup is a portal whose position is dynamically updated so that it floats
@@ -90,6 +95,10 @@
private static readonly TRIGGER_REF = 'trigger';
private static readonly POPUP_REF = 'popup';
+ static readonly POPUP_GROUP_CLASS = 'pf-popup-group';
+
+ // Any element with this class will close its containing popup group on click
+ static readonly DISMISS_POPUP_GROUP_CLASS = 'pf-dismiss-popup-group';
view({attrs, children}: m.CVnode<PopupAttrs>): m.Children {
const {
@@ -127,6 +136,7 @@
const {
className,
showArrow = true,
+ createNewGroup = true,
} = attrs;
const portalAttrs: PortalAttrs = {
@@ -168,7 +178,8 @@
portalAttrs,
m('.pf-popup',
{
- class: classNames(className),
+ class: classNames(
+ className, createNewGroup && Popup.POPUP_GROUP_CLASS),
ref: Popup.POPUP_REF,
},
showArrow && m('.pf-popup-arrow[data-popper-arrow]'),
@@ -236,14 +247,29 @@
};
private handleDocKeyPress = (e: KeyboardEvent) => {
- if (this.closeOnEscape && e.key === 'Escape') {
- this.closePopup();
+ // Close on escape keypress if we are in the toplevel group
+ const nextGroupElement =
+ this.popupElement?.querySelector(`.${Popup.POPUP_GROUP_CLASS}`);
+ if (!nextGroupElement) {
+ if (this.closeOnEscape && e.key === 'Escape') {
+ this.closePopup();
+ }
}
};
private handleContentClick = (e: Event) => {
+ // Close the popup if the clicked element:
+ // - Is in the same group as this class
+ // - Has the magic class
const target = e.target as HTMLElement;
- if (target.closest('.pf-close-parent-popup-on-click')) {
+ const childPopup =
+ this.popupElement?.querySelector(`.${Popup.POPUP_GROUP_CLASS}`);
+ if (childPopup) {
+ if (childPopup.contains(target)) {
+ return;
+ }
+ }
+ if (target.closest(`.${Popup.DISMISS_POPUP_GROUP_CLASS}`)) {
this.closePopup();
}
};
diff --git a/ui/src/frontend/widgets/timestamp.ts b/ui/src/frontend/widgets/timestamp.ts
index 4552275..d8cc841 100644
--- a/ui/src/frontend/widgets/timestamp.ts
+++ b/ui/src/frontend/widgets/timestamp.ts
@@ -14,7 +14,7 @@
import m from 'mithril';
-import {timeToCode} from '../../common/time';
+import {tpTimeToCode} from '../../common/time';
import {toTraceTime, TPTimestamp} from '../sql_types';
interface TimestampAttrs {
@@ -23,6 +23,6 @@
export class Timestamp implements m.ClassComponent<TimestampAttrs> {
view(vnode: m.Vnode<TimestampAttrs>) {
- return timeToCode(toTraceTime(vnode.attrs.ts));
+ return tpTimeToCode(toTraceTime(vnode.attrs.ts));
}
}
diff --git a/ui/src/frontend/widgets/tree.ts b/ui/src/frontend/widgets/tree.ts
index 0428a48..e797343 100644
--- a/ui/src/frontend/widgets/tree.ts
+++ b/ui/src/frontend/widgets/tree.ts
@@ -1,7 +1,10 @@
import m from 'mithril';
+
import {classNames} from '../classnames';
import {globals} from '../globals';
+
import {Button} from './button';
+import {Spinner} from './spinner';
import {hasChildren} from './utils';
export enum TreeLayout {
@@ -48,12 +51,16 @@
}
interface TreeNodeAttrs {
- // Content to display on the left hand column.
+ // Content to display in the left hand column.
// If omitted, this side will be blank.
- left?: m.Child;
- // Content to display on the right hand column.
+ left?: m.Children;
+ // Content to display in the right hand column.
// If omitted, this side will be left blank.
- right?: m.Child;
+ right?: m.Children;
+ // Content to display in the right hand column when the node is collapsed.
+ // If omitted, the value of `right` shall be shown when collapsed instead.
+ // If the node has no children, this value is never shown.
+ summary?: m.Children;
// Whether this node is collapsed or not.
// If omitted, collapsed state 'uncontrolled' - i.e. controlled internally.
collapsed?: boolean;
@@ -86,8 +93,13 @@
);
}
- private renderRight({attrs: {right}}: m.CVnode<TreeNodeAttrs>) {
- return m('.pf-tree-right', right);
+ private renderRight(vnode: m.CVnode<TreeNodeAttrs>) {
+ const {attrs: {right, summary}} = vnode;
+ if (hasChildren(vnode) && this.isCollapsed(vnode)) {
+ return m('.pf-tree-right', summary ?? right);
+ } else {
+ return m('.pf-tree-right', right);
+ }
}
private renderChildren(vnode: m.CVnode<TreeNodeAttrs>) {
@@ -126,3 +138,81 @@
return collapsed;
}
}
+
+export function dictToTree(dict: {[key: string]: m.Child}): m.Children {
+ const children: m.Child[] = [];
+ for (const key of Object.keys(dict)) {
+ children.push(m(TreeNode, {
+ left: key,
+ right: dict[key],
+ }));
+ }
+ return m(Tree, children);
+}
+
+interface LazyTreeNodeAttrs {
+ // Same as TreeNode (see above).
+ left?: m.Children;
+ // Same as TreeNode (see above).
+ right?: m.Children;
+ // Same as TreeNode (see above).
+ summary?: m.Children;
+ // A callback to be called when the TreeNode is expanded, in order to fetch
+ // child nodes.
+ // The callback must return a promise to a function which returns m.Children.
+ // The reason the promise must return a function rather than the actual
+ // children is to avoid storing vnodes between render cycles, which is a bug
+ // in Mithril.
+ fetchData: () => Promise<() => m.Children>;
+ // Whether to keep child nodes in memory after the node has been collapsed.
+ // Defaults to true
+ hoardData?: boolean;
+}
+
+// This component is a TreeNode which only loads child nodes when it's expanded.
+// This allows us to represent huge trees without having to load all the data
+// up front, and even allows us to represent infinite or recursive trees.
+export class LazyTreeNode implements m.ClassComponent<LazyTreeNodeAttrs> {
+ private collapsed: boolean = true;
+ private renderChildren = this.renderSpinner;
+
+ private renderSpinner(): m.Children {
+ return m(TreeNode, {left: m(Spinner)});
+ }
+
+ view({attrs}: m.CVnode<LazyTreeNodeAttrs>): m.Children {
+ const {
+ left,
+ right,
+ summary,
+ fetchData,
+ hoardData = true,
+ } = attrs;
+
+ return m(
+ TreeNode,
+ {
+ left,
+ right,
+ summary,
+ collapsed: this.collapsed,
+ onCollapseChanged: (collapsed) => {
+ if (collapsed) {
+ if (!hoardData) {
+ this.renderChildren = this.renderSpinner;
+ }
+ } else {
+ fetchData().then((result) => {
+ if (!this.collapsed) {
+ this.renderChildren = result;
+ globals.rafScheduler.scheduleFullRedraw();
+ }
+ });
+ }
+ this.collapsed = collapsed;
+ globals.rafScheduler.scheduleFullRedraw();
+ },
+ },
+ this.renderChildren());
+ }
+}
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/frontend/widgets_page.ts
index 8b7b2e3..989be89 100644
--- a/ui/src/frontend/widgets_page.ts
+++ b/ui/src/frontend/widgets_page.ts
@@ -34,7 +34,7 @@
import {Spinner} from './widgets/spinner';
import {Switch} from './widgets/switch';
import {TextInput} from './widgets/text_input';
-import {Tree, TreeLayout, TreeNode} from './widgets/tree';
+import {LazyTreeNode, Tree, TreeLayout, TreeNode} from './widgets/tree';
const options: {[key: string]: boolean} = {
foobar: false,
@@ -243,6 +243,18 @@
}
}
+function recursiveLazyTreeNode(
+ left: string, summary: string, hoardData: boolean): m.Children {
+ return m(LazyTreeNode, {
+ left,
+ summary,
+ hoardData,
+ fetchData: async () => {
+ await new Promise((r) => setTimeout(r, 200));
+ return () => recursiveLazyTreeNode(left, summary, hoardData);
+ },
+ });
+}
export const WidgetsPage = createPage({
view() {
@@ -563,9 +575,14 @@
left: 'Process',
right: m(Anchor, {text: '/bin/foo[789]', icon: 'open_in_new'}),
}),
+ recursiveLazyTreeNode('Lazy', '(hoarding)', true),
+ recursiveLazyTreeNode('Lazy', '(non-hoarding)', false),
m(
TreeNode,
- {left: 'Args', right: 'foo: bar, baz: qux'},
+ {
+ left: 'Args',
+ summary: 'foo: string, baz: string, quux: string[4]',
+ },
m(TreeNode, {left: 'foo', right: 'bar'}),
m(TreeNode, {left: 'baz', right: 'qux'}),
m(
@@ -604,6 +621,27 @@
m(Button, {label: 'Cancel', minimal: true}),
)),
}),
+ m('h2', 'Nested Popups'),
+ m(
+ WidgetShowcase, {
+ renderWidget: () => m(
+ Popup,
+ {
+ trigger: m(Button, {label: 'Open the popup'}),
+ },
+ m(PopupMenu2,
+ {
+ trigger: m(Button, {label: 'Select an option'}),
+ },
+ m(MenuItem, {label: 'Option 1'}),
+ m(MenuItem, {label: 'Option 2'}),
+ ),
+ m(Button, {
+ label: 'Done',
+ dismissPopup: true,
+ }),
+ ),
+ }),
);
},
});
diff --git a/ui/src/traceconv/index.ts b/ui/src/traceconv/index.ts
index 714666a..df3e8a0 100644
--- a/ui/src/traceconv/index.ts
+++ b/ui/src/traceconv/index.ts
@@ -18,6 +18,7 @@
ConversionJobName,
ConversionJobStatus,
} from '../common/conversion_jobs';
+import {TPTime} from '../common/time';
import traceconv from '../gen/traceconv';
const selfWorker = self as {} as Worker;
@@ -176,7 +177,7 @@
kind: 'ConvertTraceToPprof';
trace: Blob;
pid: number;
- ts: number;
+ ts: TPTime;
}
function isConvertTraceToPprof(msg: Args): msg is ConvertTraceToPprofArgs {
@@ -186,8 +187,7 @@
return true;
}
-async function ConvertTraceToPprof(
-trace: Blob, pid: number, ts: number) {
+async function ConvertTraceToPprof(trace: Blob, pid: number, ts: TPTime) {
const jobName = 'convert_pprof';
updateJobStatus(jobName, ConversionJobStatus.InProgress);
const args = [
diff --git a/ui/src/tracks/actual_frames/index.ts b/ui/src/tracks/actual_frames/index.ts
index 927dea4..d01abc1 100644
--- a/ui/src/tracks/actual_frames/index.ts
+++ b/ui/src/tracks/actual_frames/index.ts
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL, STR} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {TrackController} from '../../controller/track_controller';
import {NewTrackArgs, Track} from '../../frontend/track';
@@ -31,8 +32,8 @@
// Slices are stored in a columnar fashion. All fields have the same length.
strings: string[];
sliceIds: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
depths: Uint16Array;
titles: Uint16Array; // Index in |strings|.
colors?: Uint16Array; // Index in |strings|.
@@ -49,20 +50,11 @@
class ActualFramesSliceTrackController extends TrackController<Config, Data> {
static readonly kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
- private maxDurNs = 0;
+ private maxDur = 0n;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- const pxSize = this.pxSize();
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
-
- if (this.maxDurNs === 0) {
+ if (this.maxDur === 0n) {
const maxDurResult = await this.query(`
select
max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
@@ -70,12 +62,12 @@
from experimental_slice_layout
where filter_track_ids = '${this.config.trackIds.join(',')}'
`);
- this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
+ this.maxDur = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
}
const rawResult = await this.query(`
SELECT
- (s.ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (s.ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
s.ts as ts,
max(iif(s.dur = -1, (SELECT end_ts FROM trace_bounds) - s.ts, s.dur))
as dur,
@@ -97,8 +89,8 @@
join actual_frame_timeline_slice afs using(id)
where
filter_track_ids = '${this.config.trackIds.join(',')}' and
- s.ts >= ${startNs - this.maxDurNs} and
- s.ts <= ${endNs}
+ s.ts >= ${start - this.maxDur} and
+ s.ts <= ${end}
group by tsq, s.layout_depth
order by tsq, s.layout_depth
`);
@@ -111,8 +103,8 @@
length: numRows,
strings: [],
sliceIds: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
depths: new Uint16Array(numRows),
titles: new Uint16Array(numRows),
colors: new Uint16Array(numRows),
@@ -131,9 +123,9 @@
}
const it = rawResult.iter({
- 'tsq': NUM,
- 'ts': NUM,
- 'dur': NUM,
+ 'tsq': LONG,
+ 'ts': LONG,
+ 'dur': LONG,
'layoutDepth': NUM,
'id': NUM,
'name': STR,
@@ -142,16 +134,15 @@
'color': STR,
});
for (let i = 0; it.valid(); i++, it.next()) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ 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);
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
-
- slices.starts[i] = fromNs(startNsQ);
- slices.ends[i] = fromNs(endNsQ);
+ slices.starts[i] = startQ;
+ slices.ends[i] = endQ;
slices.depths[i] = it.layoutDepth;
slices.titles[i] = internString(it.name);
slices.colors![i] = internString(it.color);
diff --git a/ui/src/tracks/android_log/index.ts b/ui/src/tracks/android_log/index.ts
index b2a824b..30d6913 100644
--- a/ui/src/tracks/android_log/index.ts
+++ b/ui/src/tracks/android_log/index.ts
@@ -13,8 +13,8 @@
// limitations under the License.
import {PluginContext} from '../../common/plugin_api';
-import {NUM} from '../../common/query_result';
-import {fromNs, toNsCeil, toNsFloor} from '../../common/time';
+import {LONG, NUM} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {LIMIT} from '../../common/track_data';
import {
@@ -31,8 +31,7 @@
numEvents: number;
// Below: data quantized by resolution and aggregated by event priority.
-
- timestamps: Float64Array;
+ timestamps: BigInt64Array;
// Each Uint8 value has the i-th bit is set if there is at least one log
// event at the i-th priority level at the corresponding time in |timestamps|.
@@ -61,21 +60,15 @@
class AndroidLogTrackController extends TrackController<Config, Data> {
static readonly kind = ANDROID_LOGS_TRACK_KIND;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNsFloor(start);
- const endNs = toNsCeil(end);
-
- // |resolution| is in s/px the frontend wants.
- const quantNs = toNsCeil(resolution);
-
const queryRes = await this.query(`
select
- cast(ts / ${quantNs} as integer) * ${quantNs} as tsQuant,
+ cast(ts / ${resolution} as integer) * ${resolution} as tsQuant,
prio,
count(prio) as numEvents
from android_logs
- where ts >= ${startNs} and ts <= ${endNs}
+ where ts >= ${start} and ts <= ${end}
group by tsQuant, prio
order by tsQuant, prio limit ${LIMIT};`);
@@ -86,14 +79,13 @@
resolution,
length: rowCount,
numEvents: 0,
- timestamps: new Float64Array(rowCount),
+ timestamps: new BigInt64Array(rowCount),
priorities: new Uint8Array(rowCount),
};
-
- const it = queryRes.iter({tsQuant: NUM, prio: NUM, numEvents: NUM});
+ const it = queryRes.iter({tsQuant: LONG, prio: NUM, numEvents: NUM});
for (let row = 0; it.valid(); it.next(), row++) {
- result.timestamps[row] = fromNs(it.tsQuant);
+ result.timestamps[row] = it.tsQuant;
const prio = Math.min(it.prio, 7);
result.priorities[row] |= (1 << prio);
result.numEvents += it.numEvents;
@@ -113,16 +105,16 @@
}
renderCanvas(ctx: CanvasRenderingContext2D): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {visibleTimeScale, windowSpan} = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
- const dataStartPx = timeScale.timeToPx(data.start);
- const dataEndPx = timeScale.timeToPx(data.end);
- const visibleStartPx = timeScale.timeToPx(visibleWindowTime.start);
- const visibleEndPx = timeScale.timeToPx(visibleWindowTime.end);
+ const dataStartPx = visibleTimeScale.tpTimeToPx(data.start);
+ const dataEndPx = visibleTimeScale.tpTimeToPx(data.end);
+ const visibleStartPx = windowSpan.start;
+ const visibleEndPx = windowSpan.end;
checkerboardExcept(
ctx,
@@ -133,7 +125,7 @@
dataEndPx);
const quantWidth =
- Math.max(EVT_PX, timeScale.deltaTimeToPx(data.resolution));
+ Math.max(EVT_PX, visibleTimeScale.durationToPx(data.resolution));
const blockH = RECT_HEIGHT / LEVELS.length;
for (let i = 0; i < data.timestamps.length; i++) {
for (let lev = 0; lev < LEVELS.length; lev++) {
@@ -143,7 +135,7 @@
}
if (!hasEventsForCurColor) continue;
ctx.fillStyle = LEVELS[lev].color;
- const px = Math.floor(timeScale.timeToPx(data.timestamps[i]));
+ const px = Math.floor(visibleTimeScale.tpTimeToPx(data.timestamps[i]));
ctx.fillRect(px, MARGIN_TOP + blockH * lev, quantWidth, blockH);
} // for(lev)
} // for (timestamps)
diff --git a/ui/src/tracks/async_slices/index.ts b/ui/src/tracks/async_slices/index.ts
index 338e02a..c84e2b9 100644
--- a/ui/src/tracks/async_slices/index.ts
+++ b/ui/src/tracks/async_slices/index.ts
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL, STR} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {
TrackController,
@@ -33,8 +34,8 @@
// Slices are stored in a columnar fashion. All fields have the same length.
strings: string[];
sliceIds: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
depths: Uint16Array;
titles: Uint16Array; // Index in |strings|.
isInstant: Uint16Array;
@@ -43,31 +44,22 @@
class AsyncSliceTrackController extends TrackController<Config, Data> {
static readonly kind = ASYNC_SLICE_TRACK_KIND;
- private maxDurNs = 0;
+ private maxDurNs: TPDuration = 0n;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- const pxSize = this.pxSize();
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
-
- if (this.maxDurNs === 0) {
+ if (this.maxDurNs === 0n) {
const maxDurResult = await this.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.config.trackIds.join(',')}'
`);
- this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
+ this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
}
const queryRes = await this.query(`
SELECT
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (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,
@@ -78,8 +70,8 @@
from experimental_slice_layout
where
filter_track_ids = '${this.config.trackIds.join(',')}' and
- ts >= ${startNs - this.maxDurNs} and
- ts <= ${endNs}
+ ts >= ${start - this.maxDurNs} and
+ ts <= ${end}
group by tsq, layout_depth
order by tsq, layout_depth
`);
@@ -92,8 +84,8 @@
length: numRows,
strings: [],
sliceIds: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
depths: new Uint16Array(numRows),
titles: new Uint16Array(numRows),
isInstant: new Uint16Array(numRows),
@@ -111,9 +103,9 @@
}
const it = queryRes.iter({
- tsq: NUM,
- ts: NUM,
- dur: NUM,
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
depth: NUM,
name: STR,
id: NUM,
@@ -121,16 +113,15 @@
isIncomplete: NUM,
});
for (let row = 0; it.valid(); it.next(), row++) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ 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);
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
-
- slices.starts[row] = fromNs(startNsQ);
- slices.ends[row] = fromNs(endNsQ);
+ slices.starts[row] = startQ;
+ slices.ends[row] = endQ;
slices.depths[row] = it.depth;
slices.titles[row] = internString(it.name);
slices.sliceIds[row] = it.id;
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index 23f9a5e..4e190f9 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {Actions} from '../../common/actions';
import {cropText, drawIncompleteSlice} from '../../common/canvas_utils';
import {colorForThreadIdleSlice, hslForSlice} from '../../common/colorizer';
+import {HighPrecisionTime} from '../../common/high_precision_time';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL, STR} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {cachedHsluvToHex} from '../../frontend/hsluv_cache';
@@ -35,8 +35,6 @@
const INNER_CHEVRON_OFFSET = -3;
const INNER_CHEVRON_SCALE =
(SLICE_HEIGHT - 2 * INNER_CHEVRON_OFFSET) / SLICE_HEIGHT;
-// the lowest bucketNs gets is 2, but add some room in case of fp error
-const MIN_QUANT_NS = 3;
export interface Config {
maxDepth: number;
@@ -48,8 +46,8 @@
// Slices are stored in a columnar fashion.
strings: string[];
sliceIds: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
depths: Uint16Array;
titles: Uint16Array; // Index into strings.
colors?: Uint16Array; // Index into strings.
@@ -60,39 +58,23 @@
export class ChromeSliceTrackController extends TrackController<Config, Data> {
static kind = SLICE_TRACK_KIND;
- private maxDurNs = 0;
+ private maxDurNs: TPDuration = 0n;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- const pxSize = this.pxSize();
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
-
const tableName = this.namespaceTable('slice');
- if (this.maxDurNs === 0) {
+ if (this.maxDurNs === 0n) {
const query = `
SELECT max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
AS maxDur FROM ${tableName} WHERE track_id = ${this.config.trackId}`;
const queryRes = await this.query(query);
- this.maxDurNs = queryRes.firstRow({maxDur: NUM_NULL}).maxDur || 0;
- }
-
- // Buckets are always even and positive, don't quantize once we zoom to
- // nanosecond-scale, so that we can see exact sizes.
- let tsq = `ts`;
- if (bucketNs > MIN_QUANT_NS) {
- tsq = `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+ this.maxDurNs = queryRes.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
}
const query = `
SELECT
- ${tsq} as tsq,
+ (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
ts,
max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
depth,
@@ -103,8 +85,8 @@
thread_dur as threadDur
FROM ${tableName}
WHERE track_id = ${this.config.trackId} AND
- ts >= (${startNs - this.maxDurNs}) AND
- ts <= ${endNs}
+ ts >= (${start - this.maxDurNs}) AND
+ ts <= ${end}
GROUP BY depth, tsq`;
const queryRes = await this.query(query);
@@ -116,8 +98,8 @@
length: numRows,
strings: [],
sliceIds: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
depths: new Uint16Array(numRows),
titles: new Uint16Array(numRows),
isInstant: new Uint16Array(numRows),
@@ -136,52 +118,39 @@
}
const it = queryRes.iter({
- tsq: NUM,
- ts: NUM,
- dur: NUM,
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
depth: NUM,
sliceId: NUM,
name: STR,
isInstant: NUM,
isIncomplete: NUM,
- threadDur: NUM_NULL,
+ threadDur: LONG_NULL,
});
for (let row = 0; it.valid(); it.next(), row++) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ 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);
- let endNsQ = endNs;
- if (bucketNs > MIN_QUANT_NS) {
- endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- }
-
- let isInstant = it.isInstant;
- // Floating point rounding with large numbers of nanoseconds can mean
- // there isn't enough precision to distinguish the start and end of a very
- // short event so we just display the event as an instant when zoomed in
- // rather than fail completely if the start and end time are the same.
- if (startNsQ === endNsQ) {
- isInstant = 1;
- }
-
- slices.starts[row] = fromNs(startNsQ);
- slices.ends[row] = fromNs(endNsQ);
+ slices.starts[row] = startQ;
+ slices.ends[row] = endQ;
slices.depths[row] = it.depth;
slices.sliceIds[row] = it.sliceId;
slices.titles[row] = internString(it.name);
- slices.isInstant[row] = isInstant;
+ slices.isInstant[row] = it.isInstant;
slices.isIncomplete[row] = it.isIncomplete;
let cpuTimeRatio = 1;
- if (!isInstant && !it.isIncomplete && it.threadDur !== null) {
+ if (!it.isInstant && !it.isIncomplete && it.threadDur !== null) {
// Rounding the CPU time ratio to two decimal places and ensuring
// it is less than or equal to one, incase the thread duration exceeds
// the total duration.
- cpuTimeRatio =
- Math.min(Math.round((it.threadDur / it.dur) * 100) / 100, 1);
+ cpuTimeRatio = Math.min(
+ Math.round(BIMath.ratio(it.threadDur, it.dur) * 100) / 100, 1);
}
slices.cpuTimeRatio![row] = cpuTimeRatio;
}
@@ -209,7 +178,7 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO: fonts and colors should come from the CSS and not hardcoded here.
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {visibleTimeScale, visibleWindowTime} = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
@@ -219,10 +188,10 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end),
+ visibleTimeScale.hpTimeToPx(visibleWindowTime.start),
+ visibleTimeScale.hpTimeToPx(visibleWindowTime.end),
+ visibleTimeScale.tpTimeToPx(data.start),
+ visibleTimeScale.tpTimeToPx(data.end),
);
ctx.textAlign = 'center';
@@ -245,7 +214,9 @@
const title = data.strings[titleId];
const colorOverride = data.colors && data.strings[data.colors[i]];
if (isIncomplete) { // incomplete slice
- tEnd = visibleWindowTime.end;
+ // TODO(stevegolton): This isn't exactly equivalent, ideally we should
+ // choose tEnd once we've conerted to screen space coords.
+ tEnd = visibleWindowTime.end.toTPTime('ceil');
}
const rect = this.getSliceRect(tStart, tEnd, depth);
@@ -369,26 +340,30 @@
getSliceIndex({x, y}: {x: number, y: number}): number|void {
const data = this.data();
if (data === undefined) return;
- const {timeScale} = globals.frontendLocalState;
+ const {
+ visibleTimeScale: timeScale,
+ visibleWindowTime,
+ } = globals.frontendLocalState;
if (y < TRACK_PADDING) return;
- const instantWidthTime = timeScale.deltaPxToDuration(HALF_CHEVRON_WIDTH_PX);
- const t = timeScale.pxToTime(x);
+ const instantWidthTime = timeScale.pxDeltaToDuration(HALF_CHEVRON_WIDTH_PX);
+ const t = timeScale.pxToHpTime(x);
const depth = Math.floor((y - TRACK_PADDING) / SLICE_HEIGHT);
+
for (let i = 0; i < data.starts.length; i++) {
if (depth !== data.depths[i]) {
continue;
}
- const tStart = data.starts[i];
+ const tStart = HighPrecisionTime.fromTPTime(data.starts[i]);
if (data.isInstant[i]) {
- if (Math.abs(tStart - t) < instantWidthTime) {
+ if (tStart.sub(t).abs().lt(instantWidthTime)) {
return i;
}
} else {
- let tEnd = data.ends[i];
+ let tEnd = HighPrecisionTime.fromTPTime(data.ends[i]);
if (data.isIncomplete[i]) {
- tEnd = globals.frontendLocalState.visibleWindowTime.end;
+ tEnd = visibleWindowTime.end;
}
- if (tStart <= t && t <= tEnd) {
+ if (tStart.lte(t) && t.lte(tEnd)) {
return i;
}
}
@@ -433,19 +408,27 @@
return SLICE_HEIGHT * (this.config.maxDepth + 1) + 2 * TRACK_PADDING;
}
- getSliceRect(tStart: number, tEnd: number, depth: number): SliceRect
+ getSliceRect(tStart: TPTime, tEnd: TPTime, depth: number): SliceRect
|undefined {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
- const pxEnd = timeScale.timeToPx(visibleWindowTime.end);
- const left = Math.max(timeScale.timeToPx(tStart), 0);
- const right = Math.min(timeScale.timeToPx(tEnd), pxEnd);
+ const {
+ visibleTimeScale: timeScale,
+ visibleWindowTime,
+ windowSpan,
+ } = globals.frontendLocalState;
+
+ const pxEnd = windowSpan.end;
+ const left = Math.max(timeScale.tpTimeToPx(tStart), 0);
+ const right = Math.min(timeScale.tpTimeToPx(tEnd), pxEnd);
+
+ const visible =
+ !(visibleWindowTime.start.gt(tEnd) || visibleWindowTime.end.lt(tStart));
+
return {
left,
width: Math.max(right - left, 1),
top: TRACK_PADDING + depth * SLICE_HEIGHT,
height: SLICE_HEIGHT,
- visible:
- !(tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end),
+ visible,
};
}
}
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 9aa8f74..966404c 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -19,17 +19,16 @@
import {Actions} from '../../common/actions';
import {
EngineProxy,
+ LONG,
+ LONG_NULL,
NUM,
- NUM_NULL,
PluginContext,
STR,
TrackInfo,
} from '../../common/plugin_api';
-import {fromNs, toNs} from '../../common/time';
+import {TPDuration, TPTime, tpTimeToSeconds} from '../../common/time';
import {TrackData} from '../../common/track_data';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {NewTrackArgs, Track} from '../../frontend/track';
@@ -49,7 +48,7 @@
minimumDelta: number;
maximumRate: number;
minimumRate: number;
- timestamps: Float64Array;
+ timestamps: BigInt64Array;
lastIds: Float64Array;
minValues: Float64Array;
maxValues: Float64Array;
@@ -62,8 +61,8 @@
name: string;
maximumValue?: number;
minimumValue?: number;
- startTs?: number;
- endTs?: number;
+ startTs?: TPTime;
+ endTs?: TPTime;
namespace: string;
trackId: number;
scale?: CounterScaleOptions;
@@ -76,19 +75,10 @@
private minimumValueSeen = 0;
private maximumDeltaSeen = 0;
private minimumDeltaSeen = 0;
- private maxDurNs = 0;
+ private maxDurNs: TPDuration = 0n;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- const pxSize = this.pxSize();
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
-
if (!this.setup) {
if (this.config.namespace === undefined) {
await this.query(`
@@ -123,7 +113,7 @@
) as maxDur
from ${this.tableName('counter_view')}
`);
- this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
+ this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
const queryRes = await this.query(`
select
@@ -144,14 +134,14 @@
const queryRes = await this.query(`
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
min(value) as minValue,
max(value) as maxValue,
sum(delta) as totalDelta,
value_at_max_ts(ts, id) as lastId,
value_at_max_ts(ts, value) as lastValue
from ${this.tableName('counter_view')}
- where ts >= ${startNs - this.maxDurNs} and ts <= ${endNs}
+ where ts >= ${start - this.maxDurNs} and ts <= ${end}
group by tsq
order by tsq
`);
@@ -169,7 +159,7 @@
maximumRate: 0,
minimumRate: 0,
resolution,
- timestamps: new Float64Array(numRows),
+ timestamps: new BigInt64Array(numRows),
lastIds: new Float64Array(numRows),
minValues: new Float64Array(numRows),
maxValues: new Float64Array(numRows),
@@ -179,7 +169,7 @@
};
const it = queryRes.iter({
- 'tsq': NUM,
+ 'tsq': LONG,
'lastId': NUM,
'minValue': NUM,
'maxValue': NUM,
@@ -187,11 +177,11 @@
'totalDelta': NUM,
});
let lastValue = 0;
- let lastTs = 0;
+ let lastTs = 0n;
for (let row = 0; it.valid(); it.next(), row++) {
- const ts = fromNs(it.tsq);
+ const ts = it.tsq;
const value = it.lastValue;
- const rate = (value - lastValue) / (ts - lastTs);
+ const rate = (value - lastValue) / (tpTimeToSeconds(ts - lastTs));
lastTs = ts;
lastValue = value;
@@ -241,8 +231,8 @@
private mousePos = {x: 0, y: 0};
private hoveredValue: number|undefined = undefined;
- private hoveredTs: number|undefined = undefined;
- private hoveredTsEnd: number|undefined = undefined;
+ private hoveredTs: bigint|undefined = undefined;
+ private hoveredTsEnd: bigint|undefined = undefined;
constructor(args: NewTrackArgs) {
super(args);
@@ -285,7 +275,10 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO: fonts and colors should come from the CSS and not hardcoded here.
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale: timeScale,
+ windowSpan,
+ } = globals.frontendLocalState;
const data = this.data();
// Can't possibly draw anything.
@@ -321,7 +314,7 @@
minimumValue = data.minimumRate;
}
- const endPx = Math.floor(timeScale.timeToPx(visibleWindowTime.end));
+ const endPx = windowSpan.end;
const zeroY = MARGIN_TOP + RECT_HEIGHT / (minimumValue < 0 ? 2 : 1);
// Quantize the Y axis to quarters of powers of tens (7.5K, 10K, 12.5K).
@@ -365,8 +358,8 @@
ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
- const calculateX = (ts: number) => {
- return Math.floor(timeScale.timeToPx(ts));
+ const calculateX = (ts: TPTime) => {
+ return Math.floor(timeScale.tpTimeToPx(ts));
};
const calculateY = (value: number) => {
return MARGIN_TOP + RECT_HEIGHT -
@@ -427,10 +420,10 @@
ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
- const xStart = Math.floor(timeScale.timeToPx(this.hoveredTs));
+ const xStart = Math.floor(timeScale.tpTimeToPx(this.hoveredTs));
const xEnd = this.hoveredTsEnd === undefined ?
endPx :
- Math.floor(timeScale.timeToPx(this.hoveredTsEnd));
+ Math.floor(timeScale.tpTimeToPx(this.hoveredTsEnd));
const y = MARGIN_TOP + RECT_HEIGHT -
Math.round(((this.hoveredValue - yMin) / yRange) * RECT_HEIGHT);
@@ -463,9 +456,10 @@
// TODO(hjd): Refactor this into checkerboardExcept
{
- const endPx = timeScale.timeToPx(visibleWindowTime.end);
- const counterEndPx =
- Math.min(timeScale.timeToPx(this.config.endTs || Infinity), endPx);
+ let counterEndPx = Infinity;
+ if (this.config.endTs) {
+ counterEndPx = Math.min(timeScale.tpTimeToPx(this.config.endTs), endPx);
+ }
// Grey out RHS.
if (counterEndPx < endPx) {
@@ -479,23 +473,23 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end));
+ windowSpan.start,
+ windowSpan.end,
+ timeScale.tpTimeToPx(data.start),
+ timeScale.tpTimeToPx(data.end));
}
onMouseMove(pos: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return;
this.mousePos = pos;
- const {timeScale} = globals.frontendLocalState;
- const time = timeScale.pxToTime(pos.x);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(pos.x);
const values = this.config.scale === 'DELTA_FROM_PREVIOUS' ?
data.totalDeltas :
data.lastValues;
- const [left, right] = searchSegment(data.timestamps, time);
+ const [left, right] = searchSegment(data.timestamps, time.toTPTime());
this.hoveredTs = left === -1 ? undefined : data.timestamps[left];
this.hoveredTsEnd = right === -1 ? undefined : data.timestamps[right];
this.hoveredValue = left === -1 ? undefined : values[left];
@@ -506,20 +500,20 @@
this.hoveredTs = undefined;
}
- onMouseClick({x}: {x: number}) {
+ onMouseClick({x}: {x: number}): boolean {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
- const time = timeScale.pxToTime(x);
- const [left, right] = searchSegment(data.timestamps, time);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.timestamps, time.toTPTime());
if (left === -1) {
return false;
} else {
const counterId = data.lastIds[left];
if (counterId === -1) return true;
globals.makeSelection(Actions.selectCounter({
- leftTs: toNs(data.timestamps[left]),
- rightTs: right !== -1 ? toNs(data.timestamps[right]) : -1,
+ leftTs: data.timestamps[left],
+ rightTs: right !== -1 ? data.timestamps[right] : -1n,
id: counterId,
trackId: this.trackState.id,
}));
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index 8bb2e3b..eec39e4 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -12,16 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {searchSegment} from '../../base/binary_search';
import {assertTrue} from '../../base/logging';
import {hueForCpu} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL, QueryResult} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
-import {TrackData} from '../../common/track_data';
import {
- TrackController,
-} from '../../controller/track_controller';
+ LONG,
+ LONG_NULL,
+ NUM,
+ NUM_NULL,
+ QueryResult,
+} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
+import {TrackData} from '../../common/track_data';
+import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {NewTrackArgs, Track} from '../../frontend/track';
@@ -31,9 +36,9 @@
export interface Data extends TrackData {
maximumValue: number;
- maxTsEnd: number;
+ maxTsEnd: TPTime;
- timestamps: Float64Array;
+ timestamps: BigInt64Array;
minFreqKHz: Uint32Array;
maxFreqKHz: Uint32Array;
lastFreqKHz: Uint32Array;
@@ -51,39 +56,39 @@
class CpuFreqTrackController extends TrackController<Config, Data> {
static readonly kind = CPU_FREQ_TRACK_KIND;
- private maxDurNs = 0;
- private maxTsEndNs = 0;
+ private maxDur: TPDuration = 0n;
+ private maxTsEnd: TPTime = 0n;
private maximumValueSeen = 0;
- private cachedBucketNs = Number.MAX_SAFE_INTEGER;
+ private cachedBucketSize = BIMath.INT64_MAX;
async onSetup() {
await this.createFreqIdleViews();
this.maximumValueSeen = await this.queryMaxFrequency();
- this.maxDurNs = await this.queryMaxSourceDur();
+ this.maxDur = await this.queryMaxSourceDur();
const iter = (await this.query(`
select max(ts) as maxTs, dur, count(1) as rowCount
from ${this.tableName('freq_idle')}
- `)).firstRow({maxTs: NUM_NULL, dur: NUM_NULL, rowCount: NUM});
+ `)).firstRow({maxTs: LONG_NULL, dur: LONG_NULL, rowCount: NUM});
if (iter.maxTs === null || iter.dur === null) {
// We shoulnd't really hit this because trackDecider shouldn't create
// the track in the first place if there are no entries. But could happen
// if only one cpu has no cpufreq data.
return;
}
- this.maxTsEndNs = iter.maxTs + iter.dur;
+ this.maxTsEnd = iter.maxTs + iter.dur;
const rowCount = iter.rowCount;
- const bucketNs = this.cachedBucketSizeNs(rowCount);
- if (bucketNs === undefined) {
+ const bucketSize = this.calcCachedBucketSize(rowCount);
+ if (bucketSize === undefined) {
return;
}
await this.query(`
create table ${this.tableName('freq_idle_cached')} as
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cachedTsq,
+ (ts + ${bucketSize / 2n}) / ${bucketSize} * ${bucketSize} as cachedTsq,
min(freqValue) as minFreq,
max(freqValue) as maxFreq,
value_at_max_ts(ts, freqValue) as lastFreq,
@@ -93,24 +98,16 @@
order by cachedTsq
`);
- this.cachedBucketNs = bucketNs;
+ this.cachedBucketSize = bucketSize;
}
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
// The resolution should always be a power of two for the logic of this
// function to make sense.
- const resolutionNs = toNs(resolution);
- assertTrue(Math.log2(resolutionNs) % 1 === 0);
+ assertTrue(BIMath.popcount(resolution) === 1, `${resolution} not pow of 2`);
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs =
- Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
- const freqResult = await this.queryData(startNs, endNs, bucketNs);
+ const freqResult = await this.queryData(start, end, resolution);
assertTrue(freqResult.isComplete());
const numRows = freqResult.numRows();
@@ -120,8 +117,8 @@
resolution,
length: numRows,
maximumValue: this.maximumValue(),
- maxTsEnd: this.maxTsEndNs,
- timestamps: new Float64Array(numRows),
+ maxTsEnd: this.maxTsEnd,
+ timestamps: new BigInt64Array(numRows),
minFreqKHz: new Uint32Array(numRows),
maxFreqKHz: new Uint32Array(numRows),
lastFreqKHz: new Uint32Array(numRows),
@@ -129,14 +126,14 @@
};
const it = freqResult.iter({
- 'tsq': NUM,
+ 'tsq': LONG,
'minFreq': NUM,
'maxFreq': NUM,
'lastFreq': NUM,
'lastIdleValue': NUM,
});
for (let i = 0; it.valid(); ++i, it.next()) {
- data.timestamps[i] = fromNs(it.tsq);
+ data.timestamps[i] = it.tsq;
data.minFreqKHz[i] = it.minFreq;
data.maxFreqKHz[i] = it.maxFreq;
data.lastFreqKHz[i] = it.lastFreq;
@@ -146,36 +143,36 @@
return data;
}
- private async queryData(startNs: number, endNs: number, bucketNs: number):
+ private async queryData(start: TPTime, end: TPTime, bucketSize: TPDuration):
Promise<QueryResult> {
- const isCached = this.cachedBucketNs <= bucketNs;
+ const isCached = this.cachedBucketSize <= bucketSize;
if (isCached) {
return this.query(`
select
- cachedTsq / ${bucketNs} * ${bucketNs} as tsq,
+ cachedTsq / ${bucketSize} * ${bucketSize} as tsq,
min(minFreq) as minFreq,
max(maxFreq) as maxFreq,
value_at_max_ts(cachedTsq, lastFreq) as lastFreq,
value_at_max_ts(cachedTsq, lastIdleValue) as lastIdleValue
from ${this.tableName('freq_idle_cached')}
where
- cachedTsq >= ${startNs - this.maxDurNs} and
- cachedTsq <= ${endNs}
+ cachedTsq >= ${start - this.maxDur} and
+ cachedTsq <= ${end}
group by tsq
order by tsq
`);
}
const minTsFreq = await this.query(`
select ifnull(max(ts), 0) as minTs from ${this.tableName('freq')}
- where ts < ${startNs}
+ where ts < ${start}
`);
let minTs = minTsFreq.iter({minTs: NUM}).minTs;
if (this.config.idleTrackId !== undefined) {
const minTsIdle = await this.query(`
select ifnull(max(ts), 0) as minTs from ${this.tableName('idle')}
- where ts < ${startNs}
+ where ts < ${start}
`);
minTs = Math.min(minTsIdle.iter({minTs: NUM}).minTs, minTs);
}
@@ -185,7 +182,7 @@
`source_geq(ts, ${minTs})`;
return this.query(`
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (ts + ${bucketSize / 2n}) / ${bucketSize} * ${bucketSize} as tsq,
min(freqValue) as minFreq,
max(freqValue) as maxFreq,
value_at_max_ts(ts, freqValue) as lastFreq,
@@ -193,7 +190,7 @@
from ${this.tableName('freq_idle')}
where
${geqConstraint} and
- ts <= ${endNs}
+ ts <= ${end}
group by tsq
order by tsq
`);
@@ -207,17 +204,17 @@
return result.firstRow({'maxFreq': NUM_NULL}).maxFreq || 0;
}
- private async queryMaxSourceDur(): Promise<number> {
+ private async queryMaxSourceDur(): Promise<TPDuration> {
const maxDurFreqResult = await this.query(
`select ifnull(max(dur), 0) as maxDur from ${this.tableName('freq')}`);
- const maxDurNs = maxDurFreqResult.firstRow({'maxDur': NUM}).maxDur;
+ const maxDur = maxDurFreqResult.firstRow({'maxDur': LONG}).maxDur;
if (this.config.idleTrackId === undefined) {
- return maxDurNs;
+ return maxDur;
}
const maxDurIdleResult = await this.query(
`select ifnull(max(dur), 0) as maxDur from ${this.tableName('idle')}`);
- return Math.max(maxDurNs, maxDurIdleResult.firstRow({maxDur: NUM}).maxDur);
+ return BIMath.max(maxDur, maxDurIdleResult.firstRow({maxDur: LONG}).maxDur);
}
private async createFreqIdleViews() {
@@ -275,8 +272,8 @@
private mousePos = {x: 0, y: 0};
private hoveredValue: number|undefined = undefined;
- private hoveredTs: number|undefined = undefined;
- private hoveredTsEnd: number|undefined = undefined;
+ private hoveredTs: TPTime|undefined = undefined;
+ private hoveredTsEnd: TPTime|undefined = undefined;
private hoveredIdle: number|undefined = undefined;
constructor(args: NewTrackArgs) {
@@ -289,7 +286,11 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO: fonts and colors should come from the CSS and not hardcoded here.
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale,
+ visibleWindowTime,
+ windowSpan,
+ } = globals.frontendLocalState;
const data = this.data();
if (data === undefined || data.timestamps.length === 0) {
@@ -302,7 +303,7 @@
assertTrue(data.timestamps.length === data.maxFreqKHz.length);
assertTrue(data.timestamps.length === data.lastIdleValues.length);
- const endPx = timeScale.timeToPx(visibleWindowTime.end);
+ const endPx = windowSpan.end;
const zeroY = MARGIN_TOP + RECT_HEIGHT;
// Quantize the Y axis to quarters of powers of tens (7.5K, 10K, 12.5K).
@@ -325,18 +326,19 @@
ctx.fillStyle = `hsl(${hue}, ${saturation}%, 70%)`;
ctx.strokeStyle = `hsl(${hue}, ${saturation}%, 55%)`;
- const calculateX = (timestamp: number) => {
- return Math.floor(timeScale.timeToPx(timestamp));
+ const calculateX = (timestamp: TPTime) => {
+ return Math.floor(visibleTimeScale.tpTimeToPx(timestamp));
};
const calculateY = (value: number) => {
return zeroY - Math.round((value / yMax) * RECT_HEIGHT);
};
- const [rawStartIdx] =
- searchSegment(data.timestamps, visibleWindowTime.start);
+ const start = visibleWindowTime.start;
+ const end = visibleWindowTime.end;
+ const [rawStartIdx] = searchSegment(data.timestamps, start.toTPTime());
const startIdx = rawStartIdx === -1 ? 0 : rawStartIdx;
- const [, rawEndIdx] = searchSegment(data.timestamps, visibleWindowTime.end);
+ const [, rawEndIdx] = searchSegment(data.timestamps, end.toTPTime());
const endIdx = rawEndIdx === -1 ? data.timestamps.length : rawEndIdx;
ctx.beginPath();
@@ -383,10 +385,10 @@
// coordinates. Instead we use floating point which prevents flickering as
// we pan and zoom; this relies on the browser anti-aliasing pixels
// correctly.
- const x = timeScale.timeToPx(data.timestamps[i]);
+ const x = visibleTimeScale.tpTimeToPx(data.timestamps[i]);
const xEnd = i === data.lastIdleValues.length - 1 ?
finalX :
- timeScale.timeToPx(data.timestamps[i + 1]);
+ visibleTimeScale.tpTimeToPx(data.timestamps[i + 1]);
const width = xEnd - x;
const height = calculateY(data.lastFreqKHz[i]) - zeroY;
@@ -402,10 +404,10 @@
ctx.fillStyle = `hsl(${hue}, 45%, 75%)`;
ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`;
- const xStart = Math.floor(timeScale.timeToPx(this.hoveredTs));
+ const xStart = Math.floor(visibleTimeScale.tpTimeToPx(this.hoveredTs));
const xEnd = this.hoveredTsEnd === undefined ?
endPx :
- Math.floor(timeScale.timeToPx(this.hoveredTsEnd));
+ Math.floor(visibleTimeScale.tpTimeToPx(this.hoveredTsEnd));
const y = zeroY - Math.round((this.hoveredValue / yMax) * RECT_HEIGHT);
// Highlight line.
@@ -446,20 +448,20 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end));
+ windowSpan.start,
+ windowSpan.end,
+ visibleTimeScale.tpTimeToPx(data.start),
+ visibleTimeScale.tpTimeToPx(data.end));
}
onMouseMove(pos: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return;
this.mousePos = pos;
- const {timeScale} = globals.frontendLocalState;
- const time = timeScale.pxToTime(pos.x);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(pos.x);
- const [left, right] = searchSegment(data.timestamps, time);
+ const [left, right] = searchSegment(data.timestamps, time.toTPTime());
this.hoveredTs = left === -1 ? undefined : data.timestamps[left];
this.hoveredTsEnd = right === -1 ? undefined : data.timestamps[right];
this.hoveredValue = left === -1 ? undefined : data.lastFreqKHz[left];
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index eee7b17..abf2536 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -17,8 +17,8 @@
import {Actions} from '../../common/actions';
import {hslForSlice} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
-import {NUM} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, NUM} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {
TrackController,
@@ -36,7 +36,7 @@
export interface Data extends TrackData {
ids: Float64Array;
- tsStarts: Float64Array;
+ tsStarts: BigInt64Array;
callsiteId: Uint32Array;
}
@@ -46,7 +46,7 @@
class CpuProfileTrackController extends TrackController<Config, Data> {
static readonly kind = CPU_PROFILE_TRACK_KIND;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
const query = `select
id,
@@ -64,11 +64,11 @@
resolution,
length: numRows,
ids: new Float64Array(numRows),
- tsStarts: new Float64Array(numRows),
+ tsStarts: new BigInt64Array(numRows),
callsiteId: new Uint32Array(numRows),
};
- const it = result.iter({id: NUM, ts: NUM, callsiteId: NUM});
+ const it = result.iter({id: NUM, ts: LONG, callsiteId: NUM});
for (let row = 0; it.valid(); it.next(), ++row) {
data.ids[row] = it.id;
data.tsStarts[row] = it.ts;
@@ -93,7 +93,7 @@
private centerY = this.getHeight() / 2 + BAR_HEIGHT;
private markerWidth = (this.getHeight() - MARGIN_TOP - BAR_HEIGHT) / 2;
- private hoveredTs: number|undefined = undefined;
+ private hoveredTs: TPTime|undefined = undefined;
constructor(args: NewTrackArgs) {
super(args);
@@ -105,7 +105,7 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
const {
- timeScale,
+ visibleTimeScale: timeScale,
} = globals.frontendLocalState;
const data = this.data();
@@ -120,7 +120,7 @@
const strokeWidth = isSelected ? 3 : 0;
this.drawMarker(
ctx,
- timeScale.timeToPx(fromNs(centerX)),
+ timeScale.tpTimeToPx(centerX),
this.centerY,
isHovered,
strokeWidth,
@@ -146,8 +146,8 @@
if (clusterStartIndex !== clusterEndIndex) {
const startX = data.tsStarts[clusterStartIndex];
const endX = data.tsStarts[clusterEndIndex];
- const leftPx = timeScale.timeToPx(fromNs(startX)) - this.markerWidth;
- const rightPx = timeScale.timeToPx(fromNs(endX)) + this.markerWidth;
+ const leftPx = timeScale.tpTimeToPx(startX) - this.markerWidth;
+ const rightPx = timeScale.tpTimeToPx(endX) + this.markerWidth;
const width = rightPx - leftPx;
ctx.fillStyle = colorForSample(callsiteId, false);
ctx.fillRect(leftPx, MARGIN_TOP, width, BAR_HEIGHT);
@@ -179,9 +179,11 @@
onMouseMove({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return;
- const {timeScale} = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStarts, time);
+ const {
+ visibleTimeScale: timeScale,
+ } = globals.frontendLocalState;
+ const time = timeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
this.hoveredTs = index === -1 ? undefined : data.tsStarts[index];
}
@@ -193,10 +195,12 @@
onMouseClick({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
+ const {
+ visibleTimeScale: timeScale,
+ } = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStarts, time);
+ const time = timeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
@@ -217,13 +221,13 @@
right: number): number {
let index = -1;
if (left !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStarts[left]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[left]);
if (this.isInMarker(x, y, centerX)) {
index = left;
}
}
if (right !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStarts[right]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[right]);
if (this.isInMarker(x, y, centerX)) {
index = right;
}
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 0953d2f..2b2d03d 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -12,6 +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 {search, searchEq, searchSegment} from '../../base/binary_search';
import {assertTrue} from '../../base/logging';
import {Actions} from '../../common/actions';
@@ -22,8 +23,12 @@
} from '../../common/canvas_utils';
import {colorForThread} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
-import {NUM} from '../../common/query_result';
-import {fromNs, timeToString, toNs} from '../../common/time';
+import {LONG, NUM} from '../../common/query_result';
+import {
+ TPDuration,
+ TPTime,
+ tpTimeToString,
+} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {
TrackController,
@@ -37,8 +42,8 @@
export interface Data extends TrackData {
// Slices are stored in a columnar fashion. All fields have the same length.
ids: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
utids: Uint32Array;
isIncomplete: Uint8Array;
lastRowId: number;
@@ -51,8 +56,8 @@
class CpuSliceTrackController extends TrackController<Config, Data> {
static readonly kind = CPU_SLICE_TRACK_KIND;
- private cachedBucketNs = Number.MAX_SAFE_INTEGER;
- private maxDurNs = 0;
+ private cachedBucketSize = BIMath.INT64_MAX;
+ private maxDur: TPDuration = 0n;
private lastRowId = -1;
async onSetup() {
@@ -78,18 +83,18 @@
`);
this.lastRowId = queryLastSlice.firstRow({lastSliceId: NUM}).lastSliceId;
- const row = queryRes.firstRow({maxDur: NUM, rowCount: NUM});
- this.maxDurNs = row.maxDur;
+ const row = queryRes.firstRow({maxDur: LONG, rowCount: NUM});
+ this.maxDur = row.maxDur;
const rowCount = row.rowCount;
- const bucketNs = this.cachedBucketSizeNs(rowCount);
- if (bucketNs === undefined) {
+ const bucketSize = this.calcCachedBucketSize(rowCount);
+ if (bucketSize === undefined) {
return;
}
await this.query(`
create table ${this.tableName('sched_cached')} as
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cached_tsq,
+ (ts + ${bucketSize / 2n}) / ${bucketSize} * ${bucketSize} as cached_tsq,
ts,
max(dur) as dur,
utid,
@@ -99,29 +104,17 @@
group by cached_tsq, isIncomplete
order by cached_tsq
`);
- this.cachedBucketNs = bucketNs;
+ this.cachedBucketSize = bucketSize;
}
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const resolutionNs = toNs(resolution);
+ assertTrue(BIMath.popcount(resolution) === 1, `${resolution} not pow of 2`);
- // The resolution should always be a power of two for the logic of this
- // function to make sense.
- assertTrue(Math.log2(resolutionNs) % 1 === 0);
-
- const boundStartNs = toNs(start);
- const boundEndNs = toNs(end);
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs =
- Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
-
- const isCached = this.cachedBucketNs <= bucketNs;
+ const isCached = this.cachedBucketSize <= resolution;
const queryTsq = isCached ?
- `cached_tsq / ${bucketNs} * ${bucketNs}` :
- `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+ `cached_tsq / ${resolution} * ${resolution}` :
+ `(ts + ${resolution / 2n}) / ${resolution} * ${resolution}`;
const queryTable =
isCached ? this.tableName('sched_cached') : this.tableName('sched');
const constraintColumn = isCached ? 'cached_tsq' : 'ts';
@@ -136,8 +129,8 @@
isIncomplete
from ${queryTable}
where
- ${constraintColumn} >= ${boundStartNs - this.maxDurNs} and
- ${constraintColumn} <= ${boundEndNs}
+ ${constraintColumn} >= ${start - this.maxDur} and
+ ${constraintColumn} <= ${end}
group by tsq, isIncomplete
order by tsq
`);
@@ -150,29 +143,34 @@
length: numRows,
lastRowId: this.lastRowId,
ids: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
utids: new Uint32Array(numRows),
isIncomplete: new Uint8Array(numRows),
};
- const it = queryRes.iter(
- {tsq: NUM, ts: NUM, dur: NUM, utid: NUM, id: NUM, isIncomplete: NUM});
+ const it = queryRes.iter({
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
+ utid: NUM,
+ id: NUM,
+ isIncomplete: NUM,
+ });
for (let row = 0; it.valid(); it.next(), row++) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ const startQ = it.tsq;
+ const start = it.ts;
+ const dur = it.dur;
+ const end = start + dur;
// If the slice is incomplete, the end calculated later.
if (!it.isIncomplete) {
- let endNsQ =
- Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
- slices.ends[row] = fromNs(endNsQ);
+ const minEnd = startQ + resolution;
+ const endQ = BIMath.max(BIMath.quant(end, resolution), minEnd);
+ slices.ends[row] = endQ;
}
- slices.starts[row] = fromNs(startNsQ);
+ slices.starts[row] = startQ;
slices.utids[row] = it.utid;
slices.ids[row] = it.id;
slices.isIncomplete[row] = it.isIncomplete;
@@ -185,12 +183,10 @@
if (!slices.isIncomplete[row]) {
continue;
}
- const endNs =
- row === slices.length - 1 ? boundEndNs : toNs(slices.starts[row + 1]);
-
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, toNs(slices.starts[row]) + bucketNs);
- slices.ends[row] = fromNs(endNsQ);
+ const endTime = row === slices.length - 1 ? end : slices.starts[row + 1];
+ const minEnd = slices.starts[row] + resolution;
+ const endQ = BIMath.max(BIMath.quant(endTime, resolution), minEnd);
+ slices.ends[row] = endQ;
}
return slices;
}
@@ -223,7 +219,7 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO: fonts and colors should come from the CSS and not hardcoded here.
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {visibleTimeScale, windowSpan} = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
@@ -233,28 +229,36 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end));
+ windowSpan.start,
+ windowSpan.end,
+ visibleTimeScale.tpTimeToPx(data.start),
+ visibleTimeScale.tpTimeToPx(data.end));
this.renderSlices(ctx, data);
}
renderSlices(ctx: CanvasRenderingContext2D, data: Data): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale,
+ visibleTimeSpan,
+ visibleWindowTime,
+ } = globals.frontendLocalState;
assertTrue(data.starts.length === data.ends.length);
assertTrue(data.starts.length === data.utids.length);
+ const visWindowEndPx = visibleTimeScale.hpTimeToPx(visibleWindowTime.end);
+
ctx.textAlign = 'center';
ctx.font = '12px Roboto Condensed';
const charWidth = ctx.measureText('dbpqaouk').width / 8;
- const rawStartIdx =
- data.ends.findIndex((end) => end >= visibleWindowTime.start);
+ const startTime = visibleTimeSpan.start;
+ const endTime = visibleTimeSpan.end;
+
+ const rawStartIdx = data.ends.findIndex((end) => end >= startTime);
const startIdx = rawStartIdx === -1 ? 0 : rawStartIdx;
- const [, rawEndIdx] = searchSegment(data.starts, visibleWindowTime.end);
+ const [, rawEndIdx] = searchSegment(data.starts, endTime);
const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx;
for (let i = startIdx; i < endIdx; i++) {
@@ -266,10 +270,10 @@
// window, else it might spill over the window and the end would not be
// visible as a zigzag line.
if (data.ids[i] === data.lastRowId && data.isIncomplete[i]) {
- tEnd = visibleWindowTime.end;
+ tEnd = endTime;
}
- const rectStart = timeScale.timeToPx(tStart);
- const rectEnd = timeScale.timeToPx(tEnd);
+ const rectStart = visibleTimeScale.tpTimeToPx(tStart);
+ const rectEnd = visibleTimeScale.tpTimeToPx(tEnd);
const rectWidth = Math.max(1, rectEnd - rectStart);
const threadInfo = globals.threads.get(utid);
@@ -317,8 +321,7 @@
title = `${threadInfo.threadName} [${threadInfo.tid}]`;
}
}
- const right =
- Math.min(timeScale.timeToPx(visibleWindowTime.end), rectEnd);
+ const right = Math.min(visWindowEndPx, rectEnd);
const left = Math.max(rectStart, 0);
const visibleWidth = Math.max(right - left, 1);
title = cropText(title, charWidth, visibleWidth);
@@ -341,8 +344,8 @@
const tEnd = data.ends[startIndex];
const utid = data.utids[startIndex];
const color = colorForThread(globals.threads.get(utid));
- const rectStart = timeScale.timeToPx(tStart);
- const rectEnd = timeScale.timeToPx(tEnd);
+ const rectStart = visibleTimeScale.tpTimeToPx(tStart);
+ const rectEnd = visibleTimeScale.tpTimeToPx(tEnd);
const rectWidth = Math.max(1, rectEnd - rectStart);
// Draw a rectangle around the slice that is currently selected.
@@ -353,7 +356,7 @@
ctx.closePath();
// Draw arrow from wakeup time of current slice.
if (details.wakeupTs) {
- const wakeupPos = timeScale.timeToPx(details.wakeupTs);
+ const wakeupPos = visibleTimeScale.tpTimeToPx(details.wakeupTs);
const latencyWidth = rectStart - wakeupPos;
drawDoubleHeadedArrow(
ctx,
@@ -362,7 +365,8 @@
latencyWidth,
latencyWidth >= 20);
// Latency time with a white semi-transparent background.
- const displayText = timeToString(tStart - details.wakeupTs);
+ const latency = tStart - details.wakeupTs;
+ const displayText = tpTimeToString(latency);
const measured = ctx.measureText(displayText);
if (latencyWidth >= measured.width + 2) {
ctx.fillStyle = 'rgba(255,255,255,0.7)';
@@ -383,7 +387,8 @@
// Draw diamond if the track being drawn is the cpu of the waker.
if (this.config.cpu === details.wakerCpu && details.wakeupTs) {
- const wakeupPos = Math.floor(timeScale.timeToPx(details.wakeupTs));
+ const wakeupPos =
+ Math.floor(visibleTimeScale.tpTimeToPx(details.wakeupTs));
ctx.beginPath();
ctx.moveTo(wakeupPos, MARGIN_TOP + RECT_HEIGHT / 2 + 8);
ctx.fillStyle = 'black';
@@ -411,20 +416,20 @@
const data = this.data();
this.mousePos = pos;
if (data === undefined) return;
- const {timeScale} = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
if (pos.y < MARGIN_TOP || pos.y > MARGIN_TOP + RECT_HEIGHT) {
this.utidHoveredInThisTrack = -1;
globals.dispatch(Actions.setHoveredUtidAndPid({utid: -1, pid: -1}));
return;
}
- const t = timeScale.pxToTime(pos.x);
+ const t = visibleTimeScale.pxToHpTime(pos.x);
let hoveredUtid = -1;
for (let i = 0; i < data.starts.length; i++) {
const tStart = data.starts[i];
const tEnd = data.ends[i];
const utid = data.utids[i];
- if (tStart <= t && t <= tEnd) {
+ if (t.gte(tStart) && t.lt(tEnd)) {
hoveredUtid = utid;
break;
}
@@ -445,9 +450,9 @@
onMouseClick({x}: {x: number}) {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
- const time = timeScale.pxToTime(x);
- const index = search(data.starts, time);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(x);
+ const index = search(data.starts, time.toTPTime());
const id = index === -1 ? undefined : data.ids[index];
if (!id || this.utidHoveredInThisTrack === -1) return false;
globals.makeSelection(
diff --git a/ui/src/tracks/debug/add_debug_track_menu.ts b/ui/src/tracks/debug/add_debug_track_menu.ts
index 2982773..32bc871 100644
--- a/ui/src/tracks/debug/add_debug_track_menu.ts
+++ b/ui/src/tracks/debug/add_debug_track_menu.ts
@@ -89,7 +89,7 @@
m(FormLabel,
{for: 'track_name',
},
- 'Name'),
+ 'Track name'),
m(TextInput, {
id: 'track_name',
onkeydown: (e: KeyboardEvent) => {
@@ -109,7 +109,7 @@
FormButtonBar,
m(Button, {
label: 'Show',
- className: 'pf-close-parent-popup-on-click',
+ dismissPopup: true,
onclick: (e: Event) => {
e.preventDefault();
addDebugTrack(
diff --git a/ui/src/tracks/debug/details_tab.ts b/ui/src/tracks/debug/details_tab.ts
index c168eb6..50c2319 100644
--- a/ui/src/tracks/debug/details_tab.ts
+++ b/ui/src/tracks/debug/details_tab.ts
@@ -15,19 +15,21 @@
import m from 'mithril';
import {ColumnType} from '../../common/query_result';
+import {tpDurationFromSql, tpTimeFromSql} from '../../common/time';
import {
BottomTab,
bottomTabRegistry,
NewBottomTabArgs,
} from '../../frontend/bottom_tab';
import {globals} from '../../frontend/globals';
-import {timestampFromSqlNanos} from '../../frontend/sql_types';
+import {asTPTimestamp} from '../../frontend/sql_types';
import {Duration} from '../../frontend/widgets/duration';
import {Timestamp} from '../../frontend/widgets/timestamp';
-import {Tree, TreeNode} from '../../frontend/widgets/tree';
+import {dictToTree} from '../../frontend/widgets/tree';
+
import {ARG_PREFIX} from './add_debug_track_menu';
-interface DebugSliceDetalsTabConfig {
+interface DebugSliceDetailsTabConfig {
sqlTableName: string;
id: number;
}
@@ -42,18 +44,8 @@
return val.toString();
}
-function dictToTree(dict: {[key: string]: m.Child}): m.Children {
- const children: m.Child[] = [];
- for (const key of Object.keys(dict)) {
- children.push(m(TreeNode, {
- left: key,
- right: dict[key],
- }));
- }
- return m(Tree, children);
-}
-
-export class DebugSliceDetailsTab extends BottomTab<DebugSliceDetalsTabConfig> {
+export class DebugSliceDetailsTab extends
+ BottomTab<DebugSliceDetailsTabConfig> {
static readonly kind = 'org.perfetto.DebugSliceDetailsTab';
data: {[key: string]: ColumnType}|undefined;
@@ -78,12 +70,11 @@
if (this.data === undefined) {
return m('h2', 'Loading');
}
- // TODO(stevegolton): These type assertions are dangerous, but no more
- // dangerous than they used to be before this change.
const left = dictToTree({
'Name': this.data['name'] as string,
- 'Start time': m(Timestamp, {ts: timestampFromSqlNanos(this.data['ts'])}),
- 'Duration': m(Duration, {dur: Number(this.data['dur'])}),
+ 'Start time':
+ m(Timestamp, {ts: asTPTimestamp(tpTimeFromSql(this.data['ts']))}),
+ 'Duration': m(Duration, {dur: tpDurationFromSql(this.data['dur'])}),
'Debug slice id': `${this.config.sqlTableName}[${this.config.id}]`,
});
const args: {[key: string]: m.Child} = {};
@@ -93,7 +84,7 @@
}
}
return m(
- 'div.details-panel',
+ '.details-panel',
m('header.overview', m('span', 'Debug Slice')),
m('.details-table-multicolumn',
{
diff --git a/ui/src/tracks/debug/slice_track.ts b/ui/src/tracks/debug/slice_track.ts
index a871a9f..664840d 100644
--- a/ui/src/tracks/debug/slice_track.ts
+++ b/ui/src/tracks/debug/slice_track.ts
@@ -78,8 +78,8 @@
globals.dispatch(Actions.selectDebugSlice({
id: args.slice.id,
sqlTableName: this.config.sqlTableName,
- startS: args.slice.startS,
- durationS: args.slice.durationS,
+ start: args.slice.start,
+ duration: args.slice.duration,
trackId: this.trackId,
}));
}
diff --git a/ui/src/tracks/expected_frames/index.ts b/ui/src/tracks/expected_frames/index.ts
index f2ab086..3b7d7e2 100644
--- a/ui/src/tracks/expected_frames/index.ts
+++ b/ui/src/tracks/expected_frames/index.ts
@@ -19,12 +19,13 @@
import {NewTrackArgs, Track} from '../../frontend/track';
import {ChromeSliceTrack} from '../chrome_slices';
-import {NUM, NUM_NULL, STR} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {
TrackController,
} from '../../controller/track_controller';
import {PluginContext} from '../../common/plugin_api';
+import {BigintMath as BIMath} from '../../base/bigint_math';
export interface Config {
maxDepth: number;
@@ -35,8 +36,8 @@
// Slices are stored in a columnar fashion. All fields have the same length.
strings: string[];
sliceIds: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
depths: Uint16Array;
titles: Uint16Array; // Index in |strings|.
colors?: Uint16Array; // Index in |strings|.
@@ -46,32 +47,23 @@
class ExpectedFramesSliceTrackController extends TrackController<Config, Data> {
static readonly kind = EXPECTED_FRAMES_SLICE_TRACK_KIND;
- private maxDurNs = 0;
+ private maxDurNs: TPDuration = 0n;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- const pxSize = this.pxSize();
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
-
- if (this.maxDurNs === 0) {
+ if (this.maxDurNs === 0n) {
const maxDurResult = await this.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.config.trackIds.join(',')}'
`);
- this.maxDurNs = maxDurResult.firstRow({maxDur: NUM_NULL}).maxDur || 0;
+ this.maxDurNs = maxDurResult.firstRow({maxDur: LONG_NULL}).maxDur || 0n;
}
const queryRes = await this.query(`
SELECT
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (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 layoutDepth,
@@ -82,8 +74,8 @@
from experimental_slice_layout
where
filter_track_ids = '${this.config.trackIds.join(',')}' and
- ts >= ${startNs - this.maxDurNs} and
- ts <= ${endNs}
+ ts >= ${start - this.maxDurNs} and
+ ts <= ${end}
group by tsq, layout_depth
order by tsq, layout_depth
`);
@@ -96,8 +88,8 @@
length: numRows,
strings: [],
sliceIds: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
depths: new Uint16Array(numRows),
titles: new Uint16Array(numRows),
colors: new Uint16Array(numRows),
@@ -117,9 +109,9 @@
const greenIndex = internString('#4CAF50');
const it = queryRes.iter({
- tsq: NUM,
- ts: NUM,
- dur: NUM,
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
layoutDepth: NUM,
id: NUM,
name: STR,
@@ -127,16 +119,15 @@
isIncomplete: NUM,
});
for (let row = 0; it.valid(); it.next(), ++row) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ 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);
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
-
- slices.starts[row] = fromNs(startNsQ);
- slices.ends[row] = fromNs(endNsQ);
+ slices.starts[row] = startQ;
+ slices.ends[row] = endQ;
slices.depths[row] = it.layoutDepth;
slices.titles[row] = internString(it.name);
slices.sliceIds[row] = it.id;
diff --git a/ui/src/tracks/ftrace/index.ts b/ui/src/tracks/ftrace/index.ts
index e5913fa..fc0b9e8 100644
--- a/ui/src/tracks/ftrace/index.ts
+++ b/ui/src/tracks/ftrace/index.ts
@@ -16,8 +16,9 @@
import {colorForString} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, STR} from '../../common/query_result';
-import {fromNs, toNsCeil, toNsFloor} from '../../common/time';
+import {LONG, STR} from '../../common/query_result';
+import {TPDuration} from '../../common/time';
+import {TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {LIMIT} from '../../common/track_data';
import {
@@ -29,7 +30,7 @@
export interface Data extends TrackData {
- timestamps: Float64Array;
+ timestamps: BigInt64Array;
names: string[];
}
@@ -46,14 +47,8 @@
class FtraceRawTrackController extends TrackController<Config, Data> {
static readonly kind = FTRACE_RAW_TRACK_KIND;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNsFloor(start);
- const endNs = toNsCeil(end);
-
- // |resolution| is in s/px the frontend wants.
- const quantNs = toNsCeil(resolution);
-
const excludeList = Array.from(globals.state.ftraceFilter.excludedNames);
const excludeListSql = excludeList.map((s) => `'${s}'`).join(',');
const cpuFilter =
@@ -61,13 +56,13 @@
const queryRes = await this.query(`
select
- cast(ts / ${quantNs} as integer) * ${quantNs} as tsQuant,
+ cast(ts / ${resolution} as integer) * ${resolution} as tsQuant,
type,
name
from ftrace_event
where
name not in (${excludeListSql}) and
- ts >= ${startNs} and ts <= ${endNs} ${cpuFilter}
+ ts >= ${start} and ts <= ${end} ${cpuFilter}
group by tsQuant
order by tsQuant limit ${LIMIT};`);
@@ -77,15 +72,15 @@
end,
resolution,
length: rowCount,
- timestamps: new Float64Array(rowCount),
+ timestamps: new BigInt64Array(rowCount),
names: [],
};
const it = queryRes.iter(
- {tsQuant: NUM, type: STR, name: STR},
+ {tsQuant: LONG, type: STR, name: STR},
);
for (let row = 0; it.valid(); it.next(), row++) {
- result.timestamps[row] = fromNs(it.tsQuant);
+ result.timestamps[row] = it.tsQuant;
result.names[row] = it.name;
}
return result;
@@ -107,16 +102,19 @@
}
renderCanvas(ctx: CanvasRenderingContext2D): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale,
+ windowSpan,
+ } = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
- const dataStartPx = timeScale.timeToPx(data.start);
- const dataEndPx = timeScale.timeToPx(data.end);
- const visibleStartPx = timeScale.timeToPx(visibleWindowTime.start);
- const visibleEndPx = timeScale.timeToPx(visibleWindowTime.end);
+ const dataStartPx = visibleTimeScale.tpTimeToPx(data.start);
+ const dataEndPx = visibleTimeScale.tpTimeToPx(data.end);
+ const visibleStartPx = windowSpan.start;
+ const visibleEndPx = windowSpan.end;
checkerboardExcept(
ctx,
@@ -137,7 +135,7 @@
${Math.min(color.l + 10, 60)}%
)`;
ctx.fillStyle = hsl;
- const xPos = Math.floor(timeScale.timeToPx(data.timestamps[i]));
+ const xPos = Math.floor(visibleTimeScale.tpTimeToPx(data.timestamps[i]));
// Draw a diamond over the event
ctx.save();
diff --git a/ui/src/tracks/heap_profile/index.ts b/ui/src/tracks/heap_profile/index.ts
index b3eb40a..2de8802 100644
--- a/ui/src/tracks/heap_profile/index.ts
+++ b/ui/src/tracks/heap_profile/index.ts
@@ -17,13 +17,11 @@
import {searchSegment} from '../../base/binary_search';
import {Actions} from '../../common/actions';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, STR} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, STR} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {profileType} from '../../controller/flamegraph_controller';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {FLAMEGRAPH_HOVERED_COLOR} from '../../frontend/flamegraph';
import {globals} from '../../frontend/globals';
import {TimeScale} from '../../frontend/time_scale';
@@ -32,7 +30,7 @@
export const HEAP_PROFILE_TRACK_KIND = 'HeapProfileTrack';
export interface Data extends TrackData {
- tsStarts: Float64Array;
+ tsStarts: BigInt64Array;
types: ProfileType[];
}
@@ -42,7 +40,7 @@
class HeapProfileTrackController extends TrackController<Config, Data> {
static readonly kind = HEAP_PROFILE_TRACK_KIND;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
if (this.config.upid === undefined) {
return {
@@ -50,7 +48,7 @@
end,
resolution,
length: 0,
- tsStarts: new Float64Array(),
+ tsStarts: new BigInt64Array(),
types: new Array<ProfileType>(),
};
}
@@ -73,11 +71,11 @@
end,
resolution,
length: numRows,
- tsStarts: new Float64Array(numRows),
+ tsStarts: new BigInt64Array(numRows),
types: new Array<ProfileType>(numRows),
};
- const it = queryRes.iter({ts: NUM, type: STR});
+ const it = queryRes.iter({ts: LONG, type: STR});
for (let row = 0; it.valid(); it.next(), row++) {
data.tsStarts[row] = it.ts;
data.types[row] = profileType(it.type);
@@ -99,7 +97,7 @@
private centerY = this.getHeight() / 2;
private markerWidth = (this.getHeight() - MARGIN_TOP) / 2;
- private hoveredTs: number|undefined = undefined;
+ private hoveredTs: bigint|undefined = undefined;
constructor(args: NewTrackArgs) {
super(args);
@@ -111,7 +109,7 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
const {
- timeScale,
+ visibleTimeScale: timeScale,
} = globals.frontendLocalState;
const data = this.data();
@@ -126,7 +124,7 @@
const strokeWidth = isSelected ? 3 : 0;
this.drawMarker(
ctx,
- timeScale.timeToPx(fromNs(centerX)),
+ timeScale.tpTimeToPx(centerX),
this.centerY,
isHovered,
strokeWidth);
@@ -155,9 +153,11 @@
onMouseMove({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return;
- const {timeScale} = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStarts, time);
+ const {
+ visibleTimeScale: timeScale,
+ } = globals.frontendLocalState;
+ const time = timeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
this.hoveredTs = index === -1 ? undefined : data.tsStarts[index];
}
@@ -169,10 +169,12 @@
onMouseClick({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
+ const {
+ visibleTimeScale: timeScale,
+ } = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStarts, time);
+ const time = timeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
@@ -192,13 +194,13 @@
right: number): number {
let index = -1;
if (left !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStarts[left]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[left]);
if (this.isInMarker(x, y, centerX)) {
index = left;
}
}
if (right !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStarts[right]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[right]);
if (this.isInMarker(x, y, centerX)) {
index = right;
}
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index cfc73dd..21b13eb 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -15,13 +15,11 @@
import {searchSegment} from '../../base/binary_search';
import {Actions} from '../../common/actions';
import {PluginContext} from '../../common/plugin_api';
-import {NUM} from '../../common/query_result';
+import {LONG} from '../../common/query_result';
import {ProfileType} from '../../common/state';
-import {fromNs, toNs} from '../../common/time';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {FLAMEGRAPH_HOVERED_COLOR} from '../../frontend/flamegraph';
import {globals} from '../../frontend/globals';
import {TimeScale} from '../../frontend/time_scale';
@@ -30,7 +28,7 @@
export const PERF_SAMPLES_PROFILE_TRACK_KIND = 'PerfSamplesProfileTrack';
export interface Data extends TrackData {
- tsStartsNs: Float64Array;
+ tsStarts: BigInt64Array;
}
export interface Config {
@@ -39,7 +37,7 @@
class PerfSamplesProfileTrackController extends TrackController<Config, Data> {
static readonly kind = PERF_SAMPLES_PROFILE_TRACK_KIND;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
if (this.config.upid === undefined) {
return {
@@ -47,7 +45,7 @@
end,
resolution,
length: 0,
- tsStartsNs: new Float64Array(),
+ tsStarts: new BigInt64Array(),
};
}
const queryRes = await this.query(`
@@ -62,12 +60,12 @@
end,
resolution,
length: numRows,
- tsStartsNs: new Float64Array(numRows),
+ tsStarts: new BigInt64Array(numRows),
};
- const it = queryRes.iter({ts: NUM});
+ const it = queryRes.iter({ts: LONG});
for (let row = 0; it.valid(); it.next(), row++) {
- data.tsStartsNs[row] = it.ts;
+ data.tsStarts[row] = it.ts;
}
return data;
}
@@ -87,7 +85,7 @@
private centerY = this.getHeight() / 2;
private markerWidth = (this.getHeight() - MARGIN_TOP) / 2;
- private hoveredTs: number|undefined = undefined;
+ private hoveredTs: TPTime|undefined = undefined;
constructor(args: NewTrackArgs) {
super(args);
@@ -99,14 +97,14 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
const {
- timeScale,
+ visibleTimeScale,
} = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return;
- for (let i = 0; i < data.tsStartsNs.length; i++) {
- const centerX = data.tsStartsNs[i];
+ for (let i = 0; i < data.tsStarts.length; i++) {
+ const centerX = data.tsStarts[i];
const selection = globals.state.currentSelection;
const isHovered = this.hoveredTs === centerX;
const isSelected = selection !== null &&
@@ -115,7 +113,7 @@
const strokeWidth = isSelected ? 3 : 0;
this.drawMarker(
ctx,
- timeScale.timeToPx(fromNs(centerX)),
+ visibleTimeScale.tpTimeToPx(centerX),
this.centerY,
isHovered,
strokeWidth);
@@ -144,11 +142,12 @@
onMouseMove({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return;
- const {timeScale} = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStartsNs, time);
- const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
- this.hoveredTs = index === -1 ? undefined : data.tsStartsNs[index];
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
+ const index =
+ this.findTimestampIndex(left, visibleTimeScale, data, x, y, right);
+ this.hoveredTs = index === -1 ? undefined : data.tsStarts[index];
}
onMouseOut() {
@@ -158,15 +157,16 @@
onMouseClick({x, y}: {x: number, y: number}) {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
+ const {visibleTimeScale} = globals.frontendLocalState;
- const time = toNs(timeScale.pxToTime(x));
- const [left, right] = searchSegment(data.tsStartsNs, time);
+ const time = visibleTimeScale.pxToHpTime(x);
+ const [left, right] = searchSegment(data.tsStarts, time.toTPTime());
- const index = this.findTimestampIndex(left, timeScale, data, x, y, right);
+ const index =
+ this.findTimestampIndex(left, visibleTimeScale, data, x, y, right);
if (index !== -1) {
- const ts = data.tsStartsNs[index];
+ const ts = data.tsStarts[index];
globals.makeSelection(Actions.selectPerfSamples({
id: index,
upid: this.config.upid,
@@ -185,13 +185,13 @@
right: number): number {
let index = -1;
if (left !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStartsNs[left]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[left]);
if (this.isInMarker(x, y, centerX)) {
index = left;
}
}
if (right !== -1) {
- const centerX = timeScale.timeToPx(fromNs(data.tsStartsNs[right]));
+ const centerX = timeScale.tpTimeToPx(data.tsStarts[right]);
if (this.isInMarker(x, y, centerX)) {
index = right;
}
diff --git a/ui/src/tracks/process_scheduling/index.ts b/ui/src/tracks/process_scheduling/index.ts
index 95302b6..270157c 100644
--- a/ui/src/tracks/process_scheduling/index.ts
+++ b/ui/src/tracks/process_scheduling/index.ts
@@ -12,17 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {searchEq, searchRange, searchSegment} from '../../base/binary_search';
+
import {assertTrue} from '../../base/logging';
import {Actions} from '../../common/actions';
import {colorForThread} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, QueryResult} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {LONG, NUM, QueryResult} from '../../common/query_result';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {NewTrackArgs, Track} from '../../frontend/track';
@@ -34,8 +34,8 @@
maxCpu: number;
// Slices are stored in a columnar fashion. All fields have the same length.
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
utids: Uint32Array;
cpus: Uint32Array;
}
@@ -52,8 +52,8 @@
static readonly kind = PROCESS_SCHEDULING_TRACK_KIND;
private maxCpu = 0;
- private maxDurNs = 0;
- private cachedBucketNs = Number.MAX_SAFE_INTEGER;
+ private maxDur = 0n;
+ private cachedBucketSize = BIMath.INT64_MAX;
async onSetup() {
await this.createSchedView();
@@ -67,19 +67,19 @@
const result = (await this.query(`
select ifnull(max(dur), 0) as maxDur, count(1) as count
from ${this.tableName('process_sched')}
- `)).iter({maxDur: NUM, count: NUM});
+ `)).iter({maxDur: LONG, count: NUM});
assertTrue(result.valid());
- this.maxDurNs = result.maxDur;
+ this.maxDur = result.maxDur;
const rowCount = result.count;
- const bucketNs = this.cachedBucketSizeNs(rowCount);
- if (bucketNs === undefined) {
+ const bucketSize = this.calcCachedBucketSize(rowCount);
+ if (bucketSize === undefined) {
return;
}
await this.query(`
create table ${this.tableName('process_sched_cached')} as
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as cached_tsq,
+ (ts + ${bucketSize / 2n}) / ${bucketSize} * ${bucketSize} as cached_tsq,
ts,
max(dur) as dur,
cpu,
@@ -88,27 +88,17 @@
group by cached_tsq, cpu
order by cached_tsq, cpu
`);
- this.cachedBucketNs = bucketNs;
+ this.cachedBucketSize = bucketSize;
}
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
assertTrue(this.config.upid !== null);
- // The resolution should always be a power of two for the logic of this
- // function to make sense.
- const resolutionNs = toNs(resolution);
- assertTrue(Math.log2(resolutionNs) % 1 === 0);
+ // Resolution must always be a power of 2 for this logic to work
+ assertTrue(BIMath.popcount(resolution) === 1, `${resolution} not pow of 2`);
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs =
- Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
-
- const queryRes = await this.queryData(startNs, endNs, bucketNs);
+ const queryRes = await this.queryData(start, end, resolution);
const numRows = queryRes.numRows();
const slices: Data = {
kind: 'slice',
@@ -117,46 +107,48 @@
resolution,
length: numRows,
maxCpu: this.maxCpu,
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
cpus: new Uint32Array(numRows),
utids: new Uint32Array(numRows),
};
const it = queryRes.iter({
- tsq: NUM,
- ts: NUM,
- dur: NUM,
+ tsq: LONG,
+ ts: LONG,
+ dur: LONG,
cpu: NUM,
utid: NUM,
});
for (let row = 0; it.valid(); it.next(), row++) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
+ 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);
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
-
- slices.starts[row] = fromNs(startNsQ);
- slices.ends[row] = fromNs(endNsQ);
+ slices.starts[row] = startQ;
+ slices.ends[row] = endQ;
slices.cpus[row] = it.cpu;
slices.utids[row] = it.utid;
- slices.end = Math.max(slices.ends[row], slices.end);
+ slices.end = BIMath.max(slices.ends[row], slices.end);
}
return slices;
}
- private queryData(startNs: number, endNs: number, bucketNs: number):
+ private queryData(start: TPTime, end: TPTime, bucketSize: TPDuration):
Promise<QueryResult> {
- const isCached = this.cachedBucketNs <= bucketNs;
- const tsq = isCached ? `cached_tsq / ${bucketNs} * ${bucketNs}` :
- `(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs}`;
+ const isCached = this.cachedBucketSize <= bucketSize;
+ const tsq = isCached ?
+ `cached_tsq / ${bucketSize} * ${bucketSize}` :
+ `(ts + ${bucketSize / 2n}) / ${bucketSize} * ${bucketSize}`;
const queryTable = isCached ? this.tableName('process_sched_cached') :
this.tableName('process_sched');
const constraintColumn = isCached ? 'cached_tsq' : 'ts';
+
+ // The mouse move handler depends on slices being sorted by cpu then tsq
return this.query(`
select
${tsq} as tsq,
@@ -166,10 +158,10 @@
utid
from ${queryTable}
where
- ${constraintColumn} >= ${startNs - this.maxDurNs} and
- ${constraintColumn} <= ${endNs}
+ ${constraintColumn} >= ${start - this.maxDur} and
+ ${constraintColumn} <= ${end}
group by tsq, cpu
- order by tsq, cpu
+ order by cpu, tsq
`);
}
@@ -208,7 +200,10 @@
renderCanvas(ctx: CanvasRenderingContext2D): void {
// TODO: fonts and colors should come from the CSS and not hardcoded here.
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale,
+ visibleWindowTime,
+ } = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
@@ -218,19 +213,21 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end));
+ visibleTimeScale.hpTimeToPx(visibleWindowTime.start),
+ visibleTimeScale.hpTimeToPx(visibleWindowTime.end),
+ visibleTimeScale.tpTimeToPx(data.start),
+ visibleTimeScale.tpTimeToPx(data.end));
assertTrue(data.starts.length === data.ends.length);
assertTrue(data.starts.length === data.utids.length);
- const rawStartIdx =
- data.ends.findIndex((end) => end >= visibleWindowTime.start);
+ const startTime = visibleWindowTime.start.toTPTime('floor');
+ const rawStartIdx = data.ends.findIndex((end) => end >= startTime);
const startIdx = rawStartIdx === -1 ? data.starts.length : rawStartIdx;
- const [, rawEndIdx] = searchSegment(data.starts, visibleWindowTime.end);
+
+ const endTime = visibleWindowTime.end.toTPTime('ceil');
+ const [, rawEndIdx] = searchSegment(data.starts, endTime);
const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx;
const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu);
@@ -241,8 +238,8 @@
const utid = data.utids[i];
const cpu = data.cpus[i];
- const rectStart = timeScale.timeToPx(tStart);
- const rectEnd = timeScale.timeToPx(tEnd);
+ const rectStart = visibleTimeScale.tpTimeToPx(tStart);
+ const rectEnd = visibleTimeScale.tpTimeToPx(tEnd);
const rectWidth = rectEnd - rectStart;
if (rectWidth < 0.3) continue;
@@ -294,8 +291,8 @@
const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu);
const cpu = Math.floor((pos.y - MARGIN_TOP) / (cpuTrackHeight + 1));
- const {timeScale} = globals.frontendLocalState;
- const t = timeScale.pxToTime(pos.x);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const t = visibleTimeScale.pxToHpTime(pos.x).toTPTime('floor');
const [i, j] = searchRange(data.starts, t, searchEq(data.cpus, cpu));
if (i === j || i >= data.starts.length || t > data.ends[i]) {
diff --git a/ui/src/tracks/process_summary/index.ts b/ui/src/tracks/process_summary/index.ts
index d2d0ee8..0697ce1 100644
--- a/ui/src/tracks/process_summary/index.ts
+++ b/ui/src/tracks/process_summary/index.ts
@@ -12,15 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath} from '../../base/bigint_math';
+import {assertFalse} from '../../base/logging';
import {colorForTid} from '../../common/colorizer';
import {PluginContext} from '../../common/plugin_api';
import {NUM} from '../../common/query_result';
-import {fromNs, toNs} from '../../common/time';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {LIMIT} from '../../common/track_data';
-import {
- TrackController,
-} from '../../controller/track_controller';
+import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {NewTrackArgs, Track} from '../../frontend/track';
@@ -29,7 +29,7 @@
// TODO(dproy): Consider deduping with CPU summary data.
export interface Data extends TrackData {
- bucketSizeSeconds: number;
+ bucketSize: TPDuration;
utilizations: Float64Array;
}
@@ -45,10 +45,9 @@
static readonly kind = PROCESS_SUMMARY_TRACK;
private setup = false;
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
+ assertFalse(resolution === 0n, 'Resolution cannot be 0');
if (this.setup === false) {
await this.query(
@@ -85,33 +84,30 @@
this.setup = true;
}
- // |resolution| is in s/px we want # ns for 10px window:
+ // |resolution| is in ns/px we want # ns for 10px window:
// Max value with 1 so we don't end up with resolution 0.
- const bucketSizeNs = Math.max(1, Math.round(resolution * 10 * 1e9));
- const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs;
- const windowDurNs = Math.max(1, endNs - windowStartNs);
+ const bucketSize = resolution * 10n;
+ const windowStart = BigintMath.quant(start, bucketSize);
+ const windowDur = BigintMath.max(1n, end - windowStart);
await this.query(`update ${this.tableName('window')} set
- window_start=${windowStartNs},
- window_dur=${windowDurNs},
- quantum=${bucketSizeNs}
+ window_start=${windowStart},
+ window_dur=${windowDur},
+ quantum=${bucketSize}
where rowid = 0;`);
- return this.computeSummary(
- fromNs(windowStartNs), end, resolution, bucketSizeNs);
+ return this.computeSummary(windowStart, end, resolution, bucketSize);
}
private async computeSummary(
- start: number, end: number, resolution: number,
- bucketSizeNs: number): Promise<Data> {
- const startNs = toNs(start);
- const endNs = toNs(end);
- const numBuckets =
- Math.min(Math.ceil((endNs - startNs) / bucketSizeNs), LIMIT);
+ start: TPTime, end: TPTime, resolution: TPDuration,
+ bucketSize: TPDuration): Promise<Data> {
+ const duration = end - start;
+ const numBuckets = Math.min(Number(duration / bucketSize), LIMIT);
const query = `select
quantum_ts as bucket,
- sum(dur)/cast(${bucketSizeNs} as float) as utilization
+ sum(dur)/cast(${bucketSize} as float) as utilization
from ${this.tableName('span')}
group by quantum_ts
limit ${LIMIT}`;
@@ -121,7 +117,7 @@
end,
resolution,
length: numBuckets,
- bucketSizeSeconds: fromNs(bucketSizeNs),
+ bucketSize,
utilizations: new Float64Array(numBuckets),
};
@@ -167,25 +163,28 @@
}
renderCanvas(ctx: CanvasRenderingContext2D): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale,
+ windowSpan,
+ } = globals.frontendLocalState;
const data = this.data();
if (data === undefined) return; // Can't possibly draw anything.
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end));
+ windowSpan.start,
+ windowSpan.end,
+ visibleTimeScale.tpTimeToPx(data.start),
+ visibleTimeScale.tpTimeToPx(data.end));
this.renderSummary(ctx, data);
}
// TODO(dproy): Dedup with CPU slices.
renderSummary(ctx: CanvasRenderingContext2D, data: Data): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
- const startPx = Math.floor(timeScale.timeToPx(visibleWindowTime.start));
+ const {visibleTimeScale, windowSpan} = globals.frontendLocalState;
+ const startPx = windowSpan.start;
const bottomY = TRACK_HEIGHT;
let lastX = startPx;
@@ -202,9 +201,9 @@
for (let i = 0; i < data.utilizations.length; i++) {
// TODO(dproy): Investigate why utilization is > 1 sometimes.
const utilization = Math.min(data.utilizations[i], 1);
- const startTime = i * data.bucketSizeSeconds + data.start;
+ const startTime = BigInt(i) * data.bucketSize + data.start;
- lastX = Math.floor(timeScale.timeToPx(startTime));
+ lastX = Math.floor(visibleTimeScale.tpTimeToPx(startTime));
ctx.lineTo(lastX, lastY);
lastY = MARGIN_TOP + Math.round(SUMMARY_HEIGHT * (1 - utilization));
diff --git a/ui/src/tracks/scroll_jank/event_latency_track.ts b/ui/src/tracks/scroll_jank/event_latency_track.ts
new file mode 100644
index 0000000..d805e74
--- /dev/null
+++ b/ui/src/tracks/scroll_jank/event_latency_track.ts
@@ -0,0 +1,83 @@
+// 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 {v4 as uuidv4} from 'uuid';
+
+import {Engine} from '../../common/engine';
+import {
+ generateSqlWithInternalLayout,
+} from '../../common/internal_layout_utils';
+import {PrimaryTrackSortKey, SCROLLING_TRACK_GROUP} from '../../common/state';
+import {
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {NewTrackArgs, Track} from '../../frontend/track';
+import {DecideTracksResult} from '../chrome_scroll_jank';
+
+interface EventLatencyTrackTypes extends NamedSliceTrackTypes {}
+
+export class EventLatencyTrack extends NamedSliceTrack<EventLatencyTrackTypes> {
+ static readonly kind = 'org.chromium.ScrollJank.event_latencies';
+ createdModels = false;
+
+ static create(args: NewTrackArgs): Track {
+ return new EventLatencyTrack(args);
+ }
+
+ constructor(args: NewTrackArgs) {
+ super(args);
+ }
+
+ async initSqlTable(tableName: string) {
+ if (this.createdModels) {
+ return;
+ }
+ const sql = `CREATE VIEW ${tableName} AS ` + generateSqlWithInternalLayout({
+ columns: ['id', 'ts', 'dur', 'track_id', 'name'],
+ layoutParams: {ts: 'ts', dur: 'dur'},
+ sourceTable: 'slice',
+ whereClause: 'slice.id IN ' +
+ '(SELECT slice_id FROM event_latency_scroll_jank_cause)',
+ });
+ await this.engine.query(sql);
+ this.createdModels = true;
+ }
+
+ // At the moment we will just display the slice details. However, on select,
+ // this behavior should be customized to show jank-related data.
+}
+
+export async function addLatenciesTrack(engine: Engine):
+ Promise<DecideTracksResult> {
+ const result: DecideTracksResult = {
+ tracksToAdd: [],
+ };
+
+ await engine.query(`
+ SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+ `);
+
+ result.tracksToAdd.push({
+ id: uuidv4(),
+ engineId: engine.id,
+ kind: EventLatencyTrack.kind,
+ trackSortKey: PrimaryTrackSortKey.NULL_TRACK,
+ name: 'Scroll Janks',
+ config: {},
+ trackGroup: SCROLLING_TRACK_GROUP,
+ });
+
+ return result;
+}
diff --git a/ui/src/tracks/scroll_jank/index.ts b/ui/src/tracks/scroll_jank/index.ts
new file mode 100644
index 0000000..5f6d86b
--- /dev/null
+++ b/ui/src/tracks/scroll_jank/index.ts
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {featureFlags} from '../../common/feature_flags';
+import {PluginContext} from '../../common/plugin_api';
+import {Selection} from '../../common/state';
+import {CURRENT_SELECTION_TAG} from '../../frontend/details_panel';
+import {globals} from '../../frontend/globals';
+
+import {EventLatencyTrack} from './event_latency_track';
+import {TopLevelScrollDetailsTab} from './scroll_details_tab';
+import {
+ TOP_LEVEL_SCROLL_KIND,
+ TopLevelScrollTrack,
+} from './scroll_track';
+
+export const INPUT_LATENCY_TRACK = 'InputLatency::';
+export const SCROLL_JANK_PLUGIN_ID = 'perfetto.ScrollJank';
+export const ENABLE_SCROLL_JANK_PLUGIN_V2 = featureFlags.register({
+ id: 'enableScrollJankPluginV2',
+ name: 'Enable Scroll Jank plugin V2',
+ description: 'Adds new tracks and visualizations for scroll jank.',
+ defaultValue: false,
+});
+
+function onDetailsPanelSelectionChange(newSelection?: Selection) {
+ if (newSelection === undefined ||
+ newSelection.kind !== TOP_LEVEL_SCROLL_KIND) {
+ return;
+ }
+ const bottomTabList = globals.bottomTabList;
+ if (!bottomTabList) return;
+ bottomTabList.addTab({
+ kind: TopLevelScrollDetailsTab.kind,
+ tag: CURRENT_SELECTION_TAG,
+ config: {
+ sqlTableName: newSelection.sqlTableName,
+ id: newSelection.id,
+ },
+ });
+}
+
+function activate(ctx: PluginContext) {
+ ctx.registerTrack(TopLevelScrollTrack);
+ ctx.registerTrack(EventLatencyTrack);
+ ctx.registerOnDetailsPanelSelectionChange(onDetailsPanelSelectionChange);
+}
+
+export const plugin = {
+ pluginId: SCROLL_JANK_PLUGIN_ID,
+ activate,
+};
diff --git a/ui/src/tracks/scroll_jank/scroll_details_tab.ts b/ui/src/tracks/scroll_jank/scroll_details_tab.ts
new file mode 100644
index 0000000..b13fcae
--- /dev/null
+++ b/ui/src/tracks/scroll_jank/scroll_details_tab.ts
@@ -0,0 +1,90 @@
+// 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.
+
+// Panel for the top-level scrolls. For now, just show the scroll id, but we
+// can add things like scroll event count, janks, etc. as needed.
+
+import m from 'mithril';
+
+import {ColumnType} from '../../common/query_result';
+import {tpDurationFromSql, tpTimeFromSql} from '../../common/time';
+import {
+ BottomTab,
+ bottomTabRegistry,
+ NewBottomTabArgs,
+} from '../../frontend/bottom_tab';
+import {globals} from '../../frontend/globals';
+import {asTPTimestamp} from '../../frontend/sql_types';
+import {Duration} from '../../frontend/widgets/duration';
+import {Timestamp} from '../../frontend/widgets/timestamp';
+import {dictToTree} from '../../frontend/widgets/tree';
+
+interface TopLevelScrollTabConfig {
+ sqlTableName: string;
+ id: number;
+}
+
+export class TopLevelScrollDetailsTab extends
+ BottomTab<TopLevelScrollTabConfig> {
+ static readonly kind = 'org.perfetto.TopLevelScrollDetailsTab';
+
+ data: {[key: string]: ColumnType}|undefined;
+
+ static create(args: NewBottomTabArgs): TopLevelScrollDetailsTab {
+ return new TopLevelScrollDetailsTab(args);
+ }
+
+ constructor(args: NewBottomTabArgs) {
+ super(args);
+
+ this.engine
+ .query(`select * from ${this.config.sqlTableName} where id = ${
+ this.config.id}`)
+ .then((queryResult) => {
+ this.data = queryResult.firstRow({});
+ globals.rafScheduler.scheduleFullRedraw();
+ });
+ }
+
+ viewTab() {
+ if (this.data === undefined) {
+ return m('h2', 'Loading');
+ }
+
+ const left = dictToTree({
+ 'Scroll Id (gesture_scroll_id)': `${this.data['id']}`,
+ 'Start time':
+ m(Timestamp, {ts: asTPTimestamp(tpTimeFromSql(this.data['ts']))}),
+ 'Duration': m(Duration, {dur: tpDurationFromSql(this.data['dur'])}),
+ });
+ return m(
+ '.details-panel',
+ m('header.overview', m('span', `${this.data['name']}`)),
+ m('.details-table-multicolumn', m('.half-width-panel', left)));
+ }
+
+ getTitle(): string {
+ return `Current Chrome Scroll`;
+ }
+
+ isLoading() {
+ return this.data === undefined;
+ }
+
+ renderTabCanvas() {
+ return;
+ }
+}
+
+bottomTabRegistry.register(TopLevelScrollDetailsTab);
diff --git a/ui/src/tracks/scroll_jank/scroll_track.ts b/ui/src/tracks/scroll_jank/scroll_track.ts
new file mode 100644
index 0000000..93dc87d
--- /dev/null
+++ b/ui/src/tracks/scroll_jank/scroll_track.ts
@@ -0,0 +1,116 @@
+// 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 {v4 as uuidv4} from 'uuid';
+
+import {Actions} from '../../common/actions';
+import {Engine} from '../../common/engine';
+import {
+ generateSqlWithInternalLayout,
+} from '../../common/internal_layout_utils';
+import {
+ PrimaryTrackSortKey,
+ SCROLLING_TRACK_GROUP,
+ Selection,
+} from '../../common/state';
+import {TPTime} from '../../common/time';
+import {OnSliceClickArgs} from '../../frontend/base_slice_track';
+import {globals} from '../../frontend/globals';
+import {
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {NewTrackArgs, Track} from '../../frontend/track';
+import {DecideTracksResult} from '../chrome_scroll_jank';
+
+export const TOP_LEVEL_SCROLL_KIND = 'TOP_LEVEL_SCROLL';
+
+export interface TopLevelScrollSelection {
+ kind: 'TOP_LEVEL_SCROLL';
+ id: number;
+ sqlTableName: string;
+ start: TPTime;
+ duration: TPTime;
+}
+
+export {Data} from '../chrome_slices';
+
+interface TopLevelScrollTrackTypes extends NamedSliceTrackTypes {}
+
+export class TopLevelScrollTrack extends
+ NamedSliceTrack<TopLevelScrollTrackTypes> {
+ static readonly kind = 'org.chromium.TopLevelScrolls.scrolls';
+ createdModels = false;
+
+ static create(args: NewTrackArgs): Track {
+ return new TopLevelScrollTrack(args);
+ }
+
+ constructor(args: NewTrackArgs) {
+ super(args);
+ }
+
+ async initSqlTable(tableName: string) {
+ if (this.createdModels) {
+ return;
+ }
+ const sql =
+ `CREATE VIEW ${tableName} AS ` + generateSqlWithInternalLayout({
+ columns: [`printf("Scroll %s", CAST(id AS STRING)) AS name`, '*'],
+ layoutParams: {ts: 'ts', dur: 'dur'},
+ sourceTable: 'chrome_scrolls',
+ orderByClause: 'ts',
+ });
+ await this.engine.query(sql);
+ this.createdModels = true;
+ }
+
+ isSelectionHandled(selection: Selection) {
+ if (selection.kind !== 'TOP_LEVEL_SCROLL') {
+ return false;
+ }
+ return selection.trackId === this.trackId;
+ }
+
+ onSliceClick(args: OnSliceClickArgs<TopLevelScrollTrackTypes['slice']>) {
+ globals.dispatch(Actions.selectTopLevelScrollSlice({
+ id: args.slice.id,
+ sqlTableName: this.tableName,
+ start: args.slice.start,
+ duration: args.slice.duration,
+ trackId: this.trackId,
+ }));
+ }
+}
+
+export async function addTopLevelScrollTrack(engine: Engine):
+ Promise<DecideTracksResult> {
+ const result: DecideTracksResult = {
+ tracksToAdd: [],
+ };
+
+ await engine.query(`SELECT IMPORT('chrome.chrome_scrolls');`);
+
+ result.tracksToAdd.push({
+ id: uuidv4(),
+ engineId: engine.id,
+ kind: TopLevelScrollTrack.kind,
+ trackSortKey: PrimaryTrackSortKey.ASYNC_SLICE_TRACK,
+ name: 'Top Level Scrolls',
+ config: {},
+ trackGroup: SCROLLING_TRACK_GROUP,
+ });
+
+ return result;
+}
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index 3713999..5e90661 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -12,15 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {BigintMath as BIMath} from '../../base/bigint_math';
import {search} from '../../base/binary_search';
import {assertFalse} from '../../base/logging';
import {Actions} from '../../common/actions';
import {cropText} from '../../common/canvas_utils';
import {colorForState} from '../../common/colorizer';
+import {HighPrecisionTimeSpan} from '../../common/high_precision_time';
import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL, STR_NULL} from '../../common/query_result';
+import {LONG, NUM, NUM_NULL, STR_NULL} from '../../common/query_result';
import {translateState} from '../../common/thread_state';
-import {fromNs, toNs} from '../../common/time';
+import {TPDuration, TPTime} from '../../common/time';
import {TrackData} from '../../common/track_data';
import {TrackController} from '../../controller/track_controller';
import {checkerboardExcept} from '../../frontend/checkerboard';
@@ -33,8 +35,8 @@
export interface Data extends TrackData {
strings: string[];
ids: Float64Array;
- starts: Float64Array;
- ends: Float64Array;
+ starts: BigInt64Array;
+ ends: BigInt64Array;
cpu: Int8Array;
state: Uint16Array; // Index into |strings|.
}
@@ -46,7 +48,7 @@
class ThreadStateTrackController extends TrackController<Config, Data> {
static readonly kind = THREAD_STATE_TRACK_KIND;
- private maxDurNs = 0;
+ private maxDurNs: TPDuration = 0n;
async onSetup() {
await this.query(`
@@ -66,23 +68,14 @@
select ifnull(max(dur), 0) as maxDur
from ${this.tableName('thread_state')}
`);
- this.maxDurNs = queryRes.firstRow({maxDur: NUM}).maxDur;
+ this.maxDurNs = queryRes.firstRow({maxDur: LONG}).maxDur;
}
- async onBoundsChange(start: number, end: number, resolution: number):
+ async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration):
Promise<Data> {
- const resolutionNs = toNs(resolution);
- const startNs = toNs(start);
- const endNs = toNs(end);
-
- // ns per quantization bucket (i.e. ns per pixel). /2 * 2 is to force it to
- // be an even number, so we can snap in the middle.
- const bucketNs =
- Math.max(Math.round(resolutionNs * this.pxSize() / 2) * 2, 1);
-
const query = `
select
- (ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
+ (ts + ${resolution / 2n}) / ${resolution} * ${resolution} as tsq,
ts,
state = 'S' as is_sleep,
max(dur) as dur,
@@ -92,8 +85,8 @@
ifnull(id, -1) as id
from ${this.tableName('thread_state')}
where
- ts >= ${startNs - this.maxDurNs} and
- ts <= ${endNs}
+ ts >= ${start - this.maxDurNs} and
+ ts <= ${end}
group by tsq, is_sleep
order by tsq
`;
@@ -107,8 +100,8 @@
resolution,
length: numRows,
ids: new Float64Array(numRows),
- starts: new Float64Array(numRows),
- ends: new Float64Array(numRows),
+ starts: new BigInt64Array(numRows),
+ ends: new BigInt64Array(numRows),
strings: [],
state: new Uint16Array(numRows),
cpu: new Int8Array(numRows),
@@ -127,22 +120,21 @@
return idx;
}
const it = queryRes.iter({
- 'tsq': NUM,
- 'ts': NUM,
- 'dur': NUM,
+ 'tsq': LONG,
+ 'ts': LONG,
+ 'dur': LONG,
'cpu': NUM,
'state': STR_NULL,
'ioWait': NUM_NULL,
'id': NUM,
});
for (let row = 0; it.valid(); it.next(), row++) {
- const startNsQ = it.tsq;
- const startNs = it.ts;
- const durNs = it.dur;
- const endNs = startNs + durNs;
-
- let endNsQ = Math.floor((endNs + bucketNs / 2 - 1) / bucketNs) * bucketNs;
- endNsQ = Math.max(endNsQ, startNsQ + bucketNs);
+ 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);
const cpu = it.cpu;
const state = it.state || undefined;
@@ -151,10 +143,10 @@
// We should never have the end timestamp being the same as the bucket
// start.
- assertFalse(startNsQ === endNsQ);
+ assertFalse(startQ === endQ);
- data.starts[row] = fromNs(startNsQ);
- data.ends[row] = fromNs(endNsQ);
+ data.starts[row] = startQ;
+ data.ends[row] = endQ;
data.state[row] = internState(state, ioWait);
data.ids[row] = id;
data.cpu[row] = cpu;
@@ -186,7 +178,11 @@
}
renderCanvas(ctx: CanvasRenderingContext2D): void {
- const {timeScale, visibleWindowTime} = globals.frontendLocalState;
+ const {
+ visibleTimeScale: timeScale,
+ visibleWindowTime,
+ windowSpan,
+ } = globals.frontendLocalState;
const data = this.data();
const charWidth = ctx.measureText('dbpqaouk').width / 8;
@@ -199,10 +195,10 @@
checkerboardExcept(
ctx,
this.getHeight(),
- timeScale.timeToPx(visibleWindowTime.start),
- timeScale.timeToPx(visibleWindowTime.end),
- timeScale.timeToPx(data.start),
- timeScale.timeToPx(data.end),
+ windowSpan.start,
+ windowSpan.end,
+ timeScale.tpTimeToPx(data.start),
+ timeScale.tpTimeToPx(data.end),
);
ctx.textAlign = 'center';
@@ -220,14 +216,15 @@
const tStart = data.starts[i];
const tEnd = data.ends[i];
const state = data.strings[data.state[i]];
- if (tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end) {
+ const timeSpan = HighPrecisionTimeSpan.fromTpTime(tStart, tEnd);
+ if (!visibleWindowTime.intersects(timeSpan)) {
continue;
}
// Don't display a slice for Task Dead.
if (state === 'x') continue;
- const rectStart = timeScale.timeToPx(tStart);
- const rectEnd = timeScale.timeToPx(tEnd);
+ const rectStart = timeScale.tpTimeToPx(tStart);
+ const rectEnd = timeScale.tpTimeToPx(tEnd);
const rectWidth = rectEnd - rectStart;
const currentSelection = globals.state.currentSelection;
@@ -255,10 +252,9 @@
if (isSelected) {
drawRectOnSelected = () => {
const rectStart =
- Math.max(0 - EXCESS_WIDTH, timeScale.timeToPx(tStart));
+ Math.max(0 - EXCESS_WIDTH, timeScale.tpTimeToPx(tStart));
const rectEnd = Math.min(
- timeScale.timeToPx(visibleWindowTime.end) + EXCESS_WIDTH,
- timeScale.timeToPx(tEnd));
+ windowSpan.end + EXCESS_WIDTH, timeScale.tpTimeToPx(tEnd));
const color = colorForState(state);
ctx.strokeStyle = `hsl(${color.h},${color.s}%,${color.l * 0.7}%)`;
ctx.beginPath();
@@ -278,11 +274,10 @@
onMouseClick({x}: {x: number}) {
const data = this.data();
if (data === undefined) return false;
- const {timeScale} = globals.frontendLocalState;
- const time = timeScale.pxToTime(x);
- const index = search(data.starts, time);
+ const {visibleTimeScale} = globals.frontendLocalState;
+ const time = visibleTimeScale.pxToHpTime(x);
+ const index = search(data.starts, time.toTPTime());
if (index === -1) return false;
-
const id = data.ids[index];
globals.makeSelection(
Actions.selectThreadState({id, trackId: this.trackState.id}));