diff options
author | Daniele Di Proietto <ddiproietto@google.com> | 2023-05-17 17:01:53 +0000 |
---|---|---|
committer | Daniele Di Proietto <ddiproietto@google.com> | 2023-05-17 17:05:54 +0000 |
commit | 4b02fb3c4d903c08bfd00c6a5336cebb7d80fb55 (patch) | |
tree | 35b49166a76473d178ef2c60f946952898d9e8ab | |
parent | c254cef39ade32c4294421b522f0a1e41a86cb4a (diff) | |
parent | bad11ba2234a116bafcce7910d8bad375584c5d1 (diff) | |
download | perfetto-4b02fb3c4d903c08bfd00c6a5336cebb7d80fb55.tar.gz |
Merge commit 'bad11ba2234a116bafcce7910d8bad375584c5d1' into HEAD
Bug: 260112703
Bug: 278884624
Change-Id: I0d7fdad29902e683a40bdade9395da8b0a759b88
529 files changed, 20626 insertions, 6727 deletions
diff --git a/.clang-tidy b/.clang-tidy index 07073df36..8a822c2af 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,4 @@ -Checks: android-cloexec-*,bugprone-*,google-explicit-constructor,android-comparison-in-temp-failure-retry,modernize-use-nullptr,performance-for-range-copy,performance-noexcept-move-constructor,readability-container-size-empty,readability-else-after-return +Checks: android-cloexec-*,bugprone-*,-bugprone-easily-swappable-parameters,google-explicit-constructor,android-comparison-in-temp-failure-retry,modernize-use-nullptr,performance-for-range-copy,performance-noexcept-move-constructor,readability-container-size-empty,readability-else-after-return CheckOptions: - key: bugprone-assert-side-effect.AssertMacros value: 'PERFETTO_DCHECK' diff --git a/Android.bp b/Android.bp index f12c1c39a..f4f9a2d09 100644 --- a/Android.bp +++ b/Android.bp @@ -593,6 +593,7 @@ cc_library_shared { ":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 @@ cc_library_shared { defaults: [ "perfetto_defaults", ], + cflags: [ + "-DZLIB_IMPLEMENTATION", + ], target: { android: { shared_libs: [ "liblog", + "libz", + ], + }, + host: { + static_libs: [ + "libz", ], }, }, @@ -1735,6 +1745,11 @@ filegroup { name: "perfetto_include_perfetto_ext_base_version", } +// GN: //include/perfetto/ext/cloud_trace_processor:cloud_trace_processor +filegroup { + name: "perfetto_include_perfetto_ext_cloud_trace_processor_cloud_trace_processor", +} + // GN: //include/perfetto/ext/ipc:ipc filegroup { name: "perfetto_include_perfetto_ext_ipc_ipc", @@ -2049,11 +2064,13 @@ cc_test { ":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", @@ -2248,6 +2265,8 @@ cc_test { "perfetto_src_trace_processor_metrics_gen_cc_all_webview_metrics_descriptor", "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor", "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics", + "perfetto_src_trace_processor_prelude_table_functions_tables", + "perfetto_src_trace_processor_prelude_tables_views_tables_views", "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib", "perfetto_src_trace_processor_tables_tables_python", ], @@ -2268,6 +2287,48 @@ cc_test { test_config: "PerfettoIntegrationTests.xml", } +// GN: //protos/perfetto/cloud_trace_processor:lite +genrule { + name: "perfetto_protos_perfetto_cloud_trace_processor_lite_gen", + srcs: [ + "protos/perfetto/cloud_trace_processor/common.proto", + "protos/perfetto/cloud_trace_processor/orchestrator.proto", + "protos/perfetto/cloud_trace_processor/worker.proto", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)", + out: [ + "external/perfetto/protos/perfetto/cloud_trace_processor/common.pb.cc", + "external/perfetto/protos/perfetto/cloud_trace_processor/orchestrator.pb.cc", + "external/perfetto/protos/perfetto/cloud_trace_processor/worker.pb.cc", + ], +} + +// GN: //protos/perfetto/cloud_trace_processor:lite +genrule { + name: "perfetto_protos_perfetto_cloud_trace_processor_lite_gen_headers", + srcs: [ + "protos/perfetto/cloud_trace_processor/common.proto", + "protos/perfetto/cloud_trace_processor/orchestrator.proto", + "protos/perfetto/cloud_trace_processor/worker.proto", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)", + out: [ + "external/perfetto/protos/perfetto/cloud_trace_processor/common.pb.h", + "external/perfetto/protos/perfetto/cloud_trace_processor/orchestrator.pb.h", + "external/perfetto/protos/perfetto/cloud_trace_processor/worker.pb.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + // GN: //protos/perfetto/common:cpp genrule { name: "perfetto_protos_perfetto_common_cpp_gen", @@ -6848,6 +6909,48 @@ genrule { ], } +// GN: //protos/perfetto/trace_processor:lite +genrule { + name: "perfetto_protos_perfetto_trace_processor_lite_gen", + srcs: [ + "protos/perfetto/trace_processor/metatrace_categories.proto", + "protos/perfetto/trace_processor/stack.proto", + "protos/perfetto/trace_processor/trace_processor.proto", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)", + out: [ + "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pb.cc", + "external/perfetto/protos/perfetto/trace_processor/stack.pb.cc", + "external/perfetto/protos/perfetto/trace_processor/trace_processor.pb.cc", + ], +} + +// GN: //protos/perfetto/trace_processor:lite +genrule { + name: "perfetto_protos_perfetto_trace_processor_lite_gen_headers", + srcs: [ + "protos/perfetto/trace_processor/metatrace_categories.proto", + "protos/perfetto/trace_processor/stack.proto", + "protos/perfetto/trace_processor/trace_processor.proto", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)", + out: [ + "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pb.h", + "external/perfetto/protos/perfetto/trace_processor/stack.pb.h", + "external/perfetto/protos/perfetto/trace_processor/trace_processor.pb.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + // GN: //protos/perfetto/trace_processor:metrics_impl_zero genrule { name: "perfetto_protos_perfetto_trace_processor_metrics_impl_zero_gen", @@ -6888,7 +6991,6 @@ genrule { genrule { name: "perfetto_protos_perfetto_trace_processor_zero_gen", srcs: [ - "protos/perfetto/trace_processor/cloud_trace_processor.proto", "protos/perfetto/trace_processor/metatrace_categories.proto", "protos/perfetto/trace_processor/stack.proto", "protos/perfetto/trace_processor/trace_processor.proto", @@ -6899,7 +7001,6 @@ genrule { ], cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)", out: [ - "external/perfetto/protos/perfetto/trace_processor/cloud_trace_processor.pbzero.cc", "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pbzero.cc", "external/perfetto/protos/perfetto/trace_processor/stack.pbzero.cc", "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.cc", @@ -6910,7 +7011,6 @@ genrule { genrule { name: "perfetto_protos_perfetto_trace_processor_zero_gen_headers", srcs: [ - "protos/perfetto/trace_processor/cloud_trace_processor.proto", "protos/perfetto/trace_processor/metatrace_categories.proto", "protos/perfetto/trace_processor/stack.proto", "protos/perfetto/trace_processor/trace_processor.proto", @@ -6921,7 +7021,6 @@ genrule { ], cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(in)", out: [ - "external/perfetto/protos/perfetto/trace_processor/cloud_trace_processor.pbzero.h", "external/perfetto/protos/perfetto/trace_processor/metatrace_categories.pbzero.h", "external/perfetto/protos/perfetto/trace_processor/stack.pbzero.h", "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.h", @@ -8374,6 +8473,24 @@ genrule { ], } +// GN: //src/cloud_trace_processor:sources +filegroup { + name: "perfetto_src_cloud_trace_processor_sources", + srcs: [ + "src/cloud_trace_processor/orchestrator_impl.cc", + "src/cloud_trace_processor/trace_processor_wrapper.cc", + "src/cloud_trace_processor/worker_impl.cc", + ], +} + +// GN: //src/cloud_trace_processor:unittests +filegroup { + name: "perfetto_src_cloud_trace_processor_unittests", + srcs: [ + "src/cloud_trace_processor/trace_processor_wrapper_unittest.cc", + ], +} + // GN: //src/ipc:client filegroup { name: "perfetto_src_ipc_client", @@ -9280,7 +9397,12 @@ filegroup { name: "perfetto_src_trace_processor_db_db", srcs: [ "src/trace_processor/db/column.cc", + "src/trace_processor/db/column_overlay.cc", "src/trace_processor/db/column_storage.cc", + "src/trace_processor/db/null_overlay.cc", + "src/trace_processor/db/numeric_storage.cc", + "src/trace_processor/db/storage.cc", + "src/trace_processor/db/storage_overlay.cc", "src/trace_processor/db/table.cc", "src/trace_processor/db/view.cc", ], @@ -9292,11 +9414,39 @@ filegroup { srcs: [ "src/trace_processor/db/column_storage_overlay_unittest.cc", "src/trace_processor/db/compare_unittest.cc", - "src/trace_processor/db/table_unittest.cc", + "src/trace_processor/db/storage_unittest.cc", "src/trace_processor/db/view_unittest.cc", ], } +// GN: //src/trace_processor/db:view_unittest +genrule { + name: "perfetto_src_trace_processor_db_view_unittest", + srcs: [ + "src/trace_processor/db/view_unittest.py", + ], + tools: [ + "perfetto_src_trace_processor_db_view_unittest_binary", + ], + cmd: "$(location perfetto_src_trace_processor_db_view_unittest_binary) --gen-dir=$(genDir) --relative-input-dir=external/perfetto --inputs $(in)", + out: [ + "src/trace_processor/db/view_unittest_py.h", + ], +} + +// GN: //src/trace_processor/db:view_unittest +python_binary_host { + name: "perfetto_src_trace_processor_db_view_unittest_binary", + srcs: [ + "python/generators/trace_processor_table/public.py", + "python/generators/trace_processor_table/serialize.py", + "python/generators/trace_processor_table/util.py", + "src/trace_processor/db/view_unittest.py", + "tools/gen_tp_table_headers.py", + ], + main: "tools/gen_tp_table_headers.py", +} + // GN: //src/trace_processor:demangle cc_library_static { name: "perfetto_src_trace_processor_demangle", @@ -9890,6 +10040,7 @@ genrule { "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", @@ -10019,9 +10170,17 @@ filegroup { "src/trace_processor/prelude/functions/import.cc", "src/trace_processor/prelude/functions/layout_functions.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", ], } @@ -10050,6 +10209,14 @@ filegroup { ], } +// 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", @@ -10064,11 +10231,47 @@ filegroup { "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", ], } +// GN: //src/trace_processor/prelude/table_functions:tables +genrule { + name: "perfetto_src_trace_processor_prelude_table_functions_tables", + srcs: [ + "src/trace_processor/prelude/table_functions/tables.py", + ], + tools: [ + "perfetto_src_trace_processor_prelude_table_functions_tables_binary", + ], + cmd: "$(location perfetto_src_trace_processor_prelude_table_functions_tables_binary) --gen-dir=$(genDir) --relative-input-dir=external/perfetto --inputs $(in)", + out: [ + "src/trace_processor/prelude/table_functions/tables_py.h", + ], +} + +// GN: //src/trace_processor/prelude/table_functions:tables +python_binary_host { + name: "perfetto_src_trace_processor_prelude_table_functions_tables_binary", + srcs: [ + "python/generators/trace_processor_table/public.py", + "python/generators/trace_processor_table/serialize.py", + "python/generators/trace_processor_table/util.py", + "src/trace_processor/prelude/table_functions/tables.py", + "src/trace_processor/tables/android_tables.py", + "src/trace_processor/tables/counter_tables.py", + "src/trace_processor/tables/flow_tables.py", + "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/slice_tables.py", + "src/trace_processor/tables/trace_proto_tables.py", + "src/trace_processor/tables/track_tables.py", + "tools/gen_tp_table_headers.py", + ], + main: "tools/gen_tp_table_headers.py", +} + // GN: //src/trace_processor/prelude/table_functions:unittests filegroup { name: "perfetto_src_trace_processor_prelude_table_functions_unittests", @@ -10082,6 +10285,22 @@ filegroup { ], } +// GN: //src/trace_processor/prelude/tables_views:tables_views +genrule { + name: "perfetto_src_trace_processor_prelude_tables_views_tables_views", + srcs: [ + "src/trace_processor/prelude/tables_views/tables.sql", + "src/trace_processor/prelude/tables_views/views.sql", + ], + cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=prelude::tables_views --cpp-out=$(out) $(in)", + out: [ + "src/trace_processor/prelude/tables_views/tables_views.h", + ], + tool_files: [ + "tools/gen_amalgamated_sql.py", + ], +} + // GN: //src/trace_processor/rpc:httpd filegroup { name: "perfetto_src_trace_processor_rpc_httpd", @@ -10125,24 +10344,24 @@ filegroup { ], } -// GN: //src/trace_processor/sqlite:sqlite +// GN: //src/trace_processor/sqlite:query_constraints filegroup { - name: "perfetto_src_trace_processor_sqlite_sqlite", + name: "perfetto_src_trace_processor_sqlite_query_constraints", srcs: [ - "src/trace_processor/sqlite/db_sqlite_table.cc", - "src/trace_processor/sqlite/sql_stats_table.cc", - "src/trace_processor/sqlite/sqlite_raw_table.cc", - "src/trace_processor/sqlite/sqlite_utils.cc", - "src/trace_processor/sqlite/stats_table.cc", + "src/trace_processor/sqlite/query_constraints.cc", ], } -// GN: //src/trace_processor/sqlite:sqlite_minimal +// GN: //src/trace_processor/sqlite:sqlite filegroup { - name: "perfetto_src_trace_processor_sqlite_sqlite_minimal", + name: "perfetto_src_trace_processor_sqlite_sqlite", srcs: [ - "src/trace_processor/sqlite/query_constraints.cc", + "src/trace_processor/sqlite/db_sqlite_table.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_utils.cc", + "src/trace_processor/sqlite/stats_table.cc", ], } @@ -10161,16 +10380,21 @@ genrule { 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/common/counters.sql", + "src/trace_processor/stdlib/common/cpus.sql", "src/trace_processor/stdlib/common/metadata.sql", "src/trace_processor/stdlib/common/percentiles.sql", "src/trace_processor/stdlib/common/slices.sql", @@ -10218,7 +10442,7 @@ genrule { tools: [ "perfetto_src_trace_processor_tables_py_tables_unittest_binary", ], - cmd: "$(location perfetto_src_trace_processor_tables_py_tables_unittest_binary) --gen-dir=$(genDir) --inputs $(in) --outputs $(out)", + cmd: "$(location perfetto_src_trace_processor_tables_py_tables_unittest_binary) --gen-dir=$(genDir) --relative-input-dir=external/perfetto --inputs $(in)", out: [ "src/trace_processor/tables/py_tables_unittest_py.h", ], @@ -10262,7 +10486,7 @@ genrule { tools: [ "perfetto_src_trace_processor_tables_tables_python_binary", ], - cmd: "$(location perfetto_src_trace_processor_tables_tables_python_binary) --gen-dir=$(genDir) --inputs $(in) --outputs $(out)", + cmd: "$(location perfetto_src_trace_processor_tables_tables_python_binary) --gen-dir=$(genDir) --relative-input-dir=external/perfetto --inputs $(in)", out: [ "src/trace_processor/tables/android_tables_py.h", "src/trace_processor/tables/counter_tables_py.h", @@ -10301,7 +10525,6 @@ python_binary_host { filegroup { name: "perfetto_src_trace_processor_tables_unittests", srcs: [ - "src/trace_processor/tables/macros_unittest.cc", "src/trace_processor/tables/py_tables_unittest.cc", ], } @@ -10322,7 +10545,6 @@ filegroup { "src/trace_processor/types/destructible.cc", "src/trace_processor/types/gfp_flags.cc", "src/trace_processor/types/task_state.cc", - "src/trace_processor/types/variadic.cc", ], } @@ -10462,6 +10684,34 @@ filegroup { ], } +// GN: //src/trace_processor/views:macros_unittest +genrule { + name: "perfetto_src_trace_processor_views_macros_unittest", + srcs: [ + "src/trace_processor/views/macros_unittest.py", + ], + tools: [ + "perfetto_src_trace_processor_views_macros_unittest_binary", + ], + cmd: "$(location perfetto_src_trace_processor_views_macros_unittest_binary) --gen-dir=$(genDir) --relative-input-dir=external/perfetto --inputs $(in)", + out: [ + "src/trace_processor/views/macros_unittest_py.h", + ], +} + +// GN: //src/trace_processor/views:macros_unittest +python_binary_host { + name: "perfetto_src_trace_processor_views_macros_unittest_binary", + srcs: [ + "python/generators/trace_processor_table/public.py", + "python/generators/trace_processor_table/serialize.py", + "python/generators/trace_processor_table/util.py", + "src/trace_processor/views/macros_unittest.py", + "tools/gen_tp_table_headers.py", + ], + main: "tools/gen_tp_table_headers.py", +} + // GN: //src/trace_processor/views:unittests filegroup { name: "perfetto_src_trace_processor_views_unittests", @@ -10523,6 +10773,14 @@ filegroup { ], } +// GN: //src/traceconv:unittests +filegroup { + name: "perfetto_src_traceconv_unittests", + srcs: [ + "src/traceconv/trace_to_text_unittest.cc", + ], +} + // GN: //src/traceconv:utils filegroup { name: "perfetto_src_traceconv_utils", @@ -11077,6 +11335,15 @@ filegroup { "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", ], } @@ -11572,6 +11839,7 @@ cc_test { ":perfetto_include_perfetto_ext_base_http_http", ":perfetto_include_perfetto_ext_base_threading_threading", ":perfetto_include_perfetto_ext_base_version", + ":perfetto_include_perfetto_ext_cloud_trace_processor_cloud_trace_processor", ":perfetto_include_perfetto_ext_ipc_ipc", ":perfetto_include_perfetto_ext_trace_processor_demangle", ":perfetto_include_perfetto_ext_trace_processor_export_json", @@ -11580,6 +11848,7 @@ cc_test { ":perfetto_include_perfetto_ext_traced_traced", ":perfetto_include_perfetto_ext_tracing_core_core", ":perfetto_include_perfetto_ext_tracing_ipc_ipc", + ":perfetto_include_perfetto_profiling_pprof_builder", ":perfetto_include_perfetto_protozero_protozero", ":perfetto_include_perfetto_public_abi_base", ":perfetto_include_perfetto_public_base", @@ -11591,6 +11860,7 @@ cc_test { ":perfetto_include_perfetto_tracing_core_core", ":perfetto_include_perfetto_tracing_core_forward_decls", ":perfetto_include_perfetto_tracing_tracing", + ":perfetto_protos_perfetto_cloud_trace_processor_lite_gen", ":perfetto_protos_perfetto_common_cpp_gen", ":perfetto_protos_perfetto_common_lite_gen", ":perfetto_protos_perfetto_common_zero_gen", @@ -11666,6 +11936,7 @@ cc_test { ":perfetto_protos_perfetto_trace_power_cpp_gen", ":perfetto_protos_perfetto_trace_power_lite_gen", ":perfetto_protos_perfetto_trace_power_zero_gen", + ":perfetto_protos_perfetto_trace_processor_lite_gen", ":perfetto_protos_perfetto_trace_processor_metrics_impl_zero_gen", ":perfetto_protos_perfetto_trace_processor_zero_gen", ":perfetto_protos_perfetto_trace_profiling_cpp_gen", @@ -11704,6 +11975,8 @@ cc_test { ":perfetto_src_base_unittests", ":perfetto_src_base_unix_socket", ":perfetto_src_base_version", + ":perfetto_src_cloud_trace_processor_sources", + ":perfetto_src_cloud_trace_processor_unittests", ":perfetto_src_ipc_client", ":perfetto_src_ipc_common", ":perfetto_src_ipc_host", @@ -11741,6 +12014,7 @@ cc_test { ":perfetto_src_profiling_perf_producer_unittests", ":perfetto_src_profiling_perf_regs_parsing", ":perfetto_src_profiling_perf_unwinding", + ":perfetto_src_profiling_symbolizer_symbolize_database", ":perfetto_src_profiling_symbolizer_symbolizer", ":perfetto_src_profiling_symbolizer_unittests", ":perfetto_src_profiling_unittests", @@ -11798,17 +12072,19 @@ cc_test { ":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", @@ -11835,6 +12111,10 @@ cc_test { ":perfetto_src_trace_processor_util_zip_reader", ":perfetto_src_trace_processor_views_unittests", ":perfetto_src_trace_processor_views_views", + ":perfetto_src_traceconv_lib", + ":perfetto_src_traceconv_pprofbuilder", + ":perfetto_src_traceconv_unittests", + ":perfetto_src_traceconv_utils", ":perfetto_src_traced_probes_android_game_intervention_list_android_game_intervention_list", ":perfetto_src_traced_probes_android_game_intervention_list_unittests", ":perfetto_src_traced_probes_android_log_android_log", @@ -11882,6 +12162,7 @@ cc_test { ":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", @@ -11915,6 +12196,7 @@ cc_test { "perfetto_gtest_logcat_printer", ], generated_headers: [ + "perfetto_protos_perfetto_cloud_trace_processor_lite_gen_headers", "perfetto_protos_perfetto_common_cpp_gen_headers", "perfetto_protos_perfetto_common_lite_gen_headers", "perfetto_protos_perfetto_common_zero_gen_headers", @@ -11990,6 +12272,7 @@ cc_test { "perfetto_protos_perfetto_trace_power_cpp_gen_headers", "perfetto_protos_perfetto_trace_power_lite_gen_headers", "perfetto_protos_perfetto_trace_power_zero_gen_headers", + "perfetto_protos_perfetto_trace_processor_lite_gen_headers", "perfetto_protos_perfetto_trace_processor_metrics_impl_zero_gen_headers", "perfetto_protos_perfetto_trace_processor_zero_gen_headers", "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers", @@ -12023,6 +12306,7 @@ cc_test { "perfetto_src_protozero_testing_messages_cpp_gen_headers", "perfetto_src_protozero_testing_messages_lite_gen_headers", "perfetto_src_protozero_testing_messages_zero_gen_headers", + "perfetto_src_trace_processor_db_view_unittest", "perfetto_src_trace_processor_gen_cc_test_messages_descriptor", "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor", "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor", @@ -12033,9 +12317,13 @@ cc_test { "perfetto_src_trace_processor_metrics_gen_cc_all_webview_metrics_descriptor", "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor", "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics", + "perfetto_src_trace_processor_prelude_table_functions_tables", + "perfetto_src_trace_processor_prelude_tables_views_tables_views", "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib", "perfetto_src_trace_processor_tables_py_tables_unittest", "perfetto_src_trace_processor_tables_tables_python", + "perfetto_src_trace_processor_views_macros_unittest", + "perfetto_src_traceconv_gen_cc_trace_descriptor", "perfetto_src_traced_probes_ftrace_test_messages_cpp_gen_headers", "perfetto_src_traced_probes_ftrace_test_messages_lite_gen_headers", "perfetto_src_traced_probes_ftrace_test_messages_zero_gen_headers", @@ -12471,13 +12759,15 @@ cc_binary { ":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", @@ -12549,6 +12839,8 @@ cc_binary { "perfetto_src_trace_processor_metrics_gen_cc_all_webview_metrics_descriptor", "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor", "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics", + "perfetto_src_trace_processor_prelude_table_functions_tables", + "perfetto_src_trace_processor_prelude_tables_views_tables_views", "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib", "perfetto_src_trace_processor_tables_tables_python", ], @@ -12692,11 +12984,13 @@ cc_binary_host { ":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", @@ -12772,6 +13066,8 @@ cc_binary_host { "perfetto_src_trace_processor_metrics_gen_cc_all_webview_metrics_descriptor", "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor", "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics", + "perfetto_src_trace_processor_prelude_table_functions_tables", + "perfetto_src_trace_processor_prelude_tables_views_tables_views", "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib", "perfetto_src_trace_processor_tables_tables_python", "perfetto_src_traceconv_gen_cc_trace_descriptor", @@ -64,6 +64,152 @@ perfetto_cc_library( 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_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 +425,7 @@ perfetto_cc_library( ":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 +502,7 @@ perfetto_cc_library( ":protozero", ":src_base_base", ":src_base_version", - ], + ] + PERFETTO_CONFIG.deps.zlib, linkstatic = True, ) @@ -387,6 +534,22 @@ perfetto_filegroup( ], ) +# 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 +609,16 @@ perfetto_filegroup( ], ) +# 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 +931,16 @@ perfetto_cc_library( 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 +1032,19 @@ perfetto_genrule( ], ) +# 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", @@ -1082,10 +1278,22 @@ perfetto_filegroup( "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.cc", + "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.cc", + "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", @@ -1579,6 +1787,7 @@ perfetto_filegroup( "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", @@ -1794,17 +2003,26 @@ perfetto_filegroup( "src/trace_processor/prelude/functions/layout_functions.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", "src/trace_processor/prelude/functions/stack_functions.h", + "src/trace_processor/prelude/functions/to_ftrace.cc", + "src/trace_processor/prelude/functions/to_ftrace.h", "src/trace_processor/prelude/functions/utils.h", "src/trace_processor/prelude/functions/window_functions.h", ], ) +# 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", @@ -1816,6 +2034,15 @@ perfetto_filegroup( ], ) +# 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", @@ -1840,13 +2067,46 @@ perfetto_filegroup( "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", ], ) +# GN target: //src/trace_processor/prelude/table_functions:tables +perfetto_cc_tp_tables( + name = "src_trace_processor_prelude_table_functions_tables", + srcs = [ + "src/trace_processor/prelude/table_functions/tables.py", + ], + deps = [ + ":src_trace_processor_tables_tables_python", + ], + outs = [ + "src/trace_processor/prelude/table_functions/tables_py.h", + ], +) + +# GN target: //src/trace_processor/prelude/tables_views:sources +perfetto_filegroup( + name = "src_trace_processor_prelude_tables_views_sources", + srcs = [ + "src/trace_processor/prelude/tables_views/tables.sql", + "src/trace_processor/prelude/tables_views/views.sql", + ], +) + +# GN target: //src/trace_processor/prelude/tables_views:tables_views +perfetto_cc_amalgamated_sql( + name = "src_trace_processor_prelude_tables_views_tables_views", + deps = [ + ":src_trace_processor_prelude_tables_views_sources", + ], + outs = [ + "src/trace_processor/prelude/tables_views/tables_views.h", + ], + namespace = "prelude::tables_views", +) + # GN target: //src/trace_processor/rpc:httpd perfetto_filegroup( name = "src_trace_processor_rpc_httpd", @@ -1878,6 +2138,15 @@ perfetto_filegroup( ], ) +# 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", @@ -1885,10 +2154,13 @@ perfetto_filegroup( "src/trace_processor/sqlite/db_sqlite_table.cc", "src/trace_processor/sqlite/db_sqlite_table.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_raw_table.cc", - "src/trace_processor/sqlite/sqlite_raw_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_utils.cc", "src/trace_processor/sqlite/sqlite_utils.h", "src/trace_processor/sqlite/stats_table.cc", @@ -1896,19 +2168,6 @@ perfetto_filegroup( ], ) -# 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", @@ -1925,10 +2184,13 @@ perfetto_filegroup( 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", ], ) @@ -1936,6 +2198,7 @@ perfetto_filegroup( 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", ], ) @@ -1945,6 +2208,7 @@ perfetto_filegroup( name = "src_trace_processor_stdlib_common_common", srcs = [ "src/trace_processor/stdlib/common/counters.sql", + "src/trace_processor/stdlib/common/cpus.sql", "src/trace_processor/stdlib/common/metadata.sql", "src/trace_processor/stdlib/common/percentiles.sql", "src/trace_processor/stdlib/common/slices.sql", @@ -2001,12 +2265,7 @@ perfetto_filegroup( perfetto_filegroup( name = "src_trace_processor_tables_tables", srcs = [ - "src/trace_processor/tables/counter_tables.h", - "src/trace_processor/tables/flow_tables.h", - "src/trace_processor/tables/macros.h", "src/trace_processor/tables/macros_internal.h", - "src/trace_processor/tables/profiler_tables.h", - "src/trace_processor/tables/slice_tables.h", "src/trace_processor/tables/table_destructors.cc", ], ) @@ -2051,7 +2310,6 @@ perfetto_filegroup( "src/trace_processor/types/task_state.h", "src/trace_processor/types/tcp_state.h", "src/trace_processor/types/trace_processor_context.h", - "src/trace_processor/types/variadic.cc", "src/trace_processor/types/variadic.h", "src/trace_processor/types/version_number.h", ], @@ -2591,6 +2849,15 @@ perfetto_filegroup( ], ) +# 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", @@ -3003,6 +3270,31 @@ perfetto_py_proto_library( ], ) +# 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", @@ -3011,6 +3303,14 @@ perfetto_cc_protocpp_library( ], ) +# 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", @@ -4152,6 +4452,14 @@ perfetto_cc_protozero_library( ], ) +# 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", @@ -4175,7 +4483,6 @@ perfetto_cc_protozero_library( perfetto_proto_library( name = "protos_perfetto_trace_processor_protos", srcs = [ - "protos/perfetto/trace_processor/cloud_trace_processor.proto", "protos/perfetto/trace_processor/metatrace_categories.proto", "protos/perfetto/trace_processor/stack.proto", "protos/perfetto/trace_processor/trace_processor.proto", @@ -4713,11 +5020,14 @@ perfetto_cc_library( ":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", @@ -4804,6 +5114,7 @@ perfetto_cc_library( ":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 + @@ -4865,13 +5176,16 @@ perfetto_cc_binary( ":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", @@ -4948,6 +5262,7 @@ perfetto_cc_binary( ":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.linenoise + @@ -5076,11 +5391,14 @@ perfetto_cc_binary( ":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", @@ -5157,6 +5475,7 @@ perfetto_cc_binary( ":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", ":src_traceconv_gen_cc_trace_descriptor", ] + PERFETTO_CONFIG.deps.jsoncpp + @@ -42,6 +42,7 @@ if (enable_perfetto_platform_services) { } if (enable_perfetto_trace_processor && enable_perfetto_trace_processor_sqlite) { + all_targets += [ "src/cloud_trace_processor" ] all_targets += [ "src/trace_processor:trace_processor_shell" ] } @@ -1,11 +1,27 @@ 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: + * + SDK: + * + +v34.0 - 2023-05-02: + 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: - * + * Add support for parsing large integers from Trace Processor into + bigint. This is the default behaviour for unknown fields and can + be enabled specifically via the LONG and LONG_NULL column types. SDK: * Changed the type of the static constexpr metadata on protozero generated bindings from a function returning the metadata to @@ -19,6 +35,9 @@ Unreleased: * The new DataSourceBase::OnFlush() method allows users to properly handle Flush requests. +v33.1 - 2023-03-03: + Identical to v33.0. Version was bumped to work around prebuilt infra failures. + v33.0 - 2023-03-02: All: * Switched to a C++17-only project by removing C++11 opt-out. This completes diff --git a/bazel/deps.bzl b/bazel/deps.bzl index 27c5c800f..a105a6330 100644 --- a/bazel/deps.bzl +++ b/bazel/deps.bzl @@ -67,10 +67,10 @@ def perfetto_deps(): ) _add_repo_if_not_existing( - new_git_repository, + http_archive, name = "perfetto_dep_zlib", - remote = "https://android.googlesource.com/platform/external/zlib.git", - commit = "6d3f6aa0f87c9791ca7724c279ef61384f331dfd", + url = "https://storage.googleapis.com/perfetto/zlib-6d3f6aa0f87c9791ca7724c279ef61384f331dfd.tar.gz", + sha256 = "e9a1d6e8c936de68628ffb83a13d28a40cd6b2def2ad9378e8b951d4b8f4df18", build_file = "//bazel:zlib.BUILD", ) diff --git a/bazel/rules.bzl b/bazel/rules.bzl index 4021c58a6..79174bd33 100644 --- a/bazel/rules.bzl +++ b/bazel/rules.bzl @@ -308,7 +308,7 @@ def perfetto_cc_amalgamated_sql(name, deps, outs, namespace, **kwargs): **kwargs, ) -def perfetto_cc_tp_tables(name, srcs, outs, **kwargs): +def perfetto_cc_tp_tables(name, srcs, outs, deps = [], **kwargs): if PERFETTO_CONFIG.root[:2] != "//": fail("Expected PERFETTO_CONFIG.root to start with //") @@ -317,12 +317,21 @@ def perfetto_cc_tp_tables(name, srcs, outs, **kwargs): else: python_path = PERFETTO_CONFIG.root + "/python" + perfetto_py_library( + name = name + "_lib", + deps = [ + python_path + ":trace_processor_table_generator", + ], + srcs = srcs, + ) + perfetto_py_binary( name = name + "_tool", deps = [ + ":" + name + "_lib", python_path + ":trace_processor_table_generator", - ], - srcs = srcs + [ + ] + [d + "_lib" for d in deps], + srcs = [ "tools/gen_tp_table_headers.py", ], main = "tools/gen_tp_table_headers.py", @@ -332,9 +341,9 @@ def perfetto_cc_tp_tables(name, srcs, outs, **kwargs): cmd = ["$(location " + name + "_tool)"] cmd += ["--gen-dir", "$(RULEDIR)"] cmd += ["--inputs", "$(SRCS)"] - cmd += ["--outputs", "$(OUTS)"] if PERFETTO_CONFIG.root != "//": - cmd += ["--header-prefix", PERFETTO_CONFIG.root[2:]] + cmd += ["--import-prefix", PERFETTO_CONFIG.root[2:]] + cmd += ["--relative-input-dir", PERFETTO_CONFIG.root[2:]] perfetto_genrule( name = name + "_gen", diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn index 2ac2948cd..8eb19f7e1 100644 --- a/buildtools/BUILD.gn +++ b/buildtools/BUILD.gn @@ -129,6 +129,13 @@ config("protobuf_config") { defines = [ "HAVE_PTHREAD=1" ] cflags = [] + + # Fixed upstream in: + # https://github.com/protocolbuffers/protobuf/pull/10112 + # But we don't have that yet. + if (is_mac) { + cflags += [ "-Wno-deprecated-declarations" ] + } if (!is_clang && !is_win) { # implies gcc cflags += [ "-Wno-stringop-overread" ] } diff --git a/docs/analysis/trace-processor.md b/docs/analysis/trace-processor.md index eb022e04a..f1ef185f2 100644 --- a/docs/analysis/trace-processor.md +++ b/docs/analysis/trace-processor.md @@ -256,10 +256,10 @@ It takes an `arg_set_id` and `key` as input and returns the value looked up in the `args` table. For example, to retrieve the `prev_comm` field for `sched_switch` events in -the `raw` table. +the `ftrace_event` table. ```sql SELECT EXTRACT_ARG(arg_set_id, 'prev_comm') -FROM raw +FROM ftrace_event WHERE name = 'sched_switch' ``` @@ -271,7 +271,7 @@ SELECT FROM args WHERE key = 'prev_comm' AND args.arg_set_id = raw.arg_set_id ) -FROM raw +FROM ftrace_event WHERE name = 'sched_switch' ``` diff --git a/docs/contributing/perfetto-in-the-press.md b/docs/contributing/perfetto-in-the-press.md index 108849ab0..4546bd630 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/data-sources/cpu-scheduling.md b/docs/data-sources/cpu-scheduling.md index 062d9b983..b8447ffbc 100644 --- a/docs/data-sources/cpu-scheduling.md +++ b/docs/data-sources/cpu-scheduling.md @@ -16,12 +16,7 @@ This allows to get fine grained scheduling events such as: ## UI -When zoomed out, the UI shows a quantized view of CPU usage, which collapses the -scheduling information: - -![](/docs/images/cpu-bar-graphs.png "Quantized view of CPU run queues") - -However, by zooming in, the individual scheduling events become visible: +The UI represents individual scheduling events as slices: ![](/docs/images/cpu-zoomed.png "Detailed view of CPU run queues") diff --git a/docs/images/enable-profile-flame-graph.png b/docs/images/enable-profile-flame-graph.png Binary files differdeleted file mode 100644 index 642ce73e3..000000000 --- a/docs/images/enable-profile-flame-graph.png +++ /dev/null diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md index d4c72b087..809833bfb 100644 --- a/docs/instrumentation/tracing-sdk.md +++ b/docs/instrumentation/tracing-sdk.md @@ -30,7 +30,7 @@ repository](/examples/sdk/README.md). To start using the Client API, first check out the latest SDK release: ```bash -git clone https://android.googlesource.com/platform/external/perfetto -b v32.1 +git clone https://android.googlesource.com/platform/external/perfetto -b v34.0 ``` The SDK consists of two files, `sdk/perfetto.h` and `sdk/perfetto.cc`. These are diff --git a/docs/quickstart/callstack-sampling.md b/docs/quickstart/callstack-sampling.md index e5da5b21e..cb47edc35 100644 --- a/docs/quickstart/callstack-sampling.md +++ b/docs/quickstart/callstack-sampling.md @@ -119,11 +119,6 @@ PERFETTO_SYMBOLIZER_MODE=index PERFETTO_BINARY_PATH=path/to/directory/with/symbo ## View profile -Visualizing callstacks in the Perfetto UI is currently disabled behind a -flag. Please enable it before proceeding further: - -![Enable flame graph flag](/docs/images/enable-profile-flame-graph.png) - 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 4e1717226..6786b4dd7 100644 --- a/docs/quickstart/chrome-tracing.md +++ b/docs/quickstart/chrome-tracing.md @@ -4,6 +4,8 @@ Perfetto can capture traces right from the Chrome browser on desktop. It capture > 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/examples/sdk/README.md b/examples/sdk/README.md index 238d50d6d..476ebbe81 100644 --- a/examples/sdk/README.md +++ b/examples/sdk/README.md @@ -15,7 +15,7 @@ Dependencies: First, check out the latest Perfetto release: ```bash -git clone https://android.googlesource.com/platform/external/perfetto -b v32.1 +git clone https://android.googlesource.com/platform/external/perfetto -b v34.0 ``` Then, build using CMake: diff --git a/gn/BUILD.gn b/gn/BUILD.gn index 192ede856..749c2eef6 100644 --- a/gn/BUILD.gn +++ b/gn/BUILD.gn @@ -275,7 +275,7 @@ config("protobuf_gen_config") { "GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER", ] cflags = [] - if (!is_clang || !is_win) { + if (!is_clang && !is_win) { cflags += [ "-Wno-deprecated-declarations" ] } if (is_clang && is_win) { @@ -289,6 +289,10 @@ config("protobuf_gen_config") { "-Wno-unused-parameter", "-Wno-shadow-field-in-constructor", "-Wno-zero-as-null-pointer-constant", + + # Fixed in upstream protobuf v3.22.0 + # d37cbfd4485f("Update inlined_string_field.h"), but we don't have that. + "-Wno-undef", ] } diff --git a/gn/perfetto.gni b/gn/perfetto.gni index b778d887d..579fc0f6b 100644 --- a/gn/perfetto.gni +++ b/gn/perfetto.gni @@ -296,8 +296,9 @@ declare_args() { 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/gn/perfetto_tp_tables.gni b/gn/perfetto_tp_tables.gni index da8cfaeef..19a98c0fa 100644 --- a/gn/perfetto_tp_tables.gni +++ b/gn/perfetto_tp_tables.gni @@ -17,6 +17,10 @@ import("perfetto.gni") template("perfetto_tp_tables") { config_name = target_name + "_config" action_name = target_name + relative_args = [ + "--relative-input-dir", + rebase_path(perfetto_root_path, root_build_dir), + ] config(config_name) { include_dirs = [ root_gen_dir ] @@ -32,15 +36,17 @@ template("perfetto_tp_tables") { } deps = [ "$perfetto_root_path/python:trace_processor_table_generator" ] + if (defined(invoker.deps)) { + deps += invoker.deps + } public_configs = [ ":$config_name" ] gen_args = [ "--gen-dir", - rebase_path("$root_gen_dir", root_build_dir), + rebase_path("$root_gen_dir/$perfetto_root_path", root_build_dir), ] input_args = [ "--inputs" ] + rebase_path(invoker.sources, root_build_dir) - output_args = [ "--outputs" ] + rebase_path(outputs, root_build_dir) - args = gen_args + input_args + output_args + args = gen_args + input_args + relative_args metadata = { perfetto_action_type_for_generator = [ "tp_tables" ] @@ -56,7 +62,7 @@ template("perfetto_tp_tables") { deps = [ "$perfetto_root_path/python:trace_processor_table_generator" ] outputs = [ "$target_gen_dir/$docs_name.json" ] args = [ "--out" ] + rebase_path(outputs, root_build_dir) + - rebase_path(invoker.sources, root_build_dir) + rebase_path(invoker.sources, root_build_dir) + relative_args } } } diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni index 76594fedd..b7b1e0177 100644 --- a/gn/perfetto_unittests.gni +++ b/gn/perfetto_unittests.gni @@ -72,12 +72,10 @@ if (enable_perfetto_traced_perf) { if (enable_perfetto_trace_processor) { perfetto_unittests_targets += [ "src/trace_processor:unittests" ] - - # TODO(mohitms): reenable this once we no longer link lite and full protobuf - # simultaneously. - # perfetto_unittests_targets += [ "src/traceconv:unittests" ] + perfetto_unittests_targets += [ "src/traceconv:unittests" ] if (enable_perfetto_trace_processor_sqlite) { perfetto_unittests_targets += [ "src/trace_processor/metrics:unittests" ] + perfetto_unittests_targets += [ "src/cloud_trace_processor:unittests" ] } } diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn index d000f53ce..0af65510f 100644 --- a/gn/standalone/BUILD.gn +++ b/gn/standalone/BUILD.gn @@ -100,6 +100,18 @@ config("extra_warnings") { # TODO(lalitm): remove this when we upgrade to a GCC version which is good # enough to handle this. cflags_cc += [ "-Wno-maybe-uninitialized" ] + + # GCC's handling of detecting infinite recursion is flaky at best and + # causes some false positives. + # TODO(lalitm): remove this when we upgrade to a GCC version which is good + # enough to handle this. + cflags_cc += [ "-Wno-infinite-recursion" ] + + # GCC's handling of detecting non null arguments is flaky at best and + # causes some false positives. + # TODO(lalitm): remove this when we upgrade to a GCC version which is good + # enough to handle this. + cflags_cc += [ "-Wno-nonnull" ] } } diff --git a/gn/standalone/toolchain/win_find_msvc.py b/gn/standalone/toolchain/win_find_msvc.py index 1628c0720..6f7eb8660 100644 --- a/gn/standalone/toolchain/win_find_msvc.py +++ b/gn/standalone/toolchain/win_find_msvc.py @@ -27,6 +27,7 @@ C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14. """ import os +import itertools import subprocess import sys @@ -62,19 +63,19 @@ def main(): filt = lambda x: os.path.exists(os.path.join(x, 'ucrt', 'x64', 'ucrt.lib')) out[1] = find_max_subdir(lib_base, filt) - for year in ['2022', '2021', '2020', '2019']: - for version in [ - 'BuildTools', 'Community', 'Professional', 'Enterprise', 'Preview' - ]: - msvc_base = ('C:\\Program Files (x86)\\Microsoft Visual Studio\\' - f'{year}\\{version}\\VC\\Tools\\MSVC') - if os.path.exists(msvc_base): - filt = lambda x: os.path.exists( - os.path.join(x, 'lib', 'x64', 'libcmt.lib')) - max_msvc = find_max_subdir(msvc_base, filt) - if max_msvc is not None: - out[2] = os.path.join(msvc_base, max_msvc) - break + for try_dir in itertools.product( + ['2022', '2021', '2020', '2019'], + ['BuildTools', 'Community', 'Professional', 'Enterprise', 'Preview'], + ['Program Files', 'Program Files (x86)']): + msvc_base = (f'C:\\{try_dir[2]}\\Microsoft Visual Studio\\' + f'{try_dir[0]}\\{try_dir[1]}\\VC\\Tools\\MSVC') + if os.path.exists(msvc_base): + filt = lambda x: os.path.exists( + os.path.join(x, 'lib', 'x64', 'libcmt.lib')) + max_msvc = find_max_subdir(msvc_base, filt) + if max_msvc is not None: + out[2] = os.path.join(msvc_base, max_msvc) + break # Don't error in case of failure, GN scripts are supposed to deal with # failures and allow the user to override the dirs. diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h index 451e6d51f..22324ad7b 100644 --- a/include/perfetto/base/compiler.h +++ b/include/perfetto/base/compiler.h @@ -23,16 +23,6 @@ #include "perfetto/base/build_config.h" #include "perfetto/public/compiler.h" -#if __cplusplus >= 201703 -#define PERFETTO_IS_AT_LEAST_CPP17() 1 -#elif defined(_MSVC_LANG) && _MSVC_LANG >= 201703L -// Without additional flags, MSVC is not standard compliant and keeps -// __cplusplus stuck at an old value, even with C++17 -#define PERFETTO_IS_AT_LEAST_CPP17() 1 -#else -#define PERFETTO_IS_AT_LEAST_CPP17() 0 -#endif - // __has_attribute is supported only by clang and recent versions of GCC. // Add a layer to wrap the __has_attribute macro. #if defined(__has_attribute) diff --git a/include/perfetto/ext/base/status_or.h b/include/perfetto/ext/base/status_or.h index 46387d7e2..a6eda7e65 100644 --- a/include/perfetto/ext/base/status_or.h +++ b/include/perfetto/ext/base/status_or.h @@ -73,6 +73,10 @@ class StatusOr { std::optional<T> value_; }; +// Deduction guide to make returning StatusOr less verbose. +template <typename T> +StatusOr(T) -> StatusOr<T>; + } // namespace base } // namespace perfetto diff --git a/include/perfetto/ext/base/threading/util.h b/include/perfetto/ext/base/threading/util.h index 7b2465e68..a08a4b038 100644 --- a/include/perfetto/ext/base/threading/util.h +++ b/include/perfetto/ext/base/threading/util.h @@ -136,9 +136,12 @@ Stream<T> RunOnThreadPool(ThreadPool* pool, private: void RunFn() { - pool_->PostTask([channel = channel_, fn = fn_]() { + pool_->PostTask([channel = channel_, fn = fn_]() mutable { auto opt_value = (*fn)(); if (!opt_value) { + // Clear out the function to ensure that any captured state is freed + // before we inform the caller. + fn.reset(); channel->Close(); return; } diff --git a/include/perfetto/ext/cloud_trace_processor/BUILD.gn b/include/perfetto/ext/cloud_trace_processor/BUILD.gn new file mode 100644 index 000000000..be266cb25 --- /dev/null +++ b/include/perfetto/ext/cloud_trace_processor/BUILD.gn @@ -0,0 +1,26 @@ +# 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. + +source_set("cloud_trace_processor") { + sources = [ + "environment.h", + "orchestrator.h", + "worker.h", + ] + deps = [ + "../../../../gn:default_deps", + "../base", + "../base/threading", + ] +} diff --git a/include/perfetto/ext/cloud_trace_processor/environment.h b/include/perfetto/ext/cloud_trace_processor/environment.h new file mode 100644 index 000000000..8b1fc01ea --- /dev/null +++ b/include/perfetto/ext/cloud_trace_processor/environment.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ENVIRONMENT_H_ +#define INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ENVIRONMENT_H_ + +#include <functional> +#include <memory> +#include <vector> + +#include "perfetto/base/status.h" +#include "perfetto/base/task_runner.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/threading/stream.h" + +namespace perfetto { +namespace cloud_trace_processor { + +// Shim interface allowing embedders to change how operations which interact +// with the OS operate (e.g. IO, networking etc). +class CtpEnvironment { + public: + virtual ~CtpEnvironment(); + + // Opens the file at |path| and reads the contents in chunks, returning the + // the chunks as a Stream. The size of the chunks is implementation defined + // but should be sized to balance memory use and syscall count. + virtual base::StatusOrStream<std::vector<uint8_t>> ReadFile( + const std::string& path) = 0; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ENVIRONMENT_H_ diff --git a/include/perfetto/ext/cloud_trace_processor/orchestrator.h b/include/perfetto/ext/cloud_trace_processor/orchestrator.h new file mode 100644 index 000000000..798316185 --- /dev/null +++ b/include/perfetto/ext/cloud_trace_processor/orchestrator.h @@ -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. + */ + +#ifndef INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_H_ +#define INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_H_ + +#include <memory> +#include <vector> + +#include "perfetto/base/status.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/threading/future.h" +#include "perfetto/ext/base/threading/stream.h" + +namespace perfetto { +namespace protos { +class TracePoolCreateArgs; +class TracePoolCreateResponse; + +class TracePoolSetTracesArgs; +class TracePoolSetTracesResponse; + +class TracePoolQueryArgs; +class TracePoolQueryResponse; + +class TracePoolDestroyArgs; +class TracePoolDestroyResponse; +} // namespace protos +} // namespace perfetto + +namespace perfetto { +namespace cloud_trace_processor { + +class Worker; + +// Interface for a CloudTraceProcessor "Orchestrator". +// +// See CloudTraceProcessorOrchestrator RPC service for high-level documentation. +class Orchestrator { + public: + virtual ~Orchestrator(); + + // Returns an in-process implementation of the Orchestrator, given a group of + // workers which can be delegated to. + // + // Note that the passed workers instances can be "remote" (i.e. in another + // process or even on another machine); the returned manager will gracefully + // handle this. + static std::unique_ptr<Orchestrator> CreateInProcess( + std::vector<std::unique_ptr<Worker>> workers); + + // Creates a TracePool with the specified arguments. + virtual base::StatusOrFuture<protos::TracePoolCreateResponse> TracePoolCreate( + const protos::TracePoolCreateArgs&) = 0; + + // Associates the provided list of traces to this TracePoolShard. + virtual base::StatusOrFuture<protos::TracePoolSetTracesResponse> + TracePoolSetTraces(const protos::TracePoolSetTracesArgs&) = 0; + + // Executes a SQL query on the specified TracePool. + virtual base::StatusOrStream<protos::TracePoolQueryResponse> TracePoolQuery( + const protos::TracePoolQueryArgs&) = 0; + + // Destroys the TracePool with the specified id. + virtual base::StatusOrFuture<protos::TracePoolDestroyResponse> + TracePoolDestroy(const protos::TracePoolDestroyArgs&) = 0; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_H_ diff --git a/include/perfetto/ext/cloud_trace_processor/worker.h b/include/perfetto/ext/cloud_trace_processor/worker.h new file mode 100644 index 000000000..60dbe7763 --- /dev/null +++ b/include/perfetto/ext/cloud_trace_processor/worker.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_WORKER_H_ +#define INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_WORKER_H_ + +#include <memory> +#include <vector> + +#include "perfetto/ext/base/threading/future.h" +#include "perfetto/ext/base/threading/stream.h" + +namespace perfetto { + +namespace base { +class ThreadPool; +} + +namespace protos { +class TracePoolShardCreateArgs; +class TracePoolShardCreateResponse; + +class TracePoolShardSetTracesArgs; +class TracePoolShardSetTracesResponse; + +class TracePoolShardQueryArgs; +class TracePoolShardQueryResponse; + +class TracePoolShardDestroyArgs; +class TracePoolShardDestroyResponse; +} // namespace protos + +namespace cloud_trace_processor { + +class CtpEnvironment; + +// Interface for a CloudTraceProcessor "Worker". +// +// See CloudTraceProcessorWorker RPC service for high-level documentation. +class Worker { + public: + virtual ~Worker(); + + // Returns an in-process implementation of the Worker given an instance of + // |CtpEnvironment| and a |ThreadPool|. The |CtpEnvironment| will be used to + // perform any interaction with the OS (e.g. opening and reading files) and + // the |ThreadPool| will be used to dispatch requests to TraceProcessor. + static std::unique_ptr<Worker> CreateInProcesss(CtpEnvironment*, + base::ThreadPool*); + + // Creates a TracePoolShard which will be owned by this worker. + virtual base::StatusOrFuture<protos::TracePoolShardCreateResponse> + TracePoolShardCreate(const protos::TracePoolShardCreateArgs&) = 0; + + // Associates the provided list of traces to this TracePoolShard. + virtual base::StatusOrStream<protos::TracePoolShardSetTracesResponse> + TracePoolShardSetTraces(const protos::TracePoolShardSetTracesArgs&) = 0; + + // Executes a SQL query on the specified TracePoolShard. + virtual base::StatusOrStream<protos::TracePoolShardQueryResponse> + TracePoolShardQuery(const protos::TracePoolShardQueryArgs&) = 0; + + // Destroys the TracePoolShard with the specified id. + virtual base::StatusOrFuture<protos::TracePoolShardDestroyResponse> + TracePoolShardDestroy(const protos::TracePoolShardDestroyArgs&) = 0; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // INCLUDE_PERFETTO_EXT_CLOUD_TRACE_PROCESSOR_WORKER_H_ diff --git a/include/perfetto/ext/tracing/core/consumer.h b/include/perfetto/ext/tracing/core/consumer.h index 7a7d81af1..f55b810df 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 @@ class PERFETTO_EXPORT_COMPONENT Consumer { // 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 82e53f7bf..b82a96a01 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 @@ class PERFETTO_EXPORT_COMPONENT ConsumerEndpoint { 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 @@ class PERFETTO_EXPORT_COMPONENT TracingService { 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 @@ class PERFETTO_EXPORT_COMPONENT TracingService { 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 5c51c4aab..b24ccb5af 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 @@ namespace ipc { 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 @@ class TracingService; // 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 2210532f3..c27fcadf2 100644 --- a/include/perfetto/protozero/proto_decoder.h +++ b/include/perfetto/protozero/proto_decoder.h @@ -340,7 +340,8 @@ class PERFETTO_EXPORT_COMPONENT TypedProtoDecoderBase : public ProtoDecoder { 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 3e2a83066..e9807ab56 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 95036d148..26525faae 100644 --- a/include/perfetto/tracing/data_source.h +++ b/include/perfetto/tracing/data_source.h @@ -105,7 +105,7 @@ class PERFETTO_EXPORT_COMPONENT DataSourceBase { }; virtual void OnStart(const StartArgs&); - class StopArgs { + class PERFETTO_EXPORT_COMPONENT StopArgs { public: virtual ~StopArgs(); @@ -190,6 +190,22 @@ struct DefaultDataSourceTraits { } }; +// 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 @@ template <typename DerivedDataSource, 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 @@ class DataSource : public DataSourceBase { // 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 @@ class DataSource : public DataSourceBase { 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 @@ class DataSource : public DataSourceBase { 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 @@ class DataSource : public DataSourceBase { 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 @@ class DataSource : public DataSourceBase { 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 @@ class DataSource : public DataSourceBase { // Updates the data source descriptor. static void UpdateDescriptor(const DataSourceDescriptor& descriptor) { - type_.UpdateDescriptor(descriptor); + Helper::type().UpdateDescriptor(descriptor); } private: @@ -456,7 +473,7 @@ class DataSource : public DataSourceBase { // 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 @@ class DataSource : public DataSourceBase { 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 @@ class DataSource : public DataSourceBase { // 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 @@ PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState* // 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 @@ PERFETTO_THREAD_LOCAL internal::DataSourceThreadLocalState* // 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 b800cb32b..637c8bf2e 100644 --- a/include/perfetto/tracing/interceptor.h +++ b/include/perfetto/tracing/interceptor.h @@ -187,7 +187,7 @@ class PERFETTO_EXPORT_COMPONENT InterceptorBase { // 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/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h index 6d2777ef3..f10ef398e 100644 --- a/include/perfetto/tracing/internal/track_event_macros.h +++ b/include/perfetto/tracing/internal/track_event_macros.h @@ -156,7 +156,7 @@ // C++17 doesn't like a move constructor being defined for the EventFinalizer // class but C++11 and MSVC doesn't compile without it being defined so support // both. -#if PERFETTO_IS_AT_LEAST_CPP17() && !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC) +#if !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC) #define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD delete #else #define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD default diff --git a/protos/perfetto/cloud_trace_processor/BUILD.gn b/protos/perfetto/cloud_trace_processor/BUILD.gn new file mode 100644 index 000000000..83464341c --- /dev/null +++ b/protos/perfetto/cloud_trace_processor/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../../gn/proto_library.gni") + +SOURCES = [ + "common.proto", + "orchestrator.proto", + "worker.proto", +] + +perfetto_proto_library("@TYPE@") { + proto_generators = [ + "lite", + "zero", + "source_set", + ] + deps = [ "../trace_processor:@TYPE@" ] # needed for descriptor.proto. + sources = SOURCES +} + +if (enable_perfetto_grpc) { + perfetto_grpc_library("cloud_trace_processor_grpc") { + deps = [ ":lite" ] + sources = SOURCES + } +} diff --git a/protos/perfetto/cloud_trace_processor/common.proto b/protos/perfetto/cloud_trace_processor/common.proto new file mode 100644 index 000000000..f2fd5cb9d --- /dev/null +++ b/protos/perfetto/cloud_trace_processor/common.proto @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package perfetto.protos; + +enum TracePoolType { + TYPE_UNKNOWN = 0; + + // Indicates that the trace pool can be accessed by more than one user. This + // implies the pool is "stateless" (i.e. TraceProcessor instances do not + // retain state between RPCs). + SHARED = 1; + + // Indicates that the trace pool is only accessible by a single user at a + // time. This implies the pool is "stateful" (i.e. TraceProcessor instances + // retain state across RPCs). + DEDICATED = 2; +} diff --git a/protos/perfetto/cloud_trace_processor/orchestrator.proto b/protos/perfetto/cloud_trace_processor/orchestrator.proto new file mode 100644 index 000000000..2f16e2346 --- /dev/null +++ b/protos/perfetto/cloud_trace_processor/orchestrator.proto @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package perfetto.protos; + +import "protos/perfetto/trace_processor/trace_processor.proto"; +import "protos/perfetto/cloud_trace_processor/common.proto"; + +// RPC interface for a CloudTraceProcessor Orchestrator. +// +// Each CloudTraceProcessor instance has a single Orchestrator which is +// responsible for receiving requests for loading and querying traces from +// clients and shards these requests among a set of "Workers". +service CloudTraceProcessorOrchestrator { + // Creates a TracePool with the specified arguments. + // + // A TracePool is a logical group of traces which can be addressed with a + // single id. + // + // Pools can be "shared" or "dedicated": + // a) a shared pool has the trace processor instances backing the pool shared + // among a group of users. This implicitly means that the pools are + // "stateless" (i.e. do not preserve trace processor state between RPCs) as + // the state of one user should not interfere with the state of another. + // b) a dedicated pool belongs to a single user and can only be accessed + // by that user. These pools are "stateful" i.e. preserve trace processor + // state between RPCs. + rpc TracePoolCreate(TracePoolCreateArgs) returns (TracePoolCreateResponse); + + // Changes the set of traces associated with the specified TracePool. + // + // If this operation completes successfully, any future requests to this pool + // shard will refer to this set of traces. + rpc TracePoolSetTraces(TracePoolSetTracesArgs) + returns (TracePoolSetTracesResponse); + + // Executes a SQL query on the specified TracePool and returns a stream + // with each element being the response for executing the query on the + // associated trace. + // + // Note that each trace can return >1 result due to chunking of protos at the + // TraceProcessor::QueryResult level. + rpc TracePoolQuery(TracePoolQueryArgs) + returns (stream TracePoolQueryResponse); + + // Destroys the TracePool with the specified id. + // + // Any future requests to this pool will return an error. However, the + // same pool id (if a named pool) can be used to create a new pool. + rpc TracePoolDestroy(TracePoolDestroyArgs) returns (TracePoolDestroyResponse); +} + +// Request/Response for Orchestrator::TracePoolCreate. +message TracePoolCreateArgs { + optional TracePoolType pool_type = 1; + + // If |pool_type| == SHARED, the name which should be refer to the pool. This + // name will form part of |pool_id|. + optional string shared_pool_name = 2; +} +message TracePoolCreateResponse { + // The id of the pool which should be used to reference the pool in all future + // RPCs. For shared pools, this id is expected to be a stable transformation + // of |shared_pool_name|. + optional string pool_id = 1; +} + +// Request/Response for Orchestrator::TracePoolSetTraces. +message TracePoolSetTracesArgs { + optional string pool_id = 1; + + // The list of traces which should be associated with this pool. The existing + // loaded trace list will be diffed against this list. Traces not present in + // this list and loaded will be unloaded while traces present in this list + // and unloaded will be loaded. + repeated string traces = 2; +} +message TracePoolSetTracesResponse {} + +// Request/Response for Orchestrator::TracePoolQuery. +message TracePoolQueryArgs { + optional string pool_id = 1; + optional string sql_query = 2; +} +message TracePoolQueryResponse { + optional string trace = 1; + optional QueryResult result = 2; +} + +// Request/Response for Orchestrator::TracePoolDestroy. +message TracePoolDestroyArgs { + optional string pool_id = 1; +} +message TracePoolDestroyResponse {} diff --git a/protos/perfetto/cloud_trace_processor/worker.proto b/protos/perfetto/cloud_trace_processor/worker.proto new file mode 100644 index 000000000..407ef90d3 --- /dev/null +++ b/protos/perfetto/cloud_trace_processor/worker.proto @@ -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. + */ + +syntax = "proto2"; + +package perfetto.protos; + +import "protos/perfetto/trace_processor/trace_processor.proto"; +import "protos/perfetto/cloud_trace_processor/common.proto"; + +// Interface for a CloudTraceProcessor "Worker". +// +// Workers are are owned by a |Orchestrator| who assigns groups of traces to +// them (known as a PoolShards) and forwards any requests from end users. +// Workers are reponsible for loading assigned traces with TraceProcessor and +// executing the requests. +service CloudTraceProcessorWorker { + // Creates a TracePoolShard which will be owned by this worker and returns + // whether it was successfully created. + // + // Orchestrators are responsible for handling groups of traces which the user + // has requested to be loaded: these are known as TracePools. The orchestrator + // then breaks these pools into pieces and shards them out to workers, each of + // which is known as a TracePoolShard. + // + // Thus, a TracePoolShard is unique identified by the tuple (worker, pool id). + rpc TracePoolShardCreate(TracePoolShardCreateArgs) + returns (TracePoolShardCreateResponse); + + // Associates the provided list of traces to this TracePoolShard and returns + // a stream with each element indicating the successful load of one trace + // (which allows monitoring the progress of loads) or a terminal error if the + // assignment of any trace failed. + // + // If this operation completes successfully, any future requests to this pool + // shard will refer to this set of traces. + rpc TracePoolShardSetTraces(TracePoolShardSetTracesArgs) + returns (stream TracePoolShardSetTracesResponse); + + // Executes a SQL query on the specified TracePoolShard and returns a stream + // with each element being the response for executing the query on the + // associated trace. + // + // Note that each trace can return >1 result due to chunking of protos at the + // TraceProcessor::QueryResult level. + rpc TracePoolShardQuery(TracePoolShardQueryArgs) + returns (stream TracePoolShardQueryResponse); + + // Destroys the TracePoolShard with the specified id. + // + // Any future requests to this shard id will return an error. However, the + // same pool id can be used to create a new shard. + rpc TracePoolShardDestroy(TracePoolShardDestroyArgs) + returns (TracePoolShardDestroyResponse); +} + +// Request/Response for Worker::TracePoolShardCreate. +message TracePoolShardCreateArgs { + optional string pool_id = 1; + optional TracePoolType pool_type = 2; +} +message TracePoolShardCreateResponse {} + +// Request/Response for Worker::TracePoolShardSetTraces. +message TracePoolShardSetTracesArgs { + optional string pool_id = 1; + + // The list of traces which should be associated with this shard. The existing + // loaded trace list will be diffed against this list. Traces not present in + // this list and loaded will be unloaded while traces present in this list + // and unloaded will be loaded. + repeated string traces = 2; +} +message TracePoolShardSetTracesResponse { + optional string trace = 1; +} + +// Request/Response for Worker::TracePoolShardQuery. +message TracePoolShardQueryArgs { + optional string pool_id = 1; + optional string sql_query = 2; +} +message TracePoolShardQueryResponse { + optional string trace = 1; + optional QueryResult result = 2; +} + +// Request/Response for Worker::TracePoolShardDestroy. +message TracePoolShardDestroyArgs { + optional string pool_id = 1; +} +message TracePoolShardDestroyResponse {} diff --git a/protos/perfetto/common/observable_events.proto b/protos/perfetto/common/observable_events.proto index 0bde227f0..85767a62a 100644 --- a/protos/perfetto/common/observable_events.proto +++ b/protos/perfetto/common/observable_events.proto @@ -35,6 +35,11 @@ message ObservableEvents { // 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 @@ message ObservableEvents { 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/tracing_service_capabilities.proto b/protos/perfetto/common/tracing_service_capabilities.proto index 123f9bf34..308f18df8 100644 --- a/protos/perfetto/common/tracing_service_capabilities.proto +++ b/protos/perfetto/common/tracing_service_capabilities.proto @@ -35,4 +35,7 @@ message TracingServiceCapabilities { // 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 7b1171f4b..85f1de780 100644 --- a/protos/perfetto/config/perfetto_config.proto +++ b/protos/perfetto/config/perfetto_config.proto @@ -833,6 +833,7 @@ message ProcessStatsConfig { // 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 @@ message ProcessStatsConfig { // 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 @@ message DataSourceConfig { // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { } 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 d71e7d360..239513fed 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 @@ message ProcessStatsConfig { // 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 @@ message ProcessStatsConfig { // 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 04e637c0e..4fe3ea974 100644 --- a/protos/perfetto/config/trace_config.proto +++ b/protos/perfetto/config/trace_config.proto @@ -26,7 +26,7 @@ import "protos/perfetto/config/data_source_config.proto"; // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { } 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 d5a402573..2fdc91978 100644 --- a/protos/perfetto/ipc/consumer_port.proto +++ b/protos/perfetto/ipc/consumer_port.proto @@ -289,4 +289,8 @@ message CloneSessionResponse { // 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 5c4e66f5c..36c1abbff 100644 --- a/protos/perfetto/metrics/android/binder_metric.proto +++ b/protos/perfetto/metrics/android/binder_metric.proto @@ -56,6 +56,9 @@ message AndroidBinderMetric { 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/jank_cuj_metric.proto b/protos/perfetto/metrics/android/jank_cuj_metric.proto index fa3a7b13b..007554ff6 100644 --- a/protos/perfetto/metrics/android/jank_cuj_metric.proto +++ b/protos/perfetto/metrics/android/jank_cuj_metric.proto @@ -61,7 +61,7 @@ message AndroidJankCujMetric { optional string layer_name = 11; } - // Next id: 8 + // Next id: 10 message Frame { // Index of the frame within the single user journey. optional int64 frame_number = 1; @@ -79,9 +79,17 @@ message AndroidJankCujMetric { // Whether SF missed the frame deadline. optional bool sf_missed = 6; + + // Whether the SF callback missed before emitting jank metrics. + // SF callback is used to get the jank classification. + optional bool sf_callback_missed = 8; + + // Whether the HWUI callback missed before emitting jank metrics. + // HWUI callback is used to get the frame duration. + optional bool hwui_callback_missed = 9; } - // Next id: 16 + // Next id: 18 message Metrics { // Overall number of frames within the CUJ. optional int64 total_frames = 1; @@ -137,5 +145,11 @@ message AndroidJankCujMetric { // P99 frame duration in milliseconds. // Not available in counter_metrics. optional double frame_dur_ms_p99 = 15; + + // Number of frames with missed SF callback. + optional int64 sf_callback_missed_frames = 16; + + // Number of frames with missed HWUI callback. + optional int64 hwui_callback_missed_frames = 17; } } diff --git a/protos/perfetto/metrics/android/java_heap_stats.proto b/protos/perfetto/metrics/android/java_heap_stats.proto index 2579888ee..10a0b8b57 100644 --- a/protos/perfetto/metrics/android/java_heap_stats.proto +++ b/protos/perfetto/metrics/android/java_heap_stats.proto @@ -26,7 +26,7 @@ message JavaHeapStats { 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 @@ message JavaHeapStats { // 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 91122eb17..00585d861 100644 --- a/protos/perfetto/metrics/android/monitor_contention_metric.proto +++ b/protos/perfetto/metrics/android/monitor_contention_metric.proto @@ -21,28 +21,38 @@ package perfetto.protos; // 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 process_name = 14; + 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 blocked_method = 6; optional string short_blocking_method = 7; - optional string short_blocked_method = 8; 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 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 uint32 blocked_thread_tid = 21; + + // Binder context optional int64 binder_reply_ts = 17; optional uint32 binder_reply_tid = 18; - - repeated ThreadStateBreakdown thread_states = 19; - repeated BlockedFunctionBreakdown blocked_functions = 20; } message ThreadStateBreakdown { diff --git a/protos/perfetto/metrics/android/process_metadata.proto b/protos/perfetto/metrics/android/process_metadata.proto index fd7fe7b5d..fa766bea9 100644 --- a/protos/perfetto/metrics/android/process_metadata.proto +++ b/protos/perfetto/metrics/android/process_metadata.proto @@ -44,5 +44,8 @@ message AndroidProcessMetadata { // 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 916a2121c..67c904470 100644 --- a/protos/perfetto/metrics/android/startup_metric.proto +++ b/protos/perfetto/metrics/android/startup_metric.proto @@ -47,7 +47,7 @@ message AndroidStartupMetric { // Timing information spanning the intent received by the // activity manager to the first frame drawn. - // Next id: 33. + // Next id: 35. message ToFirstFrame { // The duration between the intent received and first frame. optional int64 dur_ns = 1; @@ -117,6 +117,14 @@ message AndroidStartupMetric { // |time_lock_contention_thread_main|. optional Slice time_monitor_contention_thread_main = 32; + // Time spent in opening dex files on the main thread of the process + // 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; @@ -216,7 +224,7 @@ message AndroidStartupMetric { 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; @@ -272,6 +280,9 @@ message AndroidStartupMetric { // 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/android/surfaceflinger.proto b/protos/perfetto/metrics/android/surfaceflinger.proto index 3cc2c9e08..1f81827e8 100644 --- a/protos/perfetto/metrics/android/surfaceflinger.proto +++ b/protos/perfetto/metrics/android/surfaceflinger.proto @@ -54,4 +54,32 @@ message AndroidSurfaceflingerMetric { // This also equals to the total duration of // "waiting for GPU completion <fence_num>" in SurfaceFlinger. optional double total_non_empty_gpu_waiting_dur_ms = 9; + + message MetricsPerDisplay { + // Display ID in SF + optional string display_id = 1; + + // Counts the number of missed frames in the trace. + optional uint32 missed_frames = 2; + + // Counts the number of missed HWC frames in the trace. + optional uint32 missed_hwc_frames = 3; + + // Counts the number of missed GPU frames in the trace. + optional uint32 missed_gpu_frames = 4; + + // Calculate the number of missed frames divided by + // total frames + optional double missed_frame_rate = 5; + + // Calculate the number of missed HWC frames divided by + // total HWC frames + optional double missed_hwc_frame_rate = 6; + + // Calculate the number of missed GPU frames divided by + // total GPU frames + optional double missed_gpu_frame_rate = 7; + } + + repeated MetricsPerDisplay metrics_per_display = 10; } diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto index 2fc2bae56..393095c36 100644 --- a/protos/perfetto/metrics/perfetto_merged_metrics.proto +++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto @@ -42,6 +42,9 @@ message AndroidProcessMetadata { // 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 @@ message AndroidBinderMetric { optional uint32 client_tid = 15; optional uint32 server_tid = 16; + + optional uint32 client_pid = 17; + optional uint32 server_pid = 18; } message ThreadStateBreakdown { @@ -880,7 +886,7 @@ message AndroidJankCujMetric { optional string layer_name = 11; } - // Next id: 8 + // Next id: 10 message Frame { // Index of the frame within the single user journey. optional int64 frame_number = 1; @@ -898,9 +904,17 @@ message AndroidJankCujMetric { // Whether SF missed the frame deadline. optional bool sf_missed = 6; + + // Whether the SF callback missed before emitting jank metrics. + // SF callback is used to get the jank classification. + optional bool sf_callback_missed = 8; + + // Whether the HWUI callback missed before emitting jank metrics. + // HWUI callback is used to get the frame duration. + optional bool hwui_callback_missed = 9; } - // Next id: 16 + // Next id: 18 message Metrics { // Overall number of frames within the CUJ. optional int64 total_frames = 1; @@ -956,6 +970,12 @@ message AndroidJankCujMetric { // P99 frame duration in milliseconds. // Not available in counter_metrics. optional double frame_dur_ms_p99 = 15; + + // Number of frames with missed SF callback. + optional int64 sf_callback_missed_frames = 16; + + // Number of frames with missed HWUI callback. + optional int64 hwui_callback_missed_frames = 17; } } @@ -1005,7 +1025,7 @@ message JavaHeapStats { optional int64 obj_count = 3; } - // Next id: 10 + // Next id: 11 message Sample { optional int64 ts = 1; // Size of the Java heap in bytes @@ -1024,6 +1044,8 @@ message JavaHeapStats { // 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 @@ -1171,28 +1193,38 @@ message AndroidMemoryUnaggregatedMetric { // 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 process_name = 14; + 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 blocked_method = 6; optional string short_blocking_method = 7; - optional string short_blocked_method = 8; 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 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 uint32 blocked_thread_tid = 21; + + // Binder context optional int64 binder_reply_ts = 17; optional uint32 binder_reply_tid = 18; - - repeated ThreadStateBreakdown thread_states = 19; - repeated BlockedFunctionBreakdown blocked_functions = 20; } message ThreadStateBreakdown { @@ -1577,7 +1609,7 @@ message AndroidStartupMetric { // Timing information spanning the intent received by the // activity manager to the first frame drawn. - // Next id: 33. + // Next id: 35. message ToFirstFrame { // The duration between the intent received and first frame. optional int64 dur_ns = 1; @@ -1647,6 +1679,14 @@ message AndroidStartupMetric { // |time_lock_contention_thread_main|. optional Slice time_monitor_contention_thread_main = 32; + // Time spent in opening dex files on the main thread of the process + // 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; @@ -1746,7 +1786,7 @@ message AndroidStartupMetric { 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; @@ -1802,6 +1842,9 @@ message AndroidStartupMetric { // 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; @@ -1860,6 +1903,34 @@ message AndroidSurfaceflingerMetric { // This also equals to the total duration of // "waiting for GPU completion <fence_num>" in SurfaceFlinger. optional double total_non_empty_gpu_waiting_dur_ms = 9; + + message MetricsPerDisplay { + // Display ID in SF + optional string display_id = 1; + + // Counts the number of missed frames in the trace. + optional uint32 missed_frames = 2; + + // Counts the number of missed HWC frames in the trace. + optional uint32 missed_hwc_frames = 3; + + // Counts the number of missed GPU frames in the trace. + optional uint32 missed_gpu_frames = 4; + + // Calculate the number of missed frames divided by + // total frames + optional double missed_frame_rate = 5; + + // Calculate the number of missed HWC frames divided by + // total HWC frames + optional double missed_hwc_frame_rate = 6; + + // Calculate the number of missed GPU frames divided by + // total GPU frames + optional double missed_gpu_frame_rate = 7; + } + + repeated MetricsPerDisplay metrics_per_display = 10; } // End of protos/perfetto/metrics/android/surfaceflinger.proto diff --git a/protos/perfetto/metrics/webview/webview_jank_approximation.proto b/protos/perfetto/metrics/webview/webview_jank_approximation.proto index 0d01a7797..cdf7299a3 100644 --- a/protos/perfetto/metrics/webview/webview_jank_approximation.proto +++ b/protos/perfetto/metrics/webview/webview_jank_approximation.proto @@ -19,9 +19,9 @@ syntax = "proto2"; package perfetto.protos; message WebViewJankApproximation { - required int32 webview_janks = 1; - required int32 webview_janks_without_startup = 2; - required int32 webview_app_janks = 3; - required int32 webview_total_janks = 4; - required int32 total_janks = 5; + optional int32 webview_janks = 1; + optional int32 webview_janks_without_startup = 2; + optional int32 webview_app_janks = 3; + optional int32 webview_total_janks = 4; + optional int32 total_janks = 5; } diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto index b86ad9f40..88d364178 100644 --- a/protos/perfetto/trace/ftrace/ftrace_event.proto +++ b/protos/perfetto/trace/ftrace/ftrace_event.proto @@ -595,5 +595,7 @@ message FtraceEvent { HostSmcFtraceEvent host_smc = 479; HostMemAbortFtraceEvent host_mem_abort = 480; SuspendResumeMinimalFtraceEvent suspend_resume_minimal = 481; + MaliMaliCSFINTERRUPTSTARTFtraceEvent mali_mali_CSF_INTERRUPT_START = 482; + MaliMaliCSFINTERRUPTENDFtraceEvent mali_mali_CSF_INTERRUPT_END = 483; } } diff --git a/protos/perfetto/trace/ftrace/mali.proto b/protos/perfetto/trace/ftrace/mali.proto index 0c59cd409..9d671666b 100644 --- a/protos/perfetto/trace/ftrace/mali.proto +++ b/protos/perfetto/trace/ftrace/mali.proto @@ -53,3 +53,13 @@ message MaliMaliKCPUFENCEWAITENDFtraceEvent { optional uint32 kctx_id = 4; optional uint32 id = 5; } +message MaliMaliCSFINTERRUPTSTARTFtraceEvent { + optional int32 kctx_tgid = 1; + optional uint32 kctx_id = 2; + optional uint64 info_val = 3; +} +message MaliMaliCSFINTERRUPTENDFtraceEvent { + optional int32 kctx_tgid = 1; + optional uint32 kctx_id = 2; + optional uint64 info_val = 3; +} diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto index 7b9279f28..e886311d7 100644 --- a/protos/perfetto/trace/perfetto_trace.proto +++ b/protos/perfetto/trace/perfetto_trace.proto @@ -833,6 +833,7 @@ message ProcessStatsConfig { // 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 @@ message ProcessStatsConfig { // 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 @@ message DataSourceConfig { // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { // 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 @@ message TraceConfig { } 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 @@ -6776,6 +6811,16 @@ message MaliMaliKCPUFENCEWAITENDFtraceEvent { optional uint32 kctx_id = 4; optional uint32 id = 5; } +message MaliMaliCSFINTERRUPTSTARTFtraceEvent { + optional int32 kctx_tgid = 1; + optional uint32 kctx_id = 2; + optional uint64 info_val = 3; +} +message MaliMaliCSFINTERRUPTENDFtraceEvent { + optional int32 kctx_tgid = 1; + optional uint32 kctx_id = 2; + optional uint64 info_val = 3; +} // End of protos/perfetto/trace/ftrace/mali.proto @@ -8279,6 +8324,8 @@ message FtraceEvent { HostSmcFtraceEvent host_smc = 479; HostMemAbortFtraceEvent host_mem_abort = 480; SuspendResumeMinimalFtraceEvent suspend_resume_minimal = 481; + MaliMaliCSFINTERRUPTSTARTFtraceEvent mali_mali_CSF_INTERRUPT_START = 482; + MaliMaliCSFINTERRUPTENDFtraceEvent mali_mali_CSF_INTERRUPT_END = 483; } } @@ -11204,6 +11251,13 @@ message ProcessStats { 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; @@ -11290,7 +11344,7 @@ message ProcessTree { // 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 4ac0c40e6..03759b441 100644 --- a/protos/perfetto/trace/ps/process_stats.proto +++ b/protos/perfetto/trace/ps/process_stats.proto @@ -76,6 +76,13 @@ message ProcessStats { 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 026766db7..62ca960cd 100644 --- a/protos/perfetto/trace/statsd/statsd_atom.proto +++ b/protos/perfetto/trace/statsd/statsd_atom.proto @@ -20,7 +20,7 @@ package perfetto.protos; // 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/BUILD.gn b/protos/perfetto/trace_processor/BUILD.gn index 5e2f37632..2f6cb71c7 100644 --- a/protos/perfetto/trace_processor/BUILD.gn +++ b/protos/perfetto/trace_processor/BUILD.gn @@ -41,10 +41,3 @@ perfetto_proto_library("metrics_impl_@TYPE@") { ] sources = [ "metrics_impl.proto" ] } - -if (enable_perfetto_grpc) { - perfetto_grpc_library("cloud_trace_processor_grpc") { - deps = [ ":lite" ] - sources = [ "cloud_trace_processor.proto" ] - } -} diff --git a/protos/perfetto/trace_processor/proto_files.gni b/protos/perfetto/trace_processor/proto_files.gni index 5f202b254..f211050d8 100644 --- a/protos/perfetto/trace_processor/proto_files.gni +++ b/protos/perfetto/trace_processor/proto_files.gni @@ -15,8 +15,7 @@ # This variable is used both by ./BUILD.gn (for the C++ proto codegen) and by # //ui/BUIlD.gn (for the TypeScript/JS proto codegen). trace_processor_protos = [ - "cloud_trace_processor", - "trace_processor", "metatrace_categories", "stack", + "trace_processor", ] diff --git a/protos/third_party/CHROMIUM_OWNERS b/protos/third_party/CHROMIUM_OWNERS index 7cc656e22..826302dc8 100644 --- a/protos/third_party/CHROMIUM_OWNERS +++ b/protos/third_party/CHROMIUM_OWNERS @@ -14,3 +14,7 @@ mohitms@google.com nuskos@google.com oksamyt@google.com kartarsingh@google.com +jkoshy@google.com +amanvr@google.com +rasikan@google.com +violettfaid@google.com diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto index f19c1cf9a..cce5e7693 100644 --- a/protos/third_party/chromium/chrome_track_event.proto +++ b/protos/third_party/chromium/chrome_track_event.proto @@ -512,6 +512,12 @@ message BrowsingContextState { // The ID of the BrowsingInstance that the BrowsingContextState belongs to. optional int32 browsing_instance_id = 1; + // The ID of the CoopRelatedGroup that the BrowsingContextState belongs to. + 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. repeated DebugAnnotation debug_annotations = 99; @@ -734,6 +740,7 @@ message RendererMainThreadTaskExecution { TASK_TYPE_INTERNAL_NAVIGATION_CANCELLATION = 80; TASK_TYPE_LOW_PRIORITY_SCRIPT_EXECUTION = 81; TASK_TYPE_STORAGE = 82; + TASK_TYPE_NETWORKING_UNFREEZABLE_IMAGE_LOADING = 83; } enum FrameType { @@ -863,6 +870,7 @@ message SequenceManagerTask { HIGH_PRIORITY_CONTINUATION = 8; NORMAL_PRIORITY_CONTINUATION = 9; LOW_PRIORITY_CONTINUATION = 10; + EXTREMELY_HIGH_PRIORITY = 11; } enum QueueName { @@ -1031,11 +1039,25 @@ message BlinkExecutionContext { SERVICE_WORKER = 5; } + // Definition of world type. + enum WorldType { + WORLD_UNKNOWN = 0; + WORLD_MAIN = 1; + WORLD_ISOLATED = 2; + WORLD_INSPECTOR_ISOLATED = 3; + WORLD_REG_EXP = 4; + WORLD_FOR_V8_CONTEXT_SNAPSHOT_NON_MAIN = 5; + WORLD_WORKER = 6; + WORLD_SHADOW_REALM = 7; + } + optional ContextType type = 1; // Contains url of frame or worker. optional string url = 2; // The origin of the execution context. optional string origin = 3; + // The world type of the execution context. + optional WorldType world_type = 4; } // Serializes the blink::SourceLocation object. @@ -1079,10 +1101,28 @@ message BlinkHighEntropyAPI { // similar to "Navigator.languages.get". optional string identifier = 1; repeated JSFunctionArgument func_arguments = 2; + + // Deprecated in favour of outer source_location. optional BlinkSourceLocation source_location = 3; } optional BlinkExecutionContext execution_context = 1; optional CalledJsApi called_api = 2; + optional BlinkSourceLocation source_location = 3; + + // Describes lookup of a font. + message FontLookup { + enum FontLookupType { + FONT_LOOKUP_UNKNOWN_TYPE = 0; + FONT_LOOKUP_UNIQUE_OR_FAMILY_NAME = 1; + FONT_LOOKUP_UNIQUE_NAME_ONLY = 2; + } + optional FontLookupType type = 1; + optional string name = 2; + optional uint64 weight = 3; + optional uint64 width = 4; + optional uint64 slope = 5; + } + optional FontLookup font_lookup = 4; } // Contains information about a tab switch measurement. diff --git a/protos/third_party/statsd/shell_data.proto b/protos/third_party/statsd/shell_data.proto index e4c31bdd0..64551ea63 100644 --- a/protos/third_party/statsd/shell_data.proto +++ b/protos/third_party/statsd/shell_data.proto @@ -20,8 +20,8 @@ package perfetto.proto; // 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/generators/stdlib_docs/parse.py b/python/generators/stdlib_docs/parse.py index 79f4cfea6..bdef71773 100644 --- a/python/generators/stdlib_docs/parse.py +++ b/python/generators/stdlib_docs/parse.py @@ -42,6 +42,8 @@ def parse_columns(docs: Union['stdlib.TableViewDocs', 'stdlib.ViewFunctionDocs'] m = re.match(Pattern['column'], line) if last_col: cols[last_col] = ' '.join(last_desc) + 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)] cols[last_col] = ' '.join(last_desc) @@ -79,6 +81,8 @@ def parse_ret(docs: "stdlib.FunctionDocs") -> Tuple[str, str]: desc.append(get_text(line)) 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)) diff --git a/python/generators/trace_processor_table/public.py b/python/generators/trace_processor_table/public.py index abd0aec09..c8bdb0be5 100644 --- a/python/generators/trace_processor_table/public.py +++ b/python/generators/trace_processor_table/public.py @@ -119,6 +119,8 @@ class Table: Representation of of a C++ table. Attributes: + python_module: Path to the Python module this table is defined in. Always + pass __file__. class_name: Name of the C++ table class. sql_name: Name of the table in SQL. columns: The columns in this table. @@ -129,6 +131,7 @@ class Table: specified table. wrapping_sql_view: See |WrappingSqlView|. """ + python_module: str class_name: str sql_name: str columns: List[Column] diff --git a/python/generators/trace_processor_table/serialize.py b/python/generators/trace_processor_table/serialize.py index 0a8715cb9..1b498c3a2 100644 --- a/python/generators/trace_processor_table/serialize.py +++ b/python/generators/trace_processor_table/serialize.py @@ -19,6 +19,8 @@ from python.generators.trace_processor_table.public import Alias from python.generators.trace_processor_table.public import ColumnFlag from python.generators.trace_processor_table.util import ParsedTable from python.generators.trace_processor_table.util import ParsedColumn +from python.generators.trace_processor_table.util import parse_type +from python.generators.trace_processor_table.util import typed_column_type class ColumnSerializer: @@ -30,8 +32,9 @@ class ColumnSerializer: self.col = self.parsed_col.column self.name = self.col.name self.flags = self.col.flags - self.typed_column_type = table.typed_column_type(self.parsed_col) - self.cpp_type = table.parse_type(self.col.type).cpp_type_with_optionality() + self.typed_column_type = typed_column_type(table.table, self.parsed_col) + self.cpp_type = parse_type(table.table, + self.col.type).cpp_type_with_optionality() self.is_implicit_id = self.parsed_col.is_implicit_id self.is_implicit_type = self.parsed_col.is_implicit_type @@ -114,7 +117,7 @@ class ColumnSerializer: return f''' columns_.emplace_back("{self.name}", &{self.name}_, ColumnFlag::{self.name}, this, static_cast<uint32_t>(columns_.size()), - overlay_idx); + olay_idx); ''' def shrink_to_fit(self) -> Optional[str]: @@ -180,11 +183,11 @@ class ColumnSerializer: if self.is_implicit_id or self.is_implicit_type: return None return f''' - schema.columns.emplace_back(Table::Schema::Column{{ - "{self.name}", ColumnType::{self.name}::SqlValueType(), false, - {str(ColumnFlag.SORTED in self.flags).lower()}, - {str(ColumnFlag.HIDDEN in self.flags).lower()}, - {str(ColumnFlag.SET_ID in self.flags).lower()}}}); + schema.columns.emplace_back(Table::Schema::Column{{ + "{self.name}", ColumnType::{self.name}::SqlValueType(), false, + {str(ColumnFlag.SORTED in self.flags).lower()}, + {str(ColumnFlag.HIDDEN in self.flags).lower()}, + {str(ColumnFlag.SET_ID in self.flags).lower()}}}); ''' def row_eq(self) -> Optional[str]: @@ -192,6 +195,42 @@ class ColumnSerializer: return None return f'ColumnType::{self.name}::Equals({self.name}, other.{self.name})' + def extend_parent_param(self) -> Optional[str]: + if self.is_implicit_id or self.is_implicit_type: + return None + if self.is_ancestor: + return None + return f'ColumnStorage<ColumnType::{self.name}::stored_type> {self.name}' + + def extend_parent_param_arg(self) -> Optional[str]: + if self.is_implicit_id or self.is_implicit_type: + return None + if self.is_ancestor: + return None + return f'std::move({self.name})' + + def static_assert_flags(self) -> Optional[str]: + if self.is_implicit_id or self.is_implicit_type: + return None + if self.is_ancestor: + return None + return f''' + static_assert( + Column::IsFlagsAndTypeValid<ColumnType::{self.name}::stored_type>( + ColumnFlag::{self.name}), + "Column type and flag combination is not valid"); + ''' + + def extend_nullable_vector(self) -> Optional[str]: + if self.is_implicit_id or self.is_implicit_type: + return None + if self.is_ancestor: + return None + return f''' + PERFETTO_DCHECK({self.name}.size() == parent_overlay.size()); + {self.name}_ = std::move({self.name}); + ''' + class TableSerializer(object): """Functions for seralizing a single Table into C++.""" @@ -243,12 +282,13 @@ class TableSerializer(object): ColumnSerializer.parent_row_initializer, delimiter=', ') row_init = self.foreach_col( ColumnSerializer.row_initializer, delimiter=',\n ') + parent_separator = ',' if row_init else '' row_eq = self.foreach_col(ColumnSerializer.row_eq, delimiter=' &&\n ') return f''' struct Row : public {self.parent_class_name}::Row {{ Row({param}, std::nullptr_t = nullptr) - : {self.parent_class_name}::Row({parent_row_init}), + : {self.parent_class_name}::Row({parent_row_init}){parent_separator} {row_init} {{ type_ = "{self.table.sql_name}"; }} @@ -297,22 +337,28 @@ class TableSerializer(object): ''' def constructor(self) -> str: - col_init = self.foreach_col( + storage_init = self.foreach_col( ColumnSerializer.storage_init, delimiter=',\n ') if self.table.parent: parent_param = f', {self.parent_class_name}* parent' parent_arg = 'parent' - parent_init = 'parent_(parent), ' + parent_init = 'parent_(parent)' + (', ' if storage_init else '') else: parent_param = '' parent_arg = 'nullptr' parent_init = '' + col_init = self.foreach_col(ColumnSerializer.column_init) + if col_init: + olay = 'uint32_t olay_idx = static_cast<uint32_t>(overlays_.size()) - 1;' + else: + olay = '' return f''' explicit {self.table_name}(StringPool* pool{parent_param}) : macros_internal::MacroTable(pool, {parent_arg}), - {parent_init}{col_init} {{ - uint32_t overlay_idx = static_cast<uint32_t>(overlays_.size()) - 1; - {self.foreach_col(ColumnSerializer.column_init)} + {parent_init}{storage_init} {{ + {self.foreach_col(ColumnSerializer.static_assert_flags)} + {olay} + {col_init} }} ''' @@ -383,6 +429,60 @@ class TableSerializer(object): }}; ''' + def extend(self) -> str: + if not self.table.parent: + return '' + params = self.foreach_col( + ColumnSerializer.extend_parent_param, delimiter='\n, ') + args = self.foreach_col( + ColumnSerializer.extend_parent_param_arg, delimiter=', ') + delim = ',' if params else '' + return f''' + static std::unique_ptr<Table> ExtendParent( + const {self.parent_class_name}& parent{delim} + {params}) {{ + return std::unique_ptr<Table>(new {self.table_name}( + parent.string_pool(), parent, RowMap(0, parent.row_count()){delim} + {args})); + }} + + static std::unique_ptr<Table> SelectAndExtendParent( + const {self.parent_class_name}& parent, + std::vector<{self.parent_class_name}::RowNumber> parent_overlay{delim} + {params}) {{ + std::vector<uint32_t> prs_untyped(parent_overlay.size()); + for (uint32_t i = 0; i < parent_overlay.size(); ++i) {{ + prs_untyped[i] = parent_overlay[i].row_number(); + }} + return std::unique_ptr<Table>(new {self.table_name}( + parent.string_pool(), parent, RowMap(std::move(prs_untyped)){delim} + {args})); + }} + ''' + + def extend_constructor(self) -> str: + if not self.table.parent: + return '' + params = self.foreach_col( + ColumnSerializer.extend_parent_param, delimiter='\n, ') + if params: + olay = 'uint32_t olay_idx = static_cast<uint32_t>(overlays_.size()) - 1;' + else: + olay = '' + return f''' + {self.table_name}(StringPool* pool, + const {self.parent_class_name}& parent, + const RowMap& parent_overlay{',' if params else ''} + {params}) + : macros_internal::MacroTable(pool, parent, parent_overlay) {{ + {self.foreach_col(ColumnSerializer.static_assert_flags)} + {self.foreach_col(ColumnSerializer.extend_nullable_vector)} + + {olay} + {self.foreach_col(ColumnSerializer.column_init)} + }} + ''' + def serialize(self) -> str: return f''' class {self.table_name} : public macros_internal::MacroTable {{ @@ -482,11 +582,14 @@ class {self.table_name} : public macros_internal::MacroTable {{ RowNumber(row_number)}}; }} + {self.extend().strip()} + {self.foreach_col(ColumnSerializer.accessor)} {self.foreach_col(ColumnSerializer.mutable_accessor)} private: + {self.extend_constructor().strip()} {self.parent_field().strip()} {self.foreach_col(ColumnSerializer.storage)} }}; @@ -502,7 +605,7 @@ def serialize_header(ifdef_guard: str, tables: List[ParsedTable], #ifndef {ifdef_guard} #define {ifdef_guard} -#include "src/trace_processor/tables/macros.h" +#include "src/trace_processor/tables/macros_internal.h" {include_paths_str} diff --git a/python/generators/trace_processor_table/util.py b/python/generators/trace_processor_table/util.py index 015f02c71..1f0dc0ce8 100644 --- a/python/generators/trace_processor_table/util.py +++ b/python/generators/trace_processor_table/util.py @@ -14,7 +14,8 @@ import dataclasses from dataclasses import dataclass -import runpy +import importlib +import sys from typing import Dict from typing import List from typing import Set @@ -92,71 +93,78 @@ class ParsedTable: table: Table columns: List[ParsedColumn] - input_path: str - - def parse_type(self, col_type: CppColumnType) -> ParsedType: - """Parses a CppColumnType into its constiuent parts.""" - - if isinstance(col_type, CppInt64): - return ParsedType('int64_t') - if isinstance(col_type, CppInt32): - return ParsedType('int32_t') - if isinstance(col_type, CppUint32): - return ParsedType('uint32_t') - if isinstance(col_type, CppDouble): - return ParsedType('double') - if isinstance(col_type, CppString): - return ParsedType('StringPool::Id') - - if isinstance(col_type, Alias): - col = next(c for c in self.columns - if c.column.name == col_type.underlying_column) - return ParsedType( - self.parse_type(col.column.type).cpp_type, - is_alias=True, - alias_underlying_name=col.column.name) - - if isinstance(col_type, CppTableId): - return ParsedType( - f'{col_type.table.class_name}::Id', id_table=col_type.table) - - if isinstance(col_type, CppSelfTableId): - return ParsedType( - f'{self.table.class_name}::Id', is_self_id=True, id_table=self.table) - - if isinstance(col_type, CppOptional): - inner = self.parse_type(col_type.inner) - assert not inner.is_optional, 'Nested optional not allowed' - return dataclasses.replace(inner, is_optional=True) - - raise Exception(f'Unknown type {col_type}') - - def typed_column_type(self, col: ParsedColumn) -> str: - """Returns the TypedColumn/IdColumn C++ type for a given column.""" - - parsed = self.parse_type(col.column.type) - if col.is_implicit_id: - return f'IdColumn<{parsed.cpp_type}>' - return f'TypedColumn<{parsed.cpp_type_with_optionality()}>' - - def find_table_deps(self) -> Set[str]: - """Finds all the other table class names this table depends on. - - By "depends", we mean this table in C++ would need the dependency to be - defined (or included) before this table is defined.""" - - deps: Set[str] = set() - if self.table.parent: - deps.add(self.table.parent.class_name) - for c in self.table.columns: - # Aliases cannot have dependencies so simply ignore them: trying to parse - # them before adding implicit columns can cause issues. - if isinstance(c.type, Alias): - continue - id_table = self.parse_type(c.type).id_table - if id_table: - deps.add(id_table.class_name) - return deps + + +def parse_type_with_cols(table: Table, cols: List[Column], + col_type: CppColumnType) -> ParsedType: + """Parses a CppColumnType into its constiuent parts.""" + + if isinstance(col_type, CppInt64): + return ParsedType('int64_t') + if isinstance(col_type, CppInt32): + return ParsedType('int32_t') + if isinstance(col_type, CppUint32): + return ParsedType('uint32_t') + if isinstance(col_type, CppDouble): + return ParsedType('double') + if isinstance(col_type, CppString): + return ParsedType('StringPool::Id') + + if isinstance(col_type, Alias): + col = next(c for c in cols if c.name == col_type.underlying_column) + return ParsedType( + parse_type(table, col.type).cpp_type, + is_alias=True, + alias_underlying_name=col.name) + + if isinstance(col_type, CppTableId): + return ParsedType( + f'{col_type.table.class_name}::Id', id_table=col_type.table) + + if isinstance(col_type, CppSelfTableId): + return ParsedType( + f'{table.class_name}::Id', is_self_id=True, id_table=table) + + if isinstance(col_type, CppOptional): + inner = parse_type(table, col_type.inner) + assert not inner.is_optional, 'Nested optional not allowed' + return dataclasses.replace(inner, is_optional=True) + + raise Exception(f'Unknown type {col_type}') + + +def parse_type(table: Table, col_type: CppColumnType) -> ParsedType: + """Parses a CppColumnType into its constiuent parts.""" + return parse_type_with_cols(table, table.columns, col_type) + + +def typed_column_type(table: Table, col: ParsedColumn) -> str: + """Returns the TypedColumn/IdColumn C++ type for a given column.""" + + parsed = parse_type(table, col.column.type) + if col.is_implicit_id: + return f'IdColumn<{parsed.cpp_type}>' + return f'TypedColumn<{parsed.cpp_type_with_optionality()}>' + + +def find_table_deps(table: Table) -> List[Table]: + """Finds all the other table class names this table depends on. + + By "depends", we mean this table in C++ would need the dependency to be + defined (or included) before this table is defined.""" + + deps: Dict[str, Table] = {} + if table.parent: + deps[table.parent.class_name] = table.parent + for c in table.columns: + # Aliases cannot have dependencies so simply ignore them: trying to parse + # them before adding implicit columns can cause issues. + if isinstance(c.type, Alias): + continue + id_table = parse_type(table, c.type).id_table + if id_table: + deps[id_table.class_name] = id_table + return list(deps.values()) def public_sql_name(table: Table) -> str: @@ -165,10 +173,9 @@ def public_sql_name(table: Table) -> str: wrapping_view = table.wrapping_sql_view return wrapping_view.view_name if wrapping_view else table.sql_name -def _create_implicit_columns_for_root(parsed: ParsedTable - ) -> List[ParsedColumn]: + +def _create_implicit_columns_for_root(table: Table) -> List[ParsedColumn]: """Given a root table, returns the implicit id and type columns.""" - table = parsed.table assert table.parent is None sql_name = public_sql_name(table) @@ -191,26 +198,25 @@ def _create_implicit_columns_for_root(parsed: ParsedTable ] -def _topological_sort_tables(parsed: List[ParsedTable]) -> List[ParsedTable]: +def _topological_sort_table_and_deps(parsed: List[Table]) -> List[Table]: """Topologically sorts a list of tables (i.e. dependenices appear earlier). See [1] for information on a topological sort. We do this to allow dependencies to be processed and appear ealier than their dependents. [1] https://en.wikipedia.org/wiki/Topological_sorting""" - table_to_parsed_table = {p.table.class_name: p for p in parsed} visited: Set[str] = set() - result: List[ParsedTable] = [] + result: List[Table] = [] # Topological sorting is really just a DFS where we put the nodes in the list # after any dependencies. - def dfs(t: ParsedTable): - if t.table.class_name in visited: + def dfs(t: Table): + if t.class_name in visited: return - visited.add(t.table.class_name) + visited.add(t.class_name) - for dep in t.find_table_deps(): - dfs(table_to_parsed_table[dep]) + for dep in find_table_deps(t): + dfs(dep) result.append(t) for p in parsed: @@ -226,26 +232,27 @@ def _to_column_doc(doc: Union[ColumnDoc, str, None]) -> Optional[ColumnDoc]: return ColumnDoc(doc=doc) -def parse_tables_from_files(input_paths: List[str]) -> List[ParsedTable]: +def parse_tables_from_modules(modules: List[str]) -> List[ParsedTable]: """Creates a list of tables with the associated paths.""" # Create a mapping from the table to a "parsed" version of the table. + tables: Dict[str, Table] = {} + for module in modules: + imported = importlib.import_module(module) + run_tables: List[Table] = imported.__dict__['ALL_TABLES'] + for table in run_tables: + existing_table = tables.get(table.class_name) + assert not existing_table or existing_table == table + tables[table.class_name] = table + + # Sort all the tables: note that this list may include tables which are not + # in |tables| dictionary due to dependencies on tables which live in a file + # not covered by |input_paths|. + sorted_tables = _topological_sort_table_and_deps(list(tables.values())) + parsed_tables: Dict[str, ParsedTable] = {} - for in_path in input_paths: - tables: List[Table] = runpy.run_path(in_path)['ALL_TABLES'] - for table in tables: - existing_table = parsed_tables.get(table.class_name) - assert not existing_table or existing_table.table == table - parsed_tables[table.class_name] = ParsedTable(table, [], in_path) - - # Sort all the tables to be in order. - sorted_tables = _topological_sort_tables(list(parsed_tables.values())) - - # Create the list of parsed columns - for i, parsed in enumerate(sorted_tables): + for table in sorted_tables: parsed_columns: List[ParsedColumn] - table = parsed.table - if table.parent: parsed_parent = parsed_tables[table.parent.class_name] parsed_columns = [ @@ -253,13 +260,17 @@ def parse_tables_from_files(input_paths: List[str]) -> List[ParsedTable]: for c in parsed_parent.columns ] else: - parsed_columns = _create_implicit_columns_for_root(parsed) + parsed_columns = _create_implicit_columns_for_root(table) for c in table.columns: doc = table.tabledoc.columns.get(c.name) if table.tabledoc else None parsed_columns.append(ParsedColumn(c, _to_column_doc(doc))) + parsed_tables[table.class_name] = ParsedTable(table, parsed_columns) - sorted_tables[i] = dataclasses.replace(parsed, columns=parsed_columns) - parsed_tables[parsed.table.class_name] = sorted_tables[i] - - return sorted_tables + # Only return tables which come directly from |input_paths|. This stops us + # generating tables which were not requested. + return [ + parsed_tables[p.class_name] + for p in sorted_tables + if p.class_name in tables + ] diff --git a/python/perfetto/prebuilts/manifests/trace_processor_shell.py b/python/perfetto/prebuilts/manifests/trace_processor_shell.py index 85fb30efd..2e9e82606 100755 --- a/python/perfetto/prebuilts/manifests/trace_processor_shell.py +++ b/python/perfetto/prebuilts/manifests/trace_processor_shell.py @@ -1,15 +1,15 @@ -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'trace_processor_shell', 'file_size': - 8583152, + 8714576, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/trace_processor_shell', 'sha256': - '35673d3546dec894b5d55147da2fad523a8f5917b42ec1c327c940b82d3ce565', + '9bdb89493f0f00db5d3a73166450ac2f6ee830de16415e79c5a0234990caa644', 'platform': 'darwin', 'machine': ['x86_64'] @@ -19,11 +19,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 7303384, + 7286968, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell', 'sha256': - 'a4d301cf8c0c01d328a9253d5ba78f4249333d4b04236cf8be0c7dad2a65e7e0', + '948536035fbe680b47b94a99d320ff459450738e4aeeb16cef18364f0023622b', 'platform': 'darwin', 'machine': ['arm64'] @@ -33,11 +33,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 8991600, + 8576688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/trace_processor_shell', 'sha256': - 'e8dd82c1ec73fbbf4165ab0d9cbbb750cff5bcf723a1eab51adc9382bf652361', + '493698c81fffcabc340c72831b175962dba5a31dfe8572a6d5af083a116af4f8', 'platform': 'linux', 'machine': ['x86_64'] @@ -47,11 +47,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 7117104, + 6125384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/trace_processor_shell', 'sha256': - 'bbfde44ec004815a36cecdc1dbc135f815f46ac6a3989c87cb0c577510c1c8fe', + '53f1e27603695cf92d22519993b6eafa9c60957d9cb33bd0b300df8573b87ebb', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -61,11 +61,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 8384816, + 8036288, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/trace_processor_shell', 'sha256': - 'c4f9a499e8c443961725448aabc04cd7dc18cb79883f6b8b615fd8f4ed7c8c16', + '2a2cda222c9d5e18b638057688babb00a3a975ccd4b7dd65f26211c2cb7767f9', 'platform': 'linux', 'machine': ['aarch64'] @@ -75,55 +75,55 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 5823560, + 5813384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/trace_processor_shell', 'sha256': - 'ec5d23fc761021fe10a7cdb66d35590dab0216b2305f5163ace98da28b535fb8' + 'f3ec4c194d0b06af5b296c1c479e6b29090e6b7cc7e58fbd55ca2919a126f0ee' }, { 'arch': 'android-arm64', 'file_name': 'trace_processor_shell', 'file_size': - 7474864, + 7294768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/trace_processor_shell', 'sha256': - 'f4877f51d0fbb8e9ead576e746a7adf7806b5cb2dffc4373a55ceeec21f615ff' + 'f44f47d4b873ec68b6fa4f4c69a3e5a13d58b4d9cb2ec591fa687d4480c1950b' }, { 'arch': 'android-x86', 'file_name': 'trace_processor_shell', 'file_size': - 8436764, + 8090716, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/trace_processor_shell', 'sha256': - '68ad60af32890f903afb7cbee7cc8f0f4f4b18dea7ab077cb1d807ea80053dcb' + '5636d8251747376787640bc3a4894ecf3091e4bf3d38b007003e1992fc5792df' }, { 'arch': 'android-x64', 'file_name': 'trace_processor_shell', 'file_size': - 8781544, + 8359784, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/trace_processor_shell', 'sha256': - 'edf5efca4cf46ffbd3586592490b14d61758198c7d46c1bc8e083b1ab19382f5' + '50440fa055ab998f6cf24f9a9a7388520cc854708735521505e10291bc52f3d0' }, { 'arch': 'windows-amd64', 'file_name': 'trace_processor_shell.exe', 'file_size': - 8252928, + 8130560, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/trace_processor_shell.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/trace_processor_shell.exe', 'sha256': - '323a210f857ce840c4d69dfa7f9b0a32501ffa4a856e5667a0916e3f8006a5d0', + '5cbcf98e29a2d989523235e11e4e0dade692a295ebf47a6c93a09a050ce9bc91', 'platform': 'win32', 'machine': ['amd64'] diff --git a/python/perfetto/prebuilts/manifests/tracebox.py b/python/perfetto/prebuilts/manifests/tracebox.py index effd4d1e6..46b064a6d 100755 --- a/python/perfetto/prebuilts/manifests/tracebox.py +++ b/python/perfetto/prebuilts/manifests/tracebox.py @@ -1,15 +1,15 @@ -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACEBOX_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'tracebox', 'file_size': - 1415776, + 1432064, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/tracebox', 'sha256': - '860cccef002f1a7216d301a09b97d7276b8a57c8d85ad1c3aa4697bb115ffca7', + '4ceb7646cd99303224ab5e7ff0a9f84c04f3c5466fff65a55dab65171ae9d482', 'platform': 'darwin', 'machine': ['x86_64'] @@ -19,11 +19,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1309272, + 1325704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/tracebox', 'sha256': - '9c079ac561064c33e9bdfe2e23e92fb95c025603e545c1aae31b2bd7de0398ad', + '2c560fcce5e19eb692e50487af134e2078347cdb79decba0c572917860528388', 'platform': 'darwin', 'machine': ['arm64'] @@ -33,11 +33,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2137040, + 2155496, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/tracebox', 'sha256': - '9eb9ce1a14432c284fecce7886786bb2555bcb6dfb4f00a2df2885984961a5fc', + '10b92180bb461a7e21be3f8b3d4640430a98d0547238ce095709213b378217d2', 'platform': 'linux', 'machine': ['x86_64'] @@ -47,11 +47,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1277896, + 1288764, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/tracebox', 'sha256': - '60c71b39be7e04d9d0278e36e7e4d33c32a03d6cc8a3782a9e5ed2484f3f2082', + 'fa28950ce2b7a9345fbb9272f2dd04d3d4eb2a87f021df25e1e649840eae60b5', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -61,11 +61,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2065704, + 2082704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/tracebox', 'sha256': - 'a8d9e9e186b5daf45ec80b975b9a3ad04cb578890beda136b821c80b9cc74995', + '85c371d79b8e23d22a293c29e6399dc311d891a6bd85d7eeaf2cb0179c69eb27', 'platform': 'linux', 'machine': ['aarch64'] @@ -75,42 +75,42 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1161172, + 1169364, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/tracebox', 'sha256': - 'f9dac5df26d471d1cf0aff942d7249da6b4122543e003813203ef128a15f93fd' + '40a3f31600f02dea10e290134d5c862e0e717f4f039756889a4e72c60f1591b6' }, { 'arch': 'android-arm64', 'file_name': 'tracebox', 'file_size': - 1764008, + 1776296, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/tracebox', 'sha256': - '3b325a09b6efae0939b73d4d74e6e01e3735508ed31b774f3a21765efab95099' + '562505fca18b34a97687dc002aeebcbf20acef68c8a8e48bed6d618c20e07c92' }, { 'arch': 'android-x86', 'file_name': 'tracebox', 'file_size': - 1755052, + 1767340, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/tracebox', 'sha256': - '86e31fa7e2b476187a0222ac2cf6a4ee7e5f8fb5b0e019c1349d14534343a581' + 'eb47eb43ba93403557dd15a61196799e945ec324d96109db2f155fb131f9996a' }, { 'arch': 'android-x64', 'file_name': 'tracebox', 'file_size': - 2034344, + 2054824, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/tracebox', 'sha256': - '7692f6ceaa5d2eb9da42486262610a1820a9b31d46255f624407bf712eff021d' + 'a3ae6d108e041ba368a9770f952772f111865d4eff7c8e4e4e2f653f45017948' }] diff --git a/python/perfetto/prebuilts/manifests/traceconv.py b/python/perfetto/prebuilts/manifests/traceconv.py index fad02d9c0..b093ac6cf 100755 --- a/python/perfetto/prebuilts/manifests/traceconv.py +++ b/python/perfetto/prebuilts/manifests/traceconv.py @@ -1,15 +1,15 @@ -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACECONV_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'traceconv', 'file_size': - 7822272, + 7904536, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/traceconv', 'sha256': - '2b1bae4755ee0dd7f3a8e55653f8a7c344f688ea29700064ef8211c55bb4ae9f', + '037f84ac943f3f4d75447c668cc49c966fe3d85eca3a455c958b24fc6a9e314a', 'platform': 'darwin', 'machine': ['x86_64'] @@ -19,11 +19,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6604056, + 6554600, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/traceconv', 'sha256': - 'c0bd6d1ebe2c61ffeefbd4f01426e9b853c81daf70530be7e78c97a4d3af100c', + 'eda545ef4fa37fdfa1b47ced7cbbe0aa3c0df9bd161cacd7c78e6c55aef98d20', 'platform': 'darwin', 'machine': ['arm64'] @@ -33,11 +33,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 8122112, + 7664384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/traceconv', 'sha256': - 'c4c57d8e7b435822a1437b2dc7f7154f6ff2e197deff1f9284bbd36bbedb004f', + '24285e6e0e873d393fa5a993bac18ec8e1ab5fae6f4e3453214e095ef36e4c45', 'platform': 'linux', 'machine': ['x86_64'] @@ -47,11 +47,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6692016, + 5657944, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/traceconv', 'sha256': - 'da666eb9f80bcbec4c959f4adf493a59ff89e4106666fe1884291078dba0243b', + 'c9af3d976f849fc75e96c2c552cb14fcc9eacce6fe7c45c4a8289080b0f66706', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -61,11 +61,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 7575344, + 7184224, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/traceconv', 'sha256': - '8ca00c39c5ec7bd78576f64c4ab05e663d803b06b36fbddf968825edbe236fca', + 'c6dc936492d58a40cd8e0b58abc46bd479e0c1c387cd1ba29198a6c9b2000d7a', 'platform': 'linux', 'machine': ['aarch64'] @@ -75,55 +75,55 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 5376396, + 5325260, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/traceconv', 'sha256': - 'b77e7f0274ba45ff32d34df347845bc996763291fcc6b2a697f56c0c9a543150' + '963267dcb58cdde9f61a952e5cb7f3557833209d3251e7fdcefc3b52db54f77b' }, { 'arch': 'android-arm64', 'file_name': 'traceconv', 'file_size': - 6793744, + 6572688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/traceconv', 'sha256': - 'e83f3d43f8782cb57e9d3e8a3cd31826c9713da9f92bd8d8be2c48872ed423eb' + '87373c351fe5e947826cd957438cab8a37a352bf83b1cbbb15fe276eee9d873a' }, { 'arch': 'android-x86', 'file_name': 'traceconv', 'file_size': - 7694692, + 7303588, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/traceconv', 'sha256': - 'c9ee2c3c91d6c68cb7f52a626767bde5e267f34c6ddf987ff73eec3d813c0a2c' + 'dfc4e714963b5ed662d29d6028ffa69e67f8cd2f9a28223f715437a260fd456f' }, { 'arch': 'android-x64', 'file_name': 'traceconv', 'file_size': - 7940680, + 7482056, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/traceconv', 'sha256': - 'f75122ca3e6bbe393b705c3bc5514d81c57f38bf408d857d89c4268b79a39e08' + '79c666c629fcffd810635270b45e58b40ed253d22650f41550057e5d8f8c49a7' }, { 'arch': 'windows-amd64', 'file_name': 'traceconv.exe', 'file_size': - 7239168, + 7072768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/traceconv.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/traceconv.exe', 'sha256': - '5be5d698f69d44b4baa8ae1f21955becad9d0e6774e967f923b8386744002cfe', + '40fac80fdeae443a924e160650c94629e6463c1fb5a4f04f4ef6e9e5e72a3965', 'platform': 'win32', 'machine': ['amd64'] diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor index 9aa5fe554..536dff949 100644 --- a/python/perfetto/trace_processor/metrics.descriptor +++ b/python/perfetto/trace_processor/metrics.descriptor @@ -1,11 +1,12 @@ -À -6protos/perfetto/metrics/android/process_metadata.protoperfetto.protos"ô +Ò +6protos/perfetto/metrics/android/process_metadata.protoperfetto.protos"† AndroidProcessMetadata name ( Rname uid (RuidI package (2/.perfetto.protos.AndroidProcessMetadata.PackageRpackageY -packages_for_uid (2/.perfetto.protos.AndroidProcessMetadata.PackageRpackagesForUidv +packages_for_uid (2/.perfetto.protos.AndroidProcessMetadata.PackageRpackagesForUid +pid (Rpidv Package! package_name ( RpackageName( apk_version_code (RapkVersionCode @@ -359,8 +360,8 @@ maxRuntime totalCountc threshold_metric (28.perfetto.protos.AndroidIrqRuntimeMetric.ThresholdMetricRthresholdMetric_ longest_irq_slices (21.perfetto.protos.AndroidIrqRuntimeMetric.IrqSliceRlongestIrqSlices - -5protos/perfetto/metrics/android/jank_cuj_metric.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"¿ +œ +5protos/perfetto/metrics/android/jank_cuj_metric.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"™
AndroidJankCujMetric; cuj (2).perfetto.protos.AndroidJankCujMetric.CujRcuj¾ Cuj @@ -376,7 +377,7 @@ totalCountc timeline_metrics (2-.perfetto.protos.AndroidJankCujMetric.MetricsRtimelineMetricsR
trace_metrics (2-.perfetto.protos.AndroidJankCujMetric.MetricsRtraceMetrics -layer_name ( R layerNameÁ +layer_name ( R layerName¡ Frame! frame_number (RframeNumber vsync (Rvsync @@ -385,7 +386,9 @@ layer_name ( R layerNameÁ dur_expected (RdurExpected app_missed (R appMissed - sf_missed (RsfMissedä + sf_missed (RsfMissed, +sf_callback_missed (RsfCallbackMissed0 +hwui_callback_missed (RhwuiCallbackMissedÞ Metrics! total_frames (RtotalFrames#
missed_frames (RmissedFrames* @@ -402,7 +405,9 @@ app_missed (R appMissed frame_dur_ms_p50 (R
frameDurMsP50' frame_dur_ms_p90
(R
frameDurMsP90' frame_dur_ms_p95 (R
frameDurMsP95' -frame_dur_ms_p99 (R
frameDurMsP99 +frame_dur_ms_p99 (R
frameDurMsP999 +sf_callback_missed_frames (RsfCallbackMissedFrames= +hwui_callback_missed_frames (RhwuiCallbackMissedFrames À 9protos/perfetto/metrics/android/java_heap_histogram.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"¹ JavaHeapHistogramW @@ -424,14 +429,14 @@ type_count (2,.perfetto.protos.JavaHeapHistogram.TypeCountR typeCount« upid (
RupidA process (2'.perfetto.protos.AndroidProcessMetadataRprocessC samples (2).perfetto.protos.JavaHeapHistogram.SampleRsamples -… -5protos/perfetto/metrics/android/java_heap_stats.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"‚ +© +5protos/perfetto/metrics/android/java_heap_stats.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"¦
JavaHeapStatsS instance_stats (2,.perfetto.protos.JavaHeapStats.InstanceStatsR
instanceStatsb HeapRoots root_type ( RrootType type_name ( RtypeName - obj_count (RobjCount + obj_count (RobjCount± Sample ts (Rts heap_size (RheapSize( @@ -441,7 +446,9 @@ type_count (2,.perfetto.protos.JavaHeapHistogram.TypeCountR typeCount« reachable_heap_native_size (RreachableHeapNativeSize. reachable_obj_count (RreachableObjCount2 anon_rss_and_swap_size (RanonRssAndSwapSize> -roots (2(.perfetto.protos.JavaHeapStats.HeapRootsRroots§ +roots (2(.perfetto.protos.JavaHeapStats.HeapRootsRroots" +
oom_score_adj + (RoomScoreAdj§
InstanceStats upid (
RupidA process (2'.perfetto.protos.AndroidProcessMetadataRprocess? @@ -673,8 +680,8 @@ maxRuntime$ name ( RnameY threads (2?.perfetto.protos.AndroidSimpleperfMetric.PerfEventMetric.ThreadRthreads total (Rtotal -è, -4protos/perfetto/metrics/android/startup_metric.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"æ+ +Ô. +4protos/perfetto/metrics/android/startup_metric.protoperfetto.protos6protos/perfetto/metrics/android/process_metadata.proto"Ò- AndroidStartupMetricG startup (2-.perfetto.protos.AndroidStartupMetric.StartupRstartupó TaskStateBreakdown$ @@ -691,7 +698,7 @@ maxRuntime$ unknown (Runknown5 Slice dur_ns (RdurNs -dur_ms (RdurMsÏ +dur_ms (RdurMsš ToFirstFrame dur_ns (RdurNs dur_ms (RdurMsr @@ -722,7 +729,9 @@ toPostForkb
time_gc_total (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeGcTotalP time_gc_on_cpu (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeGcOnCpus time_lock_contention_thread_main (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeLockContentionThreadMainy -#time_monitor_contention_thread_main (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeMonitorContentionThreadMainJ
J
JJ\ +#time_monitor_contention_thread_main (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeMonitorContentionThreadMaine +time_dex_open_thread_main! (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeDexOpenThreadMainb +time_dlopen_thread_main" (2+.perfetto.protos.AndroidStartupMetric.SliceRtimeDlopenThreadMainJ
J
JJ\ HscMetricsN full_startup (2+.perfetto.protos.AndroidStartupMetric.SliceRfullStartupd @@ -758,7 +767,8 @@ firstFrame÷ broadcast_received_count (RbroadcastReceivedCountF most_active_non_launch_processes ( RmostActiveNonLaunchProcesses& installd_dur_ns (R
installdDurNs$ -dex2oat_dur_ns (Rdex2oatDurNsï +dex2oat_dur_ns (Rdex2oatDurNs + Startup startup_id (
R startupId! @@ -777,13 +787,15 @@ activitiesq hsc (20.perfetto.protos.AndroidStartupMetric.HscMetricsRhscY report_fully_drawn (2+.perfetto.protos.AndroidStartupMetric.SliceRreportFullyDrawni optimization_status (28.perfetto.protos.AndroidStartupMetric.OptimizationStatusRoptimizationStatusT -verify_class (21.perfetto.protos.AndroidStartupMetric.VerifyClassRverifyClass? +verify_class (21.perfetto.protos.AndroidStartupMetric.VerifyClassRverifyClass +dlopen_file ( R +dlopenFile? startup_concurrent_to_launch ( RstartupConcurrentToLaunchT system_state (21.perfetto.protos.AndroidStartupMetric.SystemStateRsystemState* slow_start_reason ( RslowStartReasonJ - -4protos/perfetto/metrics/android/surfaceflinger.protoperfetto.protos"Ó +Ñ +4protos/perfetto/metrics/android/surfaceflinger.protoperfetto.protos"‡ AndroidSurfaceflingerMetric#
missed_frames (
RmissedFrames* missed_hwc_frames (
RmissedHwcFrames* @@ -793,7 +805,18 @@ activitiesq missed_gpu_frame_rate (RmissedGpuFrameRate' gpu_invocations (
RgpuInvocations2 avg_gpu_waiting_dur_ms (RavgGpuWaitingDurMsH -"total_non_empty_gpu_waiting_dur_ms (RtotalNonEmptyGpuWaitingDurMs +"total_non_empty_gpu_waiting_dur_ms (RtotalNonEmptyGpuWaitingDurMsn +metrics_per_display + (2>.perfetto.protos.AndroidSurfaceflingerMetric.MetricsPerDisplayRmetricsPerDisplayÁ +MetricsPerDisplay + +display_id ( R displayId# +
missed_frames (
RmissedFrames* +missed_hwc_frames (
RmissedHwcFrames* +missed_gpu_frames (
RmissedGpuFrames* +missed_frame_rate (RmissedFrameRate1 +missed_hwc_frame_rate (RmissedHwcFrameRate1 +missed_gpu_frame_rate (RmissedGpuFrameRate » 0protos/perfetto/metrics/android/task_names.protoperfetto.protos"õ AndroidTaskNamesC @@ -823,8 +846,8 @@ m build_id ( RbuildId address (Raddress( google_lookup_id ( RgoogleLookupId -å -3protos/perfetto/metrics/android/binder_metric.protoperfetto.protos"œ +£ +3protos/perfetto/metrics/android/binder_metric.protoperfetto.protos"Ú AndroidBinderMetrice process_breakdown (28.perfetto.protos.AndroidBinderMetric.PerProcessBreakdownRprocessBreakdown{ unaggregated_txn_breakdown (2=.perfetto.protos.AndroidBinderMetric.UnaggregatedTxnBreakdownRunaggregatedTxnBreakdown @@ -833,7 +856,7 @@ m pid (
Rpid slice_name ( R sliceName -count (
Rcountƒ +count (
RcountÁ UnaggregatedTxnBreakdown aidl_name ( RaidlName% client_process ( R
clientProcess# @@ -853,7 +876,11 @@ server_dur (R serverDur^ client_tid (
R clientTid -server_tid (
R serverTidJJ ½ +server_tid (
R serverTid + +client_pid (
R clientPid + +server_pid (
R serverPidJJ ½ ThreadStateBreakdown* thread_state_type ( RthreadStateType! thread_state ( RthreadState( @@ -864,34 +891,36 @@ server_tid (
R serverTidJJ ½ blocked_function ( RblockedFunction0 blocked_function_dur (RblockedFunctionDur4 blocked_function_count (RblockedFunctionCount -¯ -?protos/perfetto/metrics/android/monitor_contention_metric.protoperfetto.protos"Ú - +Ÿ +?protos/perfetto/metrics/android/monitor_contention_metric.protoperfetto.protos"Ê AndroidMonitorContentionMetricH -node (24.perfetto.protos.AndroidMonitorContentionMetric.NodeRnode© +node (24.perfetto.protos.AndroidMonitorContentionMetric.NodeRnode™ Node$ node_parent_id (RnodeParentId node_id (RnodeId ts (Rts -dur (Rdur' -blocking_method ( RblockingMethod% -blocked_method ( R
blockedMethod2 -short_blocking_method ( RshortBlockingMethod0 -short_blocked_method ( RshortBlockedMethod! -blocking_src ( RblockingSrc +dur (Rdur! +process_name ( RprocessName +pid (
Rpid! +waiter_count (
RwaiterCounti +
thread_states (2D.perfetto.protos.AndroidMonitorContentionMetric.ThreadStateBreakdownRthreadStatesu +blocked_functions (2H.perfetto.protos.AndroidMonitorContentionMetric.BlockedFunctionBreakdownRblockedFunctions' +blocking_method ( RblockingMethod2 +short_blocking_method ( RshortBlockingMethod! +blocking_src ( RblockingSrc0 +blocking_thread_name
( RblockingThreadName5 +is_blocking_thread_main (RisBlockingThreadMain. +blocking_thread_tid (
RblockingThreadTid% +blocked_method ( R
blockedMethod0 +short_blocked_method ( RshortBlockedMethod blocked_src ( R -blockedSrc! -waiter_count (
RwaiterCount. -blocked_thread_name ( RblockedThreadName0 -blocking_thread_name
( RblockingThreadName! -process_name ( RprocessName3 -is_blocked_thread_main (RisBlockedThreadMain5 -is_blocking_thread_main (RisBlockingThreadMain& +blockedSrc. +blocked_thread_name ( RblockedThreadName3 +is_blocked_thread_main (RisBlockedThreadMain, +blocked_thread_tid (
RblockedThreadTid& binder_reply_ts (R
binderReplyTs( -binder_reply_tid (
RbinderReplyTidi -
thread_states (2D.perfetto.protos.AndroidMonitorContentionMetric.ThreadStateBreakdownRthreadStatesu -blocked_functions (2H.perfetto.protos.AndroidMonitorContentionMetric.BlockedFunctionBreakdownRblockedFunctions‘ +binder_reply_tid (
RbinderReplyTid‘ ThreadStateBreakdown! thread_state ( RthreadState( thread_state_dur (RthreadStateDur, diff --git a/python/perfetto/trace_processor/metrics.descriptor.sha1 b/python/perfetto/trace_processor/metrics.descriptor.sha1 deleted file mode 100644 index 0a69bdf19..000000000 --- a/python/perfetto/trace_processor/metrics.descriptor.sha1 +++ /dev/null @@ -1,6 +0,0 @@ - -// SHA1(tools/gen_binary_descriptors) -// 6886b319e65925c037179e71a803b8473d06dc7d -// SHA1(protos/perfetto/metrics/metrics.proto) -// b07fa0b4ad2deed79d2d8cc320f70502e19eee0c -
\ No newline at end of file diff --git a/python/perfetto/trace_processor/trace_processor.descriptor.sha1 b/python/perfetto/trace_processor/trace_processor.descriptor.sha1 deleted file mode 100644 index 7f7827f2f..000000000 --- a/python/perfetto/trace_processor/trace_processor.descriptor.sha1 +++ /dev/null @@ -1,6 +0,0 @@ - -// SHA1(tools/gen_binary_descriptors) -// 6886b319e65925c037179e71a803b8473d06dc7d -// SHA1(protos/perfetto/trace_processor/trace_processor.proto) -// 59f86a32aa28f29e290d8ce3f97461725aa8b9f8 -
\ No newline at end of file diff --git a/src/android_stats/perfetto_atoms.h b/src/android_stats/perfetto_atoms.h index e9ef08e4f..122dfad75 100644 --- a/src/android_stats/perfetto_atoms.h +++ b/src/android_stats/perfetto_atoms.h @@ -47,6 +47,7 @@ enum class PerfettoStatsdAtom { // they log the trigger name. kTracedTriggerStartTracing = 41, kTracedTriggerStopTracing = 42, + kTracedTriggerCloneSnapshot = 53, // Guardrails inside traced. kTracedEnableTracingExistingTraceSession = 18, diff --git a/src/base/http/BUILD.gn b/src/base/http/BUILD.gn index e0861347c..dda19a743 100644 --- a/src/base/http/BUILD.gn +++ b/src/base/http/BUILD.gn @@ -40,8 +40,11 @@ perfetto_unittest_source_set("unittests") { "../../../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/metatrace.cc b/src/base/metatrace.cc index 1464ed384..51b8b8301 100644 --- a/src/base/metatrace.cc +++ b/src/base/metatrace.cc @@ -36,13 +36,6 @@ std::atomic<uint64_t> RingBuffer::rd_index_; std::atomic<bool> RingBuffer::has_overruns_; Record RingBuffer::bankruptcy_record_; -#if !PERFETTO_IS_AT_LEAST_CPP17() -constexpr size_t RingBuffer::kCapacity; -constexpr uint16_t Record::kTypeMask; -constexpr uint16_t Record::kTypeCounter; -constexpr uint16_t Record::kTypeEvent; -#endif - namespace { // std::function<> is not trivially de/constructible. This struct wraps it in a diff --git a/src/cloud_trace_processor/BUILD.gn b/src/cloud_trace_processor/BUILD.gn new file mode 100644 index 000000000..e9a4bfca5 --- /dev/null +++ b/src/cloud_trace_processor/BUILD.gn @@ -0,0 +1,64 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../gn/perfetto.gni") +import("../../gn/test.gni") + +assert( + enable_perfetto_trace_processor && enable_perfetto_trace_processor_sqlite) + +# The "core" business logic of cloud trace processor which is agnostic to the +# RPC transport. Allows wrapping with any RPC framework capable of handling +# protobufs. +static_library("cloud_trace_processor") { + complete_static_lib = true + deps = [ ":sources" ] + public_deps = [ "../../include/perfetto/ext/cloud_trace_processor" ] +} + +source_set("sources") { + sources = [ + "orchestrator_impl.cc", + "orchestrator_impl.h", + "trace_processor_wrapper.cc", + "trace_processor_wrapper.h", + "worker_impl.cc", + "worker_impl.h", + ] + deps = [ + "../../gn:default_deps", + "../../include/perfetto/ext/cloud_trace_processor", + "../../protos/perfetto/cloud_trace_processor:lite", + "../base", + "../base/threading", + "../protozero", + "../protozero:proto_ring_buffer", + "../trace_processor:lib", + "../trace_processor/rpc", + "../trace_processor/util", + ] +} + +perfetto_unittest_source_set("unittests") { + testonly = true + sources = [ "trace_processor_wrapper_unittest.cc" ] + deps = [ + ":sources", + "../../gn:default_deps", + "../../gn:gtest_and_gmock", + "../../protos/perfetto/cloud_trace_processor:lite", + "../base", + "../base/threading", + ] +} diff --git a/src/cloud_trace_processor/orchestrator_impl.cc b/src/cloud_trace_processor/orchestrator_impl.cc new file mode 100644 index 000000000..69b831fad --- /dev/null +++ b/src/cloud_trace_processor/orchestrator_impl.cc @@ -0,0 +1,210 @@ +/* + * 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/cloud_trace_processor/orchestrator_impl.h" + +#include <memory> +#include <string> +#include <utility> +#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/threading/future.h" +#include "perfetto/ext/base/threading/stream.h" +#include "perfetto/ext/cloud_trace_processor/worker.h" +#include "protos/perfetto/cloud_trace_processor/common.pb.h" +#include "protos/perfetto/cloud_trace_processor/orchestrator.pb.h" +#include "protos/perfetto/cloud_trace_processor/worker.pb.h" +#include "src/trace_processor/util/status_macros.h" + +namespace perfetto { +namespace cloud_trace_processor { +namespace { + +base::Future<base::Status> CreateResponseToStatus( + base::StatusOr<protos::TracePoolShardCreateResponse> response_or) { + return response_or.status(); +} + +base::Future<base::Status> SetTracesResponseToStatus( + base::StatusOr<protos::TracePoolShardSetTracesResponse> response_or) { + return response_or.status(); +} + +base::Future<base::StatusOr<protos::TracePoolQueryResponse>> +RpcResponseToPoolResponse( + base::StatusOr<protos::TracePoolShardQueryResponse> resp) { + RETURN_IF_ERROR(resp.status()); + protos::TracePoolQueryResponse ret; + ret.set_trace(std::move(resp->trace())); + *ret.mutable_result() = std::move(*resp->mutable_result()); + return ret; +} + +base::StatusOrStream<protos::TracePoolShardSetTracesResponse> +RoundRobinSetTraces(const std::vector<std::unique_ptr<Worker>>& workers, + const std::vector<std::string>& traces) { + uint32_t worker_idx = 0; + std::vector<protos::TracePoolShardSetTracesArgs> protos; + protos.resize(workers.size()); + for (const auto& trace : traces) { + protos[worker_idx].add_traces(trace); + worker_idx = (worker_idx + 1) % workers.size(); + } + + using ShardResponse = protos::TracePoolShardSetTracesResponse; + std::vector<base::StatusOrStream<ShardResponse>> streams; + for (uint32_t i = 0; i < protos.size(); ++i) { + streams.emplace_back(workers[i]->TracePoolShardSetTraces(protos[i])); + } + return base::FlattenStreams(std::move(streams)); +} +} // namespace + +Orchestrator::~Orchestrator() = default; + +std::unique_ptr<Orchestrator> Orchestrator::CreateInProcess( + std::vector<std::unique_ptr<Worker>> workers) { + return std::unique_ptr<Orchestrator>( + new OrchestratorImpl(std::move(workers))); +} + +OrchestratorImpl::OrchestratorImpl(std::vector<std::unique_ptr<Worker>> workers) + : workers_(std::move(workers)) {} + +base::StatusOrFuture<protos::TracePoolCreateResponse> +OrchestratorImpl::TracePoolCreate(const protos::TracePoolCreateArgs& args) { + if (args.pool_type() != protos::TracePoolType::SHARED) { + return base::StatusOr<protos::TracePoolCreateResponse>( + base::ErrStatus("Currently only SHARED pools are supported")); + } + if (!args.has_shared_pool_name()) { + return base::StatusOr<protos::TracePoolCreateResponse>( + base::ErrStatus("Pool name must be provided for SHARED pools")); + } + + std::string id = "shared:" + args.shared_pool_name(); + TracePool* exist = pools_.Find(id); + if (exist) { + return base::StatusOr<protos::TracePoolCreateResponse>( + base::ErrStatus("Pool %s already exists", id.c_str())); + } + protos::TracePoolShardCreateArgs group_args; + group_args.set_pool_id(id); + group_args.set_pool_type(args.pool_type()); + + using ShardResponse = protos::TracePoolShardCreateResponse; + std::vector<base::StatusOrStream<ShardResponse>> shards; + for (uint32_t i = 0; i < workers_.size(); ++i) { + shards.emplace_back( + base::StreamFromFuture(workers_[i]->TracePoolShardCreate(group_args))); + } + return base::FlattenStreams(std::move(shards)) + .MapFuture(&CreateResponseToStatus) + .Collect(base::AllOkCollector()) + .ContinueWith( + [this, id](base::StatusOr<ShardResponse> resp) + -> base::StatusOrFuture<protos::TracePoolCreateResponse> { + RETURN_IF_ERROR(resp.status()); + auto it_and_inserted = pools_.Insert(id, TracePool()); + if (!it_and_inserted.second) { + return base::ErrStatus("Unable to insert pool %s", id.c_str()); + } + return protos::TracePoolCreateResponse(); + }); +} + +base::StatusOrFuture<protos::TracePoolSetTracesResponse> +OrchestratorImpl::TracePoolSetTraces( + const protos::TracePoolSetTracesArgs& args) { + std::string id = args.pool_id(); + TracePool* pool = pools_.Find(id); + if (!pool) { + return base::StatusOr<protos::TracePoolSetTracesResponse>( + base::ErrStatus("Unable to find pool %s", id.c_str())); + } + if (!pool->loaded_traces.empty()) { + return base::StatusOr<protos::TracePoolSetTracesResponse>(base::ErrStatus( + "Incrementally adding/removing items to pool not currently supported")); + } + pool->loaded_traces.assign(args.traces().begin(), args.traces().end()); + return RoundRobinSetTraces(workers_, pool->loaded_traces) + .MapFuture(&SetTracesResponseToStatus) + .Collect(base::AllOkCollector()) + .ContinueWith( + [](base::Status status) + -> base::StatusOrFuture<protos::TracePoolSetTracesResponse> { + RETURN_IF_ERROR(status); + return protos::TracePoolSetTracesResponse(); + }); +} + +base::StatusOrStream<protos::TracePoolQueryResponse> +OrchestratorImpl::TracePoolQuery(const protos::TracePoolQueryArgs& args) { + TracePool* pool = pools_.Find(args.pool_id()); + if (!pool) { + return base::StreamOf(base::StatusOr<protos::TracePoolQueryResponse>( + base::ErrStatus("Unable to find pool %s", args.pool_id().c_str()))); + } + protos::TracePoolShardQueryArgs shard_args; + *shard_args.mutable_pool_id() = args.pool_id(); + *shard_args.mutable_sql_query() = args.sql_query(); + + using ShardResponse = protos::TracePoolShardQueryResponse; + std::vector<base::StatusOrStream<ShardResponse>> streams; + for (uint32_t i = 0; i < workers_.size(); ++i) { + streams.emplace_back(workers_[i]->TracePoolShardQuery(shard_args)); + } + return base::FlattenStreams(std::move(streams)) + .MapFuture(&RpcResponseToPoolResponse); +} + +base::StatusOrFuture<protos::TracePoolDestroyResponse> +OrchestratorImpl::TracePoolDestroy(const protos::TracePoolDestroyArgs& args) { + std::string id = args.pool_id(); + TracePool* pool = pools_.Find(id); + if (!pool) { + return base::StatusOr<protos::TracePoolDestroyResponse>( + base::ErrStatus("Unable to find pool %s", id.c_str())); + } + protos::TracePoolShardDestroyArgs shard_args; + *shard_args.mutable_pool_id() = id; + + using ShardResponse = protos::TracePoolShardDestroyResponse; + std::vector<base::StatusOrStream<ShardResponse>> streams; + for (uint32_t i = 0; i < workers_.size(); ++i) { + streams.emplace_back( + base::StreamFromFuture(workers_[i]->TracePoolShardDestroy(shard_args))); + } + return base::FlattenStreams(std::move(streams)) + .MapFuture( + [](base::StatusOr<ShardResponse> resp) -> base::Future<base::Status> { + return resp.status(); + }) + .Collect(base::AllOkCollector()) + .ContinueWith( + [this, id](base::Status status) + -> base::StatusOrFuture<protos::TracePoolDestroyResponse> { + RETURN_IF_ERROR(status); + PERFETTO_CHECK(pools_.Erase(id)); + return protos::TracePoolDestroyResponse(); + }); +} + +} // namespace cloud_trace_processor +} // namespace perfetto diff --git a/src/cloud_trace_processor/orchestrator_impl.h b/src/cloud_trace_processor/orchestrator_impl.h new file mode 100644 index 000000000..eef55e574 --- /dev/null +++ b/src/cloud_trace_processor/orchestrator_impl.h @@ -0,0 +1,61 @@ +/* + * 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_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_IMPL_H_ +#define SRC_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_IMPL_H_ + +#include <memory> +#include <vector> + +#include "perfetto/ext/base/flat_hash_map.h" +#include "perfetto/ext/base/threading/future.h" +#include "perfetto/ext/cloud_trace_processor/orchestrator.h" + +namespace perfetto { +namespace protos { +class TracePoolShardCreateArgs; +} + +namespace cloud_trace_processor { + +class OrchestratorImpl : public Orchestrator { + public: + explicit OrchestratorImpl(std::vector<std::unique_ptr<Worker>> workers); + + base::StatusOrStream<protos::TracePoolQueryResponse> TracePoolQuery( + const protos::TracePoolQueryArgs&) override; + + base::StatusOrFuture<protos::TracePoolCreateResponse> TracePoolCreate( + const protos::TracePoolCreateArgs&) override; + + base::StatusOrFuture<protos::TracePoolSetTracesResponse> TracePoolSetTraces( + const protos::TracePoolSetTracesArgs&) override; + + base::StatusOrFuture<protos::TracePoolDestroyResponse> TracePoolDestroy( + const protos::TracePoolDestroyArgs&) override; + + private: + struct TracePool { + std::vector<std::string> loaded_traces; + }; + std::vector<std::unique_ptr<Worker>> workers_; + base::FlatHashMap<std::string, TracePool> pools_; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // SRC_CLOUD_TRACE_PROCESSOR_ORCHESTRATOR_IMPL_H_ diff --git a/src/cloud_trace_processor/trace_processor_wrapper.cc b/src/cloud_trace_processor/trace_processor_wrapper.cc new file mode 100644 index 000000000..5093e5285 --- /dev/null +++ b/src/cloud_trace_processor/trace_processor_wrapper.cc @@ -0,0 +1,156 @@ +/* + * 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/cloud_trace_processor/trace_processor_wrapper.h" + +#include <atomic> +#include <memory> +#include <mutex> +#include <optional> +#include <string> +#include <utility> +#include <vector> + +#include "perfetto/base/status.h" +#include "perfetto/ext/base/file_utils.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/threading/future.h" +#include "perfetto/ext/base/threading/poll.h" +#include "perfetto/ext/base/threading/stream.h" +#include "perfetto/ext/base/threading/thread_pool.h" +#include "perfetto/ext/base/threading/util.h" +#include "perfetto/protozero/proto_utils.h" +#include "perfetto/protozero/scattered_heap_buffer.h" +#include "perfetto/trace_processor/trace_blob.h" +#include "perfetto/trace_processor/trace_blob_view.h" +#include "perfetto/trace_processor/trace_processor.h" +#include "protos/perfetto/cloud_trace_processor/worker.pb.h" +#include "src/protozero/proto_ring_buffer.h" +#include "src/trace_processor/rpc/query_result_serializer.h" +#include "src/trace_processor/util/status_macros.h" + +namespace perfetto { +namespace cloud_trace_processor { +namespace { + +using trace_processor::QueryResultSerializer; +using trace_processor::TraceBlob; +using trace_processor::TraceBlobView; +using trace_processor::TraceProcessor; +using Statefulness = TraceProcessorWrapper::Statefulness; + +struct QueryRunner { + QueryRunner(std::shared_ptr<TraceProcessor> _tp, + std::string _query, + std::string _trace_path, + Statefulness _statefulness) + : tp(std::move(_tp)), + query(std::move(_query)), + trace_path(std::move(_trace_path)), + statefulness(_statefulness) {} + + std::optional<protos::TracePoolShardQueryResponse> operator()() { + if (!has_more) { + if (statefulness == Statefulness::kStateless) { + tp->RestoreInitialTables(); + } + return std::nullopt; + } + // If the serializer does not exist yet, that means we have not yet run + // the query so make sure to do that first. + EnsureSerializerExists(); + has_more = serializer->Serialize(&result); + + protos::TracePoolShardQueryResponse resp; + *resp.mutable_trace() = trace_path; + resp.mutable_result()->ParseFromArray(result.data(), + static_cast<int>(result.size())); + result.clear(); + return std::make_optional(std::move(resp)); + } + + void EnsureSerializerExists() { + if (serializer) { + return; + } + auto it = tp->ExecuteQuery(query); + serializer.reset(new QueryResultSerializer(std::move(it))); + } + + std::shared_ptr<TraceProcessor> tp; + std::string query; + std::string trace_path; + TraceProcessorWrapper::Statefulness statefulness; + + // shared_ptr to allow copying when this type is coerced to std::function. + std::shared_ptr<QueryResultSerializer> serializer; + std::vector<uint8_t> result; + bool has_more = true; +}; + +} // namespace + +TraceProcessorWrapper::TraceProcessorWrapper(std::string trace_path, + base::ThreadPool* thread_pool, + Statefulness statefulness) + : trace_path_(std::move(trace_path)), + thread_pool_(thread_pool), + statefulness_(statefulness) { + trace_processor::Config config; + config.ingest_ftrace_in_raw_table = false; + trace_processor_ = TraceProcessor::CreateInstance(config); +} + +base::StatusFuture TraceProcessorWrapper::LoadTrace( + base::StatusOrStream<std::vector<uint8_t>> file_stream) { + if (trace_processor_.use_count() != 1) { + return base::ErrStatus("Request is already in flight"); + } + return std::move(file_stream) + .MapFuture( + [this](base::StatusOr<std::vector<uint8_t>> d) -> base::StatusFuture { + RETURN_IF_ERROR(d.status()); + return base::RunOnceOnThreadPool<base::Status>( + thread_pool_, [res = std::move(*d), tp = trace_processor_] { + return tp->Parse(TraceBlobView( + TraceBlob::CopyFrom(res.data(), res.size()))); + }); + }) + .Collect(base::AllOkCollector()) + .ContinueWith([this](base::Status status) -> base::StatusFuture { + RETURN_IF_ERROR(status); + return base::RunOnceOnThreadPool<base::Status>( + thread_pool_, [tp = trace_processor_] { + tp->NotifyEndOfFile(); + return base::OkStatus(); + }); + }); +} + +base::StatusOrStream<protos::TracePoolShardQueryResponse> +TraceProcessorWrapper::Query(const std::string& query) { + using StatusOrResponse = base::StatusOr<protos::TracePoolShardQueryResponse>; + if (trace_processor_.use_count() != 1) { + return base::StreamOf<StatusOrResponse>( + base::ErrStatus("Request is already in flight")); + } + return base::RunOnThreadPool<StatusOrResponse>( + thread_pool_, + QueryRunner(trace_processor_, query, trace_path_, statefulness_)); +} + +} // namespace cloud_trace_processor +} // namespace perfetto diff --git a/src/cloud_trace_processor/trace_processor_wrapper.h b/src/cloud_trace_processor/trace_processor_wrapper.h new file mode 100644 index 000000000..9916b7225 --- /dev/null +++ b/src/cloud_trace_processor/trace_processor_wrapper.h @@ -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. + */ + +#ifndef SRC_CLOUD_TRACE_PROCESSOR_TRACE_PROCESSOR_WRAPPER_H_ +#define SRC_CLOUD_TRACE_PROCESSOR_TRACE_PROCESSOR_WRAPPER_H_ + +#include "perfetto/ext/base/threading/future.h" +#include "perfetto/ext/base/threading/stream.h" +#include "perfetto/ext/base/threading/thread_pool.h" +#include "perfetto/trace_processor/trace_processor.h" +#include "src/trace_processor/rpc/query_result_serializer.h" + +namespace perfetto { +namespace protos { + +class TracePoolShardQueryResponse; + +} // namespace protos +} // namespace perfetto + +namespace perfetto { +namespace cloud_trace_processor { + +// Wrapper class around an instance of TraceProcessor to adapt it for the needs +// of a CloudTraceProcessor Worker. +class TraceProcessorWrapper { + public: + enum Statefulness { + // Indicates that the state of the trace processor instance should be purged + // after every query. + kStateless, + + // Indicates that the state of the trace processor instance should be + // preserved across queries. + kStateful, + }; + + TraceProcessorWrapper(std::string trace_path, + base::ThreadPool*, + Statefulness); + + // Loads the trace given a stream of chunks to parse. + base::StatusFuture LoadTrace( + base::StatusOrStream<std::vector<uint8_t>> file_stream); + + // Executes the given query on the trace processor and returns the results + // as a stream. + base::StatusOrStream<protos::TracePoolShardQueryResponse> Query( + const std::string& sql); + + private: + using TraceProcessor = trace_processor::TraceProcessor; + + TraceProcessorWrapper(const TraceProcessorWrapper&) = delete; + TraceProcessorWrapper& operator=(const TraceProcessorWrapper&) = delete; + + TraceProcessorWrapper(TraceProcessorWrapper&&) = delete; + TraceProcessorWrapper& operator=(TraceProcessorWrapper&&) = delete; + + const std::string trace_path_; + base::ThreadPool* thread_pool_ = nullptr; + const Statefulness statefulness_ = Statefulness::kStateless; + std::shared_ptr<TraceProcessor> trace_processor_; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // SRC_CLOUD_TRACE_PROCESSOR_TRACE_PROCESSOR_WRAPPER_H_ diff --git a/src/cloud_trace_processor/trace_processor_wrapper_unittest.cc b/src/cloud_trace_processor/trace_processor_wrapper_unittest.cc new file mode 100644 index 000000000..8739783db --- /dev/null +++ b/src/cloud_trace_processor/trace_processor_wrapper_unittest.cc @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/cloud_trace_processor/trace_processor_wrapper.h" +#include <cstdint> +#include <vector> + +#include "perfetto/base/flat_set.h" +#include "perfetto/base/platform_handle.h" +#include "perfetto/base/status.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/string_utils.h" +#include "perfetto/ext/base/string_view.h" +#include "perfetto/ext/base/threading/stream.h" +#include "perfetto/ext/base/threading/thread_pool.h" +#include "perfetto/ext/base/threading/util.h" +#include "protos/perfetto/cloud_trace_processor/worker.pb.h" +#include "test/gtest_and_gmock.h" + +namespace perfetto { +namespace cloud_trace_processor { +namespace { + +using SF = TraceProcessorWrapper::Statefulness; + +const char kSimpleSystrace[] = R"--(# tracer + surfaceflinger-598 ( 598) [004] .... 10852.771242: tracing_mark_write: B|598|some event + surfaceflinger-598 ( 598) [004] .... 10852.771245: tracing_mark_write: E|598 +)--"; + +base::StatusOr<std::vector<uint8_t>> SimpleSystrace() { + return std::vector<uint8_t>(kSimpleSystrace, + kSimpleSystrace + strlen(kSimpleSystrace)); +} + +std::vector<base::StatusOr<std::vector<uint8_t>>> SimpleSystraceChunked() { + std::string systrace(kSimpleSystrace); + std::vector<base::StatusOr<std::vector<uint8_t>>> chunks; + for (auto& chunk : base::SplitString(systrace, "\n")) { + auto with_newline = chunk + "\n"; + chunks.push_back(std::vector<uint8_t>( + with_newline.data(), with_newline.data() + with_newline.size())); + } + + return chunks; +} + +template <typename T> +T WaitForFutureReady(base::Future<T>& future) { + base::FlatSet<base::PlatformHandle> ready; + base::FlatSet<base::PlatformHandle> interested; + base::PollContext ctx(&interested, &ready); + auto res = future.Poll(&ctx); + for (; res.IsPending(); res = future.Poll(&ctx)) { + PERFETTO_CHECK(interested.size() == 1); + base::BlockUntilReadableFd(*interested.begin()); + interested = {}; + } + return res.item(); +} + +template <typename T> +std::optional<T> WaitForStreamReady(base::Stream<T>& stream) { + base::FlatSet<base::PlatformHandle> ready; + base::FlatSet<base::PlatformHandle> interested; + base::PollContext ctx(&interested, &ready); + auto res = stream.PollNext(&ctx); + for (; res.IsPending(); res = stream.PollNext(&ctx)) { + PERFETTO_CHECK(interested.size() == 1); + base::BlockUntilReadableFd(*interested.begin()); + interested = {}; + } + return res.IsDone() ? std::nullopt : std::make_optional(res.item()); +} + +TEST(TraceProcessorWrapperUnittest, Stateful) { + base::ThreadPool pool(1); + TraceProcessorWrapper wrapper("foobar", &pool, SF::kStateful); + { + auto load = wrapper.LoadTrace(base::StreamOf(SimpleSystrace())); + base::Status status = WaitForFutureReady(load); + ASSERT_TRUE(status.ok()) << status.message(); + } + { + auto stream = wrapper.Query("CREATE VIEW foo AS SELECT ts, dur FROM slice"); + auto proto = WaitForStreamReady(stream); + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } + { + auto stream = wrapper.Query("SELECT ts, dur FROM foo"); + auto proto = WaitForStreamReady(stream); + + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + + ASSERT_EQ(proto->value().trace(), "foobar"); + + auto& result = proto.value()->result(); + ASSERT_EQ(result.batch_size(), 1); + ASSERT_EQ(result.batch(0).cells_size(), 2); + + ASSERT_EQ(result.batch(0).cells(0), + protos::QueryResult::CellsBatch::CELL_VARINT); + ASSERT_EQ(result.batch(0).cells(1), + protos::QueryResult::CellsBatch::CELL_VARINT); + ASSERT_EQ(result.batch(0).varint_cells(0), 10852771242000); + ASSERT_EQ(result.batch(0).varint_cells(1), 3000); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } +} + +TEST(TraceProcessorWrapperUnittest, Stateless) { + base::ThreadPool pool(1); + TraceProcessorWrapper wrapper("foobar", &pool, SF::kStateless); + { + auto load = wrapper.LoadTrace(base::StreamOf(SimpleSystrace())); + base::Status status = WaitForFutureReady(load); + ASSERT_TRUE(status.ok()) << status.message(); + } + { + auto stream = wrapper.Query("CREATE VIEW foo AS SELECT ts, dur FROM slice"); + auto proto = WaitForStreamReady(stream); + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } + + // Second CREATE VIEW should also succeed because the first one should have + // been wiped. + { + auto stream = wrapper.Query("CREATE VIEW foo AS SELECT ts, dur FROM slice"); + auto proto = WaitForStreamReady(stream); + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } + + // Selecting from it should return an error. + { + auto stream = wrapper.Query("SELECT ts, dur FROM foo"); + auto proto = WaitForStreamReady(stream); + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + ASSERT_TRUE(proto->value().result().has_error()); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } +} + +TEST(TraceProcessorWrapperUnittest, Chunked) { + base::ThreadPool pool(1); + TraceProcessorWrapper wrapper("foobar", &pool, SF::kStateless); + { + auto chunked = SimpleSystraceChunked(); + ASSERT_EQ(chunked.size(), 3u); + auto load = wrapper.LoadTrace(base::StreamFrom(chunked)); + base::Status status = WaitForFutureReady(load); + ASSERT_TRUE(status.ok()) << status.message(); + } + { + auto stream = wrapper.Query("SELECT ts, dur FROM slice"); + auto proto = WaitForStreamReady(stream); + + ASSERT_TRUE(proto.has_value()); + ASSERT_TRUE(proto->ok()) << proto->status().message(); + + ASSERT_EQ(proto->value().trace(), "foobar"); + + auto& result = proto.value()->result(); + ASSERT_EQ(result.batch_size(), 1); + ASSERT_EQ(result.batch(0).cells_size(), 2); + + ASSERT_EQ(result.batch(0).cells(0), + protos::QueryResult::CellsBatch::CELL_VARINT); + ASSERT_EQ(result.batch(0).cells(1), + protos::QueryResult::CellsBatch::CELL_VARINT); + ASSERT_EQ(result.batch(0).varint_cells(0), 10852771242000); + ASSERT_EQ(result.batch(0).varint_cells(1), 3000); + + ASSERT_FALSE(WaitForStreamReady(stream).has_value()); + } +} + +} // namespace +} // namespace cloud_trace_processor +} // namespace perfetto diff --git a/src/cloud_trace_processor/worker_impl.cc b/src/cloud_trace_processor/worker_impl.cc new file mode 100644 index 000000000..6f115601d --- /dev/null +++ b/src/cloud_trace_processor/worker_impl.cc @@ -0,0 +1,118 @@ +/* + * 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/cloud_trace_processor/worker_impl.h" + +#include <memory> + +#include "perfetto/base/status.h" +#include "perfetto/ext/base/status_or.h" +#include "perfetto/ext/base/threading/stream.h" +#include "perfetto/ext/base/uuid.h" +#include "protos/perfetto/cloud_trace_processor/common.pb.h" +#include "protos/perfetto/cloud_trace_processor/orchestrator.pb.h" +#include "protos/perfetto/cloud_trace_processor/worker.pb.h" +#include "src/cloud_trace_processor/trace_processor_wrapper.h" +#include "src/trace_processor/util/status_macros.h" + +namespace perfetto { +namespace cloud_trace_processor { + +Worker::~Worker() = default; + +std::unique_ptr<Worker> Worker::CreateInProcesss(CtpEnvironment* environment, + base::ThreadPool* pool) { + return std::make_unique<WorkerImpl>(environment, pool); +} + +WorkerImpl::WorkerImpl(CtpEnvironment* environment, base::ThreadPool* pool) + : environment_(environment), thread_pool_(pool) {} + +base::StatusOrFuture<protos::TracePoolShardCreateResponse> +WorkerImpl::TracePoolShardCreate(const protos::TracePoolShardCreateArgs& args) { + if (args.pool_type() == protos::TracePoolType::DEDICATED) { + return base::ErrStatus("Dedicated pools are not currently supported"); + } + auto it_and_inserted = shards_.Insert(args.pool_id(), TracePoolShard()); + if (!it_and_inserted.second) { + return base::ErrStatus("Shard for pool %s already exists", + args.pool_id().c_str()); + } + return base::StatusOr(protos::TracePoolShardCreateResponse()); +} + +base::StatusOrStream<protos::TracePoolShardSetTracesResponse> +WorkerImpl::TracePoolShardSetTraces( + const protos::TracePoolShardSetTracesArgs& args) { + using Response = protos::TracePoolShardSetTracesResponse; + using StatusOrResponse = base::StatusOr<Response>; + + TracePoolShard* shard = shards_.Find(args.pool_id()); + if (!shard) { + return base::StreamOf<StatusOrResponse>(base::ErrStatus( + "Unable to find shard for pool %s", args.pool_id().c_str())); + } + + std::vector<base::StatusOrStream<Response>> streams; + for (const std::string& trace : args.traces()) { + // TODO(lalitm): add support for stateful trace processor in dedicated + // pools. + auto tp = std::make_unique<TraceProcessorWrapper>( + trace, thread_pool_, TraceProcessorWrapper::Statefulness::kStateless); + auto load_trace_future = + tp->LoadTrace(environment_->ReadFile(trace)) + .ContinueWith( + [trace](base::Status status) -> base::Future<StatusOrResponse> { + RETURN_IF_ERROR(status); + protos::TracePoolShardSetTracesResponse resp; + *resp.mutable_trace() = trace; + 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)); +} + +base::StatusOrStream<protos::TracePoolShardQueryResponse> +WorkerImpl::TracePoolShardQuery(const protos::TracePoolShardQueryArgs& args) { + using Response = protos::TracePoolShardQueryResponse; + using StatusOrResponse = base::StatusOr<Response>; + TracePoolShard* shard = shards_.Find(args.pool_id()); + if (!shard) { + return base::StreamOf<StatusOrResponse>(base::ErrStatus( + "Unable to find shard for pool %s", args.pool_id().c_str())); + } + std::vector<base::StatusOrStream<Response>> streams; + streams.reserve(shard->tps.size()); + for (std::unique_ptr<TraceProcessorWrapper>& tp : shard->tps) { + streams.emplace_back(tp->Query(args.sql_query())); + } + return base::FlattenStreams(std::move(streams)); +} + +base::StatusOrFuture<protos::TracePoolShardDestroyResponse> +WorkerImpl::TracePoolShardDestroy( + const protos::TracePoolShardDestroyArgs& args) { + if (!shards_.Erase(args.pool_id())) { + return base::ErrStatus("Unable to find shard for pool %s", + args.pool_id().c_str()); + } + return base::StatusOr(protos::TracePoolShardDestroyResponse()); +} + +} // namespace cloud_trace_processor +} // namespace perfetto diff --git a/src/cloud_trace_processor/worker_impl.h b/src/cloud_trace_processor/worker_impl.h new file mode 100644 index 000000000..c7dc7548d --- /dev/null +++ b/src/cloud_trace_processor/worker_impl.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef SRC_CLOUD_TRACE_PROCESSOR_WORKER_IMPL_H_ +#define SRC_CLOUD_TRACE_PROCESSOR_WORKER_IMPL_H_ + +#include <vector> + +#include "perfetto/ext/base/flat_hash_map.h" +#include "perfetto/ext/base/threading/thread_pool.h" +#include "perfetto/ext/cloud_trace_processor/environment.h" +#include "perfetto/ext/cloud_trace_processor/worker.h" +#include "src/cloud_trace_processor/trace_processor_wrapper.h" + +namespace perfetto { +namespace protos { + +enum GroupType : int; + +} // namespace protos +} // namespace perfetto + +namespace perfetto { +namespace cloud_trace_processor { + +class WorkerImpl : public Worker { + public: + explicit WorkerImpl(CtpEnvironment*, base::ThreadPool*); + + base::StatusOrFuture<protos::TracePoolShardCreateResponse> + TracePoolShardCreate(const protos::TracePoolShardCreateArgs&) override; + + base::StatusOrStream<protos::TracePoolShardSetTracesResponse> + TracePoolShardSetTraces(const protos::TracePoolShardSetTracesArgs&) override; + + base::StatusOrStream<protos::TracePoolShardQueryResponse> TracePoolShardQuery( + const protos::TracePoolShardQueryArgs&) override; + + base::StatusOrFuture<protos::TracePoolShardDestroyResponse> + TracePoolShardDestroy(const protos::TracePoolShardDestroyArgs&) override; + + private: + struct TracePoolShard { + std::vector<std::unique_ptr<TraceProcessorWrapper>> tps; + }; + CtpEnvironment* const environment_; + base::ThreadPool* const thread_pool_; + base::FlatHashMap<std::string, TracePoolShard> shards_; +}; + +} // namespace cloud_trace_processor +} // namespace perfetto + +#endif // SRC_CLOUD_TRACE_PROCESSOR_WORKER_IMPL_H_ diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc index 4a2b0813e..3052341f3 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 @@ void ReportFinalizeTraceUuidToAtrace(const base::Uuid& uuid) { 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 @@ Usage: %s 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, } 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, 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 @@ std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, #endif } + PERFETTO_CHECK(!snapshot_thread_); // No threads before demonization. base::Daemonize([this]() -> int { background_wait_pipe_.wr.reset(); @@ -992,9 +1012,16 @@ void PerfettoCmd::OnConnect() { 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 @@ void PerfettoCmd::ReadbackTraceDataAndQuit(const std::string& error) { // 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 @@ bool PerfettoCmd::OpenOutputFile() { } 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 @@ void PerfettoCmd::OnTraceStats(bool /*success*/, // 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 @@ void PerfettoCmd::OnObservableEvents( 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 9504ca5fa..b55cbc8c2 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 @@ class PerfettoCmd : public Consumer { 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 @@ class PerfettoCmd : public Consumer { // 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 @@ class PerfettoCmd : public Consumer { // 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/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc index 0fb7be314..fec5b6dcd 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 @@ size_t NumberOfCpus() { 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 @@ void PerfProducer::StartDataSource(DataSourceInstanceID ds_id, 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/filtering/filter_util.cc b/src/protozero/filtering/filter_util.cc index 8e81cb040..d17a36b10 100644 --- a/src/protozero/filtering/filter_util.cc +++ b/src/protozero/filtering/filter_util.cc @@ -67,9 +67,13 @@ void MultiFileErrorCollectorImpl::AddWarning(const std::string& filename, FilterUtil::FilterUtil() = default; FilterUtil::~FilterUtil() = default; -bool FilterUtil::LoadMessageDefinition(const std::string& proto_file, - const std::string& root_message, - const std::string& proto_dir_path) { +bool FilterUtil::LoadMessageDefinition( + const std::string& proto_file, + const std::string& root_message, + const std::string& proto_dir_path, + const std::set<std::string>& passthrough_fields) { + passthrough_fields_ = passthrough_fields; + passthrough_fields_seen_.clear(); // The protobuf compiler doesn't like backslashes and prints an error like: // Error C:\it7mjanpw3\perfetto-a16500 -1:0: Backslashes, consecutive slashes, // ".", or ".." are not allowed in the virtual path. @@ -122,6 +126,22 @@ bool FilterUtil::LoadMessageDefinition(const std::string& proto_file, // future without realizing) when performing the Dedupe() pass. DescriptorsByNameMap descriptors_by_full_name; ParseProtoDescriptor(root_msg, &descriptors_by_full_name); + + // If the user specified a set of fields to pass through, print an error and + // fail if any of the passed fields have not been seen while recursing in the + // schema. This is to avoid typos or naming changes to be silently ignored. + std::vector<std::string> unused_passthrough; + std::set_difference(passthrough_fields_.begin(), passthrough_fields_.end(), + passthrough_fields_seen_.begin(), + passthrough_fields_seen_.end(), + std::back_inserter(unused_passthrough)); + for (const std::string& message_and_field : unused_passthrough) { + PERFETTO_ELOG("Field not found %s", message_and_field.c_str()); + } + if (!unused_passthrough.empty()) { + PERFETTO_ELOG("Syntax: perfetto.protos.MessageName:field_name"); + return false; + } return true; } @@ -145,7 +165,15 @@ FilterUtil::Message* FilterUtil::ParseProtoDescriptor( auto& field = msg->fields[field_id]; field.name = proto_field->name(); field.type = proto_field->type_name(); - if (proto_field->message_type()) { + + std::string message_and_field = msg->full_name + ":" + field.name; + bool passthrough = false; + if (passthrough_fields_.count(message_and_field)) { + field.type = "bytes"; + passthrough = true; + passthrough_fields_seen_.insert(message_and_field); + } + if (proto_field->message_type() && !passthrough) { msg->has_nested_fields = true; // Recurse. field.nested_type = ParseProtoDescriptor(proto_field->message_type(), @@ -253,8 +281,11 @@ void FilterUtil::PrintAsText(std::optional<std::string> filter_bytecode) { } const Message* nested_type = id_and_field.second.nested_type; + bool passthrough = false; if (nested_type) { - PERFETTO_CHECK(!result.simple_field() || !filter_bytecode); + // result.simple_field might be true if the generated bytecode is + // passing through a whole submessage without recursing. + passthrough = result.simple_field(); if (seen_msgs.find(nested_type) == seen_msgs.end()) { seen_msgs.insert(nested_type); queue.emplace_back(result.nested_msg_index, nested_type); @@ -267,6 +298,8 @@ void FilterUtil::PrintAsText(std::optional<std::string> filter_bytecode) { std::string stripped_nested = nested_type ? " " + StripPrefix(nested_type->full_name, root_prefix) : ""; + if (passthrough) + stripped_nested += " # PASSTHROUGH"; fprintf(print_stream_, "%-60s %3u %-8s %-32s%s\n", stripped_name.c_str(), field_id, field.type.c_str(), field.name.c_str(), stripped_nested.c_str()); diff --git a/src/protozero/filtering/filter_util.h b/src/protozero/filtering/filter_util.h index 0c5e74fe4..276ec6ddc 100644 --- a/src/protozero/filtering/filter_util.h +++ b/src/protozero/filtering/filter_util.h @@ -22,6 +22,7 @@ #include <list> #include <map> #include <optional> +#include <set> #include <string> // We include this intentionally instead of forward declaring to allow @@ -46,9 +47,14 @@ class FilterUtil { // root_message: fully qualified message name (e.g., perfetto.protos.Trace). // If empty, the first message in the file will be used. // proto_dir_path: the root for .proto includes. If empty uses CWD. - bool LoadMessageDefinition(const std::string& proto_file, - const std::string& root_message, - const std::string& proto_dir_path); + // passthrough: an optional set of fields that should be transparently passed + // through without recursing further. + // Syntax: "perfetto.protos.TracePacket:trace_config" + bool LoadMessageDefinition( + const std::string& proto_file, + const std::string& root_message, + const std::string& proto_dir_path, + const std::set<std::string>& passthrough_fields = {}); // Deduplicates leaf messages having the same sets of field ids. // It changes the internal state and affects the behavior of next calls to @@ -103,6 +109,12 @@ class FilterUtil { // list<> because pointers need to be stable. std::list<Message> descriptors_; + std::set<std::string> passthrough_fields_; + + // Used only for debugging aid, to print out an error message when the user + // specifies a field to pass through but it doesn't exist. + std::set<std::string> passthrough_fields_seen_; + FILE* print_stream_ = stdout; }; diff --git a/src/protozero/filtering/filter_util_unittest.cc b/src/protozero/filtering/filter_util_unittest.cc index 7ad4d6f5f..2b13ae664 100644 --- a/src/protozero/filtering/filter_util_unittest.cc +++ b/src/protozero/filtering/filter_util_unittest.cc @@ -302,5 +302,42 @@ Child2.Nested 1 int64 f1 FilterToText(filter, bytecode)); } +TEST(SchemaParserTest, Passthrough) { + auto schema = MkTemp(R"( + syntax = "proto2"; + message Root { + optional int32 i32 = 13; + optional TracePacket packet = 7; + } + message TraceConfig { + optional int32 f3 = 3; + optional int64 f4 = 4; + } + message TracePacket { + optional int32 f1 = 3; + optional int64 f2 = 4; + optional TraceConfig cfg = 5; + } + )"); + + FilterUtil filter; + std::set<std::string> passthrough{"TracePacket:cfg"}; + ASSERT_TRUE( + filter.LoadMessageDefinition(schema.path(), "Root", "", passthrough)); + + EXPECT_EQ(R"(Root 7 message packet TracePacket +Root 13 int32 i32 +TracePacket 3 int32 f1 +TracePacket 4 int64 f2 +TracePacket 5 bytes cfg +)", + FilterToText(filter)); + + std::string bytecode = filter.GenerateFilterBytecode(); + // If we generate bytecode from the schema itself, all fields are allowed and + // the result is identical to the unfiltered output. + EXPECT_EQ(FilterToText(filter), FilterToText(filter, bytecode)); +} + } // namespace } // namespace protozero diff --git a/src/protozero/filtering/message_filter_unittest.cc b/src/protozero/filtering/message_filter_unittest.cc index 83cb911f2..3159bfcba 100644 --- a/src/protozero/filtering/message_filter_unittest.cc +++ b/src/protozero/filtering/message_filter_unittest.cc @@ -128,6 +128,87 @@ TEST(MessageFilterTest, EndToEnd) { } } +TEST(MessageFilterTest, Passthrough) { + auto schema = perfetto::base::TempFile::Create(); + static const char kSchema[] = R"( + syntax = "proto2"; + message TracePacket { + optional int64 timestamp = 1; + optional TraceConfig cfg = 2; + optional TraceConfig cfg_filtered = 3; + optional string other = 4; + }; + message SubConfig { + optional string f4 = 6; + } + message TraceConfig { + optional int64 f1 = 3; + optional string f2 = 4; + optional SubConfig f3 = 5; + } + )"; + + perfetto::base::WriteAll(*schema, kSchema, strlen(kSchema)); + perfetto::base::FlushFile(*schema); + + FilterUtil filter; + ASSERT_TRUE(filter.LoadMessageDefinition( + schema.path(), "", "", {"TracePacket:other", "TracePacket:cfg"})); + std::string bytecode = filter.GenerateFilterBytecode(); + ASSERT_GT(bytecode.size(), 0u); + + HeapBuffered<Message> msg; + msg->AppendVarInt(/*field_id=*/1, 10); + msg->AppendString(/*field_id=*/4, "other_string"); + + // Fill `cfg`. + auto* nest = msg->BeginNestedMessage<Message>(/*field_id=*/2); + nest->AppendVarInt(/*field_id=*/3, 100); + nest->AppendString(/*field_id=*/4, "f2.payload"); + nest->AppendString(/*field_id=*/99, "not_in_original_schema"); + auto* nest2 = nest->BeginNestedMessage<Message>(/*field_id=*/5); + nest2->AppendString(/*field_id=*/6, "subconfig.f4"); + nest2->Finalize(); + nest->Finalize(); + + // Fill `cfg_filtered`. + nest = msg->BeginNestedMessage<Message>(/*field_id=*/3); + nest->AppendVarInt(/*field_id=*/3, 200); // This should be propagated. + nest->AppendVarInt(/*field_id=*/6, 300); // This shoudl be filtered out. + nest->Finalize(); + + MessageFilter flt; + ASSERT_TRUE(flt.LoadFilterBytecode(bytecode.data(), bytecode.size())); + + std::vector<uint8_t> encoded = msg.SerializeAsArray(); + + auto filtered = flt.FilterMessage(encoded.data(), encoded.size()); + ASSERT_LT(filtered.size, encoded.size()); + + ProtoDecoder dec(filtered.data.get(), filtered.size); + EXPECT_EQ(dec.FindField(1).as_int64(), 10); + EXPECT_EQ(dec.FindField(4).as_std_string(), "other_string"); + + EXPECT_TRUE(dec.FindField(2).valid()); + ProtoDecoder nest_dec(dec.FindField(2).as_bytes()); + EXPECT_EQ(nest_dec.FindField(3).as_int32(), 100); + EXPECT_EQ(nest_dec.FindField(4).as_std_string(), "f2.payload"); + EXPECT_TRUE(nest_dec.FindField(5).valid()); + ProtoDecoder nest_dec2(nest_dec.FindField(5).as_bytes()); + EXPECT_EQ(nest_dec2.FindField(6).as_std_string(), "subconfig.f4"); + + // Field 99 should be preserved anyways even if it wasn't in the original + // schema because the whole TracePacket submessage was passed through. + EXPECT_TRUE(nest_dec.FindField(99).valid()); + EXPECT_EQ(nest_dec.FindField(99).as_std_string(), "not_in_original_schema"); + + // Check that the field `cfg_filtered` contains only `f1`,`f2`,`f3`. + EXPECT_TRUE(dec.FindField(3).valid()); + ProtoDecoder nest_dec3(dec.FindField(3).as_bytes()); + EXPECT_EQ(nest_dec3.FindField(3).as_int32(), 200); + EXPECT_FALSE(nest_dec3.FindField(6).valid()); +} + TEST(MessageFilterTest, ChangeRoot) { auto schema = perfetto::base::TempFile::Create(); static const char kSchema[] = R"( diff --git a/src/protozero/packed_repeated_fields.cc b/src/protozero/packed_repeated_fields.cc index 16d453953..a884b73ca 100644 --- a/src/protozero/packed_repeated_fields.cc +++ b/src/protozero/packed_repeated_fields.cc @@ -20,11 +20,6 @@ namespace protozero { -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr size_t PackedBufferBase::kOnStackStorageSize; -#endif - void PackedBufferBase::GrowSlowpath() { size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_); size_t old_size = static_cast<size_t>(storage_end_ - storage_begin_); diff --git a/src/protozero/proto_decoder_unittest.cc b/src/protozero/proto_decoder_unittest.cc index 0991f887b..1e962d7a6 100644 --- a/src/protozero/proto_decoder_unittest.cc +++ b/src/protozero/proto_decoder_unittest.cc @@ -592,5 +592,26 @@ TEST(ProtoDecoderTest, OneBigFieldIdOnly) { 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/shared_lib/test/BUILD.gn b/src/shared_lib/test/BUILD.gn index 220439aeb..aa87956b0 100644 --- a/src/shared_lib/test/BUILD.gn +++ b/src/shared_lib/test/BUILD.gn @@ -22,7 +22,11 @@ source_set("utils") { "../../../gn:gtest_and_gmock", "../../../include/perfetto/public", ] - sources = [ "utils.cc" ] + sources = [ + "utils.cc", + "utils.h", + ] + defines = [ "PERFETTO_SDK_DISABLE_SHLIB_EXPORT" ] } if (enable_perfetto_benchmarks) { @@ -35,6 +39,7 @@ if (enable_perfetto_benchmarks) { "../../../gn:default_deps", "../../../include/perfetto/public", ] + defines = [ "PERFETTO_SDK_DISABLE_SHLIB_EXPORT" ] sources = [ "benchmark.cc" ] } } @@ -49,6 +54,7 @@ if (enable_perfetto_integration_tests) { "../../../gn:gtest_and_gmock", "../../../include/perfetto/public", ] + defines = [ "PERFETTO_SDK_DISABLE_SHLIB_EXPORT" ] sources = [ "api_integrationtest.cc" ] } } diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list index 33ec8e916..40dd94924 100644 --- a/src/tools/ftrace_proto_gen/event_list +++ b/src/tools/ftrace_proto_gen/event_list @@ -476,3 +476,5 @@ hyp/host_hcall hyp/host_smc hyp/host_mem_abort synthetic/suspend_resume_minimal +mali/mali_CSF_INTERRUPT_START +mali/mali_CSF_INTERRUPT_END diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc index f69237313..712e3d139 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 @@ std::string SingleEventInfo(perfetto::Proto proto, // 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_filter/proto_filter.cc b/src/tools/proto_filter/proto_filter.cc index 04385c2b1..7bfcb74d8 100644 --- a/src/tools/proto_filter/proto_filter.cc +++ b/src/tools/proto_filter/proto_filter.cc @@ -49,7 +49,7 @@ Example usage: # Generate the filter bytecode from a .proto schema proto_filter -r perfetto.protos.Trace -s protos/perfetto/trace/trace.proto \ - -F /tmp/bytecode [--dedupe] + -F /tmp/bytecode [--dedupe] [-x protos.Message:field_to_pass] # List the used/filtered fields from a trace file @@ -72,14 +72,15 @@ int Main(int argc, char** argv) { {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'v'}, {"dedupe", no_argument, nullptr, 'd'}, - {"proto_path", no_argument, nullptr, 'I'}, - {"schema_in", no_argument, nullptr, 's'}, - {"root_message", no_argument, nullptr, 'r'}, - {"msg_in", no_argument, nullptr, 'i'}, - {"msg_out", no_argument, nullptr, 'o'}, - {"filter_in", no_argument, nullptr, 'f'}, - {"filter_out", no_argument, nullptr, 'F'}, - {"filter_oct_out", no_argument, nullptr, 'T'}, + {"proto_path", required_argument, nullptr, 'I'}, + {"schema_in", required_argument, nullptr, 's'}, + {"root_message", required_argument, nullptr, 'r'}, + {"msg_in", required_argument, nullptr, 'i'}, + {"msg_out", required_argument, nullptr, 'o'}, + {"filter_in", required_argument, nullptr, 'f'}, + {"filter_out", required_argument, nullptr, 'F'}, + {"filter_oct_out", required_argument, nullptr, 'T'}, + {"passthrough", required_argument, nullptr, 'x'}, {nullptr, 0, nullptr, 0}}; std::string msg_in; @@ -90,11 +91,12 @@ int Main(int argc, char** argv) { std::string filter_oct_out; std::string proto_path; std::string root_message_arg; + std::set<std::string> passthrough_fields; bool dedupe = false; for (;;) { int option = - getopt_long(argc, argv, "hvdI:s:r:i:o:f:F:T:", long_options, nullptr); + getopt_long(argc, argv, "hvdI:s:r:i:o:f:F:T:x:", long_options, nullptr); if (option == -1) break; // EOF. @@ -149,6 +151,11 @@ int Main(int argc, char** argv) { continue; } + if (option == 'x') { + passthrough_fields.insert(optarg); + continue; + } + if (option == 'h') { fprintf(stdout, kUsage); exit(0); @@ -175,8 +182,8 @@ int Main(int argc, char** argv) { protozero::FilterUtil filter; if (!schema_in.empty()) { PERFETTO_LOG("Loading proto schema from %s", schema_in.c_str()); - if (!filter.LoadMessageDefinition(schema_in, root_message_arg, - proto_path)) { + if (!filter.LoadMessageDefinition(schema_in, root_message_arg, proto_path, + passthrough_fields)) { PERFETTO_ELOG("Failed to parse proto schema from %s", schema_in.c_str()); return 1; } @@ -264,7 +271,7 @@ int Main(int argc, char** argv) { if (!msg_out.empty()) { PERFETTO_LOG("Writing filtered proto bytes (%zu bytes) into %s", msg_filtered_data.size(), msg_out.c_str()); - auto fd = base::OpenFile(msg_out, O_WRONLY | O_CREAT, 0644); + auto fd = base::OpenFile(msg_out, O_WRONLY | O_TRUNC | O_CREAT, 0644); base::WriteAll(*fd, msg_filtered_data.data(), msg_filtered_data.size()); } diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn index 29bd8c5ee..6f0660c6a 100644 --- a/src/trace_processor/BUILD.gn +++ b/src/trace_processor/BUILD.gn @@ -172,6 +172,7 @@ if (enable_perfetto_trace_processor_sqlite) { "prelude/functions", "prelude/operators", "prelude/table_functions", + "prelude/tables_views", "sqlite", "stdlib:gen_amalgamated_stdlib", "storage", diff --git a/src/trace_processor/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc index b5c2dc99b..3e3adabb0 100644 --- a/src/trace_processor/containers/bit_vector.cc +++ b/src/trace_processor/containers/bit_vector.cc @@ -88,6 +88,76 @@ BitVector::BitVector(std::vector<uint64_t> words, words_.resize(words_.size() + 8 - (words_.size() % 8u)); } +void BitVector::Resize(uint32_t new_size, bool filler) { + uint32_t old_size = size_; + if (new_size == old_size) + return; + + // Empty bitvectors should be memory efficient so we don't keep any data + // around in the bitvector. + if (new_size == 0) { + words_.clear(); + counts_.clear(); + size_ = 0; + return; + } + + // Compute the address of the new last bit in the bitvector. + Address last_addr = IndexToAddress(new_size - 1); + uint32_t old_blocks_size = static_cast<uint32_t>(counts_.size()); + uint32_t new_blocks_size = last_addr.block_idx + 1; + + // Resize the block and count vectors to have the correct number of entries. + words_.resize(Block::kWords * new_blocks_size); + counts_.resize(new_blocks_size); + + if (new_size > old_size) { + if (filler) { + // If the new space should be filled with ones, then set all the bits + // between the address of the old size and the new last address. + const Address& start = IndexToAddress(old_size); + Set(start, last_addr); + + // We then need to update the counts vector to match the changes we + // made to the blocks. + + // We start by adding the bits we set in the first block to the + // cummulative count before the range we changed. + Address end_of_block = {start.block_idx, + {Block::kWords - 1, BitWord::kBits - 1}}; + uint32_t count_in_block_after_end = + AddressToIndex(end_of_block) - AddressToIndex(start) + 1; + uint32_t set_count = CountSetBits() + count_in_block_after_end; + + for (uint32_t i = start.block_idx + 1; i <= last_addr.block_idx; ++i) { + // Set the count to the cummulative count so far. + counts_[i] = set_count; + + // Add a full block of set bits to the count. + set_count += Block::kBits; + } + } else { + // If the newly added bits are false, we just need to update the + // counts vector with the current size of the bitvector for all + // the newly added blocks. + if (new_blocks_size > old_blocks_size) { + uint32_t count = CountSetBits(); + for (uint32_t i = old_blocks_size; i < new_blocks_size; ++i) { + counts_[i] = count; + } + } + } + } else { + // Throw away all the bits after the new last bit. We do this to make + // future lookup, append and resize operations not have to worrying about + // trailing garbage bits in the last block. + BlockFromIndex(last_addr.block_idx).ClearAfter(last_addr.block_offset); + } + + // Actually update the size. + size_ = new_size; +} + BitVector BitVector::Copy() const { return BitVector(words_, counts_, size_); } @@ -100,27 +170,50 @@ BitVector::SetBitsIterator BitVector::IterateSetBits() const { return SetBitsIterator(this); } +BitVector BitVector::Not() const { + Builder builder(size()); + + // Append all words from all blocks except the last one. + uint32_t full_words = builder.BitsInCompleteWordsUntilFull(); + for (uint32_t i = 0; i < full_words; ++i) { + builder.AppendWord(ConstBitWord(&words_[i]).Not()); + } + + // Append bits from the last word. + uint32_t bits_from_last_word = builder.BitsUntilFull(); + ConstBitWord last_word(&words_[full_words]); + for (uint32_t i = 0; i < bits_from_last_word; ++i) { + builder.Append(!last_word.IsSet(i)); + } + + return std::move(builder).Build(); +} + void BitVector::UpdateSetBits(const BitVector& update) { + if (update.CountSetBits() == 0 || CountSetBits() == 0) { + *this = BitVector(); + return; + } PERFETTO_DCHECK(update.size() <= CountSetBits()); // Get the start and end ptrs for the current bitvector. // Safe because of the static_assert above. - auto* ptr = reinterpret_cast<uint64_t*>(words_.data()); - const uint64_t* ptr_end = ptr + WordCeil(size()); + uint64_t* ptr = words_.data(); + const uint64_t* ptr_end = ptr + WordCount(size()); - // Get the start and end ptrs for the current bitvector. + // Get the start and end ptrs for the update bitvector. // Safe because of the static_assert above. - auto* update_ptr = reinterpret_cast<const uint64_t*>(update.words_.data()); - const uint64_t* update_ptr_end = update_ptr + WordCeil(update.size()); + const uint64_t* update_ptr = update.words_.data(); + const uint64_t* update_ptr_end = update_ptr + WordCount(update.size()); // |update_unused_bits| contains |unused_bits_count| bits at the bottom - // which indicates the how the next |unused_bits_count| set bits in |this| + // which indicates how the next |unused_bits_count| set bits in |this| // should be changed. This is necessary because word boundaries in |this| will // almost always *not* match the word boundaries in |update|. uint64_t update_unused_bits = 0; uint8_t unused_bits_count = 0; - // The basic premise of this loop is, for each word in |this| we find the + // The basic premise of this loop is, for each word in |this| we find // enough bits from |update| to cover every set bit in the word. We then use // the PDEP x64 instruction (or equivalent instructions/software emulation) to // update the word and store it back in |this|. @@ -190,5 +283,47 @@ void BitVector::UpdateSetBits(const BitVector& update) { PERFETTO_DCHECK(update.CountSetBits() == CountSetBits()); } +BitVector BitVector::IntersectRange(uint32_t range_start, + uint32_t range_end) const { + uint32_t total_set_bits = CountSetBits(); + if (total_set_bits == 0 || range_start >= range_end) + return BitVector(); + + // We should skip all bits until the index of first set bit bigger than + // |range_start|. + uint32_t start_idx = std::max(range_start, IndexOfNthSet(0)); + uint32_t end_idx = std::min(range_end, size()); + + if (start_idx >= end_idx) + return BitVector(); + + Builder builder(end_idx); + + // All bits before start should be empty. + builder.Skip(start_idx); + + uint32_t front_bits = builder.BitsUntilWordBoundaryOrFull(); + uint32_t cur_index = start_idx; + for (uint32_t i = 0; i < front_bits; ++i, ++cur_index) { + builder.Append(IsSet(cur_index)); + } + + PERFETTO_DCHECK(cur_index == end_idx || cur_index % BitWord::kBits == 0); + uint32_t cur_words = cur_index / BitWord::kBits; + uint32_t full_words = builder.BitsInCompleteWordsUntilFull() / BitWord::kBits; + uint32_t total_full_words = cur_words + full_words; + for (; cur_words < total_full_words; ++cur_words) { + builder.AppendWord(words_[cur_words]); + } + + uint32_t last_bits = builder.BitsUntilFull(); + cur_index += full_words * BitWord::kBits; + for (uint32_t i = 0; i < last_bits; ++i, ++cur_index) { + builder.Append(IsSet(cur_index)); + } + + return std::move(builder).Build(); +} + } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/containers/bit_vector.h b/src/trace_processor/containers/bit_vector.h index 2855739b2..a9a5fbddc 100644 --- a/src/trace_processor/containers/bit_vector.h +++ b/src/trace_processor/containers/bit_vector.h @@ -23,6 +23,7 @@ #include <algorithm> #include <array> +#include <optional> #include <vector> #include "perfetto/base/logging.h" @@ -45,13 +46,15 @@ class BitVector { using AllBitsIterator = internal::AllBitsIterator; using SetBitsIterator = internal::SetBitsIterator; + static constexpr uint32_t kBitsInWord = 64; + // Builder class which allows efficiently creating a BitVector by appending // words. Using this class is generally far more efficient than trying to set // bits directly in a BitVector or even appending one bit at a time. class Builder { public: // Creates a Builder for building a BitVector of |size| bits. - explicit Builder(uint32_t size) : words_(WordCeil(size)), size_(size) {} + explicit Builder(uint32_t size) : words_(WordCount(size)), size_(size) {} // Skips forward |n| bits leaving them as zeroed. void Skip(uint32_t n) { @@ -86,17 +89,10 @@ class BitVector { 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(); - } - if (size_ % Block::kBits == 0) { - counts[no_blocks - 1] += - counts[no_blocks - 2] + - ConstBlock(&words_[Block::kWords * (no_blocks - 2)]).CountSetBits(); + for (uint32_t i = 1; i < no_blocks; ++i) { + counts[i] = counts[i - 1] + + ConstBlock(&words_[Block::kWords * (i - 1)]).CountSetBits(); } - return BitVector{std::move(words_), std::move(counts), size_}; } @@ -104,7 +100,7 @@ class BitVector { // appended to this builder before having to fallback to |Append| due to // being close to the end. uint32_t BitsInCompleteWordsUntilFull() { - uint32_t next_word = WordCeil(global_bit_offset_); + uint32_t next_word = WordCount(global_bit_offset_); uint32_t end_word = WordFloor(size_); uint32_t complete_words = next_word < end_word ? end_word - next_word : 0; return complete_words * BitWord::kBits; @@ -150,24 +146,7 @@ class BitVector { BitVector Copy() const; // Create a bitwise Not copy of the bitvector. - BitVector Not() const { - Builder builder(size()); - - // Append all words from all blocks except the last one. - uint32_t full_words = size() / BitWord::kBits; - for (uint32_t i = 0; i < full_words; ++i) { - builder.AppendWord(ConstBitWord(&words_[i]).Not()); - } - - // Append bits from the last word. - uint32_t bits_from_last_word = size() % BitWord::kBits; - ConstBitWord last_word(&words_[full_words]); - for (uint32_t i = 0; i < bits_from_last_word; ++i) { - builder.Append(!last_word.IsSet(i)); - } - - return std::move(builder).Build(); - } + BitVector Not() const; // Returns the size of the bitvector. uint32_t size() const { return static_cast<uint32_t>(size_); } @@ -298,75 +277,7 @@ class BitVector { // Truncates the BitVector if |size| < |size()| or fills the new space with // |filler| if |size| > |size()|. Calling this method is a noop if |size| == // |size()|. - void Resize(uint32_t new_size, bool filler = false) { - uint32_t old_size = size_; - if (new_size == old_size) - return; - - // Empty bitvectors should be memory efficient so we don't keep any data - // around in the bitvector. - if (new_size == 0) { - words_.clear(); - counts_.clear(); - size_ = 0; - return; - } - - // Compute the address of the new last bit in the bitvector. - Address last_addr = IndexToAddress(new_size - 1); - uint32_t old_blocks_size = static_cast<uint32_t>(counts_.size()); - uint32_t new_blocks_size = last_addr.block_idx + 1; - - // Resize the block and count vectors to have the correct number of entries. - words_.resize(Block::kWords * new_blocks_size); - counts_.resize(new_blocks_size); - - if (new_size > old_size) { - if (filler) { - // If the new space should be filled with ones, then set all the bits - // between the address of the old size and the new last address. - const Address& start = IndexToAddress(old_size); - Set(start, last_addr); - - // We then need to update the counts vector to match the changes we - // made to the blocks. - - // We start by adding the bits we set in the first block to the - // cummulative count before the range we changed. - Address end_of_block = {start.block_idx, - {Block::kWords - 1, BitWord::kBits - 1}}; - uint32_t count_in_block_after_end = - AddressToIndex(end_of_block) - AddressToIndex(start) + 1; - uint32_t set_count = CountSetBits() + count_in_block_after_end; - - for (uint32_t i = start.block_idx + 1; i <= last_addr.block_idx; ++i) { - // Set the count to the cummulative count so far. - counts_[i] = set_count; - - // Add a full block of set bits to the count. - set_count += Block::kBits; - } - } else { - // If the newly added bits are false, we just need to update the - // counts vector with the current size of the bitvector for all - // the newly added blocks. - if (new_blocks_size > old_blocks_size) { - uint32_t count = CountSetBits(); - for (uint32_t i = old_blocks_size; i < new_blocks_size; ++i) { - counts_[i] = count; - } - } - } - } else { - // Throw away all the bits after the new last bit. We do this to make - // future lookup, append and resize operations not have to worrying about - // trailing garbage bits in the last block. - BlockFromIndex(last_addr.block_idx).ClearAfter(last_addr.block_offset); - } - - // Actually update the size. - size_ = new_size; - } + void Resize(uint32_t new_size, bool filler = false); // Creates a BitVector of size |end| with the bits between |start| and |end| // filled by calling the filler function |f(index of bit)|. @@ -378,7 +289,7 @@ class BitVector { static BitVector Range(uint32_t start, uint32_t end, Filler f) { // Compute the block index and bitvector index where we start and end // working one block at a time. - uint32_t start_fast_block = BlockCeil(start); + uint32_t start_fast_block = BlockCount(start); uint32_t start_fast_idx = BlockToIndex(start_fast_block); BitVector bv(start, false); @@ -423,6 +334,10 @@ class BitVector { return bv; } + // Creates a BitVector of size |end| bit the bits between |start| and |end| + // filled with corresponding bits |this| BitVector. + BitVector IntersectRange(uint32_t range_start, uint32_t range_end) const; + // Requests the removal of unused capacity. // Matches the semantics of std::vector::shrink_to_fit. void ShrinkToFit() { @@ -469,7 +384,7 @@ class BitVector { static constexpr uint32_t ApproxBytesCost(uint32_t n) { // The two main things making up a bitvector is the cost of the blocks of // bits and the cost of the counts vector. - return BlockCeil(n) * Block::kBits + BlockCeil(n) * sizeof(uint32_t); + return BlockCount(n) * Block::kBits + BlockCount(n) * sizeof(uint32_t); } private: @@ -926,11 +841,10 @@ class BitVector { return idx / BitWord::kBits; } - // Returns the number of words which would be required to store a bit at - // |idx|. - static uint32_t WordCeil(uint32_t idx) { - // See |BlockCeil| for an explanation of this trick. - return (idx + BitWord::kBits - 1) / BitWord::kBits; + // Returns number of words (int64_t) required to store |bit_count| bits. + static uint32_t WordCount(uint32_t bit_count) { + // See |BlockCount| for an explanation of this trick. + return (bit_count + BitWord::kBits - 1) / BitWord::kBits; } static Address IndexToAddress(uint32_t idx) { @@ -949,17 +863,16 @@ class BitVector { addr.block_offset.bit_idx; } - // Rounds |idx| up to the nearest block boundary and returns the block - // index. If |idx| is already on a block boundary, the current block is - // returned. + // Returns number of blocks (arrays of 8 int64_t) required to store + // |bit_count| bits. // // This is useful to be able to find indices where "fast" algorithms can // start which work on entire blocks. - static constexpr uint32_t BlockCeil(uint32_t idx) { - // Adding |Block::kBits - 1| gives us a quick way to get the ceil. We + static constexpr uint32_t BlockCount(uint32_t bit_count) { + // Adding |Block::kBits - 1| gives us a quick way to get the count. We // do this instead of adding 1 at the end because that gives incorrect - // answers for index % Block::kBits == 0. - return (idx + Block::kBits - 1) / Block::kBits; + // answers for bit_count % Block::kBits == 0. + return (bit_count + Block::kBits - 1) / Block::kBits; } // Returns the index of the block which would store |idx|. diff --git a/src/trace_processor/containers/bit_vector_unittest.cc b/src/trace_processor/containers/bit_vector_unittest.cc index c2e44805f..dd8c9512c 100644 --- a/src/trace_processor/containers/bit_vector_unittest.cc +++ b/src/trace_processor/containers/bit_vector_unittest.cc @@ -452,6 +452,63 @@ TEST(BitVectorUnittest, IterateSetBitsStartsCorrectly) { ASSERT_FALSE(it); } +TEST(BitVectorUnittest, IntersectRange) { + BitVector bv = BitVector::Range(1, 20, [](uint32_t t) { return t % 2 == 0; }); + BitVector intersected = bv.IntersectRange(3, 10); + + ASSERT_EQ(intersected.IndexOfNthSet(0), 4u); + ASSERT_EQ(intersected.CountSetBits(), 3u); +} + +TEST(BitVectorUnittest, IntersectRangeFromStart) { + BitVector bv = BitVector::Range(1, 20, [](uint32_t t) { return t % 2 == 0; }); + BitVector intersected = bv.IntersectRange(0, 10); + + ASSERT_EQ(intersected.IndexOfNthSet(0), 2u); + ASSERT_EQ(intersected.CountSetBits(), 4u); +} + +TEST(BitVectorUnittest, IntersectRange2) { + BitVector bv{true, false, true, true, false, true}; + BitVector intersected = bv.IntersectRange(2, 4); + + ASSERT_EQ(intersected.IndexOfNthSet(0), 2u); +} + +TEST(BitVectorUnittest, IntersectRangeAfterWord) { + BitVector bv = + BitVector::Range(64 + 1, 64 + 20, [](uint32_t t) { return t % 2 == 0; }); + BitVector intersected = bv.IntersectRange(64 + 3, 64 + 10); + + ASSERT_EQ(intersected.IndexOfNthSet(0), 64 + 4u); + ASSERT_EQ(intersected.CountSetBits(), 3u); +} + +TEST(BitVectorUnittest, IntersectRangeSetBitsBeforeRange) { + BitVector bv = BitVector::Range(10, 30, [](uint32_t t) { return t < 15; }); + BitVector intersected = bv.IntersectRange(16, 50); + + ASSERT_FALSE(intersected.CountSetBits()); +} + +TEST(BitVectorUnittest, IntersectRangeSetBitOnBoundary) { + BitVector bv = BitVector(10, false); + bv.Set(5); + BitVector intersected = bv.IntersectRange(5, 20); + + ASSERT_EQ(intersected.CountSetBits(), 1u); + ASSERT_EQ(intersected.IndexOfNthSet(0), 5u); +} + +TEST(BitVectorUnittest, IntersectRangeStressTest) { + BitVector bv = + BitVector::Range(65, 1024 + 1, [](uint32_t t) { return t % 2 == 0; }); + BitVector intersected = bv.IntersectRange(30, 500); + + ASSERT_EQ(intersected.IndexOfNthSet(0), 66u); + ASSERT_EQ(intersected.CountSetBits(), 217u); +} + TEST(BitVectorUnittest, Range) { BitVector bv = BitVector::Range(1, 9, [](uint32_t t) { return t % 3 == 0; }); ASSERT_EQ(bv.size(), 9u); @@ -515,6 +572,22 @@ TEST(BitVectorUnittest, Builder) { 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; @@ -533,7 +606,7 @@ TEST(BitVectorUnittest, BuilderStressTest) { 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. @@ -550,6 +623,8 @@ TEST(BitVectorUnittest, BuilderStressTest) { 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/containers/row_map.cc b/src/trace_processor/containers/row_map.cc index b3d1d59b9..d00d4877a 100644 --- a/src/trace_processor/containers/row_map.cc +++ b/src/trace_processor/containers/row_map.cc @@ -15,6 +15,7 @@ */ #include "src/trace_processor/containers/row_map.h" +#include <unordered_set> #include "src/trace_processor/containers/row_map_algorithms.h" @@ -23,22 +24,19 @@ namespace trace_processor { namespace { -RowMap SelectRangeWithRange(uint32_t start, - uint32_t end, - uint32_t selector_start, - uint32_t selector_end) { - PERFETTO_DCHECK(start <= end); - PERFETTO_DCHECK(selector_start <= selector_end); - PERFETTO_DCHECK(selector_end <= end - start); +using Range = RowMap::Range; +using OutputIndex = RowMap::OutputIndex; +using Variant = std::variant<Range, BitVector, std::vector<OutputIndex>>; - return RowMap(start + selector_start, start + selector_end); +RowMap Select(Range range, Range selector) { + PERFETTO_DCHECK(selector.start <= selector.end); + PERFETTO_DCHECK(selector.end <= range.size()); + + return RowMap(range.start + selector.start, range.start + selector.end); } -RowMap SelectRangeWithBv(uint32_t start, - uint32_t end, - const BitVector& selector) { - PERFETTO_DCHECK(start <= end); - PERFETTO_DCHECK(selector.size() <= end - start); +RowMap Select(Range range, const BitVector& selector) { + PERFETTO_DCHECK(selector.size() <= range.size()); // If |start| == 0 and |selector.size()| <= |end - start| (which is a // precondition for this function), the BitVector we generate is going to be @@ -48,60 +46,52 @@ RowMap SelectRangeWithBv(uint32_t start, // SelectRows is called on all the table RowMaps with a BitVector. The self // RowMap will always be a range so we expect this case to be hit at least // once every filter operation. - if (start == 0u) + if (range.start == 0u) return RowMap(selector.Copy()); // We only need to resize to |start| + |selector.size()| as we know any rows // not covered by |selector| are going to be removed below. - BitVector bv(start, false); - bv.Resize(start + selector.size(), true); + BitVector bv(range.start, false); + bv.Resize(range.start + selector.size(), true); bv.UpdateSetBits(selector); return RowMap(std::move(bv)); } -RowMap SelectRangeWithIv(uint32_t start, - uint32_t end, - const std::vector<uint32_t>& selector) { - PERFETTO_DCHECK(start <= end); - +RowMap Select(Range range, const std::vector<OutputIndex>& selector) { std::vector<uint32_t> iv(selector.size()); for (uint32_t i = 0; i < selector.size(); ++i) { - PERFETTO_DCHECK(selector[i] < end - start); - iv[i] = selector[i] + start; + PERFETTO_DCHECK(selector[i] < range.size()); + iv[i] = selector[i] + range.start; } return RowMap(std::move(iv)); } -RowMap SelectBvWithRange(const BitVector& bv, - uint32_t selector_start, - uint32_t selector_end) { - PERFETTO_DCHECK(selector_start <= selector_end); - PERFETTO_DCHECK(selector_end <= bv.CountSetBits()); +RowMap Select(const BitVector& bv, Range selector) { + PERFETTO_DCHECK(selector.end <= bv.CountSetBits()); // If we're simply selecting every element in the bitvector, just // return a copy of the BitVector without iterating. BitVector ret = bv.Copy(); - if (selector_start == 0 && selector_end == bv.CountSetBits()) { + if (selector.start == 0 && selector.end == bv.CountSetBits()) { return RowMap(std::move(ret)); } for (auto it = ret.IterateSetBits(); it; it.Next()) { auto set_idx = it.ordinal(); - if (set_idx < selector_start || set_idx >= selector_end) + if (set_idx < selector.start || set_idx >= selector.end) it.Clear(); } return RowMap(std::move(ret)); } -RowMap SelectBvWithBv(const BitVector& bv, const BitVector& selector) { +RowMap Select(const BitVector& bv, const BitVector& selector) { BitVector ret = bv.Copy(); ret.UpdateSetBits(selector); return RowMap(std::move(ret)); } -RowMap SelectBvWithIv(const BitVector& bv, - const std::vector<uint32_t>& selector) { +RowMap Select(const BitVector& bv, const std::vector<uint32_t>& selector) { // The value of this constant was found by considering the benchmarks // |BM_SelectBvWithIvByConvertToIv| and |BM_SelectBvWithIvByIndexOfNthSet|. // @@ -130,21 +120,17 @@ RowMap SelectBvWithIv(const BitVector& bv, row_map_algorithms::SelectBvWithIvByIndexOfNthSet(bv, selector)); } -RowMap SelectIvWithRange(const std::vector<uint32_t>& iv, - uint32_t selector_start, - uint32_t selector_end) { - PERFETTO_DCHECK(selector_start <= selector_end); - PERFETTO_DCHECK(selector_end <= iv.size()); +RowMap Select(const std::vector<uint32_t>& iv, Range selector) { + PERFETTO_DCHECK(selector.end <= iv.size()); - std::vector<uint32_t> ret(selector_end - selector_start); - for (uint32_t i = selector_start; i < selector_end; ++i) { - ret[i - selector_start] = iv[i]; + std::vector<uint32_t> ret(selector.size()); + for (uint32_t i = selector.start; i < selector.end; ++i) { + ret[i - selector.start] = iv[i]; } return RowMap(std::move(ret)); } -RowMap SelectIvWithBv(const std::vector<uint32_t>& iv, - const BitVector& selector) { +RowMap Select(const std::vector<uint32_t>& iv, const BitVector& selector) { PERFETTO_DCHECK(selector.size() <= iv.size()); std::vector<uint32_t> copy = iv; @@ -158,83 +144,133 @@ RowMap SelectIvWithBv(const std::vector<uint32_t>& iv, return RowMap(std::move(copy)); } -RowMap SelectIvWithIv(const std::vector<uint32_t>& iv, - const std::vector<uint32_t>& selector) { +RowMap Select(const std::vector<uint32_t>& iv, + const std::vector<uint32_t>& selector) { return RowMap(row_map_algorithms::SelectIvWithIv(iv, selector)); } +Variant IntersectInternal(BitVector& first, const BitVector& second) { + for (auto set_bit = first.IterateSetBits(); set_bit; set_bit.Next()) { + if (!second.IsSet(set_bit.index())) + set_bit.Clear(); + } + return std::move(first); +} + +Variant IntersectInternal(Range first, Range second) { + // If both RowMaps have ranges, we can just take the smallest intersection + // of them as the new RowMap. + // We have this as an explicit fast path as this is very common for + // constraints on id and sorted columns to satisfy this condition. + OutputIndex start = std::max(first.start, second.start); + OutputIndex end = std::max(start, std::min(first.end, second.end)); + return Range{start, end}; +} + +Variant IntersectInternal(std::vector<OutputIndex>& first, + const std::vector<OutputIndex>& second) { + std::unordered_set<OutputIndex> lookup(second.begin(), second.end()); + first.erase(std::remove_if(first.begin(), first.end(), + [lookup](OutputIndex ind) { + return lookup.find(ind) == lookup.end(); + }), + first.end()); + return std::move(first); +} + +Variant IntersectInternal(Range range, const BitVector& bv) { + return bv.IntersectRange(range.start, range.end); +} + +Variant IntersectInternal(BitVector& bv, Range range) { + return IntersectInternal(range, bv); +} + +Variant IntersectInternal(const std::vector<OutputIndex>& index_vec, + const BitVector& bv) { + std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end()); + new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(), + [&bv](uint32_t i) { return !bv.IsSet(i); }), + new_vec.end()); + return std::move(new_vec); +} + +Variant IntersectInternal(const BitVector& bv, + const std::vector<OutputIndex>& index_vec) { + return IntersectInternal(index_vec, bv); +} + +Variant IntersectInternal(Range range, + const std::vector<OutputIndex>& index_vec) { + std::vector<OutputIndex> new_vec(index_vec.begin(), index_vec.end()); + new_vec.erase(std::remove_if(new_vec.begin(), new_vec.end(), + [range](uint32_t i) { + return i < range.start || i >= range.end; + }), + new_vec.end()); + return std::move(new_vec); +} + +Variant IntersectInternal(const std::vector<OutputIndex>& index_vec, + Range range) { + return IntersectInternal(range, index_vec); +} + } // namespace -RowMap::RowMap() : RowMap(0, 0) {} +RowMap::RowMap() : RowMap(Range()) {} RowMap::RowMap(uint32_t start, uint32_t end, OptimizeFor optimize_for) - : mode_(Mode::kRange), - start_index_(start), - end_index_(end), - optimize_for_(optimize_for) {} + : data_(Range{start, end}), optimize_for_(optimize_for) {} + +RowMap::RowMap(Variant def) : data_(std::move(def)) {} -RowMap::RowMap(BitVector bit_vector) - : mode_(Mode::kBitVector), bit_vector_(std::move(bit_vector)) {} +RowMap::RowMap(Range r) : data_(r) {} -RowMap::RowMap(std::vector<uint32_t> vec) - : mode_(Mode::kIndexVector), index_vector_(std::move(vec)) {} +// Creates a RowMap backed by a BitVector. +RowMap::RowMap(BitVector bit_vector) : data_(std::move(bit_vector)) {} + +// Creates a RowMap backed by an std::vector<uint32_t>. +RowMap::RowMap(IndexVector vec) : data_(vec) {} RowMap RowMap::Copy() const { - switch (mode_) { - case Mode::kRange: - return RowMap(start_index_, end_index_); - case Mode::kBitVector: - return RowMap(bit_vector_.Copy()); - case Mode::kIndexVector: - return RowMap(index_vector_); + if (auto* range = std::get_if<Range>(&data_)) { + return RowMap(*range); + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + return RowMap(bv->Copy()); } - PERFETTO_FATAL("For GCC"); + if (auto* vec = std::get_if<IndexVector>(&data_)) { + return RowMap(*vec); + } + NoVariantMatched(); } RowMap RowMap::SelectRowsSlow(const RowMap& selector) const { - // Pick the strategy based on the selector as there is more common code - // between selectors of the same mode than between the RowMaps being - // selected of the same mode. - switch (selector.mode_) { - case Mode::kRange: - switch (mode_) { - case Mode::kRange: - return SelectRangeWithRange(start_index_, end_index_, - selector.start_index_, - selector.end_index_); - case Mode::kBitVector: - return SelectBvWithRange(bit_vector_, selector.start_index_, - selector.end_index_); - case Mode::kIndexVector: - return SelectIvWithRange(index_vector_, selector.start_index_, - selector.end_index_); - } - break; - case Mode::kBitVector: - switch (mode_) { - case Mode::kRange: - return SelectRangeWithBv(start_index_, end_index_, - selector.bit_vector_); - case Mode::kBitVector: - return SelectBvWithBv(bit_vector_, selector.bit_vector_); - case Mode::kIndexVector: - return SelectIvWithBv(index_vector_, selector.bit_vector_); - } - break; - case Mode::kIndexVector: - switch (mode_) { - case Mode::kRange: - return SelectRangeWithIv(start_index_, end_index_, - selector.index_vector_); - case Mode::kBitVector: - return SelectBvWithIv(bit_vector_, selector.index_vector_); - case Mode::kIndexVector: - return SelectIvWithIv(index_vector_, selector.index_vector_); - } - break; - } - PERFETTO_FATAL("For GCC"); + return std::visit( + [](const auto& def, const auto& selector_def) { + return Select(def, selector_def); + }, + data_, selector.data_); +} + +void RowMap::Intersect(const RowMap& second) { + data_ = std::visit( + [](auto& def, auto& selector_def) { + return IntersectInternal(def, selector_def); + }, + data_, second.data_); } +RowMap::Iterator::Iterator(const RowMap* rm) : rm_(rm) { + if (auto* range = std::get_if<Range>(&rm_->data_)) { + ordinal_ = range->start; + return; + } + if (auto* bv = std::get_if<BitVector>(&rm_->data_)) { + set_bits_it_.reset(new BitVector::SetBitsIterator(bv->IterateSetBits())); + return; + } +} } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/containers/row_map.h b/src/trace_processor/containers/row_map.h index 8ab3915bf..283455c29 100644 --- a/src/trace_processor/containers/row_map.h +++ b/src/trace_processor/containers/row_map.h @@ -21,6 +21,7 @@ #include <memory> #include <optional> +#include <variant> #include <vector> #include "perfetto/base/logging.h" @@ -73,56 +74,25 @@ namespace trace_processor { // more efficient than a BitVector; in this case, we will make a best effort // switch to it but the cases where this happens is not precisely defined. class RowMap { - private: - // We need to declare these iterator classes before RowMap::Iterator as it - // depends on them. However, we don't want to make these public so keep them - // under a special private section. - - // Iterator for ranged mode of RowMap. - // This class should act as a drop-in replacement for - // BitVector::SetBitsIterator. - class RangeIterator { - public: - RangeIterator(const RowMap* rm) : rm_(rm), index_(rm->start_index_) {} - - void Next() { ++index_; } - - operator bool() const { return index_ < rm_->end_index_; } - - uint32_t index() const { return index_; } - - uint32_t ordinal() const { return index_ - rm_->start_index_; } - - private: - const RowMap* rm_ = nullptr; - uint32_t index_ = 0; - }; - - // Iterator for index vector mode of RowMap. - // This class should act as a drop-in replacement for - // BitVector::SetBitsIterator. - class IndexVectorIterator { - public: - IndexVectorIterator(const RowMap* rm) : rm_(rm) {} - - void Next() { ++ordinal_; } - - operator bool() const { return ordinal_ < rm_->index_vector_.size(); } + public: + using InputRow = uint32_t; + using OutputIndex = uint32_t; + using IndexVector = std::vector<OutputIndex>; - uint32_t index() const { return rm_->index_vector_[ordinal_]; } + struct Range { + Range(OutputIndex start_index, OutputIndex end_index) + : start(start_index), end(end_index) {} + Range() : start(0), end(0) {} - uint32_t ordinal() const { return ordinal_; } + OutputIndex start = 0; // This is an inclusive index. + OutputIndex end = 0; // This is an exclusive index. - private: - const RowMap* rm_ = nullptr; - uint32_t ordinal_ = 0; + uint32_t size() const { + PERFETTO_DCHECK(end >= start); + return end - start; + } }; - public: - // Input type. - using InputRow = uint32_t; - using OutputIndex = uint32_t; - // Allows efficient iteration over the rows of a RowMap. // // Note: you should usually prefer to use the methods on RowMap directly (if @@ -130,87 +100,72 @@ class RowMap { // of the RowMap on every method call. class Iterator { public: - Iterator(const RowMap* rm) : rm_(rm) { - switch (rm->mode_) { - case Mode::kRange: - range_it_.reset(new RangeIterator(rm)); - break; - case Mode::kBitVector: - set_bits_it_.reset( - new BitVector::SetBitsIterator(rm->bit_vector_.IterateSetBits())); - break; - case Mode::kIndexVector: - iv_it_.reset(new IndexVectorIterator(rm)); - break; - } - } + explicit Iterator(const RowMap* rm); Iterator(Iterator&&) noexcept = default; Iterator& operator=(Iterator&&) = default; // Forwards the iterator to the next row of the RowMap. void Next() { - switch (rm_->mode_) { - case Mode::kRange: - range_it_->Next(); - break; - case Mode::kBitVector: - set_bits_it_->Next(); - break; - case Mode::kIndexVector: - iv_it_->Next(); - break; + if (std::get_if<Range>(&rm_->data_)) { + ++ordinal_; + } else if (std::get_if<BitVector>(&rm_->data_)) { + set_bits_it_->Next(); + } else if (std::get_if<IndexVector>(&rm_->data_)) { + ++ordinal_; } } // Returns if the iterator is still valid. operator bool() const { - switch (rm_->mode_) { - case Mode::kRange: - return *range_it_; - case Mode::kBitVector: - return *set_bits_it_; - case Mode::kIndexVector: - return *iv_it_; + if (auto* range = std::get_if<Range>(&rm_->data_)) { + return ordinal_ < range->end; + } + if (std::get_if<BitVector>(&rm_->data_)) { + return bool(*set_bits_it_); + } + if (auto* vec = std::get_if<IndexVector>(&rm_->data_)) { + return ordinal_ < vec->size(); } - PERFETTO_FATAL("For GCC"); + PERFETTO_FATAL("Didn't match any variant type."); } // Returns the index pointed to by this iterator. OutputIndex index() const { - switch (rm_->mode_) { - case Mode::kRange: - return range_it_->index(); - case Mode::kBitVector: - return set_bits_it_->index(); - case Mode::kIndexVector: - return iv_it_->index(); + if (std::get_if<Range>(&rm_->data_)) { + return ordinal_; } - PERFETTO_FATAL("For GCC"); + if (std::get_if<BitVector>(&rm_->data_)) { + return set_bits_it_->index(); + } + if (auto* vec = std::get_if<IndexVector>(&rm_->data_)) { + return (*vec)[ordinal_]; + } + PERFETTO_FATAL("Didn't match any variant type."); } // Returns the row of the index the iterator points to. InputRow row() const { - switch (rm_->mode_) { - case Mode::kRange: - return range_it_->ordinal(); - case Mode::kBitVector: - return set_bits_it_->ordinal(); - case Mode::kIndexVector: - return iv_it_->ordinal(); + if (auto* range = std::get_if<Range>(&rm_->data_)) { + return ordinal_ - range->start; + } + if (std::get_if<BitVector>(&rm_->data_)) { + return set_bits_it_->ordinal(); } - PERFETTO_FATAL("For GCC"); + if (std::get_if<IndexVector>(&rm_->data_)) { + return ordinal_; + } + PERFETTO_FATAL("Didn't match any variant type."); } private: Iterator(const Iterator&) = delete; Iterator& operator=(const Iterator&) = delete; - // Only one of the below will be non-null depending on the mode of the - // RowMap. - std::unique_ptr<RangeIterator> range_it_; + // Ordinal will not be used for BitVector based RowMap. + uint32_t ordinal_ = 0; + // Not nullptr for BitVector based RowMap. std::unique_ptr<BitVector::SetBitsIterator> set_bits_it_; - std::unique_ptr<IndexVectorIterator> iv_it_; const RowMap* rm_ = nullptr; }; @@ -228,15 +183,15 @@ class RowMap { // Creates a RowMap containing the range of indices between |start| and |end| // i.e. all indices between |start| (inclusive) and |end| (exclusive). - explicit RowMap(OutputIndex start, - OutputIndex end, - OptimizeFor optimize_for = OptimizeFor::kMemory); + RowMap(OutputIndex start, + OutputIndex end, + OptimizeFor optimize_for = OptimizeFor::kMemory); // Creates a RowMap backed by a BitVector. - explicit RowMap(BitVector bit_vector); + explicit RowMap(BitVector); // Creates a RowMap backed by an std::vector<uint32_t>. - explicit RowMap(std::vector<OutputIndex> vec); + explicit RowMap(IndexVector); RowMap(const RowMap&) noexcept = delete; RowMap& operator=(const RowMap&) = delete; @@ -259,15 +214,16 @@ class RowMap { // Returns the size of the RowMap; that is the number of indices in the // RowMap. uint32_t size() const { - switch (mode_) { - case Mode::kRange: - return end_index_ - start_index_; - case Mode::kBitVector: - return bit_vector_.CountSetBits(); - case Mode::kIndexVector: - return static_cast<uint32_t>(index_vector_.size()); + if (auto* range = std::get_if<Range>(&data_)) { + return range->size(); } - PERFETTO_FATAL("For GCC"); + if (auto* bv = std::get_if<BitVector>(&data_)) { + return bv->CountSetBits(); + } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + return static_cast<uint32_t>(vec->size()); + } + NoVariantMatched(); } // Returns whether this rowmap is empty. @@ -275,57 +231,51 @@ class RowMap { // Returns the index at the given |row|. OutputIndex Get(InputRow row) const { - PERFETTO_DCHECK(row < size()); - switch (mode_) { - case Mode::kRange: - return GetRange(row); - case Mode::kBitVector: - return GetBitVector(row); - case Mode::kIndexVector: - return GetIndexVector(row); + if (auto* range = std::get_if<Range>(&data_)) { + return GetRange(*range, row); + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + return GetBitVector(*bv, row); } - PERFETTO_FATAL("For GCC"); + if (auto* vec = std::get_if<IndexVector>(&data_)) { + return GetIndexVector(*vec, row); + } + NoVariantMatched(); } // Returns whether the RowMap contains the given index. bool Contains(OutputIndex index) const { - switch (mode_) { - case Mode::kRange: { - return index >= start_index_ && index < end_index_; - } - case Mode::kBitVector: { - return index < bit_vector_.size() && bit_vector_.IsSet(index); - } - case Mode::kIndexVector: { - auto it = std::find(index_vector_.begin(), index_vector_.end(), index); - return it != index_vector_.end(); - } + if (auto* range = std::get_if<Range>(&data_)) { + return index >= range->start && index < range->end; + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + return index < bv->size() && bv->IsSet(index); + } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + return std::find(vec->begin(), vec->end(), index) != vec->end(); } - PERFETTO_FATAL("For GCC"); + NoVariantMatched(); } // Returns the first row of the given |index| in the RowMap. std::optional<InputRow> RowOf(OutputIndex index) const { - switch (mode_) { - case Mode::kRange: { - if (index < start_index_ || index >= end_index_) - return std::nullopt; - return index - start_index_; - } - case Mode::kBitVector: { - return index < bit_vector_.size() && bit_vector_.IsSet(index) - ? std::make_optional(bit_vector_.CountSetBits(index)) - : std::nullopt; - } - case Mode::kIndexVector: { - auto it = std::find(index_vector_.begin(), index_vector_.end(), index); - return it != index_vector_.end() - ? std::make_optional(static_cast<InputRow>( - std::distance(index_vector_.begin(), it))) - : std::nullopt; - } + if (auto* range = std::get_if<Range>(&data_)) { + if (index < range->start || index >= range->end) + return std::nullopt; + return index - range->start; + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + return index < bv->size() && bv->IsSet(index) + ? std::make_optional(bv->CountSetBits(index)) + : std::nullopt; + } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + auto it = std::find(vec->begin(), vec->end(), index); + return it != vec->end() ? std::make_optional(static_cast<InputRow>( + std::distance(vec->begin(), it))) + : std::nullopt; } - PERFETTO_FATAL("For GCC"); + NoVariantMatched(); } // Performs an ordered insert of the index into the current RowMap @@ -343,34 +293,36 @@ class RowMap { // the RowMap is in range or BitVector mode but is a required condition for // IndexVector mode. void Insert(OutputIndex index) { - switch (mode_) { - case Mode::kRange: - if (index == end_index_) { - // Fast path: if we're just appending to the end of the range, we can - // stay in range mode and just bump the end index. - end_index_++; - } else { - // Slow path: the insert is somewhere else other than the end. This - // means we need to switch to using a BitVector instead. - bit_vector_.Resize(start_index_, false); - bit_vector_.Resize(end_index_, true); - *this = RowMap(std::move(bit_vector_)); - - InsertIntoBitVector(index); - } - break; - case Mode::kBitVector: - InsertIntoBitVector(index); - break; - case Mode::kIndexVector: { - PERFETTO_DCHECK( - std::is_sorted(index_vector_.begin(), index_vector_.end())); - auto it = - std::upper_bound(index_vector_.begin(), index_vector_.end(), index); - index_vector_.insert(it, index); - break; + if (auto* range = std::get_if<Range>(&data_)) { + if (index == range->end) { + // Fast path: if we're just appending to the end + // of the range, we can stay in range mode and + // just bump the end index. + range->end++; + return; } + + // Slow path: the insert is somewhere else other + // than the end. This means we need to switch to + // using a BitVector instead. + BitVector bv; + bv.Resize(range->start, false); + bv.Resize(range->end, true); + InsertIntoBitVector(bv, index); + data_ = std::move(bv); + return; + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + InsertIntoBitVector(*bv, index); + return; + } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + PERFETTO_DCHECK(std::is_sorted(vec->begin(), vec->end())); + auto it = std::upper_bound(vec->begin(), vec->end(), index); + vec->insert(it, index); + return; } + NoVariantMatched(); } // Updates this RowMap by 'picking' the indices given by |picker|. @@ -413,22 +365,7 @@ class RowMap { // if (start_index <= idx && idx < end_index) // continue; // Remove(idx) - void Intersect(uint32_t start_index, uint32_t end_index) { - if (mode_ == Mode::kRange) { - // If both RowMaps have ranges, we can just take the smallest intersection - // of them as the new RowMap. - // We have this as an explicit fast path as this is very common for - // constraints on id and sorted columns to satisfy this condition. - start_index_ = std::max(start_index_, start_index); - end_index_ = std::max(start_index_, std::min(end_index_, end_index)); - return; - } - - // TODO(lalitm): improve efficiency of this if we end up needing it. - Filter([start_index, end_index](OutputIndex index) { - return index >= start_index && index < end_index; - }); - } + void Intersect(const RowMap& second); // Intersects this RowMap with |index|. If this RowMap contained |index|, then // it will *only* contain |index|. Otherwise, it will be empty. @@ -444,71 +381,82 @@ class RowMap { void Clear() { *this = RowMap(); } template <typename Comparator = bool(uint32_t, uint32_t)> - void StableSort(std::vector<uint32_t>* out, Comparator c) const { - switch (mode_) { - case Mode::kRange: - std::stable_sort(out->begin(), out->end(), - [this, c](uint32_t a, uint32_t b) { - return c(GetRange(a), GetRange(b)); - }); - break; - case Mode::kBitVector: - std::stable_sort(out->begin(), out->end(), - [this, c](uint32_t a, uint32_t b) { - return c(GetBitVector(a), GetBitVector(b)); - }); - break; - case Mode::kIndexVector: - std::stable_sort(out->begin(), out->end(), - [this, c](uint32_t a, uint32_t b) { - return c(GetIndexVector(a), GetIndexVector(b)); - }); - break; + void StableSort(IndexVector* out, Comparator c) const { + if (auto* range = std::get_if<Range>(&data_)) { + std::stable_sort(out->begin(), out->end(), + [range, c](uint32_t a, uint32_t b) { + return c(GetRange(*range, a), GetRange(*range, b)); + }); + return; + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + std::stable_sort(out->begin(), out->end(), + [&bv, c](uint32_t a, uint32_t b) { + return c(GetBitVector(*bv, a), GetBitVector(*bv, b)); + }); + return; + } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + std::stable_sort( + out->begin(), out->end(), [vec, c](uint32_t a, uint32_t b) { + return c(GetIndexVector(*vec, a), GetIndexVector(*vec, b)); + }); + return; } + NoVariantMatched(); } // Filters the indices in |out| by keeping those which meet |p|. template <typename Predicate = bool(OutputIndex)> void Filter(Predicate p) { - switch (mode_) { - case Mode::kRange: - FilterRange(p); - break; - case Mode::kBitVector: { - for (auto it = bit_vector_.IterateSetBits(); it; it.Next()) { - if (!p(it.index())) - it.Clear(); - } - break; - } - case Mode::kIndexVector: { - auto ret = std::remove_if(index_vector_.begin(), index_vector_.end(), - [p](uint32_t i) { return !p(i); }); - index_vector_.erase(ret, index_vector_.end()); - break; + if (auto* range = std::get_if<Range>(&data_)) { + data_ = FilterRange(p, *range); + return; + } + if (auto* bv = std::get_if<BitVector>(&data_)) { + for (auto it = bv->IterateSetBits(); it; it.Next()) { + if (!p(it.index())) + it.Clear(); } + return; } + if (auto* vec = std::get_if<IndexVector>(&data_)) { + auto ret = std::remove_if(vec->begin(), vec->end(), + [p](uint32_t i) { return !p(i); }); + vec->erase(ret, vec->end()); + return; + } + NoVariantMatched(); } // Returns the iterator over the rows in this RowMap. Iterator IterateRows() const { return Iterator(this); } // Returns if the RowMap is internally represented using a range. - bool IsRange() const { return mode_ == Mode::kRange; } + bool IsRange() const { return std::holds_alternative<Range>(data_); } + + // Returns if the RowMap is internally represented using a BitVector. + bool IsBitVector() const { return std::holds_alternative<BitVector>(data_); } + + // Returns if the RowMap is internally represented using an index vector. + bool IsIndexVector() const { + return std::holds_alternative<IndexVector>(data_); + } private: - enum class Mode { - kRange, - kBitVector, - kIndexVector, - }; + using Variant = std::variant<Range, BitVector, IndexVector>; + + explicit RowMap(Range); + + explicit RowMap(Variant); + // TODO(lalitm): remove this when the coupling between RowMap and // ColumnStorage Selector is broken (after filtering is moved out of here). friend class ColumnStorageOverlay; template <typename Predicate> - void FilterRange(Predicate p) { - uint32_t count = end_index_ - start_index_; + Variant FilterRange(Predicate p, Range r) { + uint32_t count = r.size(); // Optimization: if we are only going to scan a few indices, it's not // worth the haslle of working with a BitVector. @@ -517,7 +465,7 @@ class RowMap { // Optimization: weif the cost of a BitVector is more than the highest // possible cost an index vector could have, use the index vector. - uint32_t bit_vector_cost = BitVector::ApproxBytesCost(end_index_); + uint32_t bit_vector_cost = BitVector::ApproxBytesCost(r.end); uint32_t index_vector_cost_ub = sizeof(uint32_t) * count; // If either of the conditions hold which make it better to use an @@ -527,7 +475,7 @@ class RowMap { optimize_for_ == OptimizeFor::kLookupSpeed) { // Try and strike a good balance between not making the vector too // big and good performance. - std::vector<uint32_t> iv(std::min(kSmallRangeLimit, count)); + IndexVector iv(std::min(kSmallRangeLimit, count)); uint32_t out_i = 0; for (uint32_t i = 0; i < count; ++i) { @@ -537,8 +485,8 @@ class RowMap { // We keep this branch free by always writing the index but only // incrementing the out index if the return value is true. - bool value = p(i + start_index_); - iv[out_i] = i + start_index_; + bool value = p(i + r.start); + iv[out_i] = i + r.start; out_i += value; } @@ -546,58 +494,44 @@ class RowMap { iv.resize(out_i); iv.shrink_to_fit(); - *this = RowMap(std::move(iv)); - return; + return std::move(iv); } // Otherwise, create a bitvector which spans the full range using // |p| as the filler for the bits between start and end. - *this = RowMap(BitVector::Range(start_index_, end_index_, p)); - } - - void InsertIntoBitVector(uint32_t row) { - PERFETTO_DCHECK(mode_ == Mode::kBitVector); - - // If we're adding a row to precisely the end of the BitVector, just append - // true instead of resizing and then setting. - if (row == bit_vector_.size()) { - bit_vector_.AppendTrue(); - return; - } - - if (row > bit_vector_.size()) { - bit_vector_.Resize(row + 1, false); - } - bit_vector_.Set(row); + return BitVector::Range(r.start, r.end, p); } - PERFETTO_ALWAYS_INLINE OutputIndex GetRange(InputRow row) const { - PERFETTO_DCHECK(mode_ == Mode::kRange); - return start_index_ + row; + PERFETTO_ALWAYS_INLINE static OutputIndex GetRange(Range r, InputRow row) { + return r.start + row; } - PERFETTO_ALWAYS_INLINE OutputIndex GetBitVector(uint32_t row) const { - PERFETTO_DCHECK(mode_ == Mode::kBitVector); - return bit_vector_.IndexOfNthSet(row); + PERFETTO_ALWAYS_INLINE static OutputIndex GetBitVector(const BitVector& bv, + uint32_t row) { + return bv.IndexOfNthSet(row); } - PERFETTO_ALWAYS_INLINE OutputIndex GetIndexVector(uint32_t row) const { - PERFETTO_DCHECK(mode_ == Mode::kIndexVector); - return index_vector_[row]; + PERFETTO_ALWAYS_INLINE static OutputIndex GetIndexVector( + const IndexVector& vec, + uint32_t row) { + return vec[row]; } RowMap SelectRowsSlow(const RowMap& selector) const; - Mode mode_ = Mode::kRange; - - // Only valid when |mode_| == Mode::kRange. - OutputIndex start_index_ = 0; // This is an inclusive index. - OutputIndex end_index_ = 0; // This is an exclusive index. - - // Only valid when |mode_| == Mode::kBitVector. - BitVector bit_vector_; + static void InsertIntoBitVector(BitVector& bv, OutputIndex row) { + if (row == bv.size()) { + bv.AppendTrue(); + return; + } + if (row > bv.size()) + bv.Resize(row + 1, false); + bv.Set(row); + } - // Only valid when |mode_| == Mode::kIndexVector. - std::vector<OutputIndex> index_vector_; + PERFETTO_NORETURN void NoVariantMatched() const { + PERFETTO_FATAL("Didn't match any variant type."); + } + Variant data_; OptimizeFor optimize_for_ = OptimizeFor::kMemory; }; diff --git a/src/trace_processor/containers/row_map_unittest.cc b/src/trace_processor/containers/row_map_unittest.cc index 6e1ca1113..7f919bdb8 100644 --- a/src/trace_processor/containers/row_map_unittest.cc +++ b/src/trace_processor/containers/row_map_unittest.cc @@ -25,6 +25,191 @@ namespace perfetto { namespace trace_processor { namespace { +TEST(RowMapUnittest, SingleRow) { + RowMap rm(10, 20); + RowMap rm_row = rm.SingleRow(15u); + ASSERT_EQ(rm_row.size(), 1u); + ASSERT_TRUE(rm_row.Contains(15)); + ASSERT_FALSE(rm_row.Contains(11)); +} + +TEST(RowMapUnittest, CopyRange) { + RowMap rm(10, 20); + RowMap rm_copy = rm.Copy(); + ASSERT_EQ(rm_copy.size(), 10u); +} + +TEST(RowMapUnittest, CopyBitVector) { + RowMap rm(BitVector{true, false, false, false, true, true}); + RowMap rm_copy = rm.Copy(); + ASSERT_EQ(rm_copy.size(), 3u); +} + +TEST(RowMapUnittest, CopyIndexVector) { + RowMap rm(std::vector<uint32_t>{10, 17, 20, 21}); + RowMap rm_copy = rm.Copy(); + ASSERT_EQ(rm_copy.size(), 4u); +} + +TEST(RowMapUnittest, GetFromRange) { + RowMap rm(10, 20); + ASSERT_EQ(rm.Get(5), 15u); +} + +TEST(RowMapUnittest, GetFromBitVector) { + RowMap rm(BitVector{true, false, false, false, true, true}); + ASSERT_EQ(rm.Get(1), 4u); +} + +TEST(RowMapUnittest, GetFromIndexVector) { + RowMap rm(std::vector<uint32_t>{10, 17, 20, 21}); + ASSERT_EQ(rm.Get(1), 17u); +} + +TEST(RowMapUnittest, ContainsFromRange) { + RowMap rm(10, 20); + ASSERT_FALSE(rm.Contains(5)); + ASSERT_TRUE(rm.Contains(15)); +} + +TEST(RowMapUnittest, ContainsFromBitVector) { + RowMap rm(BitVector{true, false, false, false, true, true}); + ASSERT_FALSE(rm.Contains(3)); + ASSERT_TRUE(rm.Contains(5)); +} + +TEST(RowMapUnittest, ContainsFromIndexVector) { + RowMap rm(std::vector<uint32_t>{10, 17, 20, 21}); + ASSERT_FALSE(rm.Contains(5)); + ASSERT_TRUE(rm.Contains(10)); +} + +TEST(RowMapUnittest, RowOfRange) { + RowMap rm(10, 20); + ASSERT_EQ(rm.RowOf(15).value(), 5u); + ASSERT_EQ(rm.RowOf(5), std::nullopt); +} + +TEST(RowMapUnittest, RowOfBitVector) { + RowMap rm(BitVector{true, false, false, false, true, true}); + ASSERT_EQ(rm.RowOf(4), 1u); + ASSERT_EQ(rm.RowOf(1), std::nullopt); +} + +TEST(RowMapUnittest, RowOfIndexVector) { + RowMap rm(std::vector<uint32_t>{10, 17, 20, 21}); + ASSERT_EQ(rm.RowOf(17), 1u); + ASSERT_EQ(rm.RowOf(5), std::nullopt); +} + +TEST(RowMapUnittest, InsertIntoRangeAtTheEnd) { + RowMap rm(10, 20); + rm.Insert(21); + ASSERT_EQ(rm.size(), 11u); + ASSERT_TRUE(rm.Contains(21)); +} + +TEST(RowMapUnittest, InsertIntoRange) { + RowMap rm(10, 20); + rm.Insert(25); + ASSERT_EQ(rm.size(), 11u); + ASSERT_TRUE(rm.Contains(25)); +} + +TEST(RowMapUnittest, InsertIntoBitVector) { + RowMap rm(BitVector{true, false, false, false, true, true}); + rm.Insert(25); + ASSERT_EQ(rm.size(), 4u); + ASSERT_TRUE(rm.Contains(25)); +} + +TEST(RowMapUnittest, InsertIntoIndexVector) { + RowMap rm(std::vector<uint32_t>{10, 17, 20, 21}); + rm.Insert(25); + ASSERT_EQ(rm.size(), 5u); + ASSERT_TRUE(rm.Contains(25)); +} + +TEST(RowMapUnittest, SelectRowsFromRangeWithRange) { + RowMap rm(10, 20); + + RowMap selector(4, 8); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 4u); + ASSERT_EQ(selected.Get(0), 14u); +} + +TEST(RowMapUnittest, SelectRowsFromRangeWithBV) { + RowMap rm(10, 20); + // BitVector with values at 16, 18, 20 and so on. + RowMap selector( + BitVector::Range(4, 8, [](uint32_t x) { return x % 2 == 0; })); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 14u); +} + +TEST(RowMapUnittest, SelectRowsFromRangeWithIV) { + RowMap rm(10, 20); + RowMap selector(std::vector<uint32_t>{4, 6}); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 14u); +} + +TEST(RowMapUnittest, SelectRowsFromBVWithRange) { + RowMap rm(BitVector::Range(10, 50, [](uint32_t x) { return x % 2 == 0; })); + + RowMap selector(4, 8); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 4u); + ASSERT_EQ(selected.Get(0), 18u); +} + +TEST(RowMapUnittest, SelectRowsFromBVWithBV) { + RowMap rm(BitVector::Range(10, 50, [](uint32_t x) { return x % 2 == 0; })); + // BitVector with values at 16, 18, 20 and so on. + RowMap selector( + BitVector::Range(4, 8, [](uint32_t x) { return x % 2 == 0; })); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 18u); +} + +TEST(RowMapUnittest, SelectRowsFromBVWithIV) { + RowMap rm(BitVector::Range(10, 50, [](uint32_t x) { return x % 2 == 0; })); + RowMap selector(std::vector<uint32_t>{4, 6}); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 18u); +} + +TEST(RowMapUnittest, SelectRowsFromIVWithRange) { + RowMap rm(std::vector<uint32_t>{10, 12, 14, 16, 18, 20, 22, 24}); + + RowMap selector(4, 8); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 4u); + ASSERT_EQ(selected.Get(0), 18u); +} + +TEST(RowMapUnittest, SelectRowsFromIVWithBV) { + RowMap rm(std::vector<uint32_t>{10, 12, 14, 16, 18, 20, 22, 24}); + RowMap selector( + BitVector::Range(4, 8, [](uint32_t x) { return x % 2 == 0; })); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 18u); +} + +TEST(RowMapUnittest, SelectRowsFromIVWithIV) { + RowMap rm(std::vector<uint32_t>{10, 12, 14, 16, 18, 20, 22, 24}); + RowMap selector(std::vector<uint32_t>{4, 6}); + RowMap selected = rm.SelectRows(selector); + ASSERT_EQ(selected.size(), 2u); + ASSERT_EQ(selected.Get(0), 18u); +} + TEST(RowMapUnittest, SmokeRange) { RowMap rm(30, 47); @@ -327,22 +512,86 @@ TEST(RowMapUnittest, IntersectSingleAbsent) { ASSERT_EQ(rm.size(), 0u); } -TEST(RowMapUnittest, IntersectManyRange) { +TEST(RowMapUnittest, IntersectRangeWithRange) { RowMap rm(3, 7); - rm.Intersect(2, 4); + RowMap sec(2, 4); + rm.Intersect(sec); ASSERT_EQ(rm.size(), 1u); ASSERT_EQ(rm.Get(0u), 3u); } -TEST(RowMapUnittest, IntersectManyIv) { - RowMap rm(std::vector<uint32_t>{3u, 2u, 0u, 1u, 1u, 3u}); - rm.Intersect(2, 4); +TEST(RowMapUnittest, IntersectRangeWithBV) { + RowMap rm(2, 4); + RowMap sec(BitVector{true, false, true, true, false, true}); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 2u); + ASSERT_EQ(rm.Get(0), 2u); +} + +TEST(RowMapUnittest, IntersectRangeWithIV) { + RowMap rm(2, 10); + RowMap sec(std::vector<uint32_t>{0, 2, 5}); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 2u); + ASSERT_EQ(rm.Get(0u), 2u); +} + +TEST(RowMapUnittest, IntersectBVWithRange) { + RowMap rm(BitVector{true, false, true, true, false, true}); + RowMap sec(2, 4); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 2u); + ASSERT_EQ(rm.Get(0), 2u); +} + +TEST(RowMapUnittest, IntersectBVWithBV) { + RowMap rm(BitVector{true, false, true, true, false, true}); + RowMap sec(BitVector{false, true, true, false, false, true, true}); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 2u); + ASSERT_EQ(rm.Get(0), 2u); +} + +TEST(RowMapUnittest, IntersectBVWithIV) { + RowMap rm(BitVector{true, false, true, true, false, true}); + RowMap sec(std::vector<uint32_t>{0, 2, 5}); + rm.Intersect(sec); ASSERT_EQ(rm.size(), 3u); - ASSERT_EQ(rm.Get(0u), 3u); - ASSERT_EQ(rm.Get(1u), 2u); - ASSERT_EQ(rm.Get(2u), 3u); + ASSERT_EQ(rm.Get(0), 0u); +} + +TEST(RowMapUnittest, IntersectIVWithRange) { + RowMap rm(std::vector<uint32_t>{0, 2, 5}); + RowMap sec(2, 10); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 2u); + ASSERT_EQ(rm.Get(0u), 2u); +} + +TEST(RowMapUnittest, IntersectIVWithBV) { + RowMap rm(std::vector<uint32_t>{0, 2, 5}); + RowMap sec(BitVector{true, false, true, true, false, true}); + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 3u); + ASSERT_EQ(rm.Get(0), 0u); +} + +TEST(RowMapUnittest, IntersectIVWithIV) { + RowMap rm(std::vector<uint32_t>{0, 2, 5}); + RowMap sec(std::vector<uint32_t>{1, 2, 6}); + + rm.Intersect(sec); + + ASSERT_EQ(rm.size(), 1u); + ASSERT_EQ(rm.Get(0u), 2u); } } // namespace diff --git a/src/trace_processor/containers/string_pool.cc b/src/trace_processor/containers/string_pool.cc index ab0b42a97..3dacca2de 100644 --- a/src/trace_processor/containers/string_pool.cc +++ b/src/trace_processor/containers/string_pool.cc @@ -25,23 +25,6 @@ namespace perfetto { namespace trace_processor { -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr size_t StringPool::kNumBlockIndexBits; -// static -constexpr size_t StringPool::kNumBlockOffsetBits; -// static -constexpr size_t StringPool::kLargeStringFlagBitMask; -// static -constexpr size_t StringPool::kBlockOffsetBitMask; -// static -constexpr size_t StringPool::kBlockIndexBitMask; -// static -constexpr size_t StringPool::kBlockSizeBytes; -// static -constexpr size_t StringPool::kMinLargeStringSizeBytes; -#endif - StringPool::StringPool() { static_assert( StringPool::kMinLargeStringSizeBytes <= StringPool::kBlockSizeBytes + 1, diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn index bfe5e8a37..043b2af0e 100644 --- a/src/trace_processor/db/BUILD.gn +++ b/src/trace_processor/db/BUILD.gn @@ -12,6 +12,7 @@ # 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("db") { @@ -19,10 +20,22 @@ source_set("db") { "base_id.h", "column.cc", "column.h", + "column_overlay.cc", + "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.cc", + "storage_overlay.h", + "storage_variants.h", "table.cc", "table.h", "typed_column.h", @@ -40,16 +53,21 @@ source_set("db") { ] } +perfetto_tp_tables("view_unittest") { + sources = [ "view_unittest.py" ] +} + perfetto_unittest_source_set("unittests") { testonly = true sources = [ "column_storage_overlay_unittest.cc", "compare_unittest.cc", - "table_unittest.cc", + "storage_unittest.cc", "view_unittest.cc", ] deps = [ ":db", + ":view_unittest", "../../../gn:default_deps", "../../../gn:gtest_and_gmock", "../../base", diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h index 04425b9d9..f1adb96f3 100644 --- a/src/trace_processor/db/column.h +++ b/src/trace_processor/db/column.h @@ -367,6 +367,9 @@ class Column { // Returns the type of this Column in terms of SqlValue::Type. SqlValue::Type type() const { return ToSqlValueType(type_); } + // Returns the type of this Column in terms of ColumnType. + ColumnType col_type() const { return type_; } + // Test the type of this Column. template <typename T> bool IsColumnType() const { @@ -382,6 +385,9 @@ class Column { // Returns true if this column is a sorted column. bool IsSorted() const { return IsSorted(flags_); } + // Returns true if this column is a dense column. + bool IsDense() const { return IsDense(flags_); } + // Returns true if this column is a set id column. // Public for testing. bool IsSetId() const { return IsSetId(flags_); } @@ -439,31 +445,28 @@ class Column { return IsFlagsAndTypeValid(flags, ColumnTypeHelper<T>::ToColumnType()); } - protected: template <typename T> using stored_type = typename tc_internal::TypeHandler<T>::stored_type; // Returns the backing sparse vector cast to contain data of type T. // Should only be called when |type_| == ToColumnType<T>(). template <typename T> - ColumnStorage<stored_type<T>>* mutable_storage() { + const ColumnStorage<stored_type<T>>& storage() const { PERFETTO_DCHECK(ColumnTypeHelper<T>::ToColumnType() == type_); PERFETTO_DCHECK(tc_internal::TypeHandler<T>::is_optional == IsNullable()); - return static_cast<ColumnStorage<stored_type<T>>*>(storage_); + return *static_cast<ColumnStorage<stored_type<T>>*>(storage_); } + protected: // Returns the backing sparse vector cast to contain data of type T. // Should only be called when |type_| == ToColumnType<T>(). template <typename T> - const ColumnStorage<stored_type<T>>& storage() const { + ColumnStorage<stored_type<T>>* mutable_storage() { PERFETTO_DCHECK(ColumnTypeHelper<T>::ToColumnType() == type_); PERFETTO_DCHECK(tc_internal::TypeHandler<T>::is_optional == IsNullable()); - return *static_cast<ColumnStorage<stored_type<T>>*>(storage_); + return static_cast<ColumnStorage<stored_type<T>>*>(storage_); } - // Returns true if this column is a dense column. - bool IsDense() const { return IsDense(flags_); } - // Returns true if this column is a hidden column. bool IsHidden() const { return (flags_ & Flag::kHidden) != 0; } @@ -545,31 +548,31 @@ class Column { b, std::lower_bound(b, e, value, &compare::SqlValueComparator)); uint32_t end = std::distance( b, std::upper_bound(b, e, value, &compare::SqlValueComparator)); - rm->Intersect(beg, end); + rm->Intersect({beg, end}); return true; } case FilterOp::kLe: { uint32_t end = std::distance( b, std::upper_bound(b, e, value, &compare::SqlValueComparator)); - rm->Intersect(0, end); + rm->Intersect({0, end}); return true; } case FilterOp::kLt: { uint32_t end = std::distance( b, std::lower_bound(b, e, value, &compare::SqlValueComparator)); - rm->Intersect(0, end); + rm->Intersect({0, end}); return true; } case FilterOp::kGe: { uint32_t beg = std::distance( b, std::lower_bound(b, e, value, &compare::SqlValueComparator)); - rm->Intersect(beg, overlay().size()); + rm->Intersect({beg, overlay().size()}); return true; } case FilterOp::kGt: { uint32_t beg = std::distance( b, std::upper_bound(b, e, value, &compare::SqlValueComparator)); - rm->Intersect(beg, overlay().size()); + rm->Intersect({beg, overlay().size()}); return true; } case FilterOp::kNe: @@ -608,11 +611,13 @@ class Column { // Otherwise, find the end of the set and return the intersection for this. for (uint32_t i = set_id + 1; i < ov.size(); ++i) { if (st.Get(ov.Get(i)) != filter_set_id) { - rm->Intersect(set_id, i); + RowMap r(set_id, i); + rm->Intersect(r); return; } } - rm->Intersect(set_id, ov.size()); + RowMap r(set_id, ov.size()); + rm->Intersect(r); } // Slow path filter method which will perform a full table scan. diff --git a/src/trace_processor/types/variadic.cc b/src/trace_processor/db/column_overlay.cc index 9a26bd5ad..599a3b268 100644 --- a/src/trace_processor/types/variadic.cc +++ b/src/trace_processor/db/column_overlay.cc @@ -1,5 +1,5 @@ -#/* - * Copyright (C) 2018 The Android Open Source Project +/* + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "src/trace_processor/types/variadic.h" +#include "src/trace_processor/db/column_overlay.h" namespace perfetto { namespace trace_processor { +namespace column { -#if !PERFETTO_IS_AT_LEAST_CPP17() -constexpr const char* Variadic::kTypeNames[]; -#endif +ColumnOverlay::~ColumnOverlay() = default; +} // namespace column } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/db/column_overlay.h b/src/trace_processor/db/column_overlay.h new file mode 100644 index 000000000..e3bad537b --- /dev/null +++ b/src/trace_processor/db/column_overlay.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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&) const = 0; + + // Sorts (ascending) provided vector of indices based on storage. + virtual void StableSort(uint32_t* rows, uint32_t rows_size) const = 0; +}; +} // namespace column +} // namespace trace_processor +} // namespace perfetto + +#endif // SRC_TRACE_PROCESSOR_DB_COLUMN_OVERLAY_H_ diff --git a/src/trace_processor/db/column_storage.h b/src/trace_processor/db/column_storage.h index 6cf2f3069..7e6a60925 100644 --- a/src/trace_processor/db/column_storage.h +++ b/src/trace_processor/db/column_storage.h @@ -53,6 +53,7 @@ class ColumnStorage : public ColumnStorageBase { void Set(uint32_t idx, T val) { vector_[idx] = val; } uint32_t size() const { return static_cast<uint32_t>(vector_.size()); } void ShrinkToFit() { vector_.shrink_to_fit(); } + const std::vector<T>& vector() const { return vector_; } template <bool IsDense> static ColumnStorage<T> Create() { @@ -83,6 +84,14 @@ class ColumnStorage<std::optional<T>> : public ColumnStorageBase { uint32_t size() const { return nv_.size(); } bool IsDense() const { return nv_.IsDense(); } void ShrinkToFit() { nv_.ShrinkToFit(); } + // For dense columns the size of the vector is equal to size of the bit + // vector. For sparse it's equal to count set bits of the bit vector. + const std::vector<T>& non_null_vector() const { + return nv_.non_null_vector(); + } + const BitVector& non_null_bit_vector() const { + return nv_.non_null_bit_vector(); + } template <bool IsDense> static ColumnStorage<std::optional<T>> Create() { diff --git a/src/trace_processor/db/column_storage_overlay.h b/src/trace_processor/db/column_storage_overlay.h index 108dccdd9..997d4be43 100644 --- a/src/trace_processor/db/column_storage_overlay.h +++ b/src/trace_processor/db/column_storage_overlay.h @@ -183,24 +183,12 @@ class ColumnStorageOverlay { // meet |p|. However, if |this| is a BitVector, we end up needing expensive // |IndexOfNthSet| calls (as we need to convert the row to an index before // passing it to |p|). - switch (row_map_.mode_) { - case RowMap::Mode::kRange: { - auto ip = [this, p](uint32_t row) { return p(row_map_.GetRange(row)); }; - out->Filter(ip); - break; - } - case RowMap::Mode::kBitVector: { - FilterIntoScanSelfBv(out, p); - break; - } - case RowMap::Mode::kIndexVector: { - auto ip = [this, p](uint32_t row) { - return p(row_map_.GetIndexVector(row)); - }; - out->Filter(ip); - break; - } + if (row_map_.IsBitVector()) { + FilterIntoScanSelfBv(out, p); + return; } + auto ip = [this, p](uint32_t row) { return p(row_map_.Get(row)); }; + out->Filter(ip); } template <typename Comparator = bool(uint32_t, uint32_t)> @@ -217,54 +205,57 @@ class ColumnStorageOverlay { // Filters the current ColumnStorageOverlay into |out| by performing a full // scan on |row_map.bit_vector_|. See |FilterInto| for a full breakdown of the // semantics of this function. + template <typename Predicate> - void FilterIntoScanSelfBv(RowMap* out, Predicate p) const { - auto it = row_map_.bit_vector_.IterateSetBits(); - switch (out->mode_) { - case RowMap::Mode::kRange: { - // TODO(lalitm): investigate whether we can reuse the data inside - // out->bit_vector_ at some point. - BitVector bv(out->end_index_, false); - for (auto out_it = bv.IterateAllBits(); it; it.Next(), out_it.Next()) { - uint32_t ordinal = it.ordinal(); - if (ordinal < out->start_index_) - continue; - if (ordinal >= out->end_index_) - break; - - if (p(it.index())) { - out_it.Set(); - } - } - *out = RowMap(std::move(bv)); - break; - } - case RowMap::Mode::kBitVector: { - auto out_it = out->bit_vector_.IterateAllBits(); - for (; out_it; it.Next(), out_it.Next()) { - PERFETTO_DCHECK(it); - if (out_it.IsSet() && !p(it.index())) - out_it.Clear(); + struct FilterIntoScanSelfBvVisitor { + void operator()(RowMap::Range out_r) { + BitVector bv(out_r.end, false); + for (auto out_it = bv.IterateAllBits(); bv_iter; + bv_iter.Next(), out_it.Next()) { + uint32_t ordinal = bv_iter.ordinal(); + if (ordinal < out_r.start) + continue; + if (ordinal >= out_r.end) + break; + + if (p(bv_iter.index())) { + out_it.Set(); } - break; } - case RowMap::Mode::kIndexVector: { - PERFETTO_DCHECK(std::is_sorted(out->index_vector_.begin(), - out->index_vector_.end())); - auto fn = [&p, &it](uint32_t i) { - while (it.ordinal() < i) { - it.Next(); - PERFETTO_DCHECK(it); - } - PERFETTO_DCHECK(it.ordinal() == i); - return !p(it.index()); - }; - auto iv_it = std::remove_if(out->index_vector_.begin(), - out->index_vector_.end(), fn); - out->index_vector_.erase(iv_it, out->index_vector_.end()); - break; + *out = RowMap(std::move(bv)); + } + void operator()(const BitVector& out_bv) { + auto out_it = out_bv.IterateAllBits(); + for (; out_it; bv_iter.Next(), out_it.Next()) { + PERFETTO_DCHECK(bv_iter); + if (out_it.IsSet() && !p(bv_iter.index())) + out_it.Clear(); } } + void operator()(std::vector<OutputIndex>& out_vec) { + PERFETTO_DCHECK(std::is_sorted(out_vec.begin(), out_vec.end())); + auto fn = [this](uint32_t i) { + while (bv_iter.ordinal() < i) { + bv_iter.Next(); + PERFETTO_DCHECK(bv_iter); + } + PERFETTO_DCHECK(bv_iter.ordinal() == i); + return !p(bv_iter.index()); + }; + auto iv_it = std::remove_if(out_vec.begin(), out_vec.end(), fn); + out_vec.erase(iv_it, out_vec.end()); + } + RowMap* out; + Predicate p; + internal::SetBitsIterator bv_iter; + }; + + template <typename Predicate> + void FilterIntoScanSelfBv(RowMap* out, Predicate p) const { + const BitVector* bv = std::get_if<BitVector>(&row_map_.data_); + auto it = bv->IterateSetBits(); + std::visit(FilterIntoScanSelfBvVisitor<Predicate>{out, p, std::move(it)}, + out->data_); } RowMap row_map_; diff --git a/src/trace_processor/db/null_overlay.cc b/src/trace_processor/db/null_overlay.cc new file mode 100644 index 000000000..ade46c5a1 --- /dev/null +++ b/src/trace_processor/db/null_overlay.cc @@ -0,0 +1,87 @@ +/* + * 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" + +#include "src/trace_processor/db/storage_variants.h" + +namespace perfetto { +namespace trace_processor { +namespace column { + +void NullOverlay::Filter(FilterOp op, SqlValue sql_val, 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 column +} // namespace trace_processor +} // namespace perfetto diff --git a/src/trace_processor/db/null_overlay.h b/src/trace_processor/db/null_overlay.h new file mode 100644 index 000000000..43d2fed37 --- /dev/null +++ b/src/trace_processor/db/null_overlay.h @@ -0,0 +1,51 @@ +/* + * 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_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" + +namespace perfetto { +namespace trace_processor { +namespace column { + +// Overlay responsible for operations related to column nullability. +class NullOverlay : public ColumnOverlay { + public: + explicit NullOverlay(std::unique_ptr<ColumnOverlay> inner, + const BitVector* null_bv) + : inner_(std::move(inner)), null_bv_(null_bv) {} + + void Filter(FilterOp, SqlValue, RowMap&) const override; + void StableSort(uint32_t* rows, uint32_t rows_size) const override; + + private: + std::unique_ptr<ColumnOverlay> inner_; + + // Vector of data nullability. + const BitVector* null_bv_; +}; + +} // namespace column +} // namespace trace_processor +} // namespace perfetto + +#endif // SRC_TRACE_PROCESSOR_DB_NULL_OVERLAY_H_ diff --git a/src/trace_processor/db/numeric_storage.cc b/src/trace_processor/db/numeric_storage.cc new file mode 100644 index 000000000..3b66e2207 --- /dev/null +++ b/src/trace_processor/db/numeric_storage.cc @@ -0,0 +1,308 @@ + +/* + * 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 { + +// 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(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); +} + +// Responsible for invoking templated version of FastPathComparison. +void NumericStorage::CompareFast(FilterOp op, + SqlValue sql_val, + uint32_t offset, + uint32_t num_elements, + BitVector::Builder& builder) const { + PERFETTO_DCHECK(num_elements % BitVector::kBitsInWord == 0); + 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( + [this, op, offset, num_elements, &builder](auto num_val) { + using T = decltype(num_val); + auto* typed_start = static_cast<const T*>(data_) + offset; + 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, + uint32_t offset, + 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( + [this, op, offset, num_elements, &builder](auto val) { + using T = decltype(val); + auto* typed_start = static_cast<const T*>(data_) + offset; + TypedSlowPathComparison(val, op, typed_start, num_elements, builder); + }, + *val); +} + +uint32_t NumericStorage::UpperBoundIndex(NumericValue val) const { + return std::visit( + [this](auto val_data) { + using T = decltype(val_data); + const T* typed_start = static_cast<const T*>(data_); + auto upper = + std::upper_bound(typed_start, typed_start + size_, 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. +uint32_t NumericStorage::LowerBoundIndex(NumericValue val) const { + return std::visit( + [this](auto val_data) { + using T = decltype(val_data); + const T* typed_start = static_cast<const T*>(data_); + auto lower = + std::lower_bound(typed_start, typed_start + size_, val_data); + return static_cast<uint32_t>(std::distance(typed_start, lower)); + }, + val); +} + +void NumericStorage::CompareSorted(FilterOp op, + SqlValue sql_val, + 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); + uint32_t end = UpperBoundIndex(*val); + RowMap sec(beg, end); + rm.Intersect(sec); + return; + } + case FilterOp::kLe: { + uint32_t end = UpperBoundIndex(*val); + RowMap sec(0, end); + rm.Intersect(sec); + return; + } + case FilterOp::kLt: { + uint32_t end = LowerBoundIndex(*val); + RowMap sec(0, end); + rm.Intersect(sec); + return; + } + case FilterOp::kGe: { + uint32_t beg = LowerBoundIndex(*val); + RowMap sec(beg, size_); + rm.Intersect(sec); + return; + } + case FilterOp::kGt: { + uint32_t beg = UpperBoundIndex(*val); + RowMap sec(beg, size_); + rm.Intersect(sec); + return; + } + case FilterOp::kNe: + case FilterOp::kIsNull: + case FilterOp::kIsNotNull: + case FilterOp::kGlob: + rm.Clear(); + } + return; +} + +uint32_t NumericStorage::UpperBoundIndex(NumericValue val, + uint32_t* order) const { + return std::visit( + [this, order](auto val_data) { + using T = decltype(val_data); + const T* typed_start = static_cast<const T*>(data_); + auto upper = std::upper_bound(order, order + size_, val_data, + [typed_start](T val, uint32_t index) { + return val < *(typed_start + index); + }); + return static_cast<uint32_t>(std::distance(order, upper)); + }, + val); +} + +// As we don't template those functions, we need to use std::visitor to type +// `start`, hence this wrapping. +uint32_t NumericStorage::LowerBoundIndex(NumericValue val, + uint32_t* order) const { + return std::visit( + [this, order](auto val_data) { + using T = decltype(val_data); + const T* typed_start = static_cast<const T*>(data_); + auto lower = std::lower_bound(order, order + size_, val_data, + [typed_start](uint32_t index, T val) { + return *(typed_start + index) < val; + }); + return static_cast<uint32_t>(std::distance(order, lower)); + }, + val); +} + +void NumericStorage::CompareSortedIndexes(FilterOp op, + SqlValue sql_val, + uint32_t* order, + 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, order); + uint32_t end = UpperBoundIndex(*val, order); + std::vector<uint32_t> index(order + beg, order + end); + rm.Intersect(RowMap(std::move(index))); + return; + } + case FilterOp::kLe: { + uint32_t end = UpperBoundIndex(*val, order); + std::vector<uint32_t> index(order, order + end); + rm.Intersect(RowMap(std::move(index))); + return; + } + case FilterOp::kLt: { + uint32_t end = LowerBoundIndex(*val, order); + std::vector<uint32_t> index(order, order + end); + rm.Intersect(RowMap(std::move(index))); + return; + } + case FilterOp::kGe: { + uint32_t beg = LowerBoundIndex(*val, order); + std::vector<uint32_t> index(order + beg, order + size_); + rm.Intersect(RowMap(std::move(index))); + return; + } + case FilterOp::kGt: { + uint32_t beg = UpperBoundIndex(*val, order); + std::vector<uint32_t> index(order + beg, order + size_); + rm.Intersect(RowMap(std::move(index))); + 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 new file mode 100644 index 000000000..a85044bb4 --- /dev/null +++ b/src/trace_processor/db/numeric_storage.h @@ -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. + */ +#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(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 CompareFast(FilterOp op, + SqlValue val, + uint32_t offset, + uint32_t num_elements, + BitVector::Builder& builder) const override; + + void CompareSlow(FilterOp op, + SqlValue val, + uint32_t offset, + uint32_t num_elements, + BitVector::Builder& builder) const override; + + void CompareSorted(FilterOp op, SqlValue val, RowMap&) const override; + + void CompareSortedIndexes(FilterOp op, + SqlValue val, + uint32_t* order, + RowMap&) const override; + + uint32_t size() const override { return size_; } + + private: + // As we don't template those functions, we need to use std::visitor to type + // `start`, hence this wrapping. + uint32_t UpperBoundIndex(NumericValue val) const; + + // As we don't template those functions, we need to use std::visitor to type + // `start`, hence this wrapping. + uint32_t LowerBoundIndex(NumericValue val) const; + + // As we don't template those functions, we need to use std::visitor to type + // `start`, hence this wrapping. + uint32_t UpperBoundIndex(NumericValue val, uint32_t* order) const; + + // As we don't template those functions, we need to use std::visitor to type + // `start`, hence this wrapping. + uint32_t LowerBoundIndex(NumericValue val, uint32_t* order) const; + + const ColumnType type_; + const void* data_; + const uint32_t size_; +}; + +} // namespace column +} // namespace trace_processor +} // namespace perfetto +#endif // SRC_TRACE_PROCESSOR_DB_NUMERIC_STORAGE_H_ diff --git a/src/trace_processor/db/sorting_overlay.h b/src/trace_processor/db/sorting_overlay.h new file mode 100644 index 000000000..7b536a1c2 --- /dev/null +++ b/src/trace_processor/db/sorting_overlay.h @@ -0,0 +1,47 @@ +/* + * 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&) const override; + void StableSort(uint32_t* rows_order, uint32_t rows_size) const 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/protos/perfetto/trace_processor/cloud_trace_processor.proto b/src/trace_processor/db/storage.cc index b009fe3d8..4799d0479 100644 --- a/protos/perfetto/trace_processor/cloud_trace_processor.proto +++ b/src/trace_processor/db/storage.cc @@ -14,8 +14,14 @@ * limitations under the License. */ -syntax = "proto2"; +#include "src/trace_processor/db/storage.h" -package perfetto.protos; +namespace perfetto { +namespace trace_processor { +namespace column { -service CloudTraceProcessorWorkerService {}
\ No newline at end of file +Storage::~Storage() = default; + +} // namespace column +} // namespace trace_processor +} // namespace perfetto diff --git a/src/trace_processor/db/storage.h b/src/trace_processor/db/storage.h new file mode 100644 index 000000000..41e616fc8 --- /dev/null +++ b/src/trace_processor/db/storage.h @@ -0,0 +1,72 @@ +/* + * 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(uint32_t* rows, uint32_t rows_size) 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, + uint32_t offset, + 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, + uint32_t offset, + uint32_t compare_elements_count, + BitVector::Builder&) const = 0; + + // Compares sorted (asc) series data with comparator value. Should be used + // where possible. + virtual void CompareSorted(FilterOp op, SqlValue value, RowMap&) const = 0; + + // Compares sorted (asc) with `order` vector series with comparator value. + // Should be used where possible. + virtual void CompareSortedIndexes(FilterOp op, + SqlValue value, + uint32_t* order, + RowMap&) const = 0; + + // Number of elements in stored data. + virtual uint32_t size() const = 0; +}; + +} // namespace column +} // namespace trace_processor +} // namespace perfetto +#endif // SRC_TRACE_PROCESSOR_DB_STORAGE_H_ diff --git a/src/trace_processor/db/storage_overlay.cc b/src/trace_processor/db/storage_overlay.cc new file mode 100644 index 000000000..2ab4fde29 --- /dev/null +++ b/src/trace_processor/db/storage_overlay.cc @@ -0,0 +1,61 @@ +/* + * 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_overlay.h" + +#include "src/trace_processor/db/storage_variants.h" + +namespace perfetto { +namespace trace_processor { +namespace column { + +void StorageOverlay::Filter(FilterOp op, SqlValue value, RowMap& rm) const { + if (op == FilterOp::kIsNotNull) + return; + + if (op == FilterOp::kIsNull) { + rm.Clear(); + return; + } + + BitVector::Builder builder(storage_->size()); + // Slow path: we compare <64 elements and append to get us to a word + // boundary. + uint32_t front_elements = builder.BitsUntilWordBoundaryOrFull(); + storage_->CompareSlow(op, value, 0, front_elements, builder); + uint32_t cur_index = 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(); + storage_->CompareFast(op, value, cur_index, fast_path_elements, builder); + cur_index += fast_path_elements; + + // Slow path: we compare <64 elements and append to fill the Builder. + uint32_t back_elements = builder.BitsUntilFull(); + storage_->CompareSlow(op, value, cur_index, back_elements, builder); + + BitVector bv = std::move(builder).Build(); + rm.Intersect(RowMap(std::move(bv))); +} + +void StorageOverlay::StableSort(uint32_t* rows, uint32_t rows_size) const { + storage_->StableSort(rows, rows_size); +} + +} // namespace column +} // namespace trace_processor +} // namespace perfetto diff --git a/src/trace_processor/db/storage_overlay.h b/src/trace_processor/db/storage_overlay.h new file mode 100644 index 000000000..9b0b5ec3d --- /dev/null +++ b/src/trace_processor/db/storage_overlay.h @@ -0,0 +1,44 @@ +/* + * 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* storage) : storage_(storage) {} + void Filter(FilterOp, SqlValue, RowMap&) const override; + void StableSort(uint32_t* rows, uint32_t rows_size) const 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 new file mode 100644 index 000000000..8988c0030 --- /dev/null +++ b/src/trace_processor/db/storage_unittest.cc @@ -0,0 +1,294 @@ +/* + * 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/numeric_storage.h" + +#include "src/trace_processor/db/null_overlay.h" +#include "src/trace_processor/db/storage_overlay.h" +#include "test/gtest_and_gmock.h" + +namespace perfetto { +namespace trace_processor { +namespace column { + +namespace { + +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, CompareSlow) { + uint32_t size = 10; + std::vector<uint32_t> data_vec(size); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), size, ColumnType::kUint32); + BitVector::Builder builder(size); + storage.CompareSlow(FilterOp::kGe, SqlValue::Long(5), 0, size, builder); + BitVector bv = std::move(builder).Build(); + + ASSERT_EQ(bv.CountSetBits(), 5u); + ASSERT_EQ(bv.IndexOfNthSet(0), 5u); +} + +TEST(NumericStorageUnittest, CompareSlowLarge) { + uint32_t size = 1025; + std::vector<uint32_t> data_vec(size); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), size, ColumnType::kUint32); + BitVector::Builder builder(size); + storage.CompareSlow(FilterOp::kGe, SqlValue::Long(5), 0, size, builder); + BitVector bv = std::move(builder).Build(); + + ASSERT_EQ(bv.CountSetBits(), 1020u); + ASSERT_EQ(bv.IndexOfNthSet(0), 5u); +} + +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::Builder builder(128); + storage.CompareFast(FilterOp::kGe, SqlValue::Long(100), 0, 128, builder); + BitVector bv = std::move(builder).Build(); + + 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); + RowMap rm(0, 128); + storage.CompareSorted(FilterOp::kGe, SqlValue::Long(100), rm); + + ASSERT_EQ(rm.size(), 28u); + ASSERT_EQ(rm.Get(0), 100u); +} + +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); + RowMap rm(0, 10); + + storage.CompareSortedIndexes(FilterOp::kGe, SqlValue::Long(60), + sorted_order.data(), rm); + + ASSERT_EQ(rm.size(), 4u); + ASSERT_EQ(rm.Get(0), 3u); + ASSERT_EQ(rm.Get(1), 6u); + ASSERT_EQ(rm.Get(2), 5u); + ASSERT_EQ(rm.Get(3), 4u); +} + +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); + RowMap rm(0, 10); + + storage.CompareSortedIndexes(FilterOp::kLt, SqlValue::Long(60), + sorted_order.data(), rm); + + ASSERT_EQ(rm.size(), 6u); + ASSERT_EQ(rm.Get(0), 7u); +} + +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); + RowMap rm(0, 10); + + storage.CompareSortedIndexes(FilterOp::kEq, SqlValue::Long(60), + sorted_order.data(), rm); + + ASSERT_EQ(rm.size(), 1u); + ASSERT_EQ(rm.Get(0), 3u); +} + +TEST(StorageOverlayUnittests, FilterIsNull) { + std::vector<uint32_t> data_vec(1025); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), 1025, ColumnType::kUint32); + StorageOverlay overlay(&storage); + + RowMap rm(0, 1025); + overlay.Filter(FilterOp::kIsNull, SqlValue::Long(0), rm); + + ASSERT_EQ(rm.size(), 0u); +} + +TEST(StorageOverlayUnittests, FilterIsNotNull) { + std::vector<uint32_t> data_vec(1025); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), 1025, ColumnType::kUint32); + StorageOverlay overlay(&storage); + + RowMap rm(0, 1025); + overlay.Filter(FilterOp::kIsNotNull, SqlValue::Long(0), rm); + + ASSERT_EQ(rm.size(), 1025u); +} + +TEST(StorageOverlayUnittests, Filter) { + std::vector<uint32_t> data_vec(1025); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), 1025, ColumnType::kUint32); + StorageOverlay overlay(&storage); + + RowMap rm(0, 1025); + overlay.Filter(FilterOp::kGe, SqlValue::Long(200), rm); + + ASSERT_EQ(rm.size(), 825u); + ASSERT_EQ(rm.Get(0), 200u); +} + +TEST(StorageOverlayUnittests, Sort) { + 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); + StorageOverlay overlay(&storage); + + overlay.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(NullOverlayUnittest, FilterIsNull) { + std::vector<uint32_t> data_vec(10); + std::iota(data_vec.begin(), data_vec.end(), 0); + BitVector bv = BitVector::Range(0, 20, [](uint32_t t) { return t % 2 == 0; }); + + NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32); + std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage)); + NullOverlay overlay(std::move(storage_overlay), &bv); + + RowMap rm(0, 10); + overlay.Filter(FilterOp::kIsNull, SqlValue::Long(5), rm); + ASSERT_EQ(rm.size(), 5u); + ASSERT_EQ(rm.Get(0), 1u); +} + +TEST(NullOverlayUnittest, FilterIsNotNull) { + std::vector<uint32_t> data_vec(10); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32); + BitVector bv = BitVector::Range(0, 20, [](uint32_t t) { return t % 2 == 0; }); + std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage)); + NullOverlay overlay(std::move(storage_overlay), &bv); + + RowMap rm(0, 10); + overlay.Filter(FilterOp::kIsNotNull, SqlValue::Long(5), rm); + + ASSERT_EQ(rm.size(), 5u); + ASSERT_EQ(rm.Get(0), 0u); +} + +TEST(NullOverlayUnittest, Filter) { + uint32_t bv_size = 20; + uint32_t data_size = 10; + + // Prepare storage + std::vector<uint32_t> data_vec(data_size); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), data_size, ColumnType::kUint32); + + // Prepare NullOverlay + BitVector bv = + BitVector::Range(0, bv_size, [](uint32_t t) { return t % 2 == 0; }); + std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage)); + NullOverlay overlay(std::move(storage_overlay), &bv); + + RowMap rm(0, bv_size); + overlay.Filter(FilterOp::kGe, SqlValue::Long(5), rm); + + ASSERT_EQ(rm.size(), 5u); + ASSERT_EQ(rm.Get(0), 10u); +} + +TEST(NullOverlayUnittest, FilterLarge) { + uint32_t bv_size = 1000; + uint32_t data_size = 100; + + // Prepare storage + std::vector<uint32_t> data_vec(data_size); + std::iota(data_vec.begin(), data_vec.end(), 0); + NumericStorage storage(data_vec.data(), data_size, ColumnType::kUint32); + + // Prepare NullOverlay + BitVector bv = + BitVector::Range(800, bv_size, [](uint32_t t) { return t % 2 == 0; }); + std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage)); + NullOverlay overlay(std::move(storage_overlay), &bv); + + RowMap rm(0, bv_size); + overlay.Filter(FilterOp::kGe, SqlValue::Long(50), rm); + + ASSERT_EQ(rm.size(), 50u); + ASSERT_EQ(rm.Get(0), 900u); +} + +TEST(NullOverlayUnittest, Sort) { + // Prepare storage + std::vector<uint32_t> data_vec{0, 1, 2, 0, 1, 2, 0, 1, 2}; + std::vector<uint32_t> out(18); + std::iota(out.begin(), out.end(), 0); + NumericStorage storage(data_vec.data(), 9, ColumnType::kUint32); + + // Prepare NullOverlay + BitVector bv = BitVector::Range(0, 18, [](uint32_t t) { return t % 2 == 0; }); + std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage)); + NullOverlay overlay(std::move(storage_overlay), &bv); + + overlay.StableSort(out.data(), 18); + + std::vector<uint32_t> stable_out{1, 3, 5, 7, 9, 11, 13, 15, 17, + 0, 6, 12, 2, 8, 14, 4, 10, 16}; + ASSERT_EQ(out, stable_out); +} + +} // 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 new file mode 100644 index 000000000..bc169b9a0 --- /dev/null +++ b/src/trace_processor/db/storage_variants.h @@ -0,0 +1,108 @@ +/* + * 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/db/table_unittest.cc b/src/trace_processor/db/table_unittest.cc deleted file mode 100644 index 6f370ca25..000000000 --- a/src/trace_processor/db/table_unittest.cc +++ /dev/null @@ -1,130 +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. - */ - -#include "src/trace_processor/db/table.h" -#include "src/trace_processor/db/typed_column.h" -#include "src/trace_processor/tables/macros.h" - -#include "test/gtest_and_gmock.h" - -namespace perfetto { -namespace trace_processor { -namespace { - -#define PERFETTO_TP_TEST_EVENT_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestEventTable, "event") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ - C(int64_t, ts, Column::Flag::kSorted) \ - C(int64_t, dur) \ - C(uint32_t, arg_set_id, Column::Flag::kSorted | Column::Flag::kSetId) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_EVENT_TABLE_DEF); - -TestEventTable::~TestEventTable() = default; - -TEST(TableTest, SetIdColumns) { - StringPool pool; - TestEventTable table{&pool, nullptr}; - - table.Insert(TestEventTable::Row(0, 0, 0)); - table.Insert(TestEventTable::Row(1, 0, 0)); - table.Insert(TestEventTable::Row(2, 0, 2)); - table.Insert(TestEventTable::Row(3, 0, 3)); - table.Insert(TestEventTable::Row(4, 0, 4)); - table.Insert(TestEventTable::Row(5, 0, 4)); - table.Insert(TestEventTable::Row(6, 0, 4)); - table.Insert(TestEventTable::Row(7, 0, 4)); - table.Insert(TestEventTable::Row(8, 0, 8)); - - ASSERT_EQ(table.row_count(), 9u); - ASSERT_TRUE(table.arg_set_id().IsSetId()); - - // Verify that not-present ids are not returned. - { - static constexpr uint32_t kFilterArgSetId = 1; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 0u); - } - { - static constexpr uint32_t kFilterArgSetId = 9; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 0u); - } - - // Verify that kSetId flag is correctly removed after filtering/sorting. - { - static constexpr uint32_t kFilterArgSetId = 3; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 1u); - ASSERT_FALSE(res.GetColumnByName("arg_set_id")->IsSetId()); - } - { - auto res = table.Sort({table.dur().descending()}); - ASSERT_FALSE(res.GetColumnByName("arg_set_id")->IsSetId()); - } - - uint32_t arg_set_id_col_idx = - static_cast<uint32_t>(TestEventTable::ColumnIndex::arg_set_id); - - // Verify that filtering equality for real arg set ids works as expected. - { - static constexpr uint32_t kFilterArgSetId = 4; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 4u); - for (auto it = res.IterateRows(); it; it.Next()) { - uint32_t arg_set_id = - static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); - ASSERT_EQ(arg_set_id, kFilterArgSetId); - } - } - { - static constexpr uint32_t kFilterArgSetId = 0; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 2u); - for (auto it = res.IterateRows(); it; it.Next()) { - uint32_t arg_set_id = - static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); - ASSERT_EQ(arg_set_id, kFilterArgSetId); - } - } - { - static constexpr uint32_t kFilterArgSetId = 8; - auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 1u); - for (auto it = res.IterateRows(); it; it.Next()) { - uint32_t arg_set_id = - static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); - ASSERT_EQ(arg_set_id, kFilterArgSetId); - } - } - - // Verify that filtering equality for arg set ids after filtering another - // column works. - { - static constexpr uint32_t kFilterArgSetId = 4; - auto res = table.Filter( - {table.ts().ge(6), table.arg_set_id().eq(kFilterArgSetId)}); - ASSERT_EQ(res.row_count(), 2u); - for (auto it = res.IterateRows(); it; it.Next()) { - uint32_t arg_set_id = - static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); - ASSERT_EQ(arg_set_id, kFilterArgSetId); - } - } -} - -} // namespace -} // namespace trace_processor -} // namespace perfetto diff --git a/src/trace_processor/db/view_unittest.cc b/src/trace_processor/db/view_unittest.cc index 2507b927c..221753aae 100644 --- a/src/trace_processor/db/view_unittest.cc +++ b/src/trace_processor/db/view_unittest.cc @@ -15,52 +15,22 @@ */ #include "src/trace_processor/db/view.h" -#include "src/trace_processor/tables/macros.h" +#include "src/trace_processor/db/view_unittest_py.h" #include "src/trace_processor/views/macros.h" #include "test/gtest_and_gmock.h" namespace perfetto { namespace trace_processor { -namespace { +namespace tables { + +ViewThreadTable::~ViewThreadTable() = default; +ViewTrackTable::~ViewTrackTable() = default; +ViewThreadTrackTable::~ViewThreadTrackTable() = default; +ViewEventTable::~ViewEventTable() = default; +ViewSliceTable::~ViewSliceTable() = default; -#define PERFETTO_TP_TEST_THREAD_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestThreadTable, "thread_table") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ - C(StringPool::Id, name) \ - C(uint32_t, tid) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_THREAD_TABLE_DEF); - -#define PERFETTO_TP_TEST_TRACK_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestTrackTable, "track_table") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ - C(StringPool::Id, name) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_TRACK_TABLE_DEF); - -#define PERFETTO_TP_TEST_THREAD_TRACK_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestThreadTrackTable, "thread_track_table") \ - PARENT(PERFETTO_TP_TEST_TRACK_TABLE_DEF, C) \ - C(TestThreadTable::Id, utid) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_THREAD_TRACK_TABLE_DEF); - -#define PERFETTO_TP_TEST_EVENT_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestEventTable, "event_table") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ - C(int64_t, ts, Column::Flag::kSorted) \ - C(TestTrackTable::Id, track_id) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_EVENT_TABLE_DEF); - -#define PERFETTO_TP_TEST_SLICE_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestSliceTable, "slice_table") \ - PARENT(PERFETTO_TP_TEST_EVENT_TABLE_DEF, C) \ - C(StringPool::Id, name) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_SLICE_TABLE_DEF); - -TestThreadTable::~TestThreadTable() = default; -TestTrackTable::~TestTrackTable() = default; -TestThreadTrackTable::~TestThreadTrackTable() = default; -TestEventTable::~TestEventTable() = default; -TestSliceTable::~TestSliceTable() = default; +namespace { template <typename ViewSubclass> class AbstractViewTest : public ::testing::Test { @@ -106,17 +76,17 @@ class AbstractViewTest : public ::testing::Test { }; #define PERFETTO_TP_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _) \ - NAME(TestEventView, "event_view") \ - FROM(TestEventTable, event) \ - JOIN(TestTrackTable, track, id, event, track_id, View::kIdAlwaysPresent) \ + NAME(ViewEventView, "event_view") \ + FROM(ViewEventTable, event) \ + JOIN(ViewTrackTable, track, id, event, track_id, View::kIdAlwaysPresent) \ COL(id, event, id) \ COL(ts, event, ts) \ COL(track_id, event, track_id) \ COL(track_name, track, name) PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_EVENT_VIEW_DEF); -PERFETTO_TP_DEFINE_VIEW(TestEventView); +PERFETTO_TP_DEFINE_VIEW(ViewEventView); -class EventViewTest : public AbstractViewTest<TestEventView> { +class EventViewTest : public AbstractViewTest<ViewEventView> { protected: EventViewTest() { t1_id_ = track_.Insert({/* name */ Intern("foo")}).id; @@ -127,26 +97,26 @@ class EventViewTest : public AbstractViewTest<TestEventView> { event_table_.Insert({/* ts */ 102, t1_id_}); } - virtual TestEventView& view() override { return event_view_; } + virtual ViewEventView& view() override { return event_view_; } - TestTrackTable::Id t1_id_; - TestTrackTable::Id t2_id_; + ViewTrackTable::Id t1_id_; + ViewTrackTable::Id t2_id_; private: - TestEventTable event_table_{&pool_, nullptr}; - TestTrackTable track_{&pool_, nullptr}; - TestEventView event_view_{&event_table_, &track_}; + ViewEventTable event_table_{&pool_}; + ViewTrackTable track_{&pool_}; + ViewEventView event_view_{&event_table_, &track_}; }; TEST_F(EventViewTest, UnusedColumnsAreDummy) { - TestEventView::QueryResult result = QueryUsingCols({ColIdx::track_name}); + ViewEventView::QueryResult result = QueryUsingCols({ColIdx::track_name}); ASSERT_TRUE(result.columns()[ColIdx::id].IsDummy()); ASSERT_TRUE(result.columns()[ColIdx::ts].IsDummy()); ASSERT_FALSE(result.columns()[ColIdx::track_name].IsDummy()); } TEST_F(EventViewTest, Iterate) { - TestEventView::QueryResult result = Query(); + ViewEventView::QueryResult result = Query(); auto it = result.IterateRows(); ASSERT_TRUE(it); ASSERT_EQ(it.row_number().row_number(), 0u); @@ -170,13 +140,13 @@ TEST_F(EventViewTest, Iterate) { } TEST_F(EventViewTest, FilterEventEmpty) { - TestEventView::QueryResult result = Query({view().ts().eq(0)}); + ViewEventView::QueryResult result = Query({view().ts().eq(0)}); auto it = result.IterateRows(); ASSERT_FALSE(it); } TEST_F(EventViewTest, FilterEventNoUseTrack) { - TestEventView::QueryResult result = + ViewEventView::QueryResult result = Query({view().ts().eq(100)}, {}, {ColIdx::ts}); auto it = result.IterateRows(); ASSERT_TRUE(it); @@ -186,7 +156,7 @@ TEST_F(EventViewTest, FilterEventNoUseTrack) { } TEST_F(EventViewTest, FilterEventUseTrack) { - TestEventView::QueryResult result = + ViewEventView::QueryResult result = Query({view().ts().eq(100)}, {}, {ColIdx::ts, ColIdx::track_name, ColIdx::track_id}); auto it = result.IterateRows(); @@ -199,13 +169,13 @@ TEST_F(EventViewTest, FilterEventUseTrack) { } TEST_F(EventViewTest, FilterTrackEmpty) { - TestEventView::QueryResult result = Query({view().track_id().eq(102398)}); + ViewEventView::QueryResult result = Query({view().track_id().eq(102398)}); auto it = result.IterateRows(); ASSERT_FALSE(it); } TEST_F(EventViewTest, FilterTrackNoUseEvent) { - TestEventView::QueryResult result = + ViewEventView::QueryResult result = Query({view().track_name().eq("foo")}, {}, {ColIdx::track_name, ColIdx::track_id}); auto it = result.IterateRows(); @@ -221,7 +191,7 @@ TEST_F(EventViewTest, FilterTrackNoUseEvent) { } TEST_F(EventViewTest, FilterTrackUseEvent) { - TestEventView::QueryResult result = + ViewEventView::QueryResult result = Query({view().track_id().eq(t1_id_.value)}, {}, {ColIdx::ts, ColIdx::track_name, ColIdx::track_id}); auto it = result.IterateRows(); @@ -239,10 +209,10 @@ TEST_F(EventViewTest, FilterTrackUseEvent) { } #define PERFETTO_TP_THREAD_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _) \ - NAME(TestThreadEventView, "thread_event_view") \ - FROM(TestEventTable, event) \ - JOIN(TestThreadTrackTable, track, id, event, track_id, View::kNoFlag) \ - JOIN(TestThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) \ + NAME(ViewThreadEventView, "thread_event_view") \ + FROM(ViewEventTable, event) \ + JOIN(ViewThreadTrackTable, track, id, event, track_id, View::kNoFlag) \ + JOIN(ViewThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) \ COL(id, event, id) \ COL(ts, event, ts) \ COL(track_id, track, id) \ @@ -250,9 +220,9 @@ TEST_F(EventViewTest, FilterTrackUseEvent) { COL(utid, track, utid) \ COL(thread_name, thread, name) PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_EVENT_VIEW_DEF); -PERFETTO_TP_DEFINE_VIEW(TestThreadEventView); +PERFETTO_TP_DEFINE_VIEW(ViewThreadEventView); -class ThreadEventViewTest : public AbstractViewTest<TestThreadEventView> { +class ThreadEventViewTest : public AbstractViewTest<ViewThreadEventView> { protected: ThreadEventViewTest() { th1_id_ = thread_.Insert({Intern("th1"), 1}).id; @@ -275,24 +245,24 @@ class ThreadEventViewTest : public AbstractViewTest<TestThreadEventView> { event_table_.Insert({/* ts */ 107, t4_id_}); } - virtual TestThreadEventView& view() override { return event_view_; } + virtual ViewThreadEventView& view() override { return event_view_; } - TestThreadTable::Id th1_id_; - TestThreadTable::Id th2_id_; + ViewThreadTable::Id th1_id_; + ViewThreadTable::Id th2_id_; - TestTrackTable::Id t1_id_; - TestTrackTable::Id t2_id_; - TestTrackTable::Id t3_id_; - TestTrackTable::Id t4_id_; - TestTrackTable::Id t5_id_; - TestTrackTable::Id t6_id_; + ViewTrackTable::Id t1_id_; + ViewTrackTable::Id t2_id_; + ViewTrackTable::Id t3_id_; + ViewTrackTable::Id t4_id_; + ViewTrackTable::Id t5_id_; + ViewTrackTable::Id t6_id_; private: - TestEventTable event_table_{&pool_, nullptr}; - TestTrackTable track_{&pool_, nullptr}; - TestThreadTrackTable thread_track_{&pool_, &track_}; - TestThreadTable thread_{&pool_, nullptr}; - TestThreadEventView event_view_{&event_table_, &thread_track_, &thread_}; + ViewEventTable event_table_{&pool_}; + ViewTrackTable track_{&pool_}; + ViewThreadTrackTable thread_track_{&pool_, &track_}; + ViewThreadTable thread_{&pool_}; + ViewThreadEventView event_view_{&event_table_, &thread_track_, &thread_}; }; TEST_F(ThreadEventViewTest, Iterate) { @@ -427,7 +397,7 @@ TEST_F(ThreadEventViewTest, FilterEventAndThread) { } #define PERFETTO_TP_THREAD_SLICE_VIEW_DEF(NAME, FROM, JOIN, COL, _) \ - NAME(TestThreadSliceView, "thread_slice_view") \ + NAME(ViewThreadSliceView, "thread_slice_view") \ COL(id, slice, id) \ COL(ts, slice, ts) \ COL(name, slice, name) \ @@ -435,13 +405,13 @@ TEST_F(ThreadEventViewTest, FilterEventAndThread) { COL(track_name, track, name) \ COL(utid, thread, id) \ COL(thread_name, thread, name) \ - FROM(TestSliceTable, slice) \ - JOIN(TestThreadTrackTable, track, id, slice, track_id, View::kNoFlag) \ - JOIN(TestThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) + FROM(ViewSliceTable, slice) \ + JOIN(ViewThreadTrackTable, track, id, slice, track_id, View::kNoFlag) \ + JOIN(ViewThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_SLICE_VIEW_DEF); -PERFETTO_TP_DEFINE_VIEW(TestThreadSliceView); +PERFETTO_TP_DEFINE_VIEW(ViewThreadSliceView); -class ThreadSliceViewTest : public AbstractViewTest<TestThreadSliceView> { +class ThreadSliceViewTest : public AbstractViewTest<ViewThreadSliceView> { protected: ThreadSliceViewTest() { th1_id_ = thread_.Insert({Intern("th1"), 1}).id; @@ -464,25 +434,25 @@ class ThreadSliceViewTest : public AbstractViewTest<TestThreadSliceView> { slice_table_.Insert({/* ts */ 107, t4_id_, Intern("ts107")}); } - TestThreadSliceView& view() override { return slice_view_; } + ViewThreadSliceView& view() override { return slice_view_; } - TestThreadTable::Id th1_id_; - TestThreadTable::Id th2_id_; + ViewThreadTable::Id th1_id_; + ViewThreadTable::Id th2_id_; - TestTrackTable::Id t1_id_; - TestTrackTable::Id t2_id_; - TestTrackTable::Id t3_id_; - TestTrackTable::Id t4_id_; - TestTrackTable::Id t5_id_; - TestTrackTable::Id t6_id_; + ViewTrackTable::Id t1_id_; + ViewTrackTable::Id t2_id_; + ViewTrackTable::Id t3_id_; + ViewTrackTable::Id t4_id_; + ViewTrackTable::Id t5_id_; + ViewTrackTable::Id t6_id_; private: - TestEventTable event_{&pool_, nullptr}; - TestSliceTable slice_table_{&pool_, &event_}; - TestTrackTable track_{&pool_, nullptr}; - TestThreadTrackTable thread_track_{&pool_, &track_}; - TestThreadTable thread_{&pool_, nullptr}; - TestThreadSliceView slice_view_{&slice_table_, &thread_track_, &thread_}; + ViewEventTable event_{&pool_}; + ViewSliceTable slice_table_{&pool_, &event_}; + ViewTrackTable track_{&pool_}; + ViewThreadTrackTable thread_track_{&pool_, &track_}; + ViewThreadTable thread_{&pool_}; + ViewThreadSliceView slice_view_{&slice_table_, &thread_track_, &thread_}; }; TEST_F(ThreadSliceViewTest, Iterate) { @@ -600,5 +570,6 @@ TEST_F(ThreadSliceViewTest, FilterAndSort) { } } // namespace +} // namespace tables } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/db/view_unittest.py b/src/trace_processor/db/view_unittest.py new file mode 100644 index 000000000..a7f7abda7 --- /dev/null +++ b/src/trace_processor/db/view_unittest.py @@ -0,0 +1,75 @@ +# 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. +"""Contains tables for unitviewing.""" + +from python.generators.trace_processor_table.public import Column as C +from python.generators.trace_processor_table.public import ColumnFlag +from python.generators.trace_processor_table.public import Table +from python.generators.trace_processor_table.public import CppTableId +from python.generators.trace_processor_table.public import CppUint32 +from python.generators.trace_processor_table.public import CppInt64 +from python.generators.trace_processor_table.public import CppString + +VIEW_THREAD_TABLE = Table( + python_module=__file__, + class_name="ViewThreadTable", + sql_name="thread_table", + columns=[ + C("name", CppString()), + C("tid", CppUint32()), + ]) + +VIEW_TRACK_TABLE = Table( + python_module=__file__, + class_name="ViewTrackTable", + sql_name="track_table", + columns=[ + C("name", CppString()), + ]) + +VIEW_THREAD_TRACK_TABLE = Table( + python_module=__file__, + class_name="ViewThreadTrackTable", + sql_name="thread_track_table", + parent=VIEW_TRACK_TABLE, + columns=[ + C("utid", CppTableId(VIEW_THREAD_TABLE)), + ]) + +VIEW_EVENT_TABLE = Table( + python_module=__file__, + class_name="ViewEventTable", + sql_name="event_table", + columns=[ + C("ts", CppInt64(), flags=ColumnFlag.SORTED), + C("track_id", CppTableId(VIEW_TRACK_TABLE)), + ]) + +VIEW_SLICE_TABLE = Table( + python_module=__file__, + class_name="ViewSliceTable", + sql_name="slice_table", + parent=VIEW_EVENT_TABLE, + columns=[ + C("name", CppString()), + ]) + +# Keep this list sorted. +ALL_TABLES = [ + VIEW_EVENT_TABLE, + VIEW_SLICE_TABLE, + VIEW_THREAD_TABLE, + VIEW_THREAD_TRACK_TABLE, + VIEW_TRACK_TABLE, +] diff --git a/src/trace_processor/importers/common/args_translation_table.cc b/src/trace_processor/importers/common/args_translation_table.cc index 74cdc34cd..8aa9a0d82 100644 --- a/src/trace_processor/importers/common/args_translation_table.cc +++ b/src/trace_processor/importers/common/args_translation_table.cc @@ -52,25 +52,6 @@ std::string ExtractMojoInterfaceTag(const std::string& method_symbol) { } // namespace -#if !PERFETTO_IS_AT_LEAST_CPP17() -constexpr char ArgsTranslationTable::kChromeHistogramHashKey[]; -constexpr char ArgsTranslationTable::kChromeHistogramNameKey[]; - -constexpr char ArgsTranslationTable::kChromeUserEventHashKey[]; -constexpr char ArgsTranslationTable::kChromeUserEventActionKey[]; - -constexpr char ArgsTranslationTable::kChromePerformanceMarkSiteHashKey[]; -constexpr char ArgsTranslationTable::kChromePerformanceMarkSiteKey[]; - -constexpr char ArgsTranslationTable::kChromePerformanceMarkMarkHashKey[]; -constexpr char ArgsTranslationTable::kChromePerformanceMarkMarkKey[]; - -constexpr char ArgsTranslationTable::kMojoMethodMappingIdKey[]; -constexpr char ArgsTranslationTable::kMojoMethodRelPcKey[]; -constexpr char ArgsTranslationTable::kMojoMethodNameKey[]; -constexpr char ArgsTranslationTable::kMojoIntefaceTagKey[]; -#endif - ArgsTranslationTable::ArgsTranslationTable(TraceStorage* storage) : storage_(storage), interned_chrome_histogram_hash_key_( diff --git a/src/trace_processor/importers/common/track_tracker.cc b/src/trace_processor/importers/common/track_tracker.cc index 07bd84837..f6a99f1ff 100644 --- a/src/trace_processor/importers/common/track_tracker.cc +++ b/src/trace_processor/importers/common/track_tracker.cc @@ -16,12 +16,43 @@ #include "src/trace_processor/importers/common/track_tracker.h" +#include <optional> + #include "src/trace_processor/importers/common/args_tracker.h" #include "src/trace_processor/importers/common/process_tracker.h" +#include "src/trace_processor/storage/trace_storage.h" namespace perfetto { namespace trace_processor { +namespace { + +const char* GetNameForGroup(TrackTracker::Group group) { + switch (group) { + case TrackTracker::Group::kMemory: + return "Memory"; + case TrackTracker::Group::kIo: + return "IO"; + case TrackTracker::Group::kVirtio: + return "Virtio"; + case TrackTracker::Group::kNetwork: + return "Network"; + case TrackTracker::Group::kPower: + return "Power"; + case TrackTracker::Group::kDeviceState: + return "Device State"; + case TrackTracker::Group::kThermals: + return "Thermals"; + case TrackTracker::Group::kClockFrequency: + return "Clock Freqeuncy"; + case TrackTracker::Group::kSizeSentinel: + PERFETTO_FATAL("Unexpected size passed as group"); + } + PERFETTO_FATAL("For GCC"); +} + +} // namespace + TrackTracker::TrackTracker(TraceProcessorContext* context) : source_key_(context->storage->InternString("source")), source_id_key_(context->storage->InternString("source_id")), @@ -193,7 +224,8 @@ TrackId TrackTracker::GetOrCreateTriggerTrack() { return *trigger_track_id_; } -TrackId TrackTracker::InternGlobalCounterTrack(StringId name, +TrackId TrackTracker::InternGlobalCounterTrack(TrackTracker::Group group, + StringId name, SetArgsCallback callback, StringId unit, StringId description) { @@ -203,6 +235,7 @@ TrackId TrackTracker::InternGlobalCounterTrack(StringId name, } tables::CounterTrackTable::Row row(name); + row.parent_id = InternTrackForGroup(group); row.unit = unit; row.description = description; TrackId track = @@ -380,5 +413,18 @@ TrackId TrackTracker::CreatePerfCounterTrack(StringId name, return context_->storage->mutable_perf_counter_track_table()->Insert(row).id; } +TrackId TrackTracker::InternTrackForGroup(TrackTracker::Group group) { + uint32_t group_idx = static_cast<uint32_t>(group); + const std::optional<TrackId>& group_id = group_track_ids_[group_idx]; + if (group_id) { + return *group_id; + } + + StringId id = context_->storage->InternString(GetNameForGroup(group)); + TrackId track_id = context_->storage->mutable_track_table()->Insert({id}).id; + group_track_ids_[group_idx] = track_id; + return track_id; +} + } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/importers/common/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h index c4a423262..99076f349 100644 --- a/src/trace_processor/importers/common/track_tracker.h +++ b/src/trace_processor/importers/common/track_tracker.h @@ -17,6 +17,7 @@ #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_ #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACK_TRACKER_H_ +#include <optional> #include "src/trace_processor/importers/common/args_tracker.h" #include "src/trace_processor/storage/trace_storage.h" #include "src/trace_processor/types/trace_processor_context.h" @@ -27,6 +28,23 @@ namespace trace_processor { // Tracks and stores tracks based on track types, ids and scopes. class TrackTracker { public: + // Enum which groups global tracks to avoid an explosion of tracks at the top + // level. + // Try and keep members of this enum high level as every entry here + // corresponds to ~1 extra UI track. + enum class Group : uint32_t { + kMemory = 0, + kIo, + kVirtio, + kNetwork, + kPower, + kDeviceState, + kThermals, + kClockFrequency, + + // Keep this last. + kSizeSentinel, + }; using SetArgsCallback = std::function<void(ArgsTracker::BoundInserter&)>; explicit TrackTracker(TraceProcessorContext*); @@ -67,7 +85,8 @@ class TrackTracker { TrackId GetOrCreateTriggerTrack(); // Interns a global counter track into the storage. - TrackId InternGlobalCounterTrack(StringId name, + TrackId InternGlobalCounterTrack(Group group, + StringId name, SetArgsCallback = {}, StringId unit = kNullStringId, StringId description = kNullStringId); @@ -155,6 +174,12 @@ class TrackTracker { std::tie(r.source_id, r.upid, r.source_scope); } }; + static constexpr size_t kGroupCount = + static_cast<uint32_t>(Group::kSizeSentinel); + + TrackId InternTrackForGroup(Group group); + + std::array<std::optional<TrackId>, kGroupCount> group_track_ids_; std::map<UniqueTid, TrackId> thread_tracks_; std::map<UniquePid, TrackId> process_tracks_; diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc index 033d24730..27df202e2 100644 --- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc +++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc @@ -24,7 +24,7 @@ namespace perfetto { namespace trace_processor { namespace { -std::array<FtraceMessageDescriptor, 482> descriptors{{ +std::array<FtraceMessageDescriptor, 484> descriptors{{ {nullptr, 0, {}}, {nullptr, 0, {}}, {nullptr, 0, {}}, @@ -5303,6 +5303,26 @@ std::array<FtraceMessageDescriptor, 482> descriptors{{ {"start", ProtoSchemaType::kUint32}, }, }, + { + "mali_mali_CSF_INTERRUPT_START", + 3, + { + {}, + {"kctx_tgid", ProtoSchemaType::kInt32}, + {"kctx_id", ProtoSchemaType::kUint32}, + {"info_val", ProtoSchemaType::kUint64}, + }, + }, + { + "mali_mali_CSF_INTERRUPT_END", + 3, + { + {}, + {"kctx_tgid", ProtoSchemaType::kInt32}, + {"kctx_id", ProtoSchemaType::kUint32}, + {"info_val", ProtoSchemaType::kUint64}, + }, + }, }}; } // namespace diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc index 29af4bccf..ec5f26462 100644 --- a/src/trace_processor/importers/ftrace/ftrace_parser.cc +++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc @@ -25,6 +25,7 @@ #include "src/trace_processor/importers/common/metadata_tracker.h" #include "src/trace_processor/importers/common/parser_types.h" #include "src/trace_processor/importers/common/process_tracker.h" +#include "src/trace_processor/importers/common/track_tracker.h" #include "src/trace_processor/importers/ftrace/binder_tracker.h" #include "src/trace_processor/importers/ftrace/thread_state_tracker.h" #include "src/trace_processor/importers/ftrace/v4l2_tracker.h" @@ -70,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" @@ -231,6 +233,10 @@ FtraceParser::FtraceParser(TraceProcessorContext* context) 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")), @@ -869,6 +875,10 @@ util::Status FtraceParser::ParseFtraceEvent(uint32_t cpu, ParseSuspendResume(ts, fld_bytes); break; } + case FtraceEvent::kSuspendResumeMinimalFieldNumber: { + ParseSuspendResumeMinimal(ts, fld_bytes); + break; + } case FtraceEvent::kDrmVblankEventFieldNumber: case FtraceEvent::kDrmVblankEventDeliveredFieldNumber: case FtraceEvent::kDrmSchedJobFieldNumber: @@ -1013,6 +1023,12 @@ util::Status FtraceParser::ParseFtraceEvent(uint32_t cpu, mali_gpu_event_tracker_.ParseMaliGpuEvent(ts, fld.id(), pid); break; } + case FtraceEvent::kMaliMaliCSFINTERRUPTSTARTFieldNumber: + case FtraceEvent::kMaliMaliCSFINTERRUPTENDFieldNumber: { + mali_gpu_event_tracker_.ParseMaliGpuIrqEvent(ts, fld.id(), cpu, + fld_bytes); + break; + } default: break; @@ -1097,7 +1113,7 @@ void FtraceParser::ParseGenericFtrace(int64_t ts, protos::pbzero::GenericFtraceEvent::Decoder evt(blob.data, blob.size); StringId event_id = context_->storage->InternString(evt.event_name()); UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid); - RawId id = context_->storage->mutable_raw_table() + RawId id = context_->storage->mutable_ftrace_event_table() ->Insert({ts, event_id, cpu, utid}) .id; auto inserter = context_->args_tracker->AddArgsTo(id); @@ -1139,7 +1155,7 @@ void FtraceParser::ParseTypedFtraceToRaw( const auto& message_strings = ftrace_message_strings_[ftrace_id]; UniqueTid utid = context_->process_tracker->GetOrCreateThread(tid); RawId id = - context_->storage->mutable_raw_table() + context_->storage->mutable_ftrace_event_table() ->Insert({timestamp, message_strings.message_name_id, cpu, utid}) .id; auto inserter = context_->args_tracker->AddArgsTo(id); @@ -1428,8 +1444,8 @@ void FtraceParser::ParseIonHeapGrowOrShrink(int64_t timestamp, } // Push the global counter. - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(global_name_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, global_name_id); context_->event_tracker->PushCounter(timestamp, static_cast<double>(total_bytes), track); @@ -1466,8 +1482,8 @@ void FtraceParser::ParseIonStat(int64_t timestamp, protozero::ConstBytes data) { protos::pbzero::IonStatFtraceEvent::Decoder ion(data.data, data.size); // Push the global counter. - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(ion_total_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, ion_total_id_); context_->event_tracker->PushCounter( timestamp, static_cast<double>(ion.total_allocated()), track); @@ -1502,8 +1518,8 @@ void FtraceParser::ParseDmaHeapStat(int64_t timestamp, protos::pbzero::DmaHeapStatFtraceEvent::Decoder dma_heap(data.data, data.size); // Push the global counter. - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(dma_heap_total_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, dma_heap_total_id_); context_->event_tracker->PushCounter( timestamp, static_cast<double>(dma_heap.total_allocated()), track); @@ -1815,7 +1831,8 @@ void FtraceParser::ClockRate(int64_t timestamp, clock_name.data(), int(subtitle.size()), subtitle.data()); StringId name = context_->storage->InternString(counter_name.c_str()); - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kClockFrequency, name); context_->event_tracker->PushCounter(timestamp, static_cast<double>(rate), track); } @@ -2090,8 +2107,8 @@ void FtraceParser::ParseGpuMemTotal(int64_t timestamp, if (pid == 0) { // Pid 0 is used to indicate the global total track = context_->track_tracker->InternGlobalCounterTrack( - gpu_mem_total_name_id_, {}, gpu_mem_total_unit_id_, - gpu_mem_total_global_desc_id_); + TrackTracker::Group::kMemory, gpu_mem_total_name_id_, {}, + gpu_mem_total_unit_id_, gpu_mem_total_global_desc_id_); } else { // It's possible for GpuMemTotal ftrace events to be emitted by kworker // threads *after* process death. In this case, we simply want to discard @@ -2129,7 +2146,8 @@ void FtraceParser::ParseThermalTemperature(int64_t timestamp, base::StackString<255> counter_name( "%.*s Temperature", int(thermal_zone.size()), thermal_zone.data()); StringId name = context_->storage->InternString(counter_name.string_view()); - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kThermals, name); context_->event_tracker->PushCounter(timestamp, event.temp(), track); } @@ -2140,7 +2158,8 @@ void FtraceParser::ParseCdevUpdate(int64_t timestamp, base::StackString<255> counter_name("%.*s Cooling Device", int(type.size()), type.data()); StringId name = context_->storage->InternString(counter_name.string_view()); - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kThermals, name); context_->event_tracker->PushCounter( timestamp, static_cast<double>(event.target()), track); } @@ -2192,7 +2211,8 @@ void FtraceParser::ParseFastRpcDmaStat(int64_t timestamp, } // Push the global counter. - TrackId track = context_->track_tracker->InternGlobalCounterTrack(total_name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, total_name); context_->event_tracker->PushCounter( timestamp, static_cast<double>(event.total_allocated()), track); @@ -2225,7 +2245,8 @@ void FtraceParser::ParseNetifReceiveSkb(uint32_t cpu, nic_received_bytes_[name] += event.len(); uint64_t nic_received_kilobytes = nic_received_bytes_[name] / 1024; - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kNetwork, name); std::optional<CounterId> id = context_->event_tracker->PushCounter( timestamp, static_cast<double>(nic_received_kilobytes), track); if (!id) { @@ -2256,7 +2277,8 @@ void FtraceParser::ParseNetDevXmit(uint32_t cpu, nic_transmitted_bytes_[name] += evt.len(); uint64_t nic_transmitted_kilobytes = nic_transmitted_bytes_[name] / 1024; - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kNetwork, name); std::optional<CounterId> id = context_->event_tracker->PushCounter( timestamp, static_cast<double>(nic_transmitted_kilobytes), track); if (!id) { @@ -2392,14 +2414,14 @@ void FtraceParser::ParseCpuFrequencyLimits(int64_t timestamp, // Push max freq to global counter. StringId max_name = context_->storage->InternString(max_counter_name.c_str()); TrackId max_track = - context_->track_tracker->InternGlobalCounterTrack(max_name); + context_->track_tracker->InternCpuCounterTrack(max_name, evt.cpu_id()); context_->event_tracker->PushCounter( timestamp, static_cast<double>(evt.max_freq()), max_track); // Push min freq to global counter. StringId min_name = context_->storage->InternString(min_counter_name.c_str()); TrackId min_track = - context_->track_tracker->InternGlobalCounterTrack(min_name); + context_->track_tracker->InternCpuCounterTrack(min_name, evt.cpu_id()); context_->event_tracker->PushCounter( timestamp, static_cast<double>(evt.min_freq()), min_track); } @@ -2414,8 +2436,8 @@ void FtraceParser::ParseKfreeSkb(int64_t timestamp, } num_of_kfree_skb_ip_prot += 1; - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(kfree_skb_name_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kNetwork, kfree_skb_name_id_); std::optional<CounterId> id = context_->event_tracker->PushCounter( timestamp, static_cast<double>(num_of_kfree_skb_ip_prot), track); if (!id) { @@ -2435,6 +2457,7 @@ void FtraceParser::ParseCrosEcSensorhubData(int64_t timestamp, // Push the global counter. TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kDeviceState, context_->storage->InternString( base::StringView("cros_ec.cros_ec_sensorhub_data." + std::to_string(evt.ec_sensor_num())))); @@ -2474,8 +2497,8 @@ void FtraceParser::ParseUfshcdClkGating(int64_t timestamp, clk_state = 2; break; } - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(ufs_clkgating_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kNetwork, ufs_clkgating_id_); context_->event_tracker->PushCounter(timestamp, static_cast<double>(clk_state), track); } @@ -2796,8 +2819,8 @@ void FtraceParser::ParseUfshcdCommand(int64_t timestamp, uint32_t num = evt.doorbell() > 0 ? static_cast<uint32_t>(PERFETTO_POPCOUNT(evt.doorbell())) : (evt.str_t() == 1 ? 0 : 1); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(ufs_command_count_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kIo, ufs_command_count_id_); context_->event_tracker->PushCounter(timestamp, static_cast<double>(num), track); @@ -2910,6 +2933,26 @@ void FtraceParser::ParseSuspendResume(int64_t timestamp, 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); @@ -2917,8 +2960,8 @@ void FtraceParser::ParseSchedCpuUtilCfs(int64_t timestamp, StringId util_track_name_id = context_->storage->InternString(util_track_name.string_view()); - TrackId util_track = - context_->track_tracker->InternGlobalCounterTrack(util_track_name_id); + TrackId util_track = context_->track_tracker->InternCpuCounterTrack( + util_track_name_id, evt.cpu()); context_->event_tracker->PushCounter( timestamp, static_cast<double>(evt.cpu_util()), util_track); @@ -2926,8 +2969,8 @@ void FtraceParser::ParseSchedCpuUtilCfs(int64_t timestamp, StringId cap_track_name_id = context_->storage->InternString(cap_track_name.string_view()); - TrackId cap_track = - context_->track_tracker->InternGlobalCounterTrack(cap_track_name_id); + TrackId cap_track = context_->track_tracker->InternCpuCounterTrack( + cap_track_name_id, evt.cpu()); context_->event_tracker->PushCounter( timestamp, static_cast<double>(evt.capacity()), cap_track); @@ -2936,8 +2979,8 @@ void FtraceParser::ParseSchedCpuUtilCfs(int64_t timestamp, StringId nrr_track_name_id = context_->storage->InternString(nrr_track_name.string_view()); - TrackId nrr_track = - context_->track_tracker->InternGlobalCounterTrack(nrr_track_name_id); + TrackId nrr_track = context_->track_tracker->InternCpuCounterTrack( + nrr_track_name_id, evt.cpu()); context_->event_tracker->PushCounter( timestamp, static_cast<double>(evt.nr_running()), nrr_track); } diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h index abfb0e3ef..ccb40f8dd 100644 --- a/src/trace_processor/importers/ftrace/ftrace_parser.h +++ b/src/trace_processor/importers/ftrace/ftrace_parser.h @@ -216,6 +216,7 @@ class FtraceParser { 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 @@ class FtraceParser { 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/iostat_tracker.cc b/src/trace_processor/importers/ftrace/iostat_tracker.cc index 27e576204..d3c40d8f8 100644 --- a/src/trace_processor/importers/ftrace/iostat_tracker.cc +++ b/src/trace_processor/importers/ftrace/iostat_tracker.cc @@ -43,8 +43,8 @@ void IostatTracker::ParseF2fsIostat(int64_t timestamp, uint64_t value) { std::string track_name = tagPrefix + "." + std::string(counter_name); StringId string_id = context_->storage->InternString(track_name.c_str()); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(string_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kIo, string_id); context_->event_tracker->PushCounter(timestamp, static_cast<double>(value), track); }; @@ -83,8 +83,8 @@ void IostatTracker::ParseF2fsIostatLatency(int64_t timestamp, uint64_t value) { std::string track_name = tagPrefix + "." + std::string(counter_name); StringId string_id = context_->storage->InternString(track_name.c_str()); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(string_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kIo, string_id); context_->event_tracker->PushCounter(timestamp, static_cast<double>(value), track); }; diff --git a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc index c59bdcced..406479764 100644 --- a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc +++ b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h" +#include "perfetto/ext/base/string_utils.h" #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h" #include "protos/perfetto/trace/ftrace/mali.pbzero.h" #include "src/trace_processor/importers/common/async_track_set_tracker.h" @@ -35,7 +36,11 @@ MaliGpuEventTracker::MaliGpuEventTracker(TraceProcessorContext* context) mali_KCPU_FENCE_SIGNAL_id_( context->storage->InternString("mali_KCPU_FENCE_SIGNAL")), mali_KCPU_FENCE_WAIT_id_( - context->storage->InternString("mali_KCPU_FENCE_WAIT")) {} + context->storage->InternString("mali_KCPU_FENCE_WAIT")), + mali_CSF_INTERRUPT_id_( + context->storage->InternString("mali_CSF_INTERRUPT")), + mali_CSF_INTERRUPT_info_val_id_( + context->storage->InternString("info_val")) {} void MaliGpuEventTracker::ParseMaliGpuEvent(int64_t ts, int32_t field_id, @@ -76,6 +81,36 @@ void MaliGpuEventTracker::ParseMaliGpuEvent(int64_t ts, } } +void MaliGpuEventTracker::ParseMaliGpuIrqEvent(int64_t ts, + int32_t field_id, + uint32_t cpu, + protozero::ConstBytes blob) { + using protos::pbzero::FtraceEvent; + + // Since these events are called from an interrupt context they cannot be + // associated to a single process or thread. Add to a custom Mali Irq track + // instead. + base::StackString<255> track_name("Mali Irq Cpu %d", cpu); + StringId track_name_id = + context_->storage->InternString(track_name.string_view()); + TrackId track_id = + context_->track_tracker->InternCpuTrack(track_name_id, cpu); + + switch (field_id) { + case FtraceEvent::kMaliMaliCSFINTERRUPTSTARTFieldNumber: { + ParseMaliCSFInterruptStart(ts, track_id, blob); + break; + } + case FtraceEvent::kMaliMaliCSFINTERRUPTENDFieldNumber: { + ParseMaliCSFInterruptEnd(ts, track_id, blob); + break; + } + default: + PERFETTO_DFATAL("Unexpected field id"); + break; + } +} + void MaliGpuEventTracker::ParseMaliKcpuCqsSet(int64_t timestamp, TrackId track_id) { context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId, @@ -111,5 +146,34 @@ void MaliGpuEventTracker::ParseMaliKcpuFenceWaitEnd(int64_t timestamp, context_->slice_tracker->End(timestamp, track_id, kNullStringId, mali_KCPU_FENCE_WAIT_id_); } + +void MaliGpuEventTracker::ParseMaliCSFInterruptStart( + int64_t timestamp, + TrackId track_id, + protozero::ConstBytes blob) { + protos::pbzero::MaliMaliCSFINTERRUPTSTARTFtraceEvent::Decoder evt(blob.data, + blob.size); + auto args_inserter = [this, &evt](ArgsTracker::BoundInserter* inserter) { + inserter->AddArg(mali_CSF_INTERRUPT_info_val_id_, + Variadic::UnsignedInteger(evt.info_val())); + }; + + context_->slice_tracker->Begin(timestamp, track_id, kNullStringId, + mali_CSF_INTERRUPT_id_, args_inserter); +} + +void MaliGpuEventTracker::ParseMaliCSFInterruptEnd(int64_t timestamp, + TrackId track_id, + protozero::ConstBytes blob) { + protos::pbzero::MaliMaliCSFINTERRUPTSTARTFtraceEvent::Decoder evt(blob.data, + blob.size); + auto args_inserter = [this, &evt](ArgsTracker::BoundInserter* inserter) { + inserter->AddArg(mali_CSF_INTERRUPT_info_val_id_, + Variadic::UnsignedInteger(evt.info_val())); + }; + + context_->slice_tracker->End(timestamp, track_id, kNullStringId, + mali_CSF_INTERRUPT_id_, args_inserter); +} } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h index 9f74c6313..934266671 100644 --- a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h +++ b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h @@ -18,6 +18,7 @@ #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_MALI_GPU_EVENT_TRACKER_H_ #include "src/trace_processor/storage/trace_storage.h" +#include "src/trace_processor/util/descriptors.h" namespace perfetto { namespace trace_processor { @@ -28,6 +29,10 @@ class MaliGpuEventTracker { public: explicit MaliGpuEventTracker(TraceProcessorContext*); void ParseMaliGpuEvent(int64_t timestamp, int32_t field_id, uint32_t pid); + void ParseMaliGpuIrqEvent(int64_t timestamp, + int32_t field_id, + uint32_t cpu, + protozero::ConstBytes blob); private: TraceProcessorContext* context_; @@ -35,12 +40,20 @@ class MaliGpuEventTracker { StringId mali_KCPU_CQS_WAIT_id_; StringId mali_KCPU_FENCE_SIGNAL_id_; StringId mali_KCPU_FENCE_WAIT_id_; + StringId mali_CSF_INTERRUPT_id_; + StringId mali_CSF_INTERRUPT_info_val_id_; void ParseMaliKcpuFenceSignal(int64_t timestamp, TrackId track_id); void ParseMaliKcpuFenceWaitStart(int64_t timestamp, TrackId track_id); void ParseMaliKcpuFenceWaitEnd(int64_t timestamp, TrackId track_id); void ParseMaliKcpuCqsSet(int64_t timestamp, TrackId track_id); void ParseMaliKcpuCqsWaitStart(int64_t timestamp, TrackId track_id); void ParseMaliKcpuCqsWaitEnd(int64_t timestamp, TrackId track_id); + void ParseMaliCSFInterruptStart(int64_t timestamp, + TrackId track_id, + protozero::ConstBytes blob); + void ParseMaliCSFInterruptEnd(int64_t timestamp, + TrackId track_id, + protozero::ConstBytes blob); }; } // namespace trace_processor diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc index f67e5717a..68fe3b7ed 100644 --- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc +++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc @@ -240,7 +240,7 @@ void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu, if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) { // Add an entry to the raw table. - RawId id = context_->storage->mutable_raw_table() + RawId id = context_->storage->mutable_ftrace_event_table() ->Insert({ts, sched_waking_id_, cpu, curr_utid}) .id; @@ -277,7 +277,7 @@ uint32_t SchedEventTracker::AddRawEventAndStartSlice(uint32_t cpu, if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) { // Push the raw event - this is done as the raw ftrace event codepath does // not insert sched_switch. - RawId id = context_->storage->mutable_raw_table() + RawId id = context_->storage->mutable_ftrace_event_table() ->Insert({ts, sched_switch_id_, cpu, prev_utid}) .id; diff --git a/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc index d4d09a2ca..9813311c1 100644 --- a/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc +++ b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc @@ -160,8 +160,8 @@ VirtioGpuTracker::VirtioGpuQueue::VirtioGpuQueue(TraceProcessorContext* context, void VirtioGpuTracker::VirtioGpuQueue::HandleNumFree(int64_t timestamp, uint32_t num_free) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(num_free_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kVirtio, num_free_id_); context_->event_tracker->PushCounter(timestamp, static_cast<double>(num_free), track); } @@ -201,10 +201,10 @@ void VirtioGpuTracker::VirtioGpuQueue::HandleCmdResponse(int64_t timestamp, int64_t duration = timestamp - *start_timestamp; - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(latency_id_); - context_->event_tracker->PushCounter(timestamp, - static_cast<double>(duration), track); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kVirtio, latency_id_); + context_->event_tracker->PushCounter(timestamp, static_cast<double>(duration), + track); start_timestamps_.Erase(seqno); } diff --git a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc index 8acc4509e..e61a13714 100644 --- a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc +++ b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc @@ -333,6 +333,44 @@ TEST_F(FuchsiaTraceParserTest, InlineInstantEvent) { EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0); } +TEST_F(FuchsiaTraceParserTest, BooleanArguments) { + // Inline name of 8 bytes + uint64_t name_ref = uint64_t{0x8008} << 48; + // Inline category of 8 bytes + uint64_t category_ref = uint64_t{0x8008} << 32; + // Inline threadref + uint64_t threadref = uint64_t{0}; + // 2 arguments + uint64_t argument_count = uint64_t{2} << 20; + // Instant Event + uint64_t event_type = 0 << 16; + uint64_t size = 8 << 4; + uint64_t record_type = 4; + + auto header = name_ref | category_ref | threadref | event_type | + argument_count | size | record_type; + push_word(header); + // Timestamp + push_word(0xAAAAAAAAAAAAAAAA); + // Pid + tid + push_word(0xBBBBBBBBBBBBBBBB); + push_word(0xCCCCCCCCCCCCCCCC); + // Inline Category + push_word(0xDDDDDDDDDDDDDDDD); + // Inline Name + push_word(0xEEEEEEEEEEEEEEEE); + // Boolean argument true + push_word(0x0000'0001'8008'0029); + // 8 byte arg name stream + push_word(0x0000'0000'0000'0000); + // Boolean argument false + push_word(0x0000'0000'8008'002A); + // 8 byte arg name stream + push_word(0x0000'0000'0000'0000); + EXPECT_TRUE(Tokenize().ok()); + EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0); +} + TEST_F(FuchsiaTraceParserTest, FxtWithProtos) { // Serialize some protos to bytes protozero::HeapBuffered<protos::pbzero::Trace> protos; diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc index 014306013..0b18a7779 100644 --- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc +++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc @@ -55,6 +55,7 @@ constexpr uint32_t kDouble = 5; constexpr uint32_t kString = 6; constexpr uint32_t kPointer = 7; constexpr uint32_t kKoid = 8; +constexpr uint32_t kBool = 9; } // namespace @@ -169,6 +170,11 @@ FuchsiaTraceParser::ParseArgs( arg.value = fuchsia_trace_utils::ArgValue::Koid(value); break; } + case kBool: { + arg.value = fuchsia_trace_utils::ArgValue::Bool( + fuchsia_trace_utils::ReadField<bool>(arg_header, 32, 63)); + break; + } default: arg.value = fuchsia_trace_utils::ArgValue::Unknown(); break; @@ -325,6 +331,7 @@ void FuchsiaTraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord fr) { case fuchsia_trace_utils::ArgValue::kString: case fuchsia_trace_utils::ArgValue::kPointer: case fuchsia_trace_utils::ArgValue::kKoid: + case fuchsia_trace_utils::ArgValue::kBool: case fuchsia_trace_utils::ArgValue::kUnknown: context_->storage->IncrementStats( stats::fuchsia_non_numeric_counters); diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc index 47de0f8ac..48dfe2e70 100644 --- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc +++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc @@ -83,6 +83,8 @@ Variadic ArgValue::ToStorageVariadic(TraceStorage* storage) const { return Variadic::Pointer(pointer_); case ArgType::kKoid: return Variadic::Integer(static_cast<int64_t>(koid_)); + case ArgType::kBool: + return Variadic::Boolean(bool_); case ArgType::kUnknown: return Variadic::String(storage->InternString("unknown")); } diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h index f4ae23170..d4a3efa60 100644 --- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h +++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h @@ -52,6 +52,7 @@ class ArgValue { kString, kPointer, kKoid, + kBool, kUnknown, }; @@ -118,6 +119,13 @@ class ArgValue { return v; } + static ArgValue Bool(bool value) { + ArgValue v; + v.type_ = ArgType::kBool; + v.bool_ = value; + return v; + } + static ArgValue Unknown() { ArgValue v; v.type_ = ArgType::kUnknown; @@ -167,6 +175,11 @@ class ArgValue { return koid_; } + uint64_t Bool() const { + PERFETTO_DCHECK(type_ == ArgType::kBool); + return bool_; + } + Variadic ToStorageVariadic(TraceStorage*) const; private: @@ -180,6 +193,7 @@ class ArgValue { StringId string_; uint64_t pointer_; uint64_t koid_; + bool bool_; }; }; 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 88e747671..1d550818e 100644 --- a/src/trace_processor/importers/proto/android_camera_event_module.h +++ b/src/trace_processor/importers/proto/android_camera_event_module.h @@ -24,7 +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/slice_tables.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 e1feb51e1..fece0079e 100644 --- a/src/trace_processor/importers/proto/android_probes_module.cc +++ b/src/trace_processor/importers/proto/android_probes_module.cc @@ -155,7 +155,8 @@ ModuleResult AndroidProbesModule::TokenizePacket( StringId counter_name_id = context_->storage->InternString(counter_name.string_view()); TrackId track = context_->track_tracker->InternGlobalCounterTrack( - counter_name_id, [this, &desc](ArgsTracker::BoundInserter& inserter) { + TrackTracker::Group::kPower, counter_name_id, + [this, &desc](ArgsTracker::BoundInserter& inserter) { StringId raw_name = context_->storage->InternString(desc.rail_name()); inserter.AddArg(power_rail_raw_name_id_, Variadic::String(raw_name)); @@ -180,7 +181,9 @@ ModuleResult AndroidProbesModule::TokenizePacket( : 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()); @@ -206,7 +209,7 @@ void AndroidProbesModule::ParseTracePacketData( 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 754f30a3d..b165a2b4c 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 @@ AndroidProbesParser::AndroidProbesParser(TraceProcessorContext* context) 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); @@ -82,14 +83,14 @@ void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) { std::string("batt.").append(batt_name).append(".current.avg_ua"))); } if (evt.has_charge_counter_uah()) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(batt_charge_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, batt_charge_id); context_->event_tracker->PushCounter( ts, static_cast<double>(evt.charge_counter_uah()), track); } else if (evt.has_energy_counter_uwh() && evt.has_voltage_uv()) { // Calculate charge counter from energy counter and voltage. - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(batt_charge_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, batt_charge_id); auto energy = evt.energy_counter_uwh(); auto voltage = evt.voltage_uv(); if (voltage > 0) { @@ -99,26 +100,28 @@ void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) { } if (evt.has_capacity_percent()) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, batt_capacity_id); context_->event_tracker->PushCounter( ts, static_cast<double>(evt.capacity_percent()), track); } if (evt.has_current_ua()) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(batt_current_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, batt_current_id); context_->event_tracker->PushCounter( ts, static_cast<double>(evt.current_ua()), track); } if (evt.has_current_avg_ua()) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(batt_current_avg_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, batt_current_avg_id); context_->event_tracker->PushCounter( ts, static_cast<double>(evt.current_avg_ua()), track); } } -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 @@ void AndroidProbesParser::ParsePowerRails(int64_t ts, ConstBytes blob) { 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); } @@ -219,7 +226,7 @@ void AndroidProbesParser::ParseEntityStateResidency(int64_t ts, } TrackId track = context_->track_tracker->InternGlobalCounterTrack( - entity_state->overall_name); + TrackTracker::Group::kPower, entity_state->overall_name); context_->event_tracker->PushCounter( ts, double(residency.total_time_in_state_ms()), track); } @@ -396,8 +403,8 @@ void AndroidProbesParser::ParseInitialDisplayState(int64_t ts, ConstBytes blob) { protos::pbzero::InitialDisplayState::Decoder state(blob.data, blob.size); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(screen_state_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kDeviceState, screen_state_id_); context_->event_tracker->PushCounter(ts, state.display_state(), track); } @@ -427,8 +434,8 @@ void AndroidProbesParser::ParseAndroidSystemProperty(int64_t ts, std::optional<int32_t> state = base::StringToInt32(kv.value().ToStdString()); if (state) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(name_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kNetwork, name_id); context_->event_tracker->PushCounter(ts, *state, track); } } else if (name == "debug.tracing.screen_state") { @@ -442,8 +449,8 @@ void AndroidProbesParser::ParseAndroidSystemProperty(int64_t ts, std::optional<int32_t> state = base::StringToInt32(kv.value().ToStdString()); if (state) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(*mapped_name_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kDeviceState, *mapped_name_id); context_->event_tracker->PushCounter(ts, *state, track); } } diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h index 7ea91a1f4..06b1019eb 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 @@ class AndroidProbesParser { 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 @@ class AndroidProbesParser { 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/atoms.descriptor b/src/trace_processor/importers/proto/atoms.descriptor Binary files differindex daeacb700..2d4c191ea 100644 --- a/src/trace_processor/importers/proto/atoms.descriptor +++ b/src/trace_processor/importers/proto/atoms.descriptor 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 8c65fd4e4..b011fe5d2 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 @@ class ChromeSystemProbesParser { // 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/gpu_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc index 56bc16588..9022cbb02 100644 --- a/src/trace_processor/importers/proto/gpu_event_parser.cc +++ b/src/trace_processor/importers/proto/gpu_event_parser.cc @@ -735,8 +735,8 @@ void GpuEventParser::ParseGpuMemTotalEvent(int64_t ts, ConstBytes blob) { if (pid == 0) { // Pid 0 is used to indicate the global total track = context_->track_tracker->InternGlobalCounterTrack( - gpu_mem_total_name_id_, {}, gpu_mem_total_unit_id_, - gpu_mem_total_global_desc_id_); + TrackTracker::Group::kMemory, gpu_mem_total_name_id_, {}, + gpu_mem_total_unit_id_, gpu_mem_total_global_desc_id_); } else { // Process emitting the packet can be different from the pid in the event. UniqueTid utid = context_->process_tracker->UpdateThread(pid, pid); diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc index fa1ffd643..b18bdeaf5 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 @@ void ProtoTraceParser::ParseMetatraceEvent(int64_t ts, ConstBytes blob) { // 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 @@ void ProtoTraceParser::ParseMetatraceEvent(int64_t ts, ConstBytes blob) { 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/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc index 2444215d5..97779d43d 100644 --- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc +++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc @@ -647,11 +647,11 @@ TEST_F(ProtoTraceParserTest, LoadMemInfo) { meminfo->set_value(value); EXPECT_CALL(*event_, PushCounter(static_cast<int64_t>(ts), - DoubleEq(value * 1024.0), TrackId{0u})); + DoubleEq(value * 1024.0), TrackId{1u})); Tokenize(); context_.sorter->ExtractEventsForced(); - EXPECT_EQ(context_.storage->track_table().row_count(), 1u); + EXPECT_EQ(context_.storage->track_table().row_count(), 2u); } TEST_F(ProtoTraceParserTest, LoadVmStats) { @@ -665,11 +665,11 @@ TEST_F(ProtoTraceParserTest, LoadVmStats) { meminfo->set_value(value); EXPECT_CALL(*event_, PushCounter(static_cast<int64_t>(ts), DoubleEq(value), - TrackId{0u})); + TrackId{1u})); Tokenize(); context_.sorter->ExtractEventsForced(); - EXPECT_EQ(context_.storage->track_table().row_count(), 1u); + EXPECT_EQ(context_.storage->track_table().row_count(), 2u); } TEST_F(ProtoTraceParserTest, LoadProcessPacket) { diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h index 999a3392c..430b7a25c 100644 --- a/src/trace_processor/importers/proto/statsd_module.h +++ b/src/trace_processor/importers/proto/statsd_module.h @@ -26,7 +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/slice_tables.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" #include "src/trace_processor/util/descriptors.h" diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc index da175fa57..da6a2d296 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 @@ SystemProbesParser::SystemProbesParser(TraceProcessorContext* context) 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) { @@ -170,8 +180,8 @@ void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) { base::StackString<512> track_name("%s.%s", tag_prefix.c_str(), counter_name); StringId string_id = context_->storage->InternString(track_name.c_str()); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(string_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kIo, string_id); context_->event_tracker->PushCounter(ts, value, track); }; @@ -244,7 +254,7 @@ void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { } // /proc/meminfo counters are in kB, convert to bytes TrackId track = context_->track_tracker->InternGlobalCounterTrack( - meminfo_strs_id_[key]); + TrackTracker::Group::kMemory, meminfo_strs_id_[key]); context_->event_tracker->PushCounter( ts, static_cast<double>(mi.value()) * 1024., track); } @@ -259,7 +269,8 @@ void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { "%.*s %.*s", int(key.size()), key.data(), int(devfreq_subtitle.size()), devfreq_subtitle.data()); StringId name = context_->storage->InternString(counter_name.string_view()); - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kClockFrequency, name); context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()), track); } @@ -279,8 +290,8 @@ void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { context_->storage->IncrementStats(stats::vmstat_unknown_keys); continue; } - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(vmstat_strs_id_[key]); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, vmstat_strs_id_[key]); context_->event_tracker->PushCounter(ts, static_cast<double>(vm.value()), track); } @@ -348,22 +359,22 @@ void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { } if (sys_stats.has_num_forks()) { - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(num_forks_name_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kDeviceState, num_forks_name_id_); context_->event_tracker->PushCounter( ts, static_cast<double>(sys_stats.num_forks()), track); } if (sys_stats.has_num_irq_total()) { TrackId track = context_->track_tracker->InternGlobalCounterTrack( - num_irq_total_name_id_); + TrackTracker::Group::kDeviceState, num_irq_total_name_id_); context_->event_tracker->PushCounter( ts, static_cast<double>(sys_stats.num_irq_total()), track); } if (sys_stats.has_num_softirq_total()) { TrackId track = context_->track_tracker->InternGlobalCounterTrack( - num_softirq_total_name_id_); + TrackTracker::Group::kDeviceState, num_softirq_total_name_id_); context_->event_tracker->PushCounter( ts, static_cast<double>(sys_stats.num_softirq_total()), track); } @@ -380,7 +391,8 @@ void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) { node.c_str(), zone.c_str(), size_kb); StringId name = context_->storage->InternString(counter_name.string_view()); - TrackId track = context_->track_tracker->InternGlobalCounterTrack(name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kMemory, name); context_->event_tracker->PushCounter(ts, static_cast<double>(*order_it), track); order++; diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h index 77721d515..c1576a55a 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 @@ class SystemProbesParser { // 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/importers/proto/track_event_tracker.cc b/src/trace_processor/importers/proto/track_event_tracker.cc index 7591028e9..c65656499 100644 --- a/src/trace_processor/importers/proto/track_event_tracker.cc +++ b/src/trace_processor/importers/proto/track_event_tracker.cc @@ -24,11 +24,6 @@ namespace perfetto { namespace trace_processor { -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr uint64_t TrackEventTracker::kDefaultDescriptorTrackUuid; -#endif - TrackEventTracker::TrackEventTracker(TraceProcessorContext* context) : source_key_(context->storage->InternString("source")), source_id_key_(context->storage->InternString("source_id")), diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc index 958cb7e43..c0851d74c 100644 --- a/src/trace_processor/importers/systrace/systrace_line_parser.cc +++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc @@ -216,8 +216,8 @@ util::Status SystraceLineParser::ParseLine(const SystraceLine& line) { std::string clock_name_str = args["name"] + subtitle; StringId clock_name = context_->storage->InternString(base::StringView(clock_name_str)); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(clock_name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kClockFrequency, clock_name); context_->event_tracker->PushCounter(line.ts, rate.value(), track); } else if (line.event_name == "workqueue_execute_start") { auto split = base::SplitString(line.args_str, "function "); @@ -232,8 +232,8 @@ util::Status SystraceLineParser::ParseLine(const SystraceLine& line) { std::string thermal_zone = args["thermal_zone"] + " Temperature"; StringId track_name = context_->storage->InternString(base::StringView(thermal_zone)); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(track_name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kThermals, track_name); auto temp = base::StringToInt32(args["temp"]); if (!temp.has_value()) { return util::Status("Could not convert temp"); @@ -243,8 +243,8 @@ util::Status SystraceLineParser::ParseLine(const SystraceLine& line) { std::string type = args["type"] + " Cooling Device"; StringId track_name = context_->storage->InternString(base::StringView(type)); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(track_name); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kThermals, track_name); auto target = base::StringToDouble(args["target"]); if (!target.has_value()) { return util::Status("Could not convert target"); diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc index ac16506c3..b06bb7186 100644 --- a/src/trace_processor/importers/systrace/systrace_parser.cc +++ b/src/trace_processor/importers/systrace/systrace_parser.cc @@ -277,16 +277,16 @@ void SystraceParser::ParseSystracePoint( // once the UI has support for displaying instants. } else if (point.name == "ScreenState") { // Promote ScreenState to its own top level counter. - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(screen_state_id_); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kDeviceState, screen_state_id_); context_->event_tracker->PushCounter( ts, static_cast<double>(point.int_value), track); return; } else if (point.name.StartsWith("battery_stats.")) { // Promote battery_stats conters to global tracks. StringId name_id = context_->storage->InternString(point.name); - TrackId track = - context_->track_tracker->InternGlobalCounterTrack(name_id); + TrackId track = context_->track_tracker->InternGlobalCounterTrack( + TrackTracker::Group::kPower, name_id); context_->event_tracker->PushCounter( ts, static_cast<double>(point.int_value), track); return; diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h index 16e004c7a..2691e91ac 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 bf54d4454..a56666136 100644 --- a/src/trace_processor/metrics/sql/android/BUILD.gn +++ b/src/trace_processor/metrics/sql/android/BUILD.gn @@ -82,6 +82,7 @@ perfetto_sql_source_set("android") { "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 7dde09948..b88d7ed5e 100644 --- a/src/trace_processor/metrics/sql/android/android_batt.sql +++ b/src/trace_processor/metrics/sql/android/android_batt.sql @@ -49,8 +49,16 @@ FROM ( ) 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 +70,10 @@ JOIN 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', diff --git a/src/trace_processor/metrics/sql/android/android_binder.sql b/src/trace_processor/metrics/sql/android/android_binder.sql index 1d15050a4..f59f5d99b 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 @@ SELECT AndroidBinderMetric( '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 7c56db214..3538f7d86 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 @@ SELECT ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id, * 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 ( @@ -108,6 +127,17 @@ WITH all_main_thread_relevant_slices AS ( OR s.name GLOB 'relayoutWindow*' OR s.name GLOB 'ImageDecoder#decode*' ) + 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_jank_cuj.sql b/src/trace_processor/metrics/sql/android/android_jank_cuj.sql index bf7ce9ff2..299e83066 100644 --- a/src/trace_processor/metrics/sql/android/android_jank_cuj.sql +++ b/src/trace_processor/metrics/sql/android/android_jank_cuj.sql @@ -102,6 +102,8 @@ SELECT 'missed_app_frames', missed_app_frames, 'missed_sf_frames', missed_sf_frames, 'missed_frames_max_successive', missed_frames_max_successive, + 'sf_callback_missed_frames', sf_callback_missed_frames, + 'hwui_callback_missed_frames', hwui_callback_missed_frames, 'frame_dur_max', frame_dur_max) FROM android_jank_cuj_counter_metrics cm WHERE cm.cuj_id = cuj.cuj_id), @@ -111,6 +113,8 @@ SELECT 'missed_frames', SUM(app_missed OR sf_missed), 'missed_app_frames', SUM(app_missed), 'missed_sf_frames', SUM(sf_missed), + 'sf_callback_missed_frames', SUM(sf_callback_missed), + 'hwui_callback_missed_frames', SUM(hwui_callback_missed), 'frame_dur_max', MAX(f.dur), 'frame_dur_avg', CAST(AVG(f.dur) AS INTEGER), 'frame_dur_p50', CAST(PERCENTILE(f.dur, 50) AS INTEGER), @@ -129,6 +133,8 @@ SELECT 'missed_frames', SUM(app_missed OR sf_missed), 'missed_app_frames', SUM(app_missed), 'missed_sf_frames', SUM(sf_missed), + 'sf_callback_missed_frames', SUM(sf_callback_missed), + 'hwui_callback_missed_frames', SUM(hwui_callback_missed), 'frame_dur_max', MAX(f.dur), 'frame_dur_avg', CAST(AVG(f.dur) AS INTEGER), 'frame_dur_p50', CAST(PERCENTILE(f.dur, 50) AS INTEGER), @@ -150,7 +156,9 @@ SELECT 'dur', f.dur, 'dur_expected', f.dur_expected, 'app_missed', f.app_missed, - 'sf_missed', f.sf_missed)) + 'sf_missed', f.sf_missed, + 'sf_callback_missed', f.sf_callback_missed, + 'hwui_callback_missed', f.hwui_callback_missed)) FROM android_jank_cuj_frame f WHERE f.cuj_id = cuj.cuj_id ORDER BY frame_number ASC), 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 7cfaeb8b9..52e16930c 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 @@ SELECT AndroidMonitorContentionMetric( '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 da39e3450..892f6beef 100644 --- a/src/trace_processor/metrics/sql/android/android_startup.sql +++ b/src/trace_processor/metrics/sql/android/android_startup.sql @@ -209,14 +209,22 @@ SELECT 'time_gc_total', ( SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(TOTAL_GC_TIME_BY_LAUNCH(launches.startup_id))) ), + 'time_dex_open_thread_main', + 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, + launches.startup_id, 'Lock contention on*' ), 'time_monitor_contention_thread_main', DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH( - launches.startup_id, + launches.startup_id, 'Lock contention on a monitor*' ), 'time_before_start_process', ( @@ -286,6 +294,7 @@ SELECT 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 ), 'startup_concurrent_to_launch', ( SELECT RepeatedField(package) @@ -293,6 +302,11 @@ SELECT 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, @@ -362,11 +376,10 @@ SELECT WHERE MAIN_THREAD_TIME_FOR_LAUNCH_STATE_AND_IO_WAIT(launches.startup_id, 'D*', TRUE) > 155e6 UNION ALL - SELECT 'Time spent in OpenDexFilesFromOat*' + SELECT 'Main Thread - Time spent in OpenDexFilesFromOat*' AS slow_cause - WHERE - ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(launches.startup_id, 'OpenDexFilesFromOat*') - > launches.dur * 0.2 + WHERE ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE( + launches.startup_id, 'OpenDexFilesFromOat*') > launches.dur * 0.2 UNION ALL SELECT 'Time spent in bindApplication' @@ -392,9 +405,7 @@ SELECT > launches.dur * 0.15 UNION ALL - SELECT 'Potential CPU contention with ' - || MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id) - AS slow_cause + SELECT 'Potential CPU contention with another process' AS slow_cause WHERE MAIN_THREAD_TIME_FOR_LAUNCH_IN_RUNNABLE_STATE(launches.startup_id) > 100e6 AND MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id) IS NOT NULL diff --git a/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql b/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql index b14ce8490..498baf8a0 100644 --- a/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql +++ b/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql @@ -86,6 +86,37 @@ FROM ( ) WHERE event_type = 0 AND fence_id = next_fence_id; + +DROP VIEW IF EXISTS display_ids; +CREATE VIEW display_ids AS +SELECT DISTINCT display_id +FROM ( + SELECT display_id FROM frame_missed + UNION + SELECT display_id FROM hwc_frame_missed + UNION + SELECT display_id FROM gpu_frame_missed +); + +DROP VIEW IF EXISTS metrics_per_display; +CREATE VIEW metrics_per_display AS +SELECT AndroidSurfaceflingerMetric_MetricsPerDisplay( + 'display_id', d.display_id, + 'missed_frames', + (SELECT COUNT(1) FROM frame_missed WHERE value = 1 AND display_id = d.display_id), + 'missed_hwc_frames', + (SELECT COUNT(1) FROM hwc_frame_missed WHERE value = 1 AND display_id = d.display_id), + 'missed_gpu_frames', + (SELECT COUNT(1) FROM gpu_frame_missed WHERE value = 1 AND display_id = d.display_id), + 'missed_frame_rate', + (SELECT AVG(value) FROM frame_missed WHERE display_id = d.display_id), + 'missed_hwc_frame_rate', + (SELECT AVG(value) FROM hwc_frame_missed WHERE display_id = d.display_id), + 'missed_gpu_frame_rate', + (SELECT AVG(value) FROM gpu_frame_missed WHERE display_id = d.display_id) +) AS proto +FROM display_ids d; + DROP VIEW IF EXISTS android_surfaceflinger_output; CREATE VIEW android_surfaceflinger_output AS SELECT @@ -99,5 +130,6 @@ SELECT 'gpu_invocations', (SELECT COUNT(1) FROM gpu_waiting_end), 'avg_gpu_waiting_dur_ms', (SELECT AVG(dur) / 1e6 FROM gpu_waiting_span), 'total_non_empty_gpu_waiting_dur_ms', - (SELECT SUM(dur) / 1e6 FROM gpu_waiting_end) + (SELECT SUM(dur) / 1e6 FROM gpu_waiting_end), + 'metrics_per_display', (SELECT RepeatedField(proto) FROM metrics_per_display) ); diff --git a/src/trace_processor/metrics/sql/android/frame_missed.sql b/src/trace_processor/metrics/sql/android/frame_missed.sql index e6546bae0..5372177e4 100644 --- a/src/trace_processor/metrics/sql/android/frame_missed.sql +++ b/src/trace_processor/metrics/sql/android/frame_missed.sql @@ -22,12 +22,18 @@ WITH frame_missed_counters AS ( -- track should ever exist with this name (the track from -- surfaceflinger). ts - LAG(ts) OVER (ORDER BY ts) AS dur, + name, + INSTR(name, ' ') AS separator_pos, value FROM counter c JOIN process_counter_track t ON c.track_id = t.id - WHERE t.name = '{{track_name}}' + WHERE t.name GLOB '{{track_name}}*' ) SELECT + CASE + WHEN separator_pos = 0 THEN 'unspecified' + ELSE SUBSTR(name, separator_pos + 1) + END AS display_id, ts, dur, value diff --git a/src/trace_processor/metrics/sql/android/jank/cujs.sql b/src/trace_processor/metrics/sql/android/jank/cujs.sql index 2638a8e4d..03d0d96a4 100644 --- a/src/trace_processor/metrics/sql/android/jank/cujs.sql +++ b/src/trace_processor/metrics/sql/android/jank/cujs.sql @@ -90,11 +90,23 @@ SELECT FROM cuj_state_markers csm WHERE csm.cuj_id = cujs.cuj_id AND csm.marker_name GLOB '*layerId#*' LIMIT 1 - ) AS layer_id + ) AS layer_id, + ( + SELECT CAST(STR_SPLIT(csm.marker_name, 'beginVsync#', 1) AS INTEGER) + FROM cuj_state_markers csm + WHERE csm.cuj_id = cujs.cuj_id AND csm.marker_name GLOB '*beginVsync#*' + LIMIT 1 + ) AS begin_vsync, + ( + SELECT CAST(STR_SPLIT(csm.marker_name, 'endVsync#', 1) AS INTEGER) + FROM cuj_state_markers csm + WHERE csm.cuj_id = cujs.cuj_id AND csm.marker_name GLOB '*endVsync#*' + LIMIT 1 + ) AS end_vsync FROM cujs WHERE state != 'canceled' -- Older builds don't have the state markers so we allow NULL but filter out -- CUJs that are <4ms long - assuming CUJ was canceled in that case. OR (state IS NULL AND cujs.dur > 4e6) -ORDER BY ts ASC; +ORDER BY ts ASC;
\ No newline at end of file diff --git a/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql b/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql index 0d91cb8a7..687b808a2 100644 --- a/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql +++ b/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql @@ -13,8 +13,9 @@ -- See the License for the specific language governing permissions and -- limitations under the License. --- Stores the min and max vsync IDs for each of the CUJs. --- We calculate that by extracting the vsync ID from the +-- Stores the min and max vsync IDs for each of the CUJs which are extracted +-- from the CUJ markers. For backward compatibility (In case the markers don't +-- exist), We calculate that by extracting the vsync ID from the -- `Choreographer#doFrame` slices that are within the CUJ markers. DROP TABLE IF EXISTS android_jank_cuj_vsync_boundary; CREATE TABLE android_jank_cuj_vsync_boundary AS @@ -22,8 +23,8 @@ SELECT cuj.cuj_id, cuj.upid, -- also store upid to simplify further queries cuj.layer_id, -- also store layer_id to simplify further queries - MIN(vsync) AS vsync_min, - MAX(vsync) AS vsync_max + IFNULL(cuj.begin_vsync, MIN(vsync)) AS vsync_min, + IFNULL(cuj.end_vsync, MAX(vsync)) AS vsync_max FROM android_jank_cuj cuj JOIN android_jank_cuj_do_frame_slice USING (cuj_id) GROUP BY cuj.cuj_id, cuj.upid, cuj.layer_id; diff --git a/src/trace_processor/metrics/sql/android/jank/frames.sql b/src/trace_processor/metrics/sql/android/jank/frames.sql index 66f6639f0..ffef19d7f 100644 --- a/src/trace_processor/metrics/sql/android/jank/frames.sql +++ b/src/trace_processor/metrics/sql/android/jank/frames.sql @@ -13,6 +13,14 @@ -- See the License for the specific language governing permissions and -- limitations under the License. +DROP TABLE IF EXISTS vsync_missed_callback; +CREATE TABLE vsync_missed_callback AS +SELECT CAST(STR_SPLIT(name, 'Callback#', 1) AS INTEGER) AS vsync, + MAX(name GLOB '*SF*') as sf_callback_missed, + MAX(name GLOB '*HWUI*') as hwui_callback_missed +FROM slice +WHERE name GLOB '*FT#Missed*Callback*' +GROUP BY vsync; DROP TABLE IF EXISTS android_jank_cuj_frame_timeline; CREATE TABLE android_jank_cuj_frame_timeline AS @@ -35,6 +43,8 @@ SELECT OR jank_type GLOB '*SurfaceFlinger Scheduling*' OR jank_type GLOB '*Prediction Error*' OR jank_type GLOB '*Display HAL*') AS sf_missed, + IFNULL(MAX(sf_callback_missed), 0) AS sf_callback_missed, + IFNULL(MAX(hwui_callback_missed), 0) AS hwui_callback_missed, -- We use MIN to check if ALL layers finished on time MIN(on_time_finish) AS on_time_finish, MAX(timeline.ts + timeline.dur) AS ts_end_actual, @@ -54,6 +64,7 @@ JOIN actual_timeline_with_vsync timeline AND vsync <= vsync_max LEFT JOIN expected_frame_timeline_slice expected ON expected.upid = timeline.upid AND expected.name = timeline.name +LEFT JOIN vsync_missed_callback missed_callback USING(vsync) WHERE boundary.layer_id IS NULL OR ( @@ -99,6 +110,8 @@ SELECT frame_base.*, app_missed, sf_missed, + sf_callback_missed, + hwui_callback_missed, on_time_finish, ts_end_actual - ts AS dur, ts_end_actual - ts_do_frame_start AS dur_unadjusted, diff --git a/src/trace_processor/metrics/sql/android/jank/internal/counters.sql b/src/trace_processor/metrics/sql/android/jank/internal/counters.sql index fdcc04cee..d04229d94 100644 --- a/src/trace_processor/metrics/sql/android/jank/internal/counters.sql +++ b/src/trace_processor/metrics/sql/android/jank/internal/counters.sql @@ -51,6 +51,30 @@ SELECT CREATE_FUNCTION( ' ); +DROP TABLE IF EXISTS cuj_marker_missed_callback; +CREATE TABLE cuj_marker_missed_callback AS +SELECT + marker_track.name AS cuj_slice_name, + marker.ts, + marker.name AS marker_name +FROM slice marker +JOIN track marker_track on marker_track.id = marker.track_id +WHERE marker.name GLOB '*FT#Missed*'; + +SELECT CREATE_FUNCTION( + 'ANDROID_MISSED_VSYNCS_FOR_CALLBACK(cuj_slice_name STRING, ts_min INT, ts_max INT, callback_missed STRING)', + 'INT', + ' + SELECT IFNULL(SUM(marker_name GLOB $callback_missed), 0) + FROM cuj_marker_missed_callback + WHERE + cuj_slice_name = $cuj_slice_name + AND ts >= $ts_min + AND ($ts_max IS NULL OR ts <= $ts_max) + ORDER BY ts ASC LIMIT 1 + ' +); + DROP TABLE IF EXISTS android_jank_cuj_counter_metrics; CREATE TABLE android_jank_cuj_counter_metrics AS -- Order CUJs to get the ts of the next CUJ with the same name. @@ -60,6 +84,7 @@ WITH cujs_ordered AS ( SELECT cuj_id, cuj_name, + cuj_slice_name, upid, state, ts_end, @@ -83,5 +108,7 @@ SELECT ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedSfFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_sf_frames, ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxSuccessiveMissedFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_frames_max_successive, -- convert ms to nanos to align with the unit for `dur` in the other tables - ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxFrameTimeMillis', ts_earliest_allowed_counter, ts_end_next_cuj) * 1000000 AS frame_dur_max + ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxFrameTimeMillis', ts_earliest_allowed_counter, ts_end_next_cuj) * 1000000 AS frame_dur_max, + ANDROID_MISSED_VSYNCS_FOR_CALLBACK(cuj_slice_name, ts_earliest_allowed_counter, ts_end_next_cuj, '*SF*') AS sf_callback_missed_frames, + ANDROID_MISSED_VSYNCS_FOR_CALLBACK(cuj_slice_name, ts_earliest_allowed_counter, ts_end_next_cuj, '*HWUI*') AS hwui_callback_missed_frames FROM cujs_ordered cuj; 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 15627b42e..32c43f4b8 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 @@ base_stats AS ( 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 @@ closest_anon_swap AS ( -- 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 @@ heap_graph_sample_protos AS ( '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 000000000..2dcfa31a2 --- /dev/null +++ b/src/trace_processor/metrics/sql/android/network_activity_template.sql @@ -0,0 +1,74 @@ +-- +-- 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. +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) 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 acaceb777..5ccf2b1d3 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 @@ SELECT IMPORT('android.process_metadata'); 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 @@ SELECT 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/chrome/chrome_tasks_template.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql index d2886f1f5..b5464cf23 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 @@ scheduler_tasks_with_mojo AS ( 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 ec4da442d..f2f22ba7b 100644 --- a/src/trace_processor/prelude/functions/BUILD.gn +++ b/src/trace_processor/prelude/functions/BUILD.gn @@ -31,12 +31,12 @@ source_set("functions") { "layout_functions.h", "pprof_functions.cc", "pprof_functions.h", - "register_function.cc", - "register_function.h", "sqlite3_str_split.cc", "sqlite3_str_split.h", "stack_functions.cc", "stack_functions.h", + "to_ftrace.cc", + "to_ftrace.h", "utils.h", "window_functions.h", ] @@ -57,7 +57,7 @@ source_set("functions") { "../../importers/common", "../../importers/ftrace:ftrace_descriptors", "../../prelude/table_functions", - "../../sqlite:sqlite_minimal", + "../../sqlite", "../../storage", "../../types", "../../util", @@ -65,6 +65,20 @@ source_set("functions") { "../../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") { @@ -76,6 +90,6 @@ perfetto_unittest_source_set("unittests") { "../../../../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 34f23f246..aac62803f 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 ee2e73397..c90ad9ad8 100644 --- a/src/trace_processor/prelude/functions/create_function.cc +++ b/src/trace_processor/prelude/functions/create_function.cc @@ -20,6 +20,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_utils.h" #include "src/trace_processor/tp_metatrace.h" #include "src/trace_processor/util/status_macros.h" @@ -31,11 +32,11 @@ namespace { struct CreatedFunction : public SqlFunction { struct Context { - sqlite3* db; + SqliteEngine* engine; Prototype prototype; sql_argument::Type return_type; std::string sql; - sqlite3_stmt* stmt; + ScopedStmt stmt; }; static base::Status Run(Context* ctx, @@ -88,20 +89,21 @@ base::Status CreatedFunction::Run(CreatedFunction::Context* ctx, // 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); + int ret = sqlite3_step(ctx->stmt.get()); RETURN_IF_ERROR( - SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret)); + SqliteRetToStatus(ctx->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 +111,8 @@ base::Status CreatedFunction::Run(CreatedFunction::Context* ctx, 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 +126,11 @@ base::Status CreatedFunction::Run(CreatedFunction::Context* ctx, } base::Status CreatedFunction::VerifyPostConditions(Context* ctx) { - int ret = sqlite3_step(ctx->stmt); + int ret = sqlite3_step(ctx->stmt.get()); RETURN_IF_ERROR( - SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret)); + SqliteRetToStatus(ctx->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 +141,13 @@ base::Status CreatedFunction::VerifyPostConditions(Context* ctx) { } 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(SqliteEngine* engine, size_t argc, sqlite3_value** argv, SqlValue&, @@ -216,16 +211,14 @@ base::Status CreateFunction::Run(CreateFunction::Context* ctx, } 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->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,7 +245,7 @@ base::Status CreateFunction::Run(CreateFunction::Context* ctx, // 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(), + int ret = sqlite3_prepare_v2(engine->db(), sql_defn_str.data(), static_cast<int>(sql_defn_str.size()), &stmt_raw, nullptr); if (ret != SQLITE_OK) { @@ -261,19 +254,18 @@ base::Status CreateFunction::Run(CreateFunction::Context* ctx, "statement %s", prototype_str.ToStdString().c_str(), sqlite_utils::FormatErrorMessage( - stmt_raw, base::StringView(sql_defn_str), ctx->db, ret) + stmt_raw, base::StringView(sql_defn_str), 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 258c4bbbe..0dd6f4c63 100644 --- a/src/trace_processor/prelude/functions/create_function.h +++ b/src/trace_processor/prelude/functions/create_function.h @@ -20,7 +20,9 @@ #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 { @@ -34,25 +36,7 @@ struct CreateFunction : public SqlFunction { // 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 = SqliteEngine; 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 3989de42e..952924f3f 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 trace_processor { 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; - int Filter(const QueryConstraints& qc, - sqlite3_value**, - FilterHistory) override; - int Next() override; - int Eof() override; - int Column(sqlite3_context* context, int N) override; + base::Status Filter(const QueryConstraints& qc, + sqlite3_value**, + FilterHistory); + base::Status Next(); + bool Eof(); + base::Status Column(sqlite3_context* context, int N); private: ScopedStmt scoped_stmt_; @@ -57,16 +59,11 @@ class CreatedViewFunction : public SqliteTable { }; 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) { - SqliteTable::Register<CreatedViewFunction>( - db, nullptr, "internal_view_function_impl", false, true); - } + 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(); @@ -246,7 +243,7 @@ SqliteTable::Schema CreatedViewFunction::CreateSchema() { 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)); } @@ -272,13 +269,13 @@ int CreatedViewFunction::BestIndex(const QueryConstraints& qc, } CreatedViewFunction::Cursor::Cursor(CreatedViewFunction* table) - : SqliteTable::Cursor(table), table_(table) {} + : SqliteTable::BaseCursor(table), table_(table) {} CreatedViewFunction::Cursor::~Cursor() = default; -int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, - sqlite3_value** argv, - FilterHistory) { +base::Status CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory) { PERFETTO_TP_TRACE(metatrace::Category::FUNCTION, "CREATE_VIEW_FUNCTION", [this](metatrace::Record* r) { r->AddArg("Function", @@ -302,10 +299,8 @@ int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, // We only support equality constraints as we're expecting "input arguments" // to our "function". if (!sqlite_utils::IsOpEq(cs.op)) { - table_->SetErrorMessage( - sqlite3_mprintf("%s: non-equality constraint passed", - table_->prototype_.function_name.c_str())); - return SQLITE_ERROR; + return base::ErrStatus("%s: non-equality constraint passed", + table_->prototype_.function_name.c_str()); } const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)]; @@ -313,11 +308,9 @@ int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, argv[i], sql_argument::TypeToSqlValueType(arg.type()), sql_argument::TypeToHumanFriendlyString(arg.type())); if (!status.ok()) { - table_->SetErrorMessage( - sqlite3_mprintf("%s: argument %s (index %u) %s", - table_->prototype_.function_name.c_str(), - arg.name().c_str(), i, status.c_message())); - return SQLITE_ERROR; + return base::ErrStatus("%s: argument %s (index %zu) %s", + table_->prototype_.function_name.c_str(), + arg.name().c_str(), i, status.c_message()); } seen_argument_constraints++; @@ -325,12 +318,11 @@ int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, // Verify that we saw one valid constriant for every input argument. if (seen_argument_constraints < table_->prototype_.arguments.size()) { - table_->SetErrorMessage(sqlite3_mprintf( - "%s: missing value for input argument. Saw %u arguments but expected " - "%u", + return base::ErrStatus( + "%s: missing value for input argument. Saw %zu arguments but expected " + "%zu", table_->prototype_.function_name.c_str(), seen_argument_constraints, - table_->prototype_.arguments.size())); - return SQLITE_ERROR; + table_->prototype_.arguments.size()); } // Prepare the SQL definition as a statement using SQLite. @@ -361,10 +353,7 @@ int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, const auto& arg = table_->prototype_.arguments[index]; auto status = MaybeBindArgument(stmt_, table_->prototype_.function_name, arg, argv[i]); - if (!status.ok()) { - table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message())); - return SQLITE_ERROR; - } + RETURN_IF_ERROR(status); } // Reset the next call count - this is necessary because the same cursor @@ -373,26 +362,25 @@ int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc, return Next(); } -int CreatedViewFunction::Cursor::Next() { +base::Status CreatedViewFunction::Cursor::Next() { int ret = sqlite3_step(stmt_); is_eof_ = ret == SQLITE_DONE; next_call_count_++; if (ret != SQLITE_ROW && ret != SQLITE_DONE) { - table_->SetErrorMessage(sqlite3_mprintf( + return base::ErrStatus( "%s: SQLite error while stepping statement: %s", table_->prototype_.function_name.c_str(), sqlite_utils::FormatErrorMessage(stmt_, std::nullopt, table_->db_, ret) - .c_message())); - return ret; + .c_message()); } - return SQLITE_OK; + return base::OkStatus(); } -int CreatedViewFunction::Cursor::Eof() { +bool CreatedViewFunction::Cursor::Eof() { return is_eof_; } -int CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) { +base::Status CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) { size_t idx = static_cast<size_t>(i); if (table_->IsReturnValueColumn(idx)) { sqlite3_result_value(ctx, sqlite3_column_value(stmt_, i)); @@ -406,7 +394,7 @@ int CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) { PERFETTO_DCHECK(table_->IsPrimaryKeyColumn(idx)); sqlite3_result_int(ctx, next_call_count_); } - return SQLITE_OK; + return base::OkStatus(); } } // namespace @@ -494,8 +482,10 @@ base::Status CreateViewFunction::Run(CreateViewFunction::Context* ctx, 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 c91817a9e..509ea5e64 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 @@ struct CreateViewFunction : public SqlFunction { 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 9773b5f0e..da40f5fbb 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/register_function.h b/src/trace_processor/prelude/functions/register_function.h deleted file mode 100644 index acca3e62a..000000000 --- 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 index ef41f1d62..26bac5e5b 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 000000000..ba7fab7d8 --- /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 2f8730b51..e2c446827 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/sqlite_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 @@ struct StackFromStackProfileFrameFunction : public SqlFunction { } // namespace -base::Status RegisterStackFunctions(sqlite3* db, +base::Status RegisterStackFunctions(SqliteEngine* 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 5acb046b8..7fd676cfd 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 SqliteEngine; class TraceProcessorContext; // Registers the stack manipulation related functions: @@ -49,7 +50,7 @@ class TraceProcessorContext; // it generates a fake Frame // // See protos/perfetto/trace_processor/stack.proto -base::Status RegisterStackFunctions(sqlite3* db, +base::Status RegisterStackFunctions(SqliteEngine* engine, TraceProcessorContext* context); } // namespace trace_processor diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/prelude/functions/to_ftrace.cc index feaca0010..5bb138f6e 100644 --- a/src/trace_processor/sqlite/sqlite_raw_table.cc +++ b/src/trace_processor/prelude/functions/to_ftrace.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 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,12 +14,12 @@ * limitations under the License. */ -#include "src/trace_processor/sqlite/sqlite_raw_table.h" - -#include <cinttypes> +#include "src/trace_processor/prelude/functions/to_ftrace.h" #include "perfetto/base/compiler.h" +#include "perfetto/base/status.h" #include "perfetto/ext/base/string_utils.h" +#include "perfetto/trace_processor/basic_types.h" #include "src/trace_processor/importers/common/system_info_tracker.h" #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h" #include "src/trace_processor/sqlite/sqlite_utils.h" @@ -538,46 +538,24 @@ void ArgsSerializer::WriteValue(const Variadic& value) { } // namespace -SqliteRawTable::SqliteRawTable(sqlite3* db, Context context) - : DbSqliteTable(db, - {context.cache, TableComputation::kStatic, - &context.context->storage->raw_table(), nullptr}), - serializer_(context.context) { - auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) { - auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx)); - thiz->ToSystrace(ctx, argc, argv); - }; - sqlite3_create_function(db, "to_ftrace", 1, - SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr, - nullptr); -} - -SqliteRawTable::~SqliteRawTable() = default; - -void SqliteRawTable::RegisterTable(sqlite3* db, - QueryCache* cache, - TraceProcessorContext* context) { - SqliteTable::Register<SqliteRawTable, Context>(db, Context{cache, context}, - "raw"); -} - -void SqliteRawTable::ToSystrace(sqlite3_context* ctx, - int argc, - sqlite3_value** argv) { +base::Status ToFtrace::Run(Context* context, + size_t argc, + sqlite3_value** argv, + SqlValue& out, + Destructors& destructors) { if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_INTEGER) { - sqlite3_result_error(ctx, "Usage: to_ftrace(id)", -1); - return; + return base::ErrStatus("Usage: to_ftrace(id)"); } uint32_t row = static_cast<uint32_t>(sqlite3_value_int64(argv[0])); - auto str = serializer_.SerializeToString(row); + auto str = context->serializer.SerializeToString(row); if (str.get() == nullptr) { - base::StackString<128> err("to_ftrace: Cannot serialize row id %u", row); - sqlite3_result_error(ctx, err.c_str(), -1); - return; + return base::ErrStatus("to_ftrace: Cannot serialize row id %u", row); } - sqlite3_result_text(ctx, str.release(), -1, str.get_deleter()); + out = SqlValue::String(str.release()); + destructors.string_destructor = str.get_deleter(); + return base::OkStatus(); } SystraceSerializer::SystraceSerializer(TraceProcessorContext* context) diff --git a/src/trace_processor/sqlite/sqlite_raw_table.h b/src/trace_processor/prelude/functions/to_ftrace.h index dcb5c46f9..9c5506776 100644 --- a/src/trace_processor/sqlite/sqlite_raw_table.h +++ b/src/trace_processor/prelude/functions/to_ftrace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,16 +14,14 @@ * limitations under the License. */ -#ifndef SRC_TRACE_PROCESSOR_SQLITE_SQLITE_RAW_TABLE_H_ -#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE_RAW_TABLE_H_ +#ifndef SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_TO_FTRACE_H_ +#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_TO_FTRACE_H_ -#include "perfetto/base/logging.h" #include "perfetto/ext/base/flat_hash_map.h" #include "perfetto/ext/base/string_writer.h" -#include "src/trace_processor/sqlite/db_sqlite_table.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" -#include "src/trace_processor/types/variadic.h" namespace perfetto { namespace trace_processor { @@ -47,25 +45,20 @@ class SystraceSerializer { TraceProcessorContext* context_ = nullptr; }; -class SqliteRawTable : public DbSqliteTable { - public: +struct ToFtrace : public SqlFunction { struct Context { - QueryCache* cache; - TraceProcessorContext* context; + const TraceStorage* storage; + SystraceSerializer serializer; }; - SqliteRawTable(sqlite3*, Context); - ~SqliteRawTable() override; - - static void RegisterTable(sqlite3* db, QueryCache*, TraceProcessorContext*); - - private: - void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv); - - SystraceSerializer serializer_; + static base::Status Run(Context*, + size_t argc, + sqlite3_value** argv, + SqlValue& out, + Destructors& destructors); }; } // namespace trace_processor } // namespace perfetto -#endif // SRC_TRACE_PROCESSOR_SQLITE_SQLITE_RAW_TABLE_H_ +#endif // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_TO_FTRACE_H_ diff --git a/src/trace_processor/prelude/functions/utils.h b/src/trace_processor/prelude/functions/utils.h index 82b293fbb..ac848d094 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 3a76fcedb..a3fbeafa5 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 2a3daa619..3d994a7bd 100644 --- a/src/trace_processor/prelude/operators/BUILD.gn +++ b/src/trace_processor/prelude/operators/BUILD.gn @@ -42,5 +42,6 @@ perfetto_unittest_source_set("unittests") { "../../../../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 7d4ccb807..2ee2839eb 100644 --- a/src/trace_processor/prelude/operators/span_join_operator.cc +++ b/src/trace_processor/prelude/operators/span_join_operator.cc @@ -24,6 +24,7 @@ #include <utility> #include "perfetto/base/logging.h" +#include "perfetto/base/status.h" #include "perfetto/ext/base/string_splitter.h" #include "perfetto/ext/base/string_utils.h" #include "perfetto/ext/base/string_view.h" @@ -105,23 +106,9 @@ std::string EscapedSqliteValueAsString(sqlite3_value* value) { } // namespace -SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const TraceStorage*) +SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const void*) : db_(db) {} - -void SpanJoinOperatorTable::RegisterTable(sqlite3* db, - const TraceStorage* storage) { - SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_join", - /* read_write */ false, - /* requires_args */ true); - - SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_left_join", - /* read_write */ false, - /* requires_args */ true); - - SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_outer_join", - /* read_write */ false, - /* requires_args */ true); -} +SpanJoinOperatorTable::~SpanJoinOperatorTable() = default; util::Status SpanJoinOperatorTable::Init(int argc, const char* const* argv, @@ -241,7 +228,7 @@ void SpanJoinOperatorTable::CreateSchemaColsForDefn( } } -std::unique_ptr<SqliteTable::Cursor> SpanJoinOperatorTable::CreateCursor() { +std::unique_ptr<SqliteTable::BaseCursor> SpanJoinOperatorTable::CreateCursor() { return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_)); } @@ -406,14 +393,15 @@ std::string SpanJoinOperatorTable::GetNameForGlobalColumnIndex( } 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::FilterInner( - const QueryConstraints& qc, - sqlite3_value** argv) { +base::Status SpanJoinOperatorTable::Cursor::Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory) { PERFETTO_TP_TRACE(metatrace::Category::QUERY, "SPAN_JOIN_XFILTER"); bool t1_partitioned_mixed = @@ -435,7 +423,7 @@ base::Status SpanJoinOperatorTable::Cursor::FilterInner( return FindOverlappingSpan(); } -base::Status SpanJoinOperatorTable::Cursor::NextInner() { +base::Status SpanJoinOperatorTable::Cursor::Next() { RETURN_IF_ERROR(next_query_->Next()); return FindOverlappingSpan(); } @@ -556,11 +544,12 @@ SpanJoinOperatorTable::Cursor::FindEarliestFinishQuery() { return t1_less ? &t1_ : &t2_; } -int SpanJoinOperatorTable::Cursor::Eof() { +bool SpanJoinOperatorTable::Cursor::Eof() { return t1_.IsEof() || t2_.IsEof(); } -int SpanJoinOperatorTable::Cursor::Column(sqlite3_context* context, int N) { +base::Status SpanJoinOperatorTable::Cursor::Column(sqlite3_context* context, + int N) { PERFETTO_DCHECK(t1_.IsReal() || t2_.IsReal()); switch (N) { @@ -598,7 +587,7 @@ int SpanJoinOperatorTable::Cursor::Column(sqlite3_context* context, int N) { t2_.ReportSqliteResult(context, locator.col_index); } } - return SQLITE_OK; + return base::OkStatus(); } SpanJoinOperatorTable::Query::Query(SpanJoinOperatorTable* table, diff --git a/src/trace_processor/prelude/operators/span_join_operator.h b/src/trace_processor/prelude/operators/span_join_operator.h index f150a7532..3f7a006fb 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 @@ namespace trace_processor { // // 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,32 +317,17 @@ class SpanJoinOperatorTable : public SqliteTable { }; // 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; - - int Filter(const QueryConstraints& qc, - sqlite3_value** argv, - FilterHistory) override { - base::Status status = FilterInner(qc, argv); - if (!status.ok()) { - table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message())); - return SQLITE_ERROR; - } - return SQLITE_OK; - } - int Next() override { - base::Status status = NextInner(); - if (!status.ok()) { - table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message())); - return SQLITE_ERROR; - } - return SQLITE_OK; - } + ~Cursor() final; - int Column(sqlite3_context* context, int N) override; - int Eof() override; + base::Status Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory); + base::Status Next(); + base::Status Column(sqlite3_context* context, int N); + bool Eof(); private: Cursor(Cursor&) = delete; @@ -351,11 +337,7 @@ class SpanJoinOperatorTable : public SqliteTable { Cursor& operator=(Cursor&&) = default; bool IsOverlappingSpan(); - - base::Status NextInner(); - base::Status FilterInner(const QueryConstraints& qc, sqlite3_value** argv); util::Status FindOverlappingSpan(); - Query* FindEarliestFinishQuery(); Query t1_; @@ -369,15 +351,14 @@ class SpanJoinOperatorTable : public SqliteTable { 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 737e82860..661a7f101 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 @@ namespace { 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 @@ class SpanJoinOperatorTableTest : public ::testing::Test { } 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 b308c5302..68f2ff474 100644 --- a/src/trace_processor/prelude/operators/window_operator.cc +++ b/src/trace_processor/prelude/operators/window_operator.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/prelude/operators/window_operator.h" +#include "perfetto/base/status.h" #include "src/trace_processor/sqlite/sqlite_utils.h" namespace perfetto { @@ -26,11 +27,7 @@ using namespace sqlite_utils; } // namespace WindowOperatorTable::WindowOperatorTable(sqlite3*, const TraceStorage*) {} - -void WindowOperatorTable::RegisterTable(sqlite3* db, - const TraceStorage* storage) { - SqliteTable::Register<WindowOperatorTable>(db, storage, "window", true); -} +WindowOperatorTable::~WindowOperatorTable() = default; base::Status WindowOperatorTable::Init(int, const char* const*, @@ -57,54 +54,55 @@ base::Status WindowOperatorTable::Init(int, 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*) { return SQLITE_OK; } -int WindowOperatorTable::ModifyConstraints(QueryConstraints* qc) { +base::Status WindowOperatorTable::ModifyConstraints(QueryConstraints* qc) { // Remove ordering on timestamp if it is the only ordering as we are already // sorted on TS. This makes span joining significantly faster. const auto& ob = qc->order_by(); if (ob.size() == 1 && ob[0].iColumn == Column::kTs && !ob[0].desc) { qc->mutable_order_by()->clear(); } - return SQLITE_OK; + return base::OkStatus(); } -int WindowOperatorTable::Update(int argc, - sqlite3_value** argv, - sqlite3_int64*) { +base::Status WindowOperatorTable::Update(int argc, + sqlite3_value** argv, + sqlite3_int64*) { // We only support updates to ts and dur. Disallow deletes (argc == 1) and // inserts (argv[0] == null). - if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL) - return SQLITE_READONLY; + if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL) { + return base::ErrStatus( + "Invalid number/value of arguments when updating window table"); + } int64_t new_quantum = sqlite3_value_int64(argv[3]); int64_t new_start = sqlite3_value_int64(argv[4]); int64_t new_dur = sqlite3_value_int64(argv[5]); if (new_dur == 0) { - auto* err = sqlite3_mprintf("Cannot set duration of window table to zero."); - SetErrorMessage(err); - return SQLITE_ERROR; + return base::ErrStatus("Cannot set duration of window table to zero."); } quantum_ = new_quantum; window_start_ = new_start; window_dur_ = new_dur; - return SQLITE_OK; + return base::OkStatus(); } WindowOperatorTable::Cursor::Cursor(WindowOperatorTable* table) - : SqliteTable::Cursor(table), table_(table) {} + : SqliteTable::BaseCursor(table), table_(table) {} +WindowOperatorTable::Cursor::~Cursor() = default; -int WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc, - sqlite3_value** argv, - FilterHistory) { +base::Status WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory) { *this = Cursor(table_); window_start_ = table_->window_start_; window_end_ = table_->window_start_ + table_->window_dur_; @@ -123,10 +121,11 @@ int WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc, } else { filter_type_ = FilterType::kReturnAll; } - return SQLITE_OK; + return base::OkStatus(); } -int WindowOperatorTable::Cursor::Column(sqlite3_context* context, int N) { +base::Status WindowOperatorTable::Cursor::Column(sqlite3_context* context, + int N) { switch (N) { case Column::kQuantum: { sqlite3_result_int64(context, @@ -163,10 +162,10 @@ int WindowOperatorTable::Cursor::Column(sqlite3_context* context, int N) { break; } } - return SQLITE_OK; + return base::OkStatus(); } -int WindowOperatorTable::Cursor::Next() { +base::Status WindowOperatorTable::Cursor::Next() { switch (filter_type_) { case FilterType::kReturnFirst: current_ts_ = window_end_; @@ -177,10 +176,10 @@ int WindowOperatorTable::Cursor::Next() { break; } row_id_++; - return SQLITE_OK; + return base::OkStatus(); } -int WindowOperatorTable::Cursor::Eof() { +bool WindowOperatorTable::Cursor::Eof() { return current_ts_ >= window_end_; } diff --git a/src/trace_processor/prelude/operators/window_operator.h b/src/trace_processor/prelude/operators/window_operator.h index d10b81b70..5f4af16fd 100644 --- a/src/trace_processor/prelude/operators/window_operator.h +++ b/src/trace_processor/prelude/operators/window_operator.h @@ -20,6 +20,7 @@ #include <limits> #include <memory> +#include "perfetto/base/status.h" #include "src/trace_processor/sqlite/sqlite_table.h" namespace perfetto { @@ -27,7 +28,8 @@ namespace trace_processor { class TraceStorage; -class WindowOperatorTable : public SqliteTable { +class WindowOperatorTable final + : public TypedSqliteTable<WindowOperatorTable, const TraceStorage*> { public: enum Column { kRowId = 0, @@ -38,17 +40,21 @@ class WindowOperatorTable : public SqliteTable { 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. - int Filter(const QueryConstraints& qc, - sqlite3_value**, - FilterHistory) override; - int Next() override; - int Eof() override; - int Column(sqlite3_context*, int N) override; + base::Status Filter(const QueryConstraints& qc, + sqlite3_value**, + FilterHistory); + base::Status Next(); + bool Eof(); + base::Status Column(sqlite3_context*, int N); private: // Defines the data to be generated by the table. @@ -72,16 +78,15 @@ class WindowOperatorTable : public SqliteTable { 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; - int ModifyConstraints(QueryConstraints* qc) override; - int 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; @@ -91,6 +96,7 @@ class WindowOperatorTable : public SqliteTable { // 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 4f5ced3d3..2a97bca2d 100644 --- a/src/trace_processor/prelude/table_functions/BUILD.gn +++ b/src/trace_processor/prelude/table_functions/BUILD.gn @@ -13,6 +13,7 @@ # limitations under the License. import("../../../../gn/perfetto.gni") +import("../../../../gn/perfetto_tp_tables.gni") assert(enable_perfetto_trace_processor_sqlite) @@ -38,12 +39,11 @@ source_set("table_functions") { "experimental_slice_layout.h", "flamegraph_construction_algorithms.cc", "flamegraph_construction_algorithms.h", - "table_function.cc", - "table_function.h", "view.cc", "view.h", ] deps = [ + ":tables", "../../../../gn:default_deps", "../../../../gn:sqlite", "../../../base", @@ -51,12 +51,31 @@ source_set("table_functions") { "../../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") { + sources = [ "tables.py" ] + deps = [ "../../tables:tables_python" ] } source_set("unittests") { @@ -71,6 +90,7 @@ source_set("unittests") { ] deps = [ ":table_functions", + ":tables", "../../../../gn:default_deps", "../../../../gn:gtest_and_gmock", "../../containers", diff --git a/src/trace_processor/prelude/table_functions/ancestor.cc b/src/trace_processor/prelude/table_functions/ancestor.cc index c1656d9e9..9089b826f 100644 --- a/src/trace_processor/prelude/table_functions/ancestor.cc +++ b/src/trace_processor/prelude/table_functions/ancestor.cc @@ -19,6 +19,7 @@ #include <memory> #include <set> +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "src/trace_processor/sqlite/sqlite_utils.h" #include "src/trace_processor/types/trace_processor_context.h" #include "src/trace_processor/util/status_macros.h" diff --git a/src/trace_processor/prelude/table_functions/ancestor.h b/src/trace_processor/prelude/table_functions/ancestor.h index c625f165c..755a48280 100644 --- a/src/trace_processor/prelude/table_functions/ancestor.h +++ b/src/trace_processor/prelude/table_functions/ancestor.h @@ -16,40 +16,14 @@ #ifndef SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_ANCESTOR_H_ #define SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_ANCESTOR_H_ + #include <optional> #include "src/trace_processor/prelude/table_functions/table_function.h" #include "src/trace_processor/storage/trace_storage.h" -#include "src/trace_processor/tables/profiler_tables.h" -#include "src/trace_processor/tables/slice_tables.h" namespace perfetto { namespace trace_processor { -namespace tables { - -#define PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF(NAME, PARENT, C) \ - NAME(AncestorSliceTable, "ancestor_slice") \ - PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C) \ - C(tables::SliceTable::Id, start_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF); - -#define PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF(NAME, PARENT, C) \ - NAME(AncestorStackProfileCallsiteTable, \ - "experimental_ancestor_stack_profile_callsite") \ - PARENT(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF, C) \ - C(tables::StackProfileCallsiteTable::Id, start_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF); - -#define PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \ - NAME(AncestorSliceByStackTable, "ancestor_slice_by_stack") \ - PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C) \ - C(int64_t, start_stack_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF); - -} // namespace tables class TraceProcessorContext; diff --git a/src/trace_processor/prelude/table_functions/ancestor_unittest.cc b/src/trace_processor/prelude/table_functions/ancestor_unittest.cc index 08fdad4c5..afc2d3337 100644 --- a/src/trace_processor/prelude/table_functions/ancestor_unittest.cc +++ b/src/trace_processor/prelude/table_functions/ancestor_unittest.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/prelude/table_functions/ancestor.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "test/gtest_and_gmock.h" namespace perfetto { diff --git a/src/trace_processor/prelude/table_functions/connected_flow.h b/src/trace_processor/prelude/table_functions/connected_flow.h index c05e25e06..3ef3fc3e2 100644 --- a/src/trace_processor/prelude/table_functions/connected_flow.h +++ b/src/trace_processor/prelude/table_functions/connected_flow.h @@ -18,24 +18,14 @@ #define SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_CONNECTED_FLOW_H_ #include "src/trace_processor/prelude/table_functions/table_function.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "src/trace_processor/storage/trace_storage.h" -#include "src/trace_processor/tables/flow_tables.h" #include <queue> #include <set> namespace perfetto { namespace trace_processor { -namespace tables { - -#define PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF(NAME, PARENT, C) \ - NAME(ConnectedFlowTable, "not_exposed_to_sql") \ - PARENT(PERFETTO_TP_FLOW_TABLE_DEF, C) \ - C(uint32_t, start_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF); - -} // namespace tables class TraceProcessorContext; diff --git a/src/trace_processor/prelude/table_functions/descendant.cc b/src/trace_processor/prelude/table_functions/descendant.cc index 75cb8c91c..382edf27a 100644 --- a/src/trace_processor/prelude/table_functions/descendant.cc +++ b/src/trace_processor/prelude/table_functions/descendant.cc @@ -19,6 +19,7 @@ #include <memory> #include <set> +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "src/trace_processor/sqlite/sqlite_utils.h" #include "src/trace_processor/types/trace_processor_context.h" #include "src/trace_processor/util/status_macros.h" diff --git a/src/trace_processor/prelude/table_functions/descendant.h b/src/trace_processor/prelude/table_functions/descendant.h index 8e9381187..bffd9b528 100644 --- a/src/trace_processor/prelude/table_functions/descendant.h +++ b/src/trace_processor/prelude/table_functions/descendant.h @@ -24,23 +24,6 @@ namespace perfetto { namespace trace_processor { -namespace tables { - -#define PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF(NAME, PARENT, C) \ - NAME(DescendantSliceTable, "descendant_slice") \ - PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C) \ - C(uint32_t, start_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF); - -#define PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \ - NAME(DescendantSliceByStackTable, "descendant_slice_by_stack") \ - PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C) \ - C(int64_t, start_stack_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF); - -} // namespace tables class TraceProcessorContext; diff --git a/src/trace_processor/prelude/table_functions/descendant_unittest.cc b/src/trace_processor/prelude/table_functions/descendant_unittest.cc index f1ef1ca31..67399d1f5 100644 --- a/src/trace_processor/prelude/table_functions/descendant_unittest.cc +++ b/src/trace_processor/prelude/table_functions/descendant_unittest.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/prelude/table_functions/descendant.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "test/gtest_and_gmock.h" namespace perfetto { diff --git a/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc b/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc index 819837658..09363f291 100644 --- a/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc +++ b/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc @@ -19,24 +19,15 @@ #include <optional> #include "perfetto/ext/base/string_utils.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "src/trace_processor/sqlite/sqlite_utils.h" #include "src/trace_processor/storage/trace_storage.h" -#include "src/trace_processor/tables/profiler_tables.h" #include "src/trace_processor/types/trace_processor_context.h" namespace perfetto { namespace trace_processor { namespace tables { -#define PERFETTO_TP_ANNOTATED_CALLSTACK_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalAnnotatedCallstackTable, \ - "experimental_annotated_callstack") \ - PARENT(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF, C) \ - C(StringId, annotation) \ - C(tables::StackProfileCallsiteTable::Id, start_id, Column::Flag::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_ANNOTATED_CALLSTACK_TABLE_DEF); - ExperimentalAnnotatedCallstackTable::~ExperimentalAnnotatedCallstackTable() = default; diff --git a/src/trace_processor/prelude/table_functions/experimental_counter_dur.cc b/src/trace_processor/prelude/table_functions/experimental_counter_dur.cc index 3513c8922..a7d98149c 100644 --- a/src/trace_processor/prelude/table_functions/experimental_counter_dur.cc +++ b/src/trace_processor/prelude/table_functions/experimental_counter_dur.cc @@ -16,20 +16,12 @@ #include "src/trace_processor/prelude/table_functions/experimental_counter_dur.h" -#include "src/trace_processor/tables/counter_tables.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" namespace perfetto { namespace trace_processor { namespace tables { -#define PERFETTO_TP_COUNTER_DUR_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalCounterDurTable, "experimental_counter_dur") \ - PARENT(PERFETTO_TP_COUNTER_TABLE_DEF, C) \ - C(int64_t, dur) \ - C(double, delta) - -PERFETTO_TP_TABLE(PERFETTO_TP_COUNTER_DUR_TABLE_DEF); - ExperimentalCounterDurTable::~ExperimentalCounterDurTable() = default; } // namespace tables diff --git a/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc b/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc index 45a76ea14..ba3a0c6cf 100644 --- a/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc +++ b/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc @@ -16,17 +16,14 @@ #include "src/trace_processor/prelude/table_functions/experimental_sched_upid.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" + namespace perfetto { namespace trace_processor { namespace tables { -#define PERFETTO_TP_SCHED_UPID_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalSchedUpidTable, "experimental_sched_upid") \ - PARENT(PERFETTO_TP_SCHED_SLICE_TABLE_DEF, C) \ - C(std::optional<UniquePid>, upid) - -PERFETTO_TP_TABLE(PERFETTO_TP_SCHED_UPID_TABLE_DEF); ExperimentalSchedUpidTable::~ExperimentalSchedUpidTable() = default; + } // namespace tables ExperimentalSchedUpid::ExperimentalSchedUpid( diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc b/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc index e544dbbe3..d6276dce8 100644 --- a/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc +++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc @@ -20,11 +20,13 @@ #include "perfetto/ext/base/string_splitter.h" #include "perfetto/ext/base/string_utils.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "src/trace_processor/sqlite/sqlite_utils.h" namespace perfetto { namespace trace_processor { namespace tables { + ExperimentalSliceLayoutTable::~ExperimentalSliceLayoutTable() = default; } diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout.h b/src/trace_processor/prelude/table_functions/experimental_slice_layout.h index 19af803d6..3aa61bbd0 100644 --- a/src/trace_processor/prelude/table_functions/experimental_slice_layout.h +++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout.h @@ -25,18 +25,6 @@ namespace perfetto { namespace trace_processor { -namespace tables { - -#define PERFETTO_TP_SLICE_LAYOUT_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalSliceLayoutTable, "experimental_slice_layout") \ - PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C) \ - C(uint32_t, layout_depth) \ - C(StringPool::Id, filter_track_ids, Column::kHidden) - -PERFETTO_TP_TABLE(PERFETTO_TP_SLICE_LAYOUT_TABLE_DEF); - -} // namespace tables - class ExperimentalSliceLayout : public TableFunction { public: ExperimentalSliceLayout(StringPool* string_pool, diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc b/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc index 43c986d5a..164dfbbe2 100644 --- a/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc +++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc @@ -19,6 +19,7 @@ #include <algorithm> #include "src/trace_processor/containers/bit_vector.h" +#include "src/trace_processor/prelude/table_functions/tables_py.h" #include "test/gtest_and_gmock.h" namespace perfetto { diff --git a/src/trace_processor/prelude/table_functions/tables.py b/src/trace_processor/prelude/table_functions/tables.py new file mode 100644 index 000000000..4ffc5266e --- /dev/null +++ b/src/trace_processor/prelude/table_functions/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 finding ancestor events.""" + +from python.generators.trace_processor_table.public import Column as C +from python.generators.trace_processor_table.public import ColumnFlag +from python.generators.trace_processor_table.public import CppDouble +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 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 src.trace_processor.tables.counter_tables import COUNTER_TABLE +from src.trace_processor.tables.flow_tables import FLOW_TABLE +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 + +ANCESTOR_SLICE_TABLE = Table( + python_module=__file__, + class_name="AncestorSliceTable", + sql_name="ancestor_slice", + columns=[ + C("start_id", CppTableId(SLICE_TABLE), flags=ColumnFlag.HIDDEN), + ], + parent=SLICE_TABLE) + +ANCESTOR_SLICE_BY_STACK_TABLE = Table( + python_module=__file__, + class_name="AncestorSliceByStackTable", + sql_name="ancestor_slice_by_stack", + columns=[ + C("start_stack_id", CppInt64(), flags=ColumnFlag.HIDDEN), + ], + parent=SLICE_TABLE) + +ANCESTOR_STACK_PROFILE_CALLSITE_TABLE = Table( + python_module=__file__, + class_name="AncestorStackProfileCallsiteTable", + sql_name="experimental_ancestor_stack_profile_callsite", + columns=[ + C("start_id", + CppTableId(STACK_PROFILE_CALLSITE_TABLE), + flags=ColumnFlag.HIDDEN), + ], + parent=STACK_PROFILE_CALLSITE_TABLE) + +CONNECTED_FLOW_TABLE = Table( + python_module=__file__, + class_name="ConnectedFlowTable", + sql_name="not_exposed_to_sql", + columns=[ + C("start_id", CppTableId(SLICE_TABLE), flags=ColumnFlag.HIDDEN), + ], + parent=FLOW_TABLE) + +DESCENDANT_SLICE_TABLE = Table( + python_module=__file__, + class_name="DescendantSliceTable", + sql_name="descendant_slice", + columns=[ + C("start_id", CppTableId(SLICE_TABLE), flags=ColumnFlag.HIDDEN), + ], + parent=SLICE_TABLE) + +DESCENDANT_SLICE_BY_STACK_TABLE = Table( + python_module=__file__, + class_name="DescendantSliceByStackTable", + sql_name="descendant_slice_by_stack", + columns=[ + C("start_stack_id", CppInt64(), flags=ColumnFlag.HIDDEN), + ], + parent=SLICE_TABLE) + +EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE = Table( + python_module=__file__, + class_name="ExperimentalAnnotatedCallstackTable", + sql_name="experimental_annotated_callstack", + columns=[ + C("annotation", CppString()), + C("start_id", + CppTableId(STACK_PROFILE_CALLSITE_TABLE), + flags=ColumnFlag.HIDDEN), + ], + parent=STACK_PROFILE_CALLSITE_TABLE) + +EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE = Table( + python_module=__file__, + class_name="ExperimentalAnnotatedCallstackTable", + sql_name="experimental_annotated_callstack", + columns=[ + C("annotation", CppString()), + C("start_id", + CppTableId(STACK_PROFILE_CALLSITE_TABLE), + flags=ColumnFlag.HIDDEN), + ], + parent=STACK_PROFILE_CALLSITE_TABLE) + +EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE = Table( + python_module=__file__, + class_name="ExperimentalAnnotatedCallstackTable", + sql_name="experimental_annotated_callstack", + columns=[ + C("annotation", CppString()), + C("start_id", + CppTableId(STACK_PROFILE_CALLSITE_TABLE), + flags=ColumnFlag.HIDDEN), + ], + parent=STACK_PROFILE_CALLSITE_TABLE) + +EXPERIMENTAL_COUNTER_DUR_TABLE = Table( + python_module=__file__, + class_name="ExperimentalCounterDurTable", + sql_name="experimental_counter_dur", + columns=[ + C("dur", CppInt64()), + C("delta", CppDouble()), + ], + parent=COUNTER_TABLE) + +EXPERIMENTAL_SCHED_UPID_TABLE = Table( + python_module=__file__, + class_name="ExperimentalSchedUpidTable", + sql_name="experimental_sched_upid", + columns=[ + C("upid", CppOptional(CppTableId(PROCESS_TABLE))), + ], + parent=SCHED_SLICE_TABLE) + +EXPERIMENTAL_SLICE_LAYOUT_TABLE = Table( + python_module=__file__, + class_name="ExperimentalSliceLayoutTable", + sql_name="experimental_slice_layout", + columns=[ + C("layout_depth", CppUint32()), + C("filter_track_ids", CppString(), flags=ColumnFlag.HIDDEN), + ], + parent=SLICE_TABLE) + +# Keep this list sorted. +ALL_TABLES = [ + ANCESTOR_SLICE_BY_STACK_TABLE, + ANCESTOR_SLICE_TABLE, + ANCESTOR_STACK_PROFILE_CALLSITE_TABLE, + CONNECTED_FLOW_TABLE, + DESCENDANT_SLICE_BY_STACK_TABLE, + DESCENDANT_SLICE_TABLE, + EXPERIMENTAL_ANNOTATED_CALLSTACK_TABLE, + EXPERIMENTAL_COUNTER_DUR_TABLE, + EXPERIMENTAL_SCHED_UPID_TABLE, + EXPERIMENTAL_SLICE_LAYOUT_TABLE, +] diff --git a/src/trace_processor/prelude/tables_views/BUILD.gn b/src/trace_processor/prelude/tables_views/BUILD.gn new file mode 100644 index 000000000..110d0b950 --- /dev/null +++ b/src/trace_processor/prelude/tables_views/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../../../gn/perfetto.gni") +import("../../../../gn/perfetto_sql.gni") + +assert(enable_perfetto_trace_processor_sqlite) + +perfetto_amalgamated_sql_header("tables_views") { + deps = [ ":sources" ] + generated_header = "tables_views.h" + namespace = "prelude::tables_views" +} + +perfetto_sql_source_set("sources") { + sources = [ + "tables.sql", + "views.sql", + ] +} diff --git a/src/trace_processor/prelude/tables_views/tables.sql b/src/trace_processor/prelude/tables_views/tables.sql new file mode 100644 index 000000000..28ea0f57a --- /dev/null +++ b/src/trace_processor/prelude/tables_views/tables.sql @@ -0,0 +1,25 @@ +CREATE TABLE perfetto_tables(name STRING); + +CREATE TABLE trace_bounds AS +SELECT 0 AS start_ts, 0 AS end_ts; + +CREATE TABLE power_profile( + device STRING, + cpu INT, + cluster INT, + freq INT, + power DOUBLE, + UNIQUE(device, cpu, cluster, freq) +); + +CREATE TABLE trace_metrics(name STRING); + +CREATE TABLE debug_slices( + id BIGINT, + name STRING, + ts BIGINT, + dur BIGINT, + depth BIGINT +); + +CREATE VIRTUAL TABLE window USING window(); diff --git a/src/trace_processor/prelude/tables_views/views.sql b/src/trace_processor/prelude/tables_views/views.sql new file mode 100644 index 000000000..6962baefe --- /dev/null +++ b/src/trace_processor/prelude/tables_views/views.sql @@ -0,0 +1,55 @@ +CREATE VIEW counters AS +SELECT * +FROM counter v +JOIN counter_track t ON v.track_id = t.id +ORDER BY ts; + +CREATE VIEW slice AS +SELECT + *, + category AS cat, + id AS slice_id +FROM internal_slice; + +CREATE VIEW instant AS +SELECT ts, track_id, name, arg_set_id +FROM slice +WHERE dur = 0; + +CREATE VIEW sched AS +SELECT + *, + ts + dur as ts_end +FROM sched_slice; + +CREATE VIEW slices AS +SELECT * FROM slice; + +CREATE VIEW thread AS +SELECT + id as utid, + * +FROM internal_thread; + +CREATE VIEW process AS +SELECT + id as upid, + * +FROM internal_process; + +-- This should be kept in sync with GlobalArgsTracker::AddArgSet. +CREATE VIEW args AS +SELECT + *, + CASE value_type + WHEN 'int' THEN CAST(int_value AS text) + WHEN 'uint' THEN CAST(int_value AS text) + WHEN 'string' THEN string_value + WHEN 'real' THEN CAST(real_value AS text) + WHEN 'pointer' THEN printf('0x%x', int_value) + WHEN 'bool' THEN ( + CASE WHEN int_value <> 0 THEN 'true' + ELSE 'false' END) + WHEN 'json' THEN string_value + ELSE NULL END AS display_value +FROM internal_args; diff --git a/src/trace_processor/read_trace_integrationtest.cc b/src/trace_processor/read_trace_integrationtest.cc index 439f1fa16..818eb6d08 100644 --- a/src/trace_processor/read_trace_integrationtest.cc +++ b/src/trace_processor/read_trace_integrationtest.cc @@ -46,7 +46,23 @@ std::vector<uint8_t> ReadAllData(const base::ScopedFstream& f) { return raw_trace; } -TEST(ReadTraceIntegrationTest, CompressedTrace) { +bool ZlibSupported() { +#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB) + return true; +#else + return false; +#endif +} + +class ReadTraceIntegrationTest : public testing::Test { + void SetUp() override { + if (!ZlibSupported()) { + GTEST_SKIP() << "Gzip not enabled"; + } + } +}; + +TEST_F(ReadTraceIntegrationTest, CompressedTrace) { base::ScopedFstream f = OpenTestTrace("test/data/compressed.pb"); std::vector<uint8_t> raw_trace = ReadAllData(f); @@ -68,7 +84,7 @@ TEST(ReadTraceIntegrationTest, CompressedTrace) { ASSERT_EQ(packet_count, 2412u); } -TEST(ReadTraceIntegrationTest, NonProtobufShouldNotDecompress) { +TEST_F(ReadTraceIntegrationTest, NonProtobufShouldNotDecompress) { base::ScopedFstream f = OpenTestTrace("test/data/unsorted_trace.json"); std::vector<uint8_t> raw_trace = ReadAllData(f); @@ -78,7 +94,7 @@ TEST(ReadTraceIntegrationTest, NonProtobufShouldNotDecompress) { ASSERT_FALSE(status.ok()); } -TEST(ReadTraceIntegrationTest, OuterGzipDecompressTrace) { +TEST_F(ReadTraceIntegrationTest, OuterGzipDecompressTrace) { base::ScopedFstream f = OpenTestTrace("test/data/example_android_trace_30s.pb.gz"); std::vector<uint8_t> raw_compressed_trace = ReadAllData(f); @@ -96,7 +112,7 @@ TEST(ReadTraceIntegrationTest, OuterGzipDecompressTrace) { ASSERT_EQ(decompressed, raw_trace); } -TEST(ReadTraceIntegrationTest, DoubleGzipDecompressTrace) { +TEST_F(ReadTraceIntegrationTest, DoubleGzipDecompressTrace) { base::ScopedFstream f = OpenTestTrace("test/data/compressed.pb.gz"); std::vector<uint8_t> raw_compressed_trace = ReadAllData(f); diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn index 21ed49a23..704581567 100644 --- a/src/trace_processor/sqlite/BUILD.gn +++ b/src/trace_processor/sqlite/BUILD.gn @@ -21,48 +21,48 @@ source_set("sqlite") { "db_sqlite_table.cc", "db_sqlite_table.h", "query_cache.h", + "scoped_db.h", "sql_stats_table.cc", "sql_stats_table.h", - "sqlite_raw_table.cc", - "sqlite_raw_table.h", + "sqlite_engine.cc", + "sqlite_engine.h", + "sqlite_table.cc", + "sqlite_table.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", ] } @@ -75,8 +75,8 @@ perfetto_unittest_source_set("unittests") { "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 a97ec8fbe..f73c5c1c2 100644 --- a/src/trace_processor/sqlite/db_sqlite_table.cc +++ b/src/trace_processor/sqlite/db_sqlite_table.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/sqlite/db_sqlite_table.h" +#include "perfetto/base/status.h" #include "perfetto/ext/base/small_vector.h" #include "perfetto/ext/base/string_writer.h" #include "src/trace_processor/containers/bit_vector.h" @@ -134,30 +135,6 @@ DbSqliteTable::DbSqliteTable(sqlite3*, Context context) 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); -} - -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())); - bool requires_args = !status.ok(); - - std::string table_name = generator->TableName(); - Context context{cache, TableComputation::kDynamic, nullptr, - std::move(generator)}; - SqliteTable::Register<DbSqliteTable, Context>( - db, std::move(context), table_name, false, requires_args); -} - base::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) { switch (computation_) { case TableComputation::kStatic: @@ -232,9 +209,9 @@ void DbSqliteTable::BestIndex(const Table::Schema& schema, info->sqlite_omit_order_by = true; } -int DbSqliteTable::ModifyConstraints(QueryConstraints* qc) { +base::Status DbSqliteTable::ModifyConstraints(QueryConstraints* qc) { ModifyConstraints(schema_, qc); - return SQLITE_OK; + return base::OkStatus(); } void DbSqliteTable::ModifyConstraints(const Table::Schema& schema, @@ -392,14 +369,15 @@ DbSqliteTable::QueryCost DbSqliteTable::EstimateCost( 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, @@ -453,9 +431,9 @@ void DbSqliteTable::Cursor::TryCacheCreateSortedTable( }); } -int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc, - sqlite3_value** argv, - FilterHistory history) { +base::Status DbSqliteTable::Cursor::Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory history) { // Clear out the iterator before filtering to ensure the destructor is run // before the table's destructor. iterator_ = std::nullopt; @@ -511,10 +489,8 @@ int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc, constraints_, orders_, cols_used_bv, computed_table); if (!status.ok()) { - auto* sqlite_err = sqlite3_mprintf( - "%s: %s", db_sqlite_table_->name().c_str(), status.c_message()); - db_sqlite_table_->SetErrorMessage(sqlite_err); - return SQLITE_CONSTRAINT; + return base::ErrStatus("%s: %s", db_sqlite_table_->name().c_str(), + status.c_message()); } PERFETTO_DCHECK(computed_table); dynamic_table_ = std::move(computed_table); @@ -631,25 +607,24 @@ int DbSqliteTable::Cursor::Filter(const QueryConstraints& qc, eof_ = !*iterator_; } - - return SQLITE_OK; + return base::OkStatus(); } -int DbSqliteTable::Cursor::Next() { +base::Status DbSqliteTable::Cursor::Next() { if (mode_ == Mode::kSingleRow) { eof_ = true; } else { iterator_->Next(); eof_ = !*iterator_; } - return SQLITE_OK; + return base::OkStatus(); } -int DbSqliteTable::Cursor::Eof() { +bool DbSqliteTable::Cursor::Eof() { return eof_; } -int DbSqliteTable::Cursor::Column(sqlite3_context* ctx, int raw_col) { +base::Status DbSqliteTable::Cursor::Column(sqlite3_context* ctx, int raw_col) { uint32_t column = static_cast<uint32_t>(raw_col); SqlValue value = mode_ == Mode::kSingleRow ? SourceTable()->GetColumn(column).Get(*single_row_) @@ -663,7 +638,7 @@ int DbSqliteTable::Cursor::Column(sqlite3_context* ctx, int raw_col) { // SQLite no longer cares about the bytes pointer. sqlite_utils::ReportSqlValue(ctx, value, sqlite_utils::kSqliteStatic, sqlite_utils::kSqliteStatic); - return SQLITE_OK; + return base::OkStatus(); } } // namespace trace_processor diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h index 6a2282d0f..96a63e51a 100644 --- a/src/trace_processor/sqlite/db_sqlite_table.h +++ b/src/trace_processor/sqlite/db_sqlite_table.h @@ -17,6 +17,7 @@ #ifndef SRC_TRACE_PROCESSOR_SQLITE_DB_SQLITE_TABLE_H_ #define SRC_TRACE_PROCESSOR_SQLITE_DB_SQLITE_TABLE_H_ +#include "perfetto/base/status.h" #include "src/trace_processor/containers/bit_vector.h" #include "src/trace_processor/db/table.h" #include "src/trace_processor/prelude/table_functions/table_function.h" @@ -26,32 +27,48 @@ 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; // Implementation of SqliteTable::Cursor. - int Filter(const QueryConstraints& qc, - sqlite3_value** argv, - FilterHistory) override; - int Next() override; - int Eof() override; - int Column(sqlite3_context*, int N) override; + base::Status Filter(const QueryConstraints& qc, + sqlite3_value** argv, + FilterHistory); + base::Status Next(); + bool Eof(); + base::Status Column(sqlite3_context*, int N); private: enum class Mode { @@ -108,36 +125,15 @@ class DbSqliteTable : public SqliteTable { 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; - int 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/query_cache.h b/src/trace_processor/sqlite/query_cache.h index f3b822304..1bb9d69c9 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 aafc93fc7..e6cdf87be 100644 --- a/src/trace_processor/sqlite/sql_stats_table.cc +++ b/src/trace_processor/sqlite/sql_stats_table.cc @@ -22,6 +22,7 @@ #include <bitset> #include <numeric> +#include "perfetto/base/status.h" #include "src/trace_processor/sqlite/sqlite_utils.h" #include "src/trace_processor/storage/trace_storage.h" @@ -30,10 +31,7 @@ namespace trace_processor { SqlStatsTable::SqlStatsTable(sqlite3*, const TraceStorage* storage) : storage_(storage) {} - -void SqlStatsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) { - SqliteTable::Register<SqlStatsTable>(db, storage, "sqlstats"); -} +SqlStatsTable::~SqlStatsTable() = default; base::Status SqlStatsTable::Init(int, const char* const*, Schema* schema) { *schema = Schema( @@ -50,8 +48,8 @@ base::Status SqlStatsTable::Init(int, const char* const*, Schema* schema) { 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*) { @@ -59,28 +57,29 @@ int SqlStatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) { } 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; -int SqlStatsTable::Cursor::Filter(const QueryConstraints&, - sqlite3_value**, - FilterHistory) { +base::Status SqlStatsTable::Cursor::Filter(const QueryConstraints&, + sqlite3_value**, + FilterHistory) { *this = Cursor(table_); num_rows_ = storage_->sql_stats().size(); - return SQLITE_OK; + return base::OkStatus(); } -int SqlStatsTable::Cursor::Next() { +base::Status SqlStatsTable::Cursor::Next() { row_++; - return SQLITE_OK; + return base::OkStatus(); } -int SqlStatsTable::Cursor::Eof() { +bool SqlStatsTable::Cursor::Eof() { return row_ >= num_rows_; } -int SqlStatsTable::Cursor::Column(sqlite3_context* context, int col) { +base::Status SqlStatsTable::Cursor::Column(sqlite3_context* context, int col) { const TraceStorage::SqlStats& stats = storage_->sql_stats(); switch (col) { case Column::kQuery: @@ -97,7 +96,7 @@ int SqlStatsTable::Cursor::Column(sqlite3_context* context, int col) { sqlite3_result_int64(context, stats.times_ended()[row_]); break; } - return SQLITE_OK; + return base::OkStatus(); } } // namespace trace_processor diff --git a/src/trace_processor/sqlite/sql_stats_table.h b/src/trace_processor/sqlite/sql_stats_table.h index df8d9f540..d78224c7f 100644 --- a/src/trace_processor/sqlite/sql_stats_table.h +++ b/src/trace_processor/sqlite/sql_stats_table.h @@ -20,6 +20,7 @@ #include <limits> #include <memory> +#include "perfetto/base/status.h" #include "src/trace_processor/sqlite/sqlite_table.h" namespace perfetto { @@ -30,7 +31,8 @@ class TraceStorage; // 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, @@ -40,18 +42,18 @@ class SqlStatsTable : public SqliteTable { }; // Implementation of the SQLite cursor interface. - class Cursor : public SqliteTable::Cursor { + class Cursor final : public SqliteTable::BaseCursor { public: - Cursor(SqlStatsTable* storage); - ~Cursor() override; + explicit Cursor(SqlStatsTable* storage); + ~Cursor() final; // Implementation of SqliteTable::Cursor. - int Filter(const QueryConstraints&, - sqlite3_value**, - FilterHistory) override; - int Next() override; - int Eof() override; - int Column(sqlite3_context*, int N) override; + base::Status Filter(const QueryConstraints&, + sqlite3_value**, + FilterHistory); + base::Status Next(); + bool Eof(); + base::Status Column(sqlite3_context*, int N); private: Cursor(Cursor&) = delete; @@ -67,13 +69,12 @@ class SqlStatsTable : public SqliteTable { }; 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 new file mode 100644 index 000000000..7ffc3ed7e --- /dev/null +++ b/src/trace_processor/sqlite/sqlite_engine.cc @@ -0,0 +1,154 @@ +/* + * 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_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. +#if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) +// defined in sqlite_src/ext/misc/percentile.c +extern "C" int sqlite3_percentile_init(sqlite3* db, + char** error, + const sqlite3_api_routines* api); +#endif // PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) + +namespace perfetto { +namespace trace_processor { +namespace { + +void EnsureSqliteInitialized() { + // sqlite3_initialize isn't actually thread-safe despite being documented + // as such; we need to make sure multiple TraceProcessorImpl instances don't + // call it concurrently and only gets called once per process, instead. + static bool init_once = [] { return sqlite3_initialize() == SQLITE_OK; }(); + PERFETTO_CHECK(init_once); +} + +void InitializeSqlite(sqlite3* db) { + char* error = nullptr; + sqlite3_exec(db, "PRAGMA temp_store=2", nullptr, nullptr, &error); + if (error) { + PERFETTO_FATAL("Error setting pragma temp_store: %s", error); + } +// In Android tree builds, we don't have the percentile module. +// Just don't include it. +#if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) + sqlite3_percentile_init(db, &error, nullptr); + if (error) { + PERFETTO_ELOG("Error initializing: %s", error); + sqlite3_free(error); + } +#endif +} + +} // namespace + +SqliteEngine::SqliteEngine() : query_cache_(new QueryCache()) { + sqlite3* db = nullptr; + EnsureSqliteInitialized(); + PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK); + InitializeSqlite(db); + db_.reset(std::move(db)); +} + +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::RegisterTable(const Table& table, + const std::string& table_name) { + DbSqliteTable::Context context{query_cache_.get(), + DbSqliteTable::TableComputation::kStatic, + &table, nullptr}; + 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(db_.get(), 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 SqliteEngine::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)}; + RegisterVirtualTableModule<DbSqliteTable>(table_name, std::move(context), + SqliteTable::kEponymousOnly, false); +} + +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 +} // namespace perfetto diff --git a/src/trace_processor/sqlite/sqlite_engine.h b/src/trace_processor/sqlite/sqlite_engine.h new file mode 100644 index 000000000..2af8b81d4 --- /dev/null +++ b/src/trace_processor/sqlite/sqlite_engine.h @@ -0,0 +1,263 @@ +/* + * 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_ENGINE_H_ +#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 { + +// Wrapper class around SQLite C API. +// +// The goal of this class is to provide a one-stop-shop mechanism to use SQLite. +// Benefits of this include: +// 1) It allows us to add code which intercepts registration of functions +// and tables and keeps track of this for later lookup. +// 2) Allows easily auditing the SQLite APIs we use making it easy to determine +// what functionality we rely on. +class SqliteEngine { + public: + 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 trace processor C++ function to be runnable from SQL. + // + // The format of the function is given by the |SqlFunction|. + // + // |db|: sqlite3 database object + // |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 + // 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 = 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 function with SQLite. + void RegisterTableFunction(std::unique_ptr<TableFunction> fn); + + // Registers a SQLite virtual table module with the given name. + // + // This API only exists for internal/legacy use: most callers should use + // one of the RegisterTable* APIs above. + 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. + // + // This API only exists for internal use. Most callers should never call this + // directly: instead use one of the RegisterTable* APIs above. + base::Status DeclareVirtualTable(const std::string& create_stmt); + + // Saves a SQLite table across a pair of xDisconnect/xConnect callbacks. + // + // This API only exists for internal use. Most callers should never call this + // directly. + base::Status SaveSqliteTable(const std::string& table_name, + std::unique_ptr<SqliteTable>); + + // Restores a SQLite table across a pair of xDisconnect/xConnect callbacks. + // + // This API only exists for internal use. Most callers should never call this + // directly. + base::StatusOr<std::unique_ptr<SqliteTable>> RestoreSqliteTable( + const std::string& table_name); + + // Gets the context for a registered SQL function. + // + // This API only exists for internal use. Most callers should never call this + // directly. + void* GetFunctionContext(const std::string& name, int argc); + + sqlite3* db() const { return db_.get(); } + + private: + 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()); + } + }; + + std::unique_ptr<QueryCache> query_cache_; + base::FlatHashMap<std::string, std::unique_ptr<SqliteTable>> saved_tables_; + base::FlatHashMap<std::pair<std::string, int>, void*, FnHasher> fn_ctx_; + + ScopedDb db_; +}; + +} // 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 SqliteEngine::RegisterSqlFunction(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_.get(), 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); + } + *fn_ctx_.Insert(std::make_pair(name, argc), ctx).first = ctx; + return base::OkStatus(); +} + +template <typename Function> +base::Status SqliteEngine::RegisterSqlFunction( + const char* name, + int argc, + std::unique_ptr<typename Function::Context> user_data, + bool deterministic) { + int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0); + void* fn_ctx = user_data.get(); + int ret = sqlite3_create_function_v2( + db_.get(), 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); + } + *fn_ctx_.Insert(std::make_pair(name, argc), fn_ctx).first = fn_ctx; + return base::OkStatus(); +} + +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 a0c5ce997..2ad925c87 100644 --- a/src/trace_processor/sqlite/sqlite_table.cc +++ b/src/trace_processor/sqlite/sqlite_table.cc @@ -20,9 +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 { @@ -153,98 +160,16 @@ bool SqliteTable::debug = false; 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 = 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; -} - -int SqliteTable::ModifyConstraints(QueryConstraints*) { - return SQLITE_OK; +base::Status SqliteTable::ModifyConstraints(QueryConstraints*) { + return base::OkStatus(); } int SqliteTable::FindFunction(const char*, FindFunctionFn*, void**) { return 0; } -int SqliteTable::Update(int, sqlite3_value**, sqlite3_int64*) { - return SQLITE_READONLY; +base::Status SqliteTable::Update(int, sqlite3_value**, sqlite3_int64*) { + return base::ErrStatus("Updating not supported"); } bool SqliteTable::ReadConstraints(int idxNum, const char* idxStr, int argc) { @@ -274,16 +199,20 @@ bool SqliteTable::ReadConstraints(int idxNum, const char* idxStr, int argc) { 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; -int SqliteTable::Cursor::RowId(sqlite3_int64*) { - return SQLITE_ERROR; -} +//////////////////////////////////////////////////////////////////////////////// +// SqliteTable::Column implementation +//////////////////////////////////////////////////////////////////////////////// SqliteTable::Column::Column(size_t index, std::string name, @@ -291,6 +220,12 @@ SqliteTable::Column::Column(size_t index, 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)) { @@ -302,7 +237,6 @@ SqliteTable::Schema::Schema(std::vector<Column> columns, } } -SqliteTable::Schema::Schema() = default; SqliteTable::Schema::Schema(const Schema&) = default; SqliteTable::Schema& SqliteTable::Schema::operator=(const Schema&) = default; @@ -334,5 +268,170 @@ std::string SqliteTable::Schema::ToCreateTableStmt() const { 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 de946692a..2d0cabcc1 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 @@ class SqliteTable : public sqlite3_vtab { 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,39 +90,39 @@ class SqliteTable : public sqlite3_vtab { 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 int 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 int Next() = 0; + base::Status Next(); // Called to check if the cursor has reached eof. Column will be called iff // this method returns true. - virtual int Eof() = 0; + bool Eof(); // Used to extract the value from the column at index |N|. - virtual int Column(sqlite3_context* context, int N) = 0; + base::Status Column(sqlite3_context* context, int N); - // Optional methods to implement. - virtual int RowId(sqlite3_int64*); + 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; - SqliteTable* table_ = nullptr; }; @@ -159,6 +152,24 @@ class SqliteTable : public sqlite3_vtab { 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. @@ -185,187 +196,39 @@ class SqliteTable : public sqlite3_vtab { int64_t estimated_rows = 0; }; - template <typename Context> - struct TableDescriptor { - SqliteTable::Factory<Context> factory; - Context context; - sqlite3_module module = {}; - }; - SqliteTable(); - // Called by derived classes to register themselves with the SQLite db. - // |read_write| specifies whether the table can also be written to. - // |requires_args| should be true if the table requires arguments in order to - // be instantiated. - // 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, - bool read_write = false, - bool requires_args = false) { - using TCursor = typename TTable::Cursor; - - std::unique_ptr<TableDescriptor<Context>> desc( - new TableDescriptor<Context>()); - 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<Context>*>(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; - }; - - module->xCreate = create_fn; - module->xConnect = create_fn; - module->xDisconnect = destroy_fn; - module->xDestroy = destroy_fn; - module->xOpen = [](sqlite3_vtab* t, sqlite3_vtab_cursor** c) { - return static_cast<TTable*>(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) { - auto* c = static_cast<Cursor*>(vc); - bool is_cached = c->table_->ReadConstraints(i, s, a); - - auto history = is_cached ? Cursor::FilterHistory::kSame - : Cursor::FilterHistory::kDifferent; - return static_cast<TCursor*>(c)->Filter(c->table_->qc_cache_, v, history); - }; - module->xNext = [](sqlite3_vtab_cursor* c) { - return static_cast<TCursor*>(c)->Next(); - }; - module->xEof = [](sqlite3_vtab_cursor* c) { - return static_cast<TCursor*>(c)->Eof(); - }; - module->xColumn = [](sqlite3_vtab_cursor* c, sqlite3_context* a, int b) { - return static_cast<TCursor*>(c)->Column(a, b); - }; - module->xRowid = [](sqlite3_vtab_cursor* c, sqlite3_int64* r) { - return static_cast<TCursor*>(c)->RowId(r); - }; - 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 (read_write) { - module->xUpdate = [](sqlite3_vtab* t, int a, sqlite3_value** v, - sqlite3_int64* r) { - return static_cast<TTable*>(t)->Update(a, v, r); - }; - } - - int res = sqlite3_create_module_v2( - db, module_name.c_str(), module, desc.release(), - [](void* arg) { delete static_cast<TableDescriptor<Context>*>(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 requiring arguments aren't registered because they - // can't be automatically instantiated for exporting. - if (!requires_args) { - 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. using FindFunctionFn = void (*)(sqlite3_context*, int, sqlite3_value**); - virtual int ModifyConstraints(QueryConstraints* qc); + virtual base::Status ModifyConstraints(QueryConstraints* qc); virtual int FindFunction(const char* name, FindFunctionFn* fn, void** args); // At registration time, the function should also pass true for |read_write|. - virtual int Update(int, sqlite3_value**, sqlite3_int64*); + virtual base::Status Update(int, sqlite3_value**, sqlite3_int64*); - void SetErrorMessage(char* error) { - sqlite3_free(zErrMsg); - zErrMsg = error; - } + 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*); + 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 @@ -384,6 +247,177 @@ class SqliteTable : public sqlite3_vtab { 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/stats_table.cc b/src/trace_processor/sqlite/stats_table.cc index e4b7787c5..296a2f501 100644 --- a/src/trace_processor/sqlite/stats_table.cc +++ b/src/trace_processor/sqlite/stats_table.cc @@ -16,6 +16,7 @@ #include "src/trace_processor/sqlite/stats_table.h" +#include "perfetto/base/status.h" #include "src/trace_processor/sqlite/sqlite_utils.h" namespace perfetto { @@ -24,9 +25,7 @@ namespace trace_processor { StatsTable::StatsTable(sqlite3*, const TraceStorage* storage) : storage_(storage) {} -void StatsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) { - SqliteTable::Register<StatsTable>(db, storage, "stats"); -} +StatsTable::~StatsTable() = default; util::Status StatsTable::Init(int, const char* const*, Schema* schema) { *schema = Schema( @@ -46,8 +45,8 @@ util::Status StatsTable::Init(int, const char* const*, Schema* schema) { 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*) { @@ -55,16 +54,20 @@ int StatsTable::BestIndex(const QueryConstraints&, BestIndexInfo*) { } 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; -int StatsTable::Cursor::Filter(const QueryConstraints&, - sqlite3_value**, - FilterHistory) { +base::Status StatsTable::Cursor::Filter(const QueryConstraints&, + sqlite3_value**, + FilterHistory) { *this = Cursor(table_); - return SQLITE_OK; + return base::OkStatus(); } -int StatsTable::Cursor::Column(sqlite3_context* ctx, int N) { +base::Status StatsTable::Cursor::Column(sqlite3_context* ctx, int N) { const auto kSqliteStatic = sqlite_utils::kSqliteStatic; switch (N) { case Column::kName: @@ -114,16 +117,16 @@ int StatsTable::Cursor::Column(sqlite3_context* ctx, int N) { PERFETTO_FATAL("Unknown column %d", N); break; } - return SQLITE_OK; + return base::OkStatus(); } -int StatsTable::Cursor::Next() { +base::Status StatsTable::Cursor::Next() { static_assert(stats::kTypes[0] == stats::kSingle, "the first stats entry cannot be indexed"); const auto* cur_entry = &storage_->stats()[key_]; if (stats::kTypes[key_] == stats::kIndexed) { if (++index_ != cur_entry->indexed_values.end()) { - return SQLITE_OK; + return base::OkStatus(); } } while (++key_ < stats::kNumKeys) { @@ -134,10 +137,10 @@ int StatsTable::Cursor::Next() { break; } } - return SQLITE_OK; + return base::OkStatus(); } -int StatsTable::Cursor::Eof() { +bool StatsTable::Cursor::Eof() { return key_ >= stats::kNumKeys; } diff --git a/src/trace_processor/sqlite/stats_table.h b/src/trace_processor/sqlite/stats_table.h index 3213a2a1d..2824cb62b 100644 --- a/src/trace_processor/sqlite/stats_table.h +++ b/src/trace_processor/sqlite/stats_table.h @@ -30,20 +30,22 @@ namespace trace_processor { // 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: - Cursor(StatsTable*); + explicit Cursor(StatsTable*); + ~Cursor() final; // Implementation of SqliteTable::Cursor. - int Filter(const QueryConstraints&, - sqlite3_value**, - FilterHistory) override; - int Next() override; - int Eof() override; - int Column(sqlite3_context*, int N) override; + base::Status Filter(const QueryConstraints&, + sqlite3_value**, + FilterHistory); + base::Status Next(); + bool Eof(); + base::Status Column(sqlite3_context*, int N); private: Cursor(Cursor&) = delete; @@ -58,14 +60,13 @@ class StatsTable : public SqliteTable { 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 86bdf6d91..9596be885 100644 --- a/src/trace_processor/stdlib/android/BUILD.gn +++ b/src/trace_processor/stdlib/android/BUILD.gn @@ -18,9 +18,12 @@ perfetto_sql_source_set("android") { 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 000000000..2507cb1fa --- /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 LONG The counter value. +-- @ret STRING The human-readable name for the counter value. +SELECT CREATE_FUNCTION( + '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 "disconn" + 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 "uninit" + ELSE "unknown" + END + WHEN $track = "battery_stats.data_conn" + THEN + CASE $value + WHEN 0 THEN "oos" + WHEN 1 THEN "gprs" + WHEN 2 THEN "edge" + WHEN 3 THEN "umts" + WHEN 4 THEN "cdma" + WHEN 5 THEN "evdo_0" + WHEN 6 THEN "evdo_A" + WHEN 7 THEN "1xrtt" + WHEN 8 THEN "hsdpa" + WHEN 9 THEN "hsupa" + WHEN 10 THEN "hspa" + WHEN 11 THEN "iden" + WHEN 12 THEN "evdo_b" + WHEN 13 THEN "lte" + WHEN 14 THEN "ehrpd" + WHEN 15 THEN "hspap" + WHEN 16 THEN "gsm" + WHEN 17 THEN "td_scdma" + WHEN 18 THEN "iwlan" + WHEN 19 THEN "lte_ca" + WHEN 20 THEN "nr" + WHEN 21 THEN "emngcy" + 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, + BATTERY_STATS_COUNTER_TO_STRING(name, value) AS value_name, + IFNULL(LEAD(ts) OVER (PARTITION BY track_id 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 51a9d75f7..a31fff0f1 100644 --- a/src/trace_processor/stdlib/android/binder.sql +++ b/src/trace_processor/stdlib/android/binder.sql @@ -90,6 +90,7 @@ WITH 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 @@ WITH 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 @@ SELECT 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 @@ SELECT 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 c1de58f44..595c1fed3 100644 --- a/src/trace_processor/stdlib/android/monitor_contention.sql +++ b/src/trace_processor/stdlib/android/monitor_contention.sql @@ -192,10 +192,13 @@ SELECT 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 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 000000000..a88615afc --- /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 20fb82e28..6154f00af 100644 --- a/src/trace_processor/stdlib/android/process_metadata.sql +++ b/src/trace_processor/stdlib/android/process_metadata.sql @@ -50,11 +50,19 @@ FROM process 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 000000000..9ce6bf18b --- /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 b3ea475bc..7ba85fd17 100644 --- a/src/trace_processor/stdlib/chrome/BUILD.gn +++ b/src/trace_processor/stdlib/chrome/BUILD.gn @@ -15,5 +15,8 @@ import("../../../../gn/perfetto_sql.gni") perfetto_sql_source_set("chrome_sql") { - sources = [ "cpu_powerups.sql" ] + sources = [ + "chrome_scrolls.sql", + "cpu_powerups.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 000000000..1ad7f31ab --- /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/common/BUILD.gn b/src/trace_processor/stdlib/common/BUILD.gn index 8147bcf33..38088fc4c 100644 --- a/src/trace_processor/stdlib/common/BUILD.gn +++ b/src/trace_processor/stdlib/common/BUILD.gn @@ -17,6 +17,7 @@ import("../../../../gn/perfetto_sql.gni") perfetto_sql_source_set("common") { sources = [ "counters.sql", + "cpus.sql", "metadata.sql", "percentiles.sql", "slices.sql", diff --git a/src/trace_processor/stdlib/common/cpus.sql b/src/trace_processor/stdlib/common/cpus.sql new file mode 100644 index 000000000..3caec3f3f --- /dev/null +++ b/src/trace_processor/stdlib/common/cpus.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. + + +CREATE TABLE internal_cpu_sizes AS +SELECT 0 AS n, 'little' AS size +UNION +SELECT 1 AS n, 'big' AS size +UNION +SELECT 2 AS n, 'huge' AS size; + +CREATE TABLE internal_ranked_cpus AS +SELECT + (DENSE_RANK() OVER win) - 1 AS n, + cpu +FROM ( + SELECT + track.cpu AS cpu, + MAX(counter.value) AS maxfreq + FROM counter + JOIN cpu_counter_track AS track + ON (counter.track_id = track.id) + WHERE track.name = "cpufreq" + GROUP BY track.cpu +) +WINDOW win AS (ORDER BY maxfreq); + +-- Guess size of CPU. +-- On some multicore devices the cores are heterogeneous and divided +-- into two or more 'sizes'. In a typical case a device might have 8 +-- cores of which 4 are 'little' (low power & low performance) and 4 +-- are 'big' (high power & high performance). This functions attempts +-- to map a given CPU index onto the relevant descriptor. For +-- 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. +SELECT CREATE_FUNCTION( + 'GUESS_CPU_SIZE(cpu_index INT)', + 'STRING', + ' + SELECT + IIF((SELECT COUNT(DISTINCT n) FROM internal_ranked_cpus) >= 2, size, null) as size + FROM internal_ranked_cpus + LEFT JOIN internal_cpu_sizes USING(n) + WHERE cpu = $cpu_index; + ' +); diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h index 4fe2071e0..66fc9409f 100644 --- a/src/trace_processor/storage/trace_storage.h +++ b/src/trace_processor/storage/trace_storage.h @@ -513,6 +513,13 @@ class TraceStorage { const tables::RawTable& raw_table() const { return raw_table_; } tables::RawTable* mutable_raw_table() { return &raw_table_; } + const tables::FtraceEventTable& ftrace_event_table() const { + return ftrace_event_table_; + } + tables::FtraceEventTable* mutable_ftrace_event_table() { + return &ftrace_event_table_; + } + const tables::CpuTable& cpu_table() const { return cpu_table_; } tables::CpuTable* mutable_cpu_table() { return &cpu_table_; } @@ -895,11 +902,8 @@ class TraceStorage { SqlStats sql_stats_; - // Raw events are every ftrace event in the trace. The raw event includes - // the timestamp and the pid. The args for the raw event will be in the - // args table. This table can be used to generate a text version of the - // trace. tables::RawTable raw_table_{&string_pool_}; + tables::FtraceEventTable ftrace_event_table_{&string_pool_, &raw_table_}; tables::CpuTable cpu_table_{&string_pool_}; diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn index 83145a487..9ec397ffc 100644 --- a/src/trace_processor/tables/BUILD.gn +++ b/src/trace_processor/tables/BUILD.gn @@ -32,12 +32,7 @@ perfetto_tp_tables("tables_python") { source_set("tables") { sources = [ - "counter_tables.h", - "flow_tables.h", - "macros.h", "macros_internal.h", - "profiler_tables.h", - "slice_tables.h", "table_destructors.cc", ] deps = [ @@ -54,10 +49,7 @@ perfetto_tp_tables("py_tables_unittest") { source_set("unittests") { testonly = true - sources = [ - "macros_unittest.cc", - "py_tables_unittest.cc", - ] + sources = [ "py_tables_unittest.cc" ] deps = [ ":py_tables_unittest", ":tables", @@ -71,10 +63,13 @@ if (enable_perfetto_benchmarks) { source_set("benchmarks") { testonly = true deps = [ - ":tables", + ":py_tables_benchmark", "../../../gn:benchmark", "../../../gn:default_deps", ] - sources = [ "macros_benchmark.cc" ] + sources = [ "py_tables_benchmark.cc" ] + } + perfetto_tp_tables("py_tables_benchmark") { + sources = [ "py_tables_benchmark.py" ] } } diff --git a/src/trace_processor/tables/android_tables.py b/src/trace_processor/tables/android_tables.py index b77afcbc8..03dff073b 100644 --- a/src/trace_processor/tables/android_tables.py +++ b/src/trace_processor/tables/android_tables.py @@ -28,6 +28,7 @@ from python.generators.trace_processor_table.public import CppUint32 from src.trace_processor.tables.metadata_tables import THREAD_TABLE ANDROID_LOG_TABLE = Table( + python_module=__file__, class_name="AndroidLogTable", sql_name="android_logs", columns=[ @@ -54,6 +55,7 @@ ANDROID_LOG_TABLE = Table( })) ANDROID_GAME_INTERVENTION_LIST_TABLE = Table( + python_module=__file__, class_name='AndroidGameInterventionListTable', sql_name='android_game_intervention_list', columns=[ @@ -126,6 +128,7 @@ This is generated by the game_mode_intervention data-source. })) ANDROID_DUMPSTATE_TABLE = Table( + python_module=__file__, class_name='AndroidDumpstateTable', sql_name='android_dumpstate', columns=[ diff --git a/src/trace_processor/tables/counter_tables.h b/src/trace_processor/tables/counter_tables.h deleted file mode 100644 index 69668b2bb..000000000 --- a/src/trace_processor/tables/counter_tables.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_TRACE_PROCESSOR_TABLES_COUNTER_TABLES_H_ -#define SRC_TRACE_PROCESSOR_TABLES_COUNTER_TABLES_H_ - -#include "src/trace_processor/tables/macros.h" -#include "src/trace_processor/tables/track_tables_py.h" - -namespace perfetto { -namespace trace_processor { -namespace tables { - -// @tablegroup Events -// @param arg_set_id {@joinable args.arg_set_id} -#define PERFETTO_TP_COUNTER_TABLE_DEF(NAME, PARENT, C) \ - NAME(CounterTable, "counter") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(int64_t, ts, Column::Flag::kSorted) \ - C(CounterTrackTable::Id, track_id) \ - C(double, value) \ - C(std::optional<uint32_t>, arg_set_id) - -} // namespace tables -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_COUNTER_TABLES_H_ diff --git a/src/trace_processor/tables/counter_tables.py b/src/trace_processor/tables/counter_tables.py index 1dd649539..e88245210 100644 --- a/src/trace_processor/tables/counter_tables.py +++ b/src/trace_processor/tables/counter_tables.py @@ -26,6 +26,7 @@ from python.generators.trace_processor_table.public import CppUint32 from src.trace_processor.tables.track_tables import COUNTER_TRACK_TABLE COUNTER_TABLE = Table( + python_module=__file__, class_name='CounterTable', sql_name='counter', columns=[ diff --git a/src/trace_processor/tables/flow_tables.h b/src/trace_processor/tables/flow_tables.h deleted file mode 100644 index a00ee8921..000000000 --- a/src/trace_processor/tables/flow_tables.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_TRACE_PROCESSOR_TABLES_FLOW_TABLES_H_ -#define SRC_TRACE_PROCESSOR_TABLES_FLOW_TABLES_H_ - -#include "src/trace_processor/tables/macros.h" -#include "src/trace_processor/tables/slice_tables.h" - -namespace perfetto { -namespace trace_processor { -namespace tables { - -// @param arg_set_id {@joinable args.arg_set_id} -#define PERFETTO_TP_FLOW_TABLE_DEF(NAME, PARENT, C) \ - NAME(FlowTable, "flow") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(SliceTable::Id, slice_out) \ - C(SliceTable::Id, slice_in) \ - C(uint32_t, arg_set_id) - -} // namespace tables -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_FLOW_TABLES_H_ diff --git a/src/trace_processor/tables/flow_tables.py b/src/trace_processor/tables/flow_tables.py index 0491a3111..df0ae4c82 100644 --- a/src/trace_processor/tables/flow_tables.py +++ b/src/trace_processor/tables/flow_tables.py @@ -22,6 +22,7 @@ from python.generators.trace_processor_table.public import CppUint32 from src.trace_processor.tables.slice_tables import SLICE_TABLE FLOW_TABLE = Table( + python_module=__file__, class_name='FlowTable', sql_name='flow', columns=[ diff --git a/src/trace_processor/tables/macros.h b/src/trace_processor/tables/macros.h deleted file mode 100644 index a085066ab..000000000 --- a/src/trace_processor/tables/macros.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_TRACE_PROCESSOR_TABLES_MACROS_H_ -#define SRC_TRACE_PROCESSOR_TABLES_MACROS_H_ - -#include "src/trace_processor/tables/macros_internal.h" - -namespace perfetto { -namespace trace_processor { - -// Usage of the below macros -// These macros have two different invocation patterns depending on whether you -// are defining a root table or a derived table (see below for definitions and -// examples). If you're not sure which one you need, you probably want a derived -// table. -// -// Root tables -// Root tables act as the ultimate parent of a heirarcy of tables. All rows of -// child tables will be some subset of rows in the parent. Real world examples -// of root tables include EventTable and TrackTable. -// -// All root tables implicitly contain an 'id' column which contains the row -// index for each row in the table. -// -// Suppose we want to define EventTable with columns 'ts' and 'arg_set_id'. -// -// Then we would invoke the macro as follows: -// #define PERFETTO_TP_EVENT_TABLE_DEF(NAME, PARENT, C) -// NAME(EventTable, "event") -// PERFETTO_TP_ROOT_TABLE(PARENT, C) -// C(int64_t, ts, Column::kSorted) -// C(uint32_t, arg_set_id) -// PERFETTO_TP_TABLE(PERFETTO_TP_EVENT_TABLE_DEF); -// -// Note the call to PERFETTO_TP_ROOT_TABLE; this macro (defined below) should -// be called by root tables passing the PARENT and C and allows for correct type -// checking of root tables. -// -// Derived tables -// Suppose we want to derive a table called SliceTable which inherits all -// columns from EventTable (with EventTable's definition macro being -// PERFETTO_TP_EVENT_TABLE_DEF) and columns 'dur' and 'depth'. -// -// Then, we would invoke the macro as follows: -// #define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C) -// NAME(SliceTable, "slice") -// PARENT(PERFETTO_TP_EVENT_TABLE_DEF, C) -// C(int64_t, dur) -// C(uint8_t, depth) -// PERFETTO_TP_TABLE(PERFETTO_TP_SLICE_TABLE_DEF); - -// Macro definition using when defining a new root table. -// -// This macro should be called by passing PARENT and C in root tables; this -// allows for correct type-checking of columns. -// -// See the top of the file for how this should be used. -#define PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) - -// The macro used to define storage backed tables. -// See the top of the file for how this should be used. -// -// This macro takes one argument: the full definition of the table; the -// definition is a function macro taking three arguments: -// 1. NAME, a function macro taking two argument: the name of the new class -// being defined and the name of the table when exposed to SQLite. -// 2. PARENT, a function macro taking two arguments: a) the definition of -// the parent table if this table -// is a root table b) C, the third parameter of the macro definition (see -// below). For root tables, PARENT and C are passsed to -// PERFETTO_TP_ROOT_TABLE instead of PARENT called directly. -// 3. C, a function macro taking two or three parameters: -// a) the type of a column -// b) the name of a column -// c) (optional) the flags of the column (see Column::Flag -// for details). -// This macro should be invoked as many times as there are columns in the -// table with the information about them. -#define PERFETTO_TP_TABLE(DEF) \ - PERFETTO_TP_TABLE_INTERNAL( \ - PERFETTO_TP_TABLE_NAME(DEF), PERFETTO_TP_TABLE_CLASS(DEF), \ - PERFETTO_TP_TABLE_CLASS(PERFETTO_TP_PARENT_DEF(DEF)), DEF) - -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_MACROS_H_ diff --git a/src/trace_processor/tables/macros_internal.h b/src/trace_processor/tables/macros_internal.h index 059e7b7ad..b2f1f1526 100644 --- a/src/trace_processor/tables/macros_internal.h +++ b/src/trace_processor/tables/macros_internal.h @@ -57,30 +57,6 @@ class RootParentTable : public Table { explicit RootParentTable(std::nullptr_t); }; -// IdHelper is used to figure out the Id type for a table. -// -// We do this using templates with the following algorithm: -// 1. If the parent class is anything but RootParentTable, the Id of the -// table is the same as the Id of the parent. -// 2. If the parent class is RootParentTable (i.e. the table is a root -// table), then the Id is the one defined in the table itself. -// The net result of this is that all tables in the hierarchy get the -// same type of Id - the one defined in the root table of that hierarchy. -// -// Reasoning: We do this because using uint32_t is very overloaded and -// having a wrapper type for ids is very helpful to avoid confusion with -// row indices (especially because ids and row indices often appear in -// similar places in the codebase - that is at insertion in parsers and -// in trackers). -template <typename ParentClass, typename Class> -struct IdHelper { - using Id = typename ParentClass::Id; -}; -template <typename Class> -struct IdHelper<RootParentTable, Class> { - using Id = typename Class::DefinedId; -}; - // The parent class for all macro generated tables. // This class is used to extract common code from the macro tables to reduce // code size. @@ -284,665 +260,6 @@ class AbstractConstRowReference { }; } // namespace macros_internal - -// Ignore GCC warning about a missing argument for a variadic macro parameter. -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC system_header -#endif - -// Basic helper macros. -#define PERFETTO_TP_NOOP(...) - -// Gets the class name from a table definition. -#define PERFETTO_TP_EXTRACT_TABLE_CLASS(class_name, ...) class_name -#define PERFETTO_TP_TABLE_CLASS(DEF) \ - DEF(PERFETTO_TP_EXTRACT_TABLE_CLASS, PERFETTO_TP_NOOP, PERFETTO_TP_NOOP) - -// Gets the table name from the table definition. -#define PERFETTO_TP_EXTRACT_TABLE_NAME(_, table_name) table_name -#define PERFETTO_TP_TABLE_NAME(DEF) \ - DEF(PERFETTO_TP_EXTRACT_TABLE_NAME, PERFETTO_TP_NOOP, PERFETTO_TP_NOOP) - -// Gets the parent definition from a table definition. -#define PERFETTO_TP_EXTRACT_PARENT_DEF(PARENT_DEF, _) PARENT_DEF -#define PERFETTO_TP_PARENT_DEF(DEF) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_EXTRACT_PARENT_DEF, PERFETTO_TP_NOOP) - -// Invokes FN on each column in the definition of the table. We define a -// recursive macro as we need to walk up the hierarchy until we hit the root. -// Currently, we hardcode 5 levels but this can be increased as necessary. -#define PERFETTO_TP_ALL_COLUMNS_0(DEF, arg) \ - static_assert(false, "Macro recursion depth exceeded"); -#define PERFETTO_TP_ALL_COLUMNS_1(DEF, arg) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_ALL_COLUMNS_0, arg) -#define PERFETTO_TP_ALL_COLUMNS_2(DEF, arg) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_ALL_COLUMNS_1, arg) -#define PERFETTO_TP_ALL_COLUMNS_3(DEF, arg) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_ALL_COLUMNS_2, arg) -#define PERFETTO_TP_ALL_COLUMNS_4(DEF, arg) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_ALL_COLUMNS_3, arg) -#define PERFETTO_TP_ALL_COLUMNS(DEF, arg) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_ALL_COLUMNS_4, arg) - -// Invokes FN on each column in the table definition. -#define PERFETTO_TP_TABLE_COLUMNS(DEF, FN) \ - DEF(PERFETTO_TP_NOOP, PERFETTO_TP_NOOP, FN) - -// Invokes FN on each column in every ancestor of the table. -#define PERFETTO_TP_PARENT_COLUMNS(DEF, FN) \ - PERFETTO_TP_ALL_COLUMNS(PERFETTO_TP_PARENT_DEF(DEF), FN) - -// Basic macros for extracting column info from a schema. -#define PERFETTO_TP_NAME_COMMA(type, name, ...) name, -#define PERFETTO_TP_TYPE_NAME_COMMA(type, name, ...) type name, - -// Constructor parameters of Table::Row. -// We name this name_c to avoid a clash with the field names of -// Table::Row. -#define PERFETTO_TP_ROW_CONSTRUCTOR(type, name, ...) type name##_c = {}, - -// Constructor parameters for parent of Row. -#define PERFETTO_TP_PARENT_ROW_CONSTRUCTOR(type, name, ...) name##_c, - -// Initializes the members of Table::Row. -#define PERFETTO_TP_ROW_INITIALIZER(type, name, ...) name = name##_c; - -// Defines the variable in Table::Row. -#define PERFETTO_TP_ROW_DEFINITION(type, name, ...) type name = {}; - -// Used to generate an equality implementation on Table::Row. -#define PERFETTO_TP_ROW_EQUALS(type, name, ...) \ - TypedColumn<type>::Equals(other.name, name)&& - -// Defines the parent row field in Insert. -#define PERFETTO_TP_PARENT_ROW_INSERT(type, name, ...) row.name, - -// Defines the member variable in the Table. -#define PERFETTO_TP_TABLE_MEMBER(type, name, ...) \ - ColumnStorage<TypedColumn<type>::stored_type> name##_; - -#define PERFETTO_TP_COLUMN_FLAG_HAS_FLAG_COL(type, name, flags) \ - static constexpr uint32_t name##_flags() { \ - return static_cast<uint32_t>(flags) | TypedColumn<type>::default_flags(); \ - } - -#define PERFETTO_TP_COLUMN_FLAG_NO_FLAG_COL(type, name) \ - static constexpr uint32_t name##_flags() { \ - return TypedColumn<type>::default_flags(); \ - } - -#define PERFETTO_TP_PARENT_COLUMN_FLAG_HAS_FLAG_COL(type, name, flags) \ - static constexpr uint32_t name##_flags() { \ - return (static_cast<uint32_t>(flags) | \ - TypedColumn<type>::default_flags()) & \ - ~Column::kNoCrossTableInheritFlags; \ - } - -#define PERFETTO_TP_PARENT_COLUMN_FLAG_NO_FLAG_COL(type, name) \ - static constexpr uint32_t name##_flags() { \ - return TypedColumn<type>::default_flags() & \ - ~Column::kNoCrossTableInheritFlags; \ - } - -#define PERFETTO_TP_COLUMN_FLAG_CHOOSER(type, name, maybe_flags, fn, ...) fn - -// MSVC has slightly different rules about __VA_ARGS__ expansion. This makes it -// behave similarly to GCC/Clang. -// See https://stackoverflow.com/q/5134523/14028266 . -#define PERFETTO_TP_EXPAND_VA_ARGS(x) x - -#define PERFETTO_TP_COLUMN_FLAG(...) \ - PERFETTO_TP_EXPAND_VA_ARGS(PERFETTO_TP_COLUMN_FLAG_CHOOSER( \ - __VA_ARGS__, PERFETTO_TP_COLUMN_FLAG_HAS_FLAG_COL, \ - PERFETTO_TP_COLUMN_FLAG_NO_FLAG_COL)(__VA_ARGS__)) - -#define PERFETTO_TP_PARENT_COLUMN_FLAG(...) \ - PERFETTO_TP_EXPAND_VA_ARGS(PERFETTO_TP_COLUMN_FLAG_CHOOSER( \ - __VA_ARGS__, PERFETTO_TP_PARENT_COLUMN_FLAG_HAS_FLAG_COL, \ - PERFETTO_TP_PARENT_COLUMN_FLAG_NO_FLAG_COL)(__VA_ARGS__)) - -// Creates the sparse vector with the given flags. -#define PERFETTO_TP_TABLE_CONSTRUCTOR_SV(type, name, ...) \ - name##_(ColumnStorage<TypedColumn<type>::stored_type>::Create< \ - (name##_flags() & Column::Flag::kDense) != 0>()), - -// Invokes the chosen column constructor by passing the given args. -#define PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN(type, name, ...) \ - columns_.emplace_back(#name, &name##_, name##_flags(), this, \ - static_cast<uint32_t>(columns_.size()), \ - static_cast<uint32_t>(overlays_.size()) - 1); - -// Inserts the value into the corresponding column. -#define PERFETTO_TP_COLUMN_APPEND(type, name, ...) \ - mutable_##name()->Append(std::move(row.name)); - -// Creates a schema entry for the corresponding column. -#define PERFETTO_TP_COLUMN_SCHEMA(type, name, ...) \ - schema.columns.emplace_back(Table::Schema::Column{ \ - #name, TypedColumn<type>::SqlValueType(), false, \ - static_cast<bool>(name##_flags() & Column::Flag::kSorted), \ - static_cast<bool>(name##_flags() & Column::Flag::kHidden), \ - static_cast<bool>(name##_flags() & Column::Flag::kSetId)}); - -// Defines the immutable accessor for a column. -#define PERFETTO_TP_TABLE_COL_GETTER(type, name, ...) \ - const TypedColumn<type>& name() const { \ - return static_cast<const TypedColumn<type>&>(columns_[ColumnIndex::name]); \ - } - -// Defines the accessors for a column. -#define PERFETTO_TP_TABLE_MUTABLE_COL_GETTER(type, name, ...) \ - TypedColumn<type>* mutable_##name() { \ - return static_cast<TypedColumn<type>*>(&columns_[ColumnIndex::name]); \ - } - -// Defines the accessors for a column. -#define PERFETTO_TP_TABLE_STATIC_ASSERT_FLAG(type, name, ...) \ - static_assert(Column::IsFlagsAndTypeValid<TypedColumn<type>::stored_type>( \ - name##_flags()), \ - "Column type and flag combination is not valid"); - -// Defines the parameter for the |ExtendParent| function. -#define PERFETTO_TP_TABLE_EXTEND_PARAM(type, name, ...) \ - ColumnStorage<TypedColumn<type>::stored_type> name, - -// Defines the parameter passing for the |ExtendParent| function. -#define PERFETTO_TP_TABLE_EXTEND_PARAM_PASSING(type, name, ...) std::move(name), - -// Sets the table nullable vector to the parameter passed in the -// |SelectAndExtendParent| function. -#define PERFETTO_TP_TABLE_EXTEND_SET_NV(type, name, ...) \ - PERFETTO_DCHECK(name.size() == parent_overlay.size()); \ - name##_ = std::move(name); - -// Definition used as the parent of root tables. -#define PERFETTO_TP_ROOT_TABLE_PARENT_DEF(NAME, PARENT, C) \ - NAME(macros_internal::RootParentTable, "root") - -// Defines the getter for the column value in the RowReference. -#define PERFETTO_TP_TABLE_CONST_ROW_REF_GETTER(type, name, ...) \ - type name() const { return table_->name()[row_number_]; } - -// Defines the accessor for the column value in the RowReference. -#define PERFETTO_TP_TABLE_ROW_REF_SETTER(type, name, ...) \ - void set_##name(TypedColumn<type>::non_optional_type v) const { \ - return mutable_table()->mutable_##name()->Set(row_number_, v); \ - } - -// Defines the getter for the column value in the ConstIterator. -#define PERFETTO_TP_TABLE_CONST_IT_GETTER(type, name, ...) \ - type name() const { \ - const auto& col = table_->name(); \ - return col.GetAtIdx(its_[col.overlay_index()].index()); \ - } - -// Defines the setter for the column value in the Iterator. -#define PERFETTO_TP_TABLE_IT_SETTER(type, name, ...) \ - void set_##name(TypedColumn<type>::non_optional_type v) { \ - auto* col = mutable_table_->mutable_##name(); \ - col->SetAtIdx(its_[col->overlay_index()].index(), v); \ - } - -// Defines the column index constexpr declaration. -#define PERFETTO_TP_COLUMN_INDEX(type, name, ...) \ - static constexpr uint32_t name = static_cast<uint32_t>(ColumnIndexEnum::name); - -// Defines an alias for column type for each column. -#define PERFETTO_TP_COLUMN_TYPE_USING(type, name, ...) \ - using name = TypedColumn<type>; - -// Calls ShrinkToFit on each column. -#define PERFETTO_TP_COLUMN_SHRINK_TO_FIT(type, name, ...) name##_.ShrinkToFit(); - -// For more general documentation, see PERFETTO_TP_TABLE in macros.h. -#define PERFETTO_TP_TABLE_INTERNAL(table_name, class_name, parent_class_name, \ - DEF) \ - class class_name : public macros_internal::MacroTable { \ - public: \ - /* Forward declaration to allow free usage below. */ \ - class ConstRowReference; \ - class RowReference; \ - class RowNumber; \ - class ConstIterator; \ - \ - private: \ - /* \ - * Allows IdHelper to access DefinedId for root tables. \ - * Needs to be defined here to allow the public using declaration of Id \ - * below to work correctly. \ - */ \ - friend struct macros_internal::IdHelper<parent_class_name, class_name>; \ - \ - /* Whether or not this is a root table */ \ - static constexpr bool kIsRootTable = \ - std::is_same<parent_class_name, \ - macros_internal::RootParentTable>::value; \ - \ - /* Aliases to reduce clutter in class defintions below. */ \ - using AbstractRowNumber = macros_internal:: \ - AbstractRowNumber<class_name, ConstRowReference, RowReference>; \ - using AbstractConstRowReference = \ - macros_internal::AbstractConstRowReference<class_name, RowNumber>; \ - using AbstractConstIterator = \ - macros_internal::AbstractConstIterator<ConstIterator, \ - class_name, \ - RowNumber, \ - ConstRowReference>; \ - \ - enum class ColumnIndexEnum { \ - id, \ - type, /* Expands to col1, col2, ... */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_NAME_COMMA) kNumCols \ - }; \ - \ - /* \ - * Defines a new id type for a hierarchy of tables. \ - * We define it here as we need this type to be visible for the public \ - * using declaration of Id below. \ - * Note: This type will only used if this table is a root table. \ - */ \ - struct DefinedId : public BaseId { \ - DefinedId() = default; \ - explicit constexpr DefinedId(uint32_t v) : BaseId(v) {} \ - }; \ - static_assert(std::is_trivially_destructible<DefinedId>::value, \ - "Inheritance used without trivial destruction"); \ - \ - static constexpr uint32_t id_flags() { return Column::kIdFlags; } \ - static constexpr uint32_t type_flags() { return Column::kNoFlag; } \ - PERFETTO_TP_PARENT_COLUMNS(DEF, PERFETTO_TP_PARENT_COLUMN_FLAG) \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_COLUMN_FLAG) \ - \ - public: \ - /* \ - * This defines the type of the id to be the type of the root \ - * table of the hierarchy - see IdHelper for more details. \ - */ \ - using Id = macros_internal::IdHelper<parent_class_name, class_name>::Id; \ - \ - struct ColumnIndex { \ - static constexpr uint32_t id = \ - static_cast<uint32_t>(ColumnIndexEnum::id); \ - static constexpr uint32_t type = \ - static_cast<uint32_t>(ColumnIndexEnum::type); \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_COLUMN_INDEX) \ - }; \ - \ - struct ColumnType { \ - using id = IdColumn<Id>; \ - using type = TypedColumn<StringPool::Id>; \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_COLUMN_TYPE_USING) \ - }; \ - \ - struct Row : parent_class_name::Row { \ - /* \ - * Expands to Row(col_type1 col1_c, std::optional<col_type2> col2_c, \ - * ...) \ - */ \ - Row(PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_ROW_CONSTRUCTOR) \ - std::nullptr_t = nullptr) \ - : parent_class_name::Row(PERFETTO_TP_PARENT_COLUMNS( \ - DEF, \ - PERFETTO_TP_PARENT_ROW_CONSTRUCTOR) nullptr) { \ - type_ = table_name; \ - \ - /* \ - * Expands to \ - * col1 = col1_c; \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_ROW_INITIALIZER) \ - } \ - \ - bool operator==(const class_name::Row& other) const { \ - return PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_ROW_EQUALS) true; \ - } \ - \ - /* \ - * Expands to \ - * col_type1 col1 = {}; \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_ROW_DEFINITION) \ - }; \ - static_assert(std::is_trivially_destructible<Row>::value, \ - "Inheritance used without trivial destruction"); \ - \ - /* \ - * Reference to a row which exists in the table. \ - * \ - * Allows caller code to store and instances of this object without \ - * having to interact with row numbers. \ - */ \ - class ConstRowReference : public AbstractConstRowReference { \ - public: \ - ConstRowReference(const class_name* table, uint32_t row_number) \ - : AbstractConstRowReference(table, row_number) {} \ - \ - PERFETTO_TP_TABLE_CONST_ROW_REF_GETTER(Id, id) \ - PERFETTO_TP_TABLE_CONST_ROW_REF_GETTER(StringPool::Id, type) \ - \ - /* \ - * Expands to \ - * col1_type col1() const { return table_->col1()[row_]; } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_CONST_ROW_REF_GETTER) \ - }; \ - static_assert(std::is_trivially_destructible<ConstRowReference>::value, \ - "Inheritance used without trivial destruction"); \ - \ - /* \ - * Reference to a row which exists in the table. \ - * \ - * Allows caller code to store and instances of this object without \ - * having to interact with row numbers. \ - */ \ - class RowReference : public ConstRowReference { \ - public: \ - RowReference(class_name* table, uint32_t row_number) \ - : ConstRowReference(table, row_number) {} \ - \ - /* \ - * Expands to \ - * void set_col1(col1_type v) { table_->mutable_col1()->Set(row, v); } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_ROW_REF_SETTER) \ - \ - private: \ - class_name* mutable_table() const { \ - return const_cast<class_name*>(table_); \ - } \ - }; \ - static_assert(std::is_trivially_destructible<RowReference>::value, \ - "Inheritance used without trivial destruction"); \ - \ - /* \ - * Strongly typed wrapper around the row index. Prefer storing this over \ - * storing RowReference to reduce memory usage \ - */ \ - class RowNumber : public AbstractRowNumber { \ - public: \ - explicit RowNumber(uint32_t row_number) \ - : AbstractRowNumber(row_number) {} \ - }; \ - static_assert(std::is_trivially_destructible<RowNumber>::value, \ - "Inheritance used without trivial destruction"); \ - \ - /* Return value of Insert giving access to id and row number */ \ - struct IdAndRow { \ - Id id; \ - uint32_t row; \ - RowReference row_reference; \ - RowNumber row_number; \ - }; \ - \ - /* \ - * Strongly typed const iterator for this macro table. \ - * \ - * Allows efficient retrieval of values from this table without having to \ - * deal with row numbers, ColumnStorageOverlays or indices. \ - */ \ - class ConstIterator : public AbstractConstIterator { \ - public: \ - PERFETTO_TP_TABLE_CONST_IT_GETTER(Id, id) \ - PERFETTO_TP_TABLE_CONST_IT_GETTER(StringPool::Id, type) \ - \ - /* \ - * Expands to \ - * col1_type col1() const { return table_->col1().GetAtIdx(i); } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_CONST_IT_GETTER) \ - \ - protected: \ - /* \ - * Must not be public to avoid buggy code because of inheritance \ - * without virtual destructor. \ - */ \ - explicit ConstIterator(const class_name* table, \ - std::vector<ColumnStorageOverlay> overlays) \ - : AbstractConstIterator(table, std::move(overlays)) {} \ - \ - uint32_t CurrentRowNumber() const { \ - /* \ - * Because the last ColumnStorageOverlay belongs to this table it \ - * will be dense (i.e. every row in the table will be part of this \ - * ColumnStorageOverlay + will be represented with a range). This \ - * means that the index() of the last ColumnStorageOverlay iterator \ - * is precisely the row number in table! \ - */ \ - return its_.back().index(); \ - } \ - \ - private: \ - friend class class_name; \ - friend class AbstractConstIterator; \ - }; \ - \ - /* \ - * Strongly typed iterator for this macro table. \ - * \ - * Enhances ConstIterator by also allowing values in the table to be set \ - * as well as retrieved. \ - */ \ - class Iterator : public ConstIterator { \ - public: \ - /* \ - * Expands to \ - * void set_col1(col1_type v) { table_->mut_col1()->SetAtIdx(i, v); } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_IT_SETTER) \ - \ - /* \ - * Returns a RowReference to the current row. \ - */ \ - RowReference row_reference() const { \ - return RowReference(mutable_table_, CurrentRowNumber()); \ - } \ - \ - private: \ - friend class class_name; \ - \ - /* \ - * Must not be public to avoid buggy code because of inheritance \ - * without virtual destructor. \ - */ \ - explicit Iterator(class_name* table, \ - std::vector<ColumnStorageOverlay> overlays) \ - : ConstIterator(table, std::move(overlays)), \ - mutable_table_(table) {} \ - \ - class_name* mutable_table_ = nullptr; \ - }; \ - \ - class_name(StringPool* pool, parent_class_name* parent) \ - : macros_internal::MacroTable(pool, parent), \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_CONSTRUCTOR_SV) \ - parent_(parent) { \ - PERFETTO_CHECK(kIsRootTable == (parent == nullptr)); \ - \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_STATIC_ASSERT_FLAG) \ - \ - /* \ - * Expands to \ - * columns_.emplace_back("col1", col1_, Column::kNoFlag, this, \ - * static_cast<uint32_t>(columns_.size()), \ - * static_cast<uint32_t>(overlays_.size()) - 1); \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN); \ - } \ - ~class_name() override; \ - \ - IdAndRow Insert(const Row& row) { \ - PERFETTO_DCHECK(allow_inserts_); \ - \ - Id id; \ - uint32_t row_number = row_count(); \ - if (kIsRootTable) { \ - id = Id{row_number}; \ - type_.Append(string_pool_->InternString(row.type())); \ - } else { \ - PERFETTO_DCHECK(parent_); \ - id = Id{parent_->Insert(row).id}; \ - UpdateOverlaysAfterParentInsert(); \ - } \ - \ - /* \ - * Expands to \ - * col1_.Append(row.col1); \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_COLUMN_APPEND); \ - \ - UpdateSelfOverlayAfterInsert(); \ - return {id, row_number, RowReference(this, row_number), \ - RowNumber(row_number)}; \ - } \ - \ - static Table::Schema ComputeStaticSchema() { \ - Table::Schema schema; \ - schema.columns.emplace_back(Table::Schema::Column{ \ - "id", SqlValue::Type::kLong, true, true, false, false}); \ - schema.columns.emplace_back(Table::Schema::Column{ \ - "type", SqlValue::Type::kString, false, false, false, false}); \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_COLUMN_SCHEMA); \ - return schema; \ - } \ - \ - void ShrinkToFit() { \ - type_.ShrinkToFit(); \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_COLUMN_SHRINK_TO_FIT); \ - } \ - \ - /* Iterates the table. */ \ - ConstIterator IterateRows() const { \ - return ConstIterator(this, CopyOverlays()); \ - } \ - \ - /* Iterates the table. */ \ - Iterator IterateRows() { return Iterator(this, CopyOverlays()); } \ - \ - /* Filters the Table using the specified filter constraints. */ \ - ConstIterator FilterToIterator( \ - const std::vector<Constraint>& cs, \ - RowMap::OptimizeFor opt = RowMap::OptimizeFor::kMemory) const { \ - return ConstIterator(this, FilterAndApplyToOverlays(cs, opt)); \ - } \ - \ - /* Filters the Table using the specified filter constraints. */ \ - Iterator FilterToIterator( \ - const std::vector<Constraint>& cs, \ - RowMap::OptimizeFor opt = RowMap::OptimizeFor::kMemory) { \ - return Iterator(this, FilterAndApplyToOverlays(cs, opt)); \ - } \ - \ - /* Returns a ConstRowReference to the row pointed to by |find_id|. */ \ - std::optional<ConstRowReference> FindById(Id find_id) const { \ - std::optional<uint32_t> row = id().IndexOf(find_id); \ - if (!row) \ - return std::nullopt; \ - return ConstRowReference(this, *row); \ - } \ - \ - /* Returns a RowReference to the row pointed to by |find_id|. */ \ - std::optional<RowReference> FindById(Id find_id) { \ - std::optional<uint32_t> row = id().IndexOf(find_id); \ - if (!row) \ - return std::nullopt; \ - return RowReference(this, *row); \ - } \ - \ - const IdColumn<Id>& id() const { \ - return static_cast<const IdColumn<Id>&>( \ - columns_[static_cast<uint32_t>(ColumnIndex::id)]); \ - } \ - PERFETTO_TP_TABLE_COL_GETTER(StringPool::Id, type) \ - \ - /* Returns the name of the table */ \ - static constexpr const char* Name() { return table_name; } \ - \ - /* \ - * Creates a filled instance of this class by selecting all rows in \ - * parent and filling the table columns with the provided vectors. \ - */ \ - static std::unique_ptr<Table> ExtendParent( \ - const parent_class_name& parent, \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_EXTEND_PARAM) \ - std::nullptr_t = nullptr) { \ - return std::unique_ptr<Table>(new class_name( \ - parent.string_pool(), parent, RowMap(0, parent.row_count()), \ - PERFETTO_TP_TABLE_COLUMNS( \ - DEF, PERFETTO_TP_TABLE_EXTEND_PARAM_PASSING) nullptr)); \ - } \ - \ - /* \ - * Creates a filled instance of this class by first selecting all rows in \ - * parent given by |rows| and filling the table columns with the provided \ - * vectors. \ - */ \ - static std::unique_ptr<Table> SelectAndExtendParent( \ - const parent_class_name& parent, \ - std::vector<parent_class_name::RowNumber> parent_row_overlay, \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_EXTEND_PARAM) \ - std::nullptr_t = nullptr) { \ - std::vector<uint32_t> prs_untyped(parent_row_overlay.size()); \ - for (uint32_t i = 0; i < parent_row_overlay.size(); ++i) { \ - prs_untyped[i] = parent_row_overlay[i].row_number(); \ - } \ - return std::unique_ptr<Table>(new class_name( \ - parent.string_pool(), parent, RowMap(std::move(prs_untyped)), \ - PERFETTO_TP_TABLE_COLUMNS( \ - DEF, PERFETTO_TP_TABLE_EXTEND_PARAM_PASSING) nullptr)); \ - } \ - \ - /* \ - * Expands to \ - * const TypedColumn<col1_type>& col1() { return col1_; } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_COL_GETTER) \ - \ - /* \ - * Expands to \ - * TypedColumn<col1_type>* mutable_col1() { return &col1_; } \ - * ... \ - */ \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_MUTABLE_COL_GETTER) \ - \ - private: \ - class_name(StringPool* pool, \ - const parent_class_name& parent, \ - RowMap parent_overlay, \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_EXTEND_PARAM) \ - std::nullptr_t = nullptr) \ - : macros_internal::MacroTable(pool, parent, parent_overlay) { \ - PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_TABLE_STATIC_ASSERT_FLAG) \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_EXTEND_SET_NV) \ - \ - /* \ - * Expands to \ - * columns_.emplace_back("col1", col1_, Column::kNoFlag, this, \ - * static_cast<uint32_t>(columns_.size()), \ - * static_cast<uint32_t>(overlays_.size()) - 1); \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_CONSTRUCTOR_COLUMN); \ - } \ - \ - /* \ - * Expands to \ - * NullableVector<col1_type> col1_; \ - * ... \ - */ \ - PERFETTO_TP_TABLE_COLUMNS(DEF, PERFETTO_TP_TABLE_MEMBER) \ - \ - parent_class_name* parent_ = nullptr; \ - } - } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/tables/memory_tables.py b/src/trace_processor/tables/memory_tables.py index 65263f87d..f270aa7e0 100644 --- a/src/trace_processor/tables/memory_tables.py +++ b/src/trace_processor/tables/memory_tables.py @@ -26,6 +26,7 @@ from python.generators.trace_processor_table.public import CppUint32 from src.trace_processor.tables.track_tables import TRACK_TABLE MEMORY_SNAPSHOT_TABLE = Table( + python_module=__file__, class_name='MemorySnapshotTable', sql_name='memory_snapshot', columns=[ @@ -43,6 +44,7 @@ MEMORY_SNAPSHOT_TABLE = Table( })) PROCESS_MEMORY_SNAPSHOT_TABLE = Table( + python_module=__file__, class_name='ProcessMemorySnapshotTable', sql_name='process_memory_snapshot', columns=[ @@ -58,6 +60,7 @@ PROCESS_MEMORY_SNAPSHOT_TABLE = Table( })) MEMORY_SNAPSHOT_NODE_TABLE = Table( + python_module=__file__, class_name='MemorySnapshotNodeTable', sql_name='memory_snapshot_node', columns=[ @@ -81,6 +84,7 @@ MEMORY_SNAPSHOT_NODE_TABLE = Table( })) MEMORY_SNAPSHOT_EDGE_TABLE = Table( + python_module=__file__, class_name='MemorySnapshotEdgeTable', sql_name='memory_snapshot_edge', columns=[ diff --git a/src/trace_processor/tables/metadata_tables.py b/src/trace_processor/tables/metadata_tables.py index 621f3fcfa..0219aa980 100644 --- a/src/trace_processor/tables/metadata_tables.py +++ b/src/trace_processor/tables/metadata_tables.py @@ -29,6 +29,7 @@ from python.generators.trace_processor_table.public import CppSelfTableId from python.generators.trace_processor_table.public import WrappingSqlView PROCESS_TABLE = Table( + python_module=__file__, class_name='ProcessTable', sql_name='internal_process', columns=[ @@ -100,6 +101,7 @@ PROCESS_TABLE = Table( })) THREAD_TABLE = Table( + python_module=__file__, class_name='ThreadTable', sql_name='internal_thread', columns=[ @@ -159,27 +161,59 @@ THREAD_TABLE = Table( })) RAW_TABLE = Table( + python_module=__file__, class_name='RawTable', sql_name='raw', columns=[ C('ts', CppInt64(), flags=ColumnFlag.SORTED), C('name', CppString()), C('cpu', CppUint32()), - C('utid', CppUint32()), + C('utid', CppTableId(THREAD_TABLE)), C('arg_set_id', CppUint32()), ], tabledoc=TableDoc( - doc='''''', + doc=''' + Contains 'raw' events from the trace for some types of events. This + table only exists for debugging purposes and should not be relied on + in production usecases (i.e. metrics, standard library etc). + ''', group='Misc', columns={ - 'arg_set_id': '''''', - 'ts': '''''', - 'name': '''''', - 'cpu': '''''', - 'utid': '''''' + 'arg_set_id': + ColumnDoc( + 'The set of key/value pairs associated with this event.', + joinable='args.arg_set_id'), + 'ts': + 'The timestamp of this event.', + 'name': + ''' + The name of the event. For ftrace events, this will be the + ftrace event name. + ''', + 'cpu': + 'The CPU this event was emitted on.', + 'utid': + 'The thread this event was emitted on.' })) +FTRACE_EVENT_TABLE = Table( + python_module=__file__, + class_name='FtraceEventTable', + sql_name='ftrace_event', + parent=RAW_TABLE, + 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', + columns={})) + ARG_TABLE = Table( + python_module=__file__, class_name='ArgTable', sql_name='internal_args', columns=[ @@ -206,6 +240,7 @@ ARG_TABLE = Table( })) METADATA_TABLE = Table( + python_module=__file__, class_name='MetadataTable', sql_name='metadata', columns=[ @@ -225,6 +260,7 @@ METADATA_TABLE = Table( })) FILEDESCRIPTOR_TABLE = Table( + python_module=__file__, class_name='FiledescriptorTable', sql_name='filedescriptor', columns=[ @@ -261,6 +297,7 @@ number.''' })) EXP_MISSING_CHROME_PROC_TABLE = Table( + python_module=__file__, class_name='ExpMissingChromeProcTable', sql_name='experimental_missing_chrome_processes', columns=[ @@ -278,6 +315,7 @@ EXP_MISSING_CHROME_PROC_TABLE = Table( })) CPU_TABLE = Table( + python_module=__file__, class_name='CpuTable', sql_name='cpu', columns=[ @@ -298,6 +336,7 @@ the same cluster''', })) CPU_FREQ_TABLE = Table( + python_module=__file__, class_name='CpuFreqTable', sql_name='cpu_freq', columns=[ @@ -311,6 +350,7 @@ CPU_FREQ_TABLE = Table( })) CLOCK_SNAPSHOT_TABLE = Table( + python_module=__file__, class_name='ClockSnapshotTable', sql_name='clock_snapshot', columns=[ @@ -344,7 +384,15 @@ otherwise.''', # Keep this list sorted. ALL_TABLES = [ - ARG_TABLE, CLOCK_SNAPSHOT_TABLE, CPU_FREQ_TABLE, CPU_TABLE, - EXP_MISSING_CHROME_PROC_TABLE, FILEDESCRIPTOR_TABLE, METADATA_TABLE, - PROCESS_TABLE, RAW_TABLE, THREAD_TABLE + ARG_TABLE, + CLOCK_SNAPSHOT_TABLE, + CPU_FREQ_TABLE, + CPU_TABLE, + EXP_MISSING_CHROME_PROC_TABLE, + FILEDESCRIPTOR_TABLE, + METADATA_TABLE, + PROCESS_TABLE, + RAW_TABLE, + THREAD_TABLE, + FTRACE_EVENT_TABLE, ] diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h deleted file mode 100644 index f0e84b259..000000000 --- a/src/trace_processor/tables/profiler_tables.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_TRACE_PROCESSOR_TABLES_PROFILER_TABLES_H_ -#define SRC_TRACE_PROCESSOR_TABLES_PROFILER_TABLES_H_ - -#include "src/trace_processor/tables/macros.h" -#include "src/trace_processor/tables/track_tables_py.h" - -namespace perfetto { -namespace trace_processor { -namespace tables { - -// A callsite. This is a list of frames that were on the stack. -// This is generated by the stack profilers: heapprofd and traced_perf. -// @param depth distance from the bottom-most frame of the callstack. -// @param parent_id parent frame on the callstack. NULL for the bottom-most. -// @param frame_id frame at this position in the callstack. -// @tablegroup Callstack profilers -#define PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF(NAME, PARENT, C) \ - NAME(StackProfileCallsiteTable, "stack_profile_callsite") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(uint32_t, depth) \ - C(std::optional<StackProfileCallsiteTable::Id>, parent_id) \ - C(StackProfileFrameTable::Id, frame_id) - -} // namespace tables -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_PROFILER_TABLES_H_ diff --git a/src/trace_processor/tables/profiler_tables.py b/src/trace_processor/tables/profiler_tables.py index d42800969..c712adff9 100644 --- a/src/trace_processor/tables/profiler_tables.py +++ b/src/trace_processor/tables/profiler_tables.py @@ -28,6 +28,7 @@ from python.generators.trace_processor_table.public import CppUint32 from src.trace_processor.tables.track_tables import TRACK_TABLE PROFILER_SMAPS_TABLE = Table( + python_module=__file__, class_name='ProfilerSmapsTable', sql_name='profiler_smaps', columns=[ @@ -94,6 +95,7 @@ timestamp.''', })) PACKAGE_LIST_TABLE = Table( + python_module=__file__, class_name='PackageListTable', sql_name='package_list', columns=[ @@ -123,6 +125,7 @@ This is generated by the packages_list data-source. })) STACK_PROFILE_MAPPING_TABLE = Table( + python_module=__file__, class_name='StackProfileMappingTable', sql_name='stack_profile_mapping', columns=[ @@ -151,6 +154,7 @@ This is generated by the stack profilers: heapprofd and traced_perf. })) STACK_PROFILE_FRAME_TABLE = Table( + python_module=__file__, class_name='StackProfileFrameTable', sql_name='stack_profile_frame', columns=[ @@ -181,6 +185,7 @@ symbol information of this frame.''', })) STACK_PROFILE_CALLSITE_TABLE = Table( + python_module=__file__, class_name='StackProfileCallsiteTable', sql_name='stack_profile_callsite', columns=[ @@ -204,6 +209,7 @@ This is generated by the stack profilers: heapprofd and traced_perf. })) STACK_SAMPLE_TABLE = Table( + python_module=__file__, class_name='StackSampleTable', sql_name='stack_sample', columns=[ @@ -221,6 +227,7 @@ STACK_SAMPLE_TABLE = Table( })) CPU_PROFILE_STACK_SAMPLE_TABLE = Table( + python_module=__file__, class_name='CpuProfileStackSampleTable', sql_name='cpu_profile_stack_sample', columns=[ @@ -239,6 +246,7 @@ CPU_PROFILE_STACK_SAMPLE_TABLE = Table( })) PERF_SAMPLE_TABLE = Table( + python_module=__file__, class_name='PerfSampleTable', sql_name='perf_sample', columns=[ @@ -278,6 +286,7 @@ streams (i.e. multiple data sources).''' })) SYMBOL_TABLE = Table( + python_module=__file__, class_name='SymbolTable', sql_name='stack_profile_symbol', columns=[ @@ -323,6 +332,7 @@ SYMBOL_TABLE = Table( })) HEAP_PROFILE_ALLOCATION_TABLE = Table( + python_module=__file__, class_name='HeapProfileAllocationTable', sql_name='heap_profile_allocation', columns=[ @@ -367,6 +377,7 @@ that were freed.''', })) EXPERIMENTAL_FLAMEGRAPH_NODES_TABLE = Table( + python_module=__file__, class_name='ExperimentalFlamegraphNodesTable', sql_name='experimental_flamegraph_nodes', columns=[ @@ -421,6 +432,7 @@ EXPERIMENTAL_FLAMEGRAPH_NODES_TABLE = Table( })) HEAP_GRAPH_CLASS_TABLE = Table( + python_module=__file__, class_name='HeapGraphClassTable', sql_name='heap_graph_class', columns=[ @@ -455,6 +467,7 @@ TODO(lalitm): resolve this''', })) HEAP_GRAPH_OBJECT_TABLE = Table( + python_module=__file__, class_name='HeapGraphObjectTable', sql_name='heap_graph_object', columns=[ @@ -500,6 +513,7 @@ false, this object is uncollected garbage.''', })) HEAP_GRAPH_REFERENCE_TABLE = Table( + python_module=__file__, class_name='HeapGraphReferenceTable', sql_name='heap_graph_reference', columns=[ @@ -537,6 +551,7 @@ deobfuscation mapping was provided for it, the deobfuscated name.''' })) VULKAN_MEMORY_ALLOCATIONS_TABLE = Table( + python_module=__file__, class_name='VulkanMemoryAllocationsTable', sql_name='vulkan_memory_allocations', columns=[ @@ -576,6 +591,7 @@ VULKAN_MEMORY_ALLOCATIONS_TABLE = Table( })) GPU_COUNTER_GROUP_TABLE = Table( + python_module=__file__, class_name='GpuCounterGroupTable', sql_name='gpu_counter_group', columns=[ diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/py_tables_benchmark.cc index c946696bd..45a96e897 100644 --- a/src/trace_processor/tables/macros_benchmark.cc +++ b/src/trace_processor/tables/py_tables_benchmark.cc @@ -16,35 +16,16 @@ #include <benchmark/benchmark.h> -#include "src/trace_processor/tables/macros.h" +#include "src/trace_processor/tables/py_tables_benchmark_py.h" namespace perfetto { namespace trace_processor { -namespace { - -#define PERFETTO_TP_ROOT_TEST_TABLE(NAME, PARENT, C) \ - NAME(RootTestTable, "root_table") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(uint32_t, root_sorted, Column::Flag::kSorted) \ - C(uint32_t, root_non_null) \ - C(uint32_t, root_non_null_2) \ - C(std::optional<uint32_t>, root_nullable) - -PERFETTO_TP_TABLE(PERFETTO_TP_ROOT_TEST_TABLE); - -#define PERFETTO_TP_CHILD_TABLE(NAME, PARENT, C) \ - NAME(ChildTestTable, "child_table") \ - PARENT(PERFETTO_TP_ROOT_TEST_TABLE, C) \ - C(uint32_t, child_sorted, Column::Flag::kSorted) \ - C(uint32_t, child_non_null) \ - C(std::optional<uint32_t>, child_nullable) - -PERFETTO_TP_TABLE(PERFETTO_TP_CHILD_TABLE); +namespace tables { RootTestTable::~RootTestTable() = default; ChildTestTable::~ChildTestTable() = default; -} // namespace +} // namespace tables } // namespace trace_processor } // namespace perfetto @@ -74,16 +55,16 @@ void TableSortArgs(benchmark::internal::Benchmark* b) { } // namespace -using perfetto::trace_processor::ChildTestTable; -using perfetto::trace_processor::RootTestTable; using perfetto::trace_processor::RowMap; using perfetto::trace_processor::SqlValue; using perfetto::trace_processor::StringPool; using perfetto::trace_processor::Table; +using perfetto::trace_processor::tables::ChildTestTable; +using perfetto::trace_processor::tables::RootTestTable; static void BM_TableInsert(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); for (auto _ : state) { benchmark::DoNotOptimize(root.Insert({})); @@ -93,7 +74,7 @@ BENCHMARK(BM_TableInsert); static void BM_TableIteratorChild(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -116,7 +97,7 @@ BENCHMARK(BM_TableIteratorChild)->Apply(TableFilterArgs); static void BM_TableFilterAndSortRoot(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); uint32_t partitions = 8; @@ -140,7 +121,7 @@ BENCHMARK(BM_TableFilterAndSortRoot)->Apply(TableFilterArgs); static void BM_TableFilterRootId(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); for (uint32_t i = 0; i < size; ++i) @@ -154,7 +135,7 @@ BENCHMARK(BM_TableFilterRootId)->Apply(TableFilterArgs); static void BM_TableFilterRootIdAndOther(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -173,7 +154,7 @@ BENCHMARK(BM_TableFilterRootIdAndOther)->Apply(TableFilterArgs); static void BM_TableFilterChildId(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -190,7 +171,7 @@ BENCHMARK(BM_TableFilterChildId)->Apply(TableFilterArgs); static void BM_TableFilterChildIdAndSortedInRoot(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -213,7 +194,7 @@ BENCHMARK(BM_TableFilterChildIdAndSortedInRoot)->Apply(TableFilterArgs); static void BM_TableFilterRootNonNullEqMatchMany(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); uint32_t partitions = size / 1024; @@ -232,7 +213,7 @@ BENCHMARK(BM_TableFilterRootNonNullEqMatchMany)->Apply(TableFilterArgs); static void BM_TableFilterRootMultipleNonNull(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); uint32_t partitions = size / 512; @@ -254,7 +235,7 @@ BENCHMARK(BM_TableFilterRootMultipleNonNull)->Apply(TableFilterArgs); static void BM_TableFilterRootNullableEqMatchMany(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); uint32_t partitions = size / 512; @@ -277,7 +258,7 @@ BENCHMARK(BM_TableFilterRootNullableEqMatchMany)->Apply(TableFilterArgs); static void BM_TableFilterChildNonNullEqMatchMany(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -299,7 +280,7 @@ BENCHMARK(BM_TableFilterChildNonNullEqMatchMany)->Apply(TableFilterArgs); static void BM_TableFilterChildNullableEqMatchMany(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -325,7 +306,7 @@ BENCHMARK(BM_TableFilterChildNullableEqMatchMany)->Apply(TableFilterArgs); static void BM_TableFilterChildNonNullEqMatchManyInParent( benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -349,7 +330,7 @@ BENCHMARK(BM_TableFilterChildNonNullEqMatchManyInParent) static void BM_TableFilterChildNullableEqMatchManyInParent( benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -372,7 +353,7 @@ BENCHMARK(BM_TableFilterChildNullableEqMatchManyInParent) static void BM_TableFilterParentSortedEq(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -390,7 +371,7 @@ BENCHMARK(BM_TableFilterParentSortedEq)->Apply(TableFilterArgs); static void BM_TableFilterParentSortedAndOther(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -415,7 +396,7 @@ BENCHMARK(BM_TableFilterParentSortedAndOther)->Apply(TableFilterArgs); static void BM_TableFilterChildSortedEq(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -435,7 +416,7 @@ BENCHMARK(BM_TableFilterChildSortedEq)->Apply(TableFilterArgs); static void BM_TableFilterChildSortedEqInParent(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -458,7 +439,7 @@ BENCHMARK(BM_TableFilterChildSortedEqInParent)->Apply(TableFilterArgs); static void BM_TableSortRootNonNull(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -479,7 +460,7 @@ BENCHMARK(BM_TableSortRootNonNull)->Apply(TableSortArgs); static void BM_TableSortRootNullable(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -501,7 +482,7 @@ BENCHMARK(BM_TableSortRootNullable)->Apply(TableSortArgs); static void BM_TableSortChildNonNullInParent(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); @@ -529,7 +510,7 @@ BENCHMARK(BM_TableSortChildNonNullInParent)->Apply(TableSortArgs); static void BM_TableSortChildNullableInParent(benchmark::State& state) { StringPool pool; - RootTestTable root(&pool, nullptr); + RootTestTable root(&pool); ChildTestTable child(&pool, &root); uint32_t size = static_cast<uint32_t>(state.range(0)); diff --git a/src/trace_processor/tables/py_tables_benchmark.py b/src/trace_processor/tables/py_tables_benchmark.py new file mode 100644 index 000000000..8a2c1f900 --- /dev/null +++ b/src/trace_processor/tables/py_tables_benchmark.py @@ -0,0 +1,48 @@ +# 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. +"""Contains tables for unittesting.""" + +from python.generators.trace_processor_table.public import Column as C +from python.generators.trace_processor_table.public import ColumnFlag +from python.generators.trace_processor_table.public import Table +from python.generators.trace_processor_table.public import CppOptional +from python.generators.trace_processor_table.public import CppUint32 + +ROOT_TABLE = Table( + python_module=__file__, + class_name="RootTestTable", + sql_name="root_table", + columns=[ + C("root_sorted", CppUint32(), flags=ColumnFlag.SORTED), + C("root_non_null", CppUint32()), + C("root_non_null_2", CppUint32()), + C("root_nullable", CppOptional(CppUint32())), + ]) + +CHILD_TABLE = Table( + python_module=__file__, + class_name="ChildTestTable", + sql_name="child_table", + parent=ROOT_TABLE, + columns=[ + C("child_sorted", CppUint32(), flags=ColumnFlag.SORTED), + C("child_non_null", CppUint32()), + C("child_nullable", CppOptional(CppUint32())), + ]) + +# Keep this list sorted. +ALL_TABLES = [ + ROOT_TABLE, + CHILD_TABLE, +] diff --git a/src/trace_processor/tables/py_tables_unittest.cc b/src/trace_processor/tables/py_tables_unittest.cc index 71b5f3947..36facd63d 100644 --- a/src/trace_processor/tables/py_tables_unittest.cc +++ b/src/trace_processor/tables/py_tables_unittest.cc @@ -15,6 +15,7 @@ */ #include "src/trace_processor/db/column.h" +#include "src/trace_processor/db/column_storage.h" #include "src/trace_processor/tables/py_tables_unittest_py.h" #include "test/gtest_and_gmock.h" @@ -24,6 +25,7 @@ namespace trace_processor { namespace tables { TestEventTable::~TestEventTable() = default; +TestEventChildTable::~TestEventChildTable() = default; TestSliceTable::~TestSliceTable() = default; TestArgsTable::~TestArgsTable() = default; @@ -34,6 +36,7 @@ class PyTablesUnittest : public ::testing::Test { StringPool pool_; TestEventTable event_{&pool_}; + TestEventChildTable event_child_{&pool_, &event_}; TestSliceTable slice_{&pool_, &event_}; TestArgsTable args_{&pool_}; }; @@ -163,6 +166,150 @@ TEST_F(PyTablesUnittest, ParentAndChildInsert) { ASSERT_EQ(slice_.dur()[1], 20); } +TEST_F(PyTablesUnittest, Extend) { + event_.Insert(TestEventTable::Row(50, 0)); + event_.Insert(TestEventTable::Row(100, 1)); + event_.Insert(TestEventTable::Row(150, 2)); + + ColumnStorage<int64_t> dur; + dur.Append(512); + dur.Append(1024); + dur.Append(2048); + + auto slice_ext = TestSliceTable::ExtendParent(event_, std::move(dur)); + ASSERT_EQ(slice_ext->row_count(), 3u); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::ts].Get(0).AsLong(), + 50); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::dur].Get(0).AsLong(), + 512); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::ts].Get(1).AsLong(), + 100); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::dur].Get(1).AsLong(), + 1024); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::ts].Get(2).AsLong(), + 150); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::dur].Get(2).AsLong(), + 2048); +} + +TEST_F(PyTablesUnittest, SelectAndExtend) { + event_.Insert(TestEventTable::Row(50, 0)); + event_.Insert(TestEventTable::Row(100, 1)); + event_.Insert(TestEventTable::Row(150, 2)); + + std::vector<TestEventTable::RowNumber> rows; + rows.emplace_back(TestEventTable::RowNumber(1)); + ColumnStorage<int64_t> dur; + dur.Append(1024); + + auto slice_ext = TestSliceTable::SelectAndExtendParent( + event_, std::move(rows), std::move(dur)); + ASSERT_EQ(slice_ext->row_count(), 1u); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::ts].Get(0).AsLong(), + 100); + ASSERT_EQ( + slice_ext->columns()[TestSliceTable::ColumnIndex::dur].Get(0).AsLong(), + 1024); +} + +TEST_F(PyTablesUnittest, SetIdColumns) { + StringPool pool; + TestArgsTable table{&pool}; + + table.Insert(TestArgsTable::Row(0, 100)); + table.Insert(TestArgsTable::Row(0, 200)); + table.Insert(TestArgsTable::Row(2, 200)); + table.Insert(TestArgsTable::Row(3, 300)); + table.Insert(TestArgsTable::Row(4, 200)); + table.Insert(TestArgsTable::Row(4, 500)); + table.Insert(TestArgsTable::Row(4, 900)); + table.Insert(TestArgsTable::Row(4, 200)); + table.Insert(TestArgsTable::Row(8, 400)); + + ASSERT_EQ(table.row_count(), 9u); + ASSERT_TRUE(table.arg_set_id().IsSetId()); + + // Verify that not-present ids are not returned. + { + static constexpr uint32_t kFilterArgSetId = 1; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 0u); + } + { + static constexpr uint32_t kFilterArgSetId = 9; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 0u); + } + + // Verify that kSetId flag is correctly removed after filtering/sorting. + { + static constexpr uint32_t kFilterArgSetId = 3; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 1u); + ASSERT_FALSE(res.GetColumnByName("arg_set_id")->IsSetId()); + } + { + auto res = table.Sort({table.type().descending()}); + ASSERT_FALSE(res.GetColumnByName("arg_set_id")->IsSetId()); + } + + uint32_t arg_set_id_col_idx = + static_cast<uint32_t>(TestArgsTable::ColumnIndex::arg_set_id); + + // Verify that filtering equality for real arg set ids works as expected. + { + static constexpr uint32_t kFilterArgSetId = 4; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 4u); + for (auto it = res.IterateRows(); it; it.Next()) { + uint32_t arg_set_id = + static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); + ASSERT_EQ(arg_set_id, kFilterArgSetId); + } + } + { + static constexpr uint32_t kFilterArgSetId = 0; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 2u); + for (auto it = res.IterateRows(); it; it.Next()) { + uint32_t arg_set_id = + static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); + ASSERT_EQ(arg_set_id, kFilterArgSetId); + } + } + { + static constexpr uint32_t kFilterArgSetId = 8; + auto res = table.Filter({table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 1u); + for (auto it = res.IterateRows(); it; it.Next()) { + uint32_t arg_set_id = + static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); + ASSERT_EQ(arg_set_id, kFilterArgSetId); + } + } + + // Verify that filtering equality for arg set ids after filtering another + // column works. + { + static constexpr uint32_t kFilterArgSetId = 4; + auto res = table.Filter( + {table.int_value().eq(200), table.arg_set_id().eq(kFilterArgSetId)}); + ASSERT_EQ(res.row_count(), 2u); + for (auto it = res.IterateRows(); it; it.Next()) { + uint32_t arg_set_id = + static_cast<uint32_t>(it.Get(arg_set_id_col_idx).AsLong()); + ASSERT_EQ(arg_set_id, kFilterArgSetId); + } + } +} + } // namespace } // namespace tables } // namespace trace_processor diff --git a/src/trace_processor/tables/py_tables_unittest.py b/src/trace_processor/tables/py_tables_unittest.py index 24cb17c62..8acc68810 100644 --- a/src/trace_processor/tables/py_tables_unittest.py +++ b/src/trace_processor/tables/py_tables_unittest.py @@ -17,40 +17,48 @@ from python.generators.trace_processor_table.public import Column as C from python.generators.trace_processor_table.public import ColumnFlag from python.generators.trace_processor_table.public import CppInt64 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 CppUint32 EVENT_TABLE = Table( + python_module=__file__, class_name="TestEventTable", sql_name="event", columns=[ C("ts", CppInt64(), flags=ColumnFlag.SORTED), C("arg_set_id", CppUint32()), - ], - tabledoc=TableDoc(doc='', group='', columns={})) + ]) + +EVENT_CHILD_TABLE = Table( + python_module=__file__, + class_name="TestEventChildTable", + sql_name="event", + parent=EVENT_TABLE, + columns=[]) SLICE_TABLE = Table( + python_module=__file__, class_name="TestSliceTable", sql_name="slice", parent=EVENT_TABLE, columns=[ C("dur", CppInt64()), - ], - tabledoc=TableDoc(doc='', group='', columns={})) + ]) ARGS_TABLE = Table( + python_module=__file__, class_name="TestArgsTable", sql_name="args", columns=[ C("arg_set_id", CppUint32(), flags=ColumnFlag.SET_ID | ColumnFlag.SORTED), - ], - tabledoc=TableDoc(doc='', group='', columns={})) + C("int_value", CppInt64()), + ]) # Keep this list sorted. ALL_TABLES = [ ARGS_TABLE, EVENT_TABLE, + EVENT_CHILD_TABLE, SLICE_TABLE, ] diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h deleted file mode 100644 index e33ae19af..000000000 --- a/src/trace_processor/tables/slice_tables.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SRC_TRACE_PROCESSOR_TABLES_SLICE_TABLES_H_ -#define SRC_TRACE_PROCESSOR_TABLES_SLICE_TABLES_H_ - -#include "src/trace_processor/tables/macros.h" -#include "src/trace_processor/tables/track_tables_py.h" - -namespace perfetto { -namespace trace_processor { -namespace tables { - -#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C) \ - NAME(SliceTable, "internal_slice") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(int64_t, ts, Column::Flag::kSorted) \ - C(int64_t, dur) \ - C(TrackTable::Id, track_id) \ - C(std::optional<StringPool::Id>, category) \ - C(std::optional<StringPool::Id>, name) \ - C(uint32_t, depth) \ - C(int64_t, stack_id) \ - C(int64_t, parent_stack_id) \ - C(std::optional<SliceTable::Id>, parent_id) \ - C(uint32_t, arg_set_id) \ - C(std::optional<int64_t>, thread_ts) \ - C(std::optional<int64_t>, thread_dur) \ - C(std::optional<int64_t>, thread_instruction_count) \ - C(std::optional<int64_t>, thread_instruction_delta) - -#define PERFETTO_TP_SCHED_SLICE_TABLE_DEF(NAME, PARENT, C) \ - NAME(SchedSliceTable, "sched_slice") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(int64_t, ts, Column::Flag::kSorted) \ - C(int64_t, dur) \ - C(uint32_t, cpu) \ - C(uint32_t, utid) \ - C(StringPool::Id, end_state) \ - C(int32_t, priority) - -} // namespace tables -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_SLICE_TABLES_H_ diff --git a/src/trace_processor/tables/slice_tables.py b/src/trace_processor/tables/slice_tables.py index 50b8c3edb..f58d53d7e 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 @@ -29,6 +30,7 @@ from python.generators.trace_processor_table.public import WrappingSqlView from src.trace_processor.tables.track_tables import TRACK_TABLE SLICE_TABLE = Table( + python_module=__file__, class_name='SliceTable', sql_name='internal_slice', columns=[ @@ -49,26 +51,77 @@ SLICE_TABLE = Table( ], wrapping_sql_view=WrappingSqlView('slice'), tabledoc=TableDoc( - doc='''''', + doc=''' + Contains slices from userspace which explains what threads were doing + during the trace. + ''', 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': '''''' + '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. + ''', + '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. + ''', })) SCHED_SLICE_TABLE = Table( + python_module=__file__, class_name='SchedSliceTable', sql_name='sched_slice', columns=[ @@ -82,8 +135,8 @@ SCHED_SLICE_TABLE = Table( 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. + These slices are collected when the Linux "ftrace" data source is + used with the "sched/switch" and "sched/wakeup*" events enabled. ''', group='Events', columns={ @@ -96,45 +149,69 @@ used with the "sched/switch" and "sched/wakeup*" events enabled. '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).''', + ''' + 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('utid', CppUint32()), C('state', CppString()), C('io_wait', CppOptional(CppUint32())), C('blocked_function', CppOptional(CppString())), C('waker_utid', CppOptional(CppUint32())), ], tabledoc=TableDoc( - doc='''''', + doc=''' + This table contains the scheduling state of every thread on the + system during the trace. It is a subset of the |sched_slice| (sched) + table which only contains the times where threads were actually + scheduled. + ''', group='Events', columns={ - 'utid': '''''', - 'ts': '''''', - 'dur': '''''', - 'cpu': '''''', - 'state': '''''', - 'io_wait': '''''', - 'blocked_function': '''''', - 'waker_utid': '''''' + '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.''', + '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. + ''' })) GPU_SLICE_TABLE = Table( + python_module=__file__, class_name='GpuSliceTable', sql_name='gpu_slice', columns=[ @@ -169,6 +246,7 @@ GPU_SLICE_TABLE = Table( })) GRAPHICS_FRAME_SLICE_TABLE = Table( + python_module=__file__, class_name='GraphicsFrameSliceTable', sql_name='frame_slice', columns=[ @@ -191,6 +269,7 @@ GRAPHICS_FRAME_SLICE_TABLE = Table( })) EXPECTED_FRAME_TIMELINE_SLICE_TABLE = Table( + python_module=__file__, class_name='ExpectedFrameTimelineSliceTable', sql_name='expected_frame_timeline_slice', columns=[ @@ -202,7 +281,7 @@ EXPECTED_FRAME_TIMELINE_SLICE_TABLE = Table( parent=SLICE_TABLE, tabledoc=TableDoc( doc='''''', - group='Misc', + group='Events', columns={ 'display_frame_token': '''''', 'surface_frame_token': '''''', @@ -211,6 +290,7 @@ EXPECTED_FRAME_TIMELINE_SLICE_TABLE = Table( })) ACTUAL_FRAME_TIMELINE_SLICE_TABLE = Table( + python_module=__file__, class_name='ActualFrameTimelineSliceTable', sql_name='actual_frame_timeline_slice', columns=[ @@ -228,7 +308,7 @@ ACTUAL_FRAME_TIMELINE_SLICE_TABLE = Table( parent=SLICE_TABLE, tabledoc=TableDoc( doc='''''', - group='Misc', + group='Events', columns={ 'display_frame_token': '''''', 'surface_frame_token': '''''', @@ -243,6 +323,7 @@ ACTUAL_FRAME_TIMELINE_SLICE_TABLE = Table( })) EXPERIMENTAL_FLAT_SLICE_TABLE = Table( + python_module=__file__, class_name='ExperimentalFlatSliceTable', sql_name='experimental_flat_slice', columns=[ @@ -257,18 +338,35 @@ EXPERIMENTAL_FLAT_SLICE_TABLE = Table( C('end_bound', CppInt64(), flags=ColumnFlag.HIDDEN), ], tabledoc=TableDoc( - doc='''''', + doc=''' + An experimental table which "flattens" stacks of slices to contain + only the "deepest" slice at any point in time on each track. + ''', group='Misc', 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. diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc index 5025ab3fa..3dcf1130f 100644 --- a/src/trace_processor/tables/table_destructors.cc +++ b/src/trace_processor/tables/table_destructors.cc @@ -42,6 +42,7 @@ CounterTable::~CounterTable() = default; // metadata_tables_py.h RawTable::~RawTable() = default; +FtraceEventTable::~FtraceEventTable() = default; ArgTable::~ArgTable() = default; ExpMissingChromeProcTable::~ExpMissingChromeProcTable() = default; MetadataTable::~MetadataTable() = default; diff --git a/src/trace_processor/tables/trace_proto_tables.h b/src/trace_processor/tables/trace_proto_tables.h deleted file mode 100644 index aa57b593c..000000000 --- a/src/trace_processor/tables/trace_proto_tables.h +++ /dev/null @@ -1,52 +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_TABLES_TRACE_PROTO_TABLES_H_ -#define SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_ - -#include "src/trace_processor/tables/macros.h" - -namespace perfetto { -namespace trace_processor { -namespace tables { - -// Experimental table, subject to arbitrary breaking changes. -#define PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalProtoPathTable, "experimental_proto_path") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(std::optional<ExperimentalProtoPathTable::Id>, parent_id) \ - C(StringPool::Id, field_type) \ - C(std::optional<StringPool::Id>, field_name) \ - C(std::optional<uint32_t>, arg_set_id) - -PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF); - -#define PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF(NAME, PARENT, C) \ - NAME(ExperimentalProtoContentTable, "experimental_proto_content") \ - PERFETTO_TP_ROOT_TABLE(PARENT, C) \ - C(StringPool::Id, path) \ - C(ExperimentalProtoPathTable::Id, path_id) \ - C(int64_t, total_size) \ - C(int64_t, size) \ - C(int64_t, count) - -PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF); - -} // namespace tables -} // namespace trace_processor -} // namespace perfetto - -#endif // SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_ diff --git a/src/trace_processor/tables/trace_proto_tables.py b/src/trace_processor/tables/trace_proto_tables.py index f6f5f9d74..e9c975452 100644 --- a/src/trace_processor/tables/trace_proto_tables.py +++ b/src/trace_processor/tables/trace_proto_tables.py @@ -24,6 +24,7 @@ from python.generators.trace_processor_table.public import CppTableId from python.generators.trace_processor_table.public import CppUint32 EXPERIMENTAL_PROTO_PATH_TABLE = Table( + python_module=__file__, class_name='ExperimentalProtoPathTable', sql_name='experimental_proto_path', columns=[ @@ -45,6 +46,7 @@ EXPERIMENTAL_PROTO_PATH_TABLE = Table( })) EXPERIMENTAL_PROTO_CONTENT_TABLE = Table( + python_module=__file__, class_name='ExperimentalProtoContentTable', sql_name='experimental_proto_content', columns=[ diff --git a/src/trace_processor/tables/track_tables.py b/src/trace_processor/tables/track_tables.py index bb5a0df9a..3cbce7689 100644 --- a/src/trace_processor/tables/track_tables.py +++ b/src/trace_processor/tables/track_tables.py @@ -25,6 +25,7 @@ from python.generators.trace_processor_table.public import CppSelfTableId from python.generators.trace_processor_table.public import CppUint32 TRACK_TABLE = Table( + python_module=__file__, class_name="TrackTable", sql_name="track", columns=[ @@ -62,6 +63,7 @@ TRACK_TABLE = Table( })) PROCESS_TRACK_TABLE = Table( + python_module=__file__, class_name="ProcessTrackTable", sql_name="process_track", columns=[ @@ -81,6 +83,7 @@ PROCESS_TRACK_TABLE = Table( })) THREAD_TRACK_TABLE = Table( + python_module=__file__, class_name='ThreadTrackTable', sql_name='thread_track', columns=[ @@ -101,6 +104,7 @@ THREAD_TRACK_TABLE = Table( })) CPU_TRACK_TABLE = Table( + python_module=__file__, class_name='CpuTrackTable', sql_name='cpu_track', columns=[ @@ -113,6 +117,7 @@ CPU_TRACK_TABLE = Table( columns={'cpu': 'The CPU associated with this track'})) GPU_TRACK_TABLE = Table( + python_module=__file__, class_name='GpuTrackTable', sql_name='gpu_track', columns=[ @@ -134,6 +139,7 @@ GPU_TRACK_TABLE = Table( })) COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='CounterTrackTable', sql_name='counter_track', columns=[ @@ -156,6 +162,7 @@ COUNTER_TRACK_TABLE = Table( })) THREAD_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='ThreadCounterTrackTable', sql_name='thread_counter_track', columns=[ @@ -174,6 +181,7 @@ THREAD_COUNTER_TRACK_TABLE = Table( })) PROCESS_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='ProcessCounterTrackTable', sql_name='process_counter_track', columns=[ @@ -193,6 +201,7 @@ PROCESS_COUNTER_TRACK_TABLE = Table( })) CPU_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='CpuCounterTrackTable', sql_name='cpu_counter_track', columns=[ @@ -205,6 +214,7 @@ CPU_COUNTER_TRACK_TABLE = Table( columns={'cpu': 'The CPU this track is associated with'})) IRQ_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='IrqCounterTrackTable', sql_name='irq_counter_track', columns=[ @@ -217,6 +227,7 @@ IRQ_COUNTER_TRACK_TABLE = Table( columns={'irq': 'The identifier for the hardirq.'})) SOFTIRQ_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='SoftirqCounterTrackTable', sql_name='softirq_counter_track', columns=[ @@ -229,6 +240,7 @@ SOFTIRQ_COUNTER_TRACK_TABLE = Table( columns={'softirq': 'The identifier for the softirq.'})) GPU_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='GpuCounterTrackTable', sql_name='gpu_counter_track', columns=[ @@ -241,6 +253,7 @@ GPU_COUNTER_TRACK_TABLE = Table( columns={'gpu_id': 'The identifier for the GPU.'})) PERF_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='PerfCounterTrackTable', sql_name='perf_counter_track', columns=[ @@ -265,6 +278,7 @@ PERF_COUNTER_TRACK_TABLE = Table( })) ENERGY_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='EnergyCounterTrackTable', sql_name='energy_counter_track', columns=[ @@ -286,6 +300,7 @@ ENERGY_COUNTER_TRACK_TABLE = Table( })) UID_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='UidCounterTrackTable', sql_name='uid_counter_track', columns=[ @@ -298,6 +313,7 @@ UID_COUNTER_TRACK_TABLE = Table( columns={'uid': 'uid of process for which breakdowns are emitted'})) ENERGY_PER_UID_COUNTER_TRACK_TABLE = Table( + python_module=__file__, class_name='EnergyPerUidCounterTrackTable', sql_name='energy_per_uid_counter_track', columns=[ diff --git a/src/trace_processor/tp_metatrace.cc b/src/trace_processor/tp_metatrace.cc index 79e6dbcc5..bf44dcf4c 100644 --- a/src/trace_processor/tp_metatrace.cc +++ b/src/trace_processor/tp_metatrace.cc @@ -24,19 +24,14 @@ namespace { 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/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc index ee3f5d948..f64b1c16c 100644 --- a/src/trace_processor/trace_processor_impl.cc +++ b/src/trace_processor/trace_processor_impl.cc @@ -53,9 +53,10 @@ #include "src/trace_processor/prelude/functions/import.h" #include "src/trace_processor/prelude/functions/layout_functions.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" #include "src/trace_processor/prelude/functions/utils.h" #include "src/trace_processor/prelude/functions/window_functions.h" #include "src/trace_processor/prelude/operators/span_join_operator.h" @@ -71,9 +72,9 @@ #include "src/trace_processor/prelude/table_functions/experimental_slice_layout.h" #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/scoped_db.h" #include "src/trace_processor/sqlite/sql_stats_table.h" -#include "src/trace_processor/sqlite/sqlite_raw_table.h" #include "src/trace_processor/sqlite/sqlite_table.h" #include "src/trace_processor/sqlite/sqlite_utils.h" #include "src/trace_processor/sqlite/stats_table.h" @@ -96,15 +97,6 @@ #include "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h" #include "src/trace_processor/stdlib/amalgamated_stdlib.h" -// In Android and Chromium tree builds, we don't have the percentile module. -// Just don't include it. -#if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) -// defined in sqlite_src/ext/misc/percentile.c -extern "C" int sqlite3_percentile_init(sqlite3* db, - char** error, - const sqlite3_api_routines* api); -#endif // PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) - namespace perfetto { namespace trace_processor { namespace { @@ -114,35 +106,17 @@ const char kAllTablesQuery[] = "* FROM sqlite_temp_master)"; template <typename SqlFunction, typename Ptr = typename SqlFunction::Context*> -void RegisterFunction(sqlite3* db, +void RegisterFunction(SqliteEngine* 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()); } -void InitializeSqlite(sqlite3* db) { - char* error = nullptr; - sqlite3_exec(db, "PRAGMA temp_store=2", nullptr, nullptr, &error); - if (error) { - PERFETTO_FATAL("Error setting pragma temp_store: %s", error); - } - sqlite3_str_split_init(db); -// In Android tree builds, we don't have the percentile module. -// Just don't include it. -#if PERFETTO_BUILDFLAG(PERFETTO_TP_PERCENTILE) - sqlite3_percentile_init(db, &error, nullptr); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } -#endif -} - void BuildBoundsTable(sqlite3* db, std::pair<int64_t, int64_t> bounds) { char* error = nullptr; sqlite3_exec(db, "DELETE FROM trace_bounds", nullptr, nullptr, &error); @@ -152,167 +126,16 @@ void BuildBoundsTable(sqlite3* db, std::pair<int64_t, int64_t> bounds) { return; } - char* insert_sql = sqlite3_mprintf("INSERT INTO trace_bounds VALUES(%" PRId64 - ", %" PRId64 ")", - bounds.first, bounds.second); - - sqlite3_exec(db, insert_sql, nullptr, nullptr, &error); - sqlite3_free(insert_sql); + base::StackString<1024> sql("INSERT INTO trace_bounds VALUES(%" PRId64 + ", %" PRId64 ")", + bounds.first, bounds.second); + sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &error); if (error) { PERFETTO_ELOG("Error inserting bounds table: %s", error); sqlite3_free(error); } } -void CreateBuiltinTables(sqlite3* db) { - char* error = nullptr; - sqlite3_exec(db, "CREATE TABLE perfetto_tables(name STRING)", nullptr, - nullptr, &error); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } - sqlite3_exec(db, "CREATE TABLE trace_bounds(start_ts BIGINT, end_ts BIGINT)", - nullptr, nullptr, &error); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } - // Ensure that the entries in power_profile are unique to prevent duplicates - // when the power_profile is augmented with additional profiles. - sqlite3_exec(db, - "CREATE TABLE power_profile(" - "device STRING, cpu INT, cluster INT, freq INT, power DOUBLE," - "UNIQUE(device, cpu, cluster, freq));", - nullptr, nullptr, &error); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } - sqlite3_exec(db, "CREATE TABLE trace_metrics(name STRING)", nullptr, nullptr, - &error); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } - // This is a table intended to be used for metric debugging/developing. Data - // in the table is shown specially in the UI, and users can insert rows into - // this table to draw more things. - sqlite3_exec(db, - "CREATE TABLE debug_slices (id BIGINT, name STRING, ts BIGINT," - "dur BIGINT, depth BIGINT)", - nullptr, nullptr, &error); - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } - - // Initialize the bounds table with some data so even before parsing any data, - // we still have a valid table. - BuildBoundsTable(db, std::make_pair(0, 0)); -} - -void MaybeRegisterError(char* error) { - if (error) { - PERFETTO_ELOG("Error initializing: %s", error); - sqlite3_free(error); - } -} - -void CreateBuiltinViews(sqlite3* db) { - char* error = nullptr; - sqlite3_exec(db, - "CREATE VIEW counters AS " - "SELECT * " - "FROM counter v " - "JOIN counter_track t " - "ON v.track_id = t.id " - "ORDER BY ts;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW slice AS " - "SELECT " - " *, " - " category AS cat, " - " id AS slice_id " - "FROM internal_slice;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW instant AS " - "SELECT " - "ts, track_id, name, arg_set_id " - "FROM slice " - "WHERE dur = 0;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW sched AS " - "SELECT " - "*, " - "ts + dur as ts_end " - "FROM sched_slice;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW slices AS " - "SELECT * FROM slice;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW thread AS " - "SELECT " - "id as utid, " - "* " - "FROM internal_thread;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW process AS " - "SELECT " - "id as upid, " - "* " - "FROM internal_process;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - // This should be kept in sync with GlobalArgsTracker::AddArgSet. - sqlite3_exec(db, - "CREATE VIEW args AS " - "SELECT " - "*, " - "CASE value_type " - " WHEN 'int' THEN CAST(int_value AS text) " - " WHEN 'uint' THEN CAST(int_value AS text) " - " WHEN 'string' THEN string_value " - " WHEN 'real' THEN CAST(real_value AS text) " - " WHEN 'pointer' THEN printf('0x%x', int_value) " - " WHEN 'bool' THEN ( " - " CASE WHEN int_value <> 0 THEN 'true' " - " ELSE 'false' END) " - " WHEN 'json' THEN string_value " - "ELSE NULL END AS display_value " - "FROM internal_args;", - nullptr, nullptr, &error); - MaybeRegisterError(error); - - sqlite3_exec(db, - "CREATE VIEW ftrace_event AS " - "SELECT * FROM raw " - "WHERE " - " name NOT LIKE 'chrome_event.%' AND" - " name NOT LIKE 'track_event.%'", - nullptr, nullptr, &error); - MaybeRegisterError(error); -} - struct ValueAtMaxTsContext { bool initialized; int value_type; @@ -415,7 +238,7 @@ std::vector<std::string> SanitizeMetricMountPaths( } void SetupMetrics(TraceProcessor* tp, - sqlite3* db, + SqliteEngine* engine, std::vector<metrics::SqlMetricFile>* sql_metrics, const std::vector<std::string>& extension_paths) { const std::vector<std::string> sanitized_extension_paths = @@ -445,10 +268,11 @@ void SetupMetrics(TraceProcessor* tp, } } - 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})); @@ -456,21 +280,13 @@ void SetupMetrics(TraceProcessor* tp, // functions are supported. { auto ret = sqlite3_create_function_v2( - db, "RepeatedField", 1, SQLITE_UTF8, nullptr, nullptr, + engine->db(), "RepeatedField", 1, SQLITE_UTF8, nullptr, nullptr, metrics::RepeatedFieldStep, metrics::RepeatedFieldFinal, nullptr); if (ret) PERFETTO_FATAL("Error initializing RepeatedField"); } } -void EnsureSqliteInitialized() { - // sqlite3_initialize isn't actually thread-safe despite being documented - // as such; we need to make sure multiple TraceProcessorImpl instances don't - // call it concurrently and only gets called once per process, instead. - static bool init_once = [] { return sqlite3_initialize() == SQLITE_OK; }(); - PERFETTO_CHECK(init_once); -} - void InsertIntoTraceMetricsTable(sqlite3* db, const std::string& metric_name) { char* insert_sql = sqlite3_mprintf( "INSERT INTO trace_metrics(name) VALUES('%q')", metric_name.c_str()); @@ -639,8 +455,8 @@ const char* TraceTypeToString(TraceType trace_type) { } // Register SQL functions only used in local development instances. -void RegisterDevFunctions(sqlite3* db) { - RegisterFunction<WriteFile>(db, "WRITE_FILE", 2); +void RegisterDevFunctions(SqliteEngine* engine) { + RegisterFunction<WriteFile>(engine, "WRITE_FILE", 2); } sql_modules::NameToModule GetStdlibModules() { @@ -653,6 +469,17 @@ sql_modules::NameToModule GetStdlibModules() { return modules; } +void InitializePreludeTablesViews(sqlite3* db) { + for (const auto& file_to_sql : prelude::tables_views::kFileToSql) { + char* errmsg_raw = nullptr; + int err = sqlite3_exec(db, file_to_sql.sql, nullptr, nullptr, &errmsg_raw); + ScopedSqliteString errmsg(errmsg_raw); + if (err != SQLITE_OK) { + PERFETTO_FATAL("Failed to initialize prelude %s", errmsg_raw); + } + } +} + } // namespace template <typename View> @@ -685,65 +512,78 @@ TraceProcessorImpl::TraceProcessorImpl(const Config& cfg) context_.content_analyzer.reset(new ProtoContentAnalyzer(&context_)); } + sqlite3_str_split_init(engine_.db()); RegisterAdditionalModules(&context_); - sqlite3* db = nullptr; - EnsureSqliteInitialized(); - PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK); - InitializeSqlite(db); - CreateBuiltinTables(db); - CreateBuiltinViews(db); - db_.reset(std::move(db)); - // New style function registration. if (cfg.enable_dev_features) { - RegisterDevFunctions(db); + RegisterDevFunctions(&engine_); } - RegisterFunction<Glob>(db, "glob", 2); - RegisterFunction<Hash>(db, "HASH", -1); - RegisterFunction<Base64Encode>(db, "BASE64_ENCODE", 1); - RegisterFunction<Demangle>(db, "DEMANGLE", 1); - RegisterFunction<SourceGeq>(db, "SOURCE_GEQ", -1); - RegisterFunction<ExportJson>(db, "EXPORT_JSON", 1, context_.storage.get(), - false); - RegisterFunction<ExtractArg>(db, "EXTRACT_ARG", 2, context_.storage.get()); - RegisterFunction<AbsTimeStr>(db, "ABS_TIME_STR", 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_, "EXTRACT_ARG", 2, + context_.storage.get()); + RegisterFunction<AbsTimeStr>(&engine_, "ABS_TIME_STR", 1, context_.clock_converter.get()); - RegisterFunction<ToMonotonic>(db, "TO_MONOTONIC", 1, + RegisterFunction<ToMonotonic>(&engine_, "TO_MONOTONIC", 1, context_.clock_converter.get()); - RegisterFunction<CreateFunction>( - db, "CREATE_FUNCTION", 3, - std::unique_ptr<CreateFunction::Context>( - new CreateFunction::Context{db_.get(), &create_function_state_})); + RegisterFunction<CreateFunction>(&engine_, "CREATE_FUNCTION", 3, &engine_); RegisterFunction<CreateViewFunction>( - db, "CREATE_VIEW_FUNCTION", 3, + &engine_, "CREATE_VIEW_FUNCTION", 3, std::unique_ptr<CreateViewFunction::Context>( - new CreateViewFunction::Context{db_.get()})); - RegisterFunction<Import>(db, "IMPORT", 1, + new CreateViewFunction::Context{engine_.db()})); + RegisterFunction<Import>(&engine_, "IMPORT", 1, std::unique_ptr<Import::Context>(new Import::Context{ - db_.get(), this, &sql_modules_})); + engine_.db(), this, &sql_modules_})); + RegisterFunction<ToFtrace>( + &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(db); - RegisterValueAtMaxTsFunction(db); + RegisterLastNonNullFunction(engine_.db()); + RegisterValueAtMaxTsFunction(engine_.db()); { - base::Status status = RegisterStackFunctions(db, &context_); + base::Status status = RegisterStackFunctions(&engine_, &context_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { - base::Status status = PprofFunctions::Register(db, &context_); + base::Status status = PprofFunctions::Register(engine_.db(), &context_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } { - base::Status status = LayoutFunctions::Register(db, &context_); + base::Status status = LayoutFunctions::Register(engine_.db(), &context_); if (!status.ok()) PERFETTO_ELOG("%s", status.c_message()); } + const TraceStorage* storage = context_.storage.get(); + + // Operator tables. + engine_.RegisterVirtualTableModule<SpanJoinOperatorTable>( + "span_join", storage, SqliteTable::TableType::kExplicitCreate, false); + engine_.RegisterVirtualTableModule<SpanJoinOperatorTable>( + "span_left_join", storage, SqliteTable::TableType::kExplicitCreate, + false); + engine_.RegisterVirtualTableModule<SpanJoinOperatorTable>( + "span_outer_join", storage, SqliteTable::TableType::kExplicitCreate, + false); + engine_.RegisterVirtualTableModule<WindowOperatorTable>( + "window", storage, SqliteTable::TableType::kExplicitCreate, true); + RegisterCreateViewFunctionModule(&engine_); + + // Initalize the tables and views in the prelude. + InitializePreludeTablesViews(engine_.db()); + auto stdlib_modules = GetStdlibModules(); for (auto module_it = stdlib_modules.GetIterator(); module_it; ++module_it) { base::Status status = @@ -752,23 +592,13 @@ TraceProcessorImpl::TraceProcessorImpl(const Config& cfg) PERFETTO_ELOG("%s", status.c_message()); } - SetupMetrics(this, *db_, &sql_metrics_, cfg.skip_builtin_metric_paths); - - // Setup the query cache. - query_cache_.reset(new QueryCache()); - - const TraceStorage* storage = context_.storage.get(); - - SqlStatsTable::RegisterTable(*db_, storage); - StatsTable::RegisterTable(*db_, storage); - - // Operator tables. - SpanJoinOperatorTable::RegisterTable(*db_, storage); - WindowOperatorTable::RegisterTable(*db_, storage); - CreateViewFunction::RegisterTable(*db_); + SetupMetrics(this, &engine_, &sql_metrics_, cfg.skip_builtin_metric_paths); - // New style tables but with some custom logic. - SqliteRawTable::RegisterTable(*db_, query_cache_.get(), &context_); + // Legacy tables. + engine_.RegisterVirtualTableModule<SqlStatsTable>( + "sqlstats", storage, SqliteTable::TableType::kEponymousOnly, false); + engine_.RegisterVirtualTableModule<StatsTable>( + "stats", storage, SqliteTable::TableType::kEponymousOnly, false); // Tables dynamically generated at query time. RegisterTableFunction(std::unique_ptr<ExperimentalFlamegraph>( @@ -810,6 +640,8 @@ TraceProcessorImpl::TraceProcessorImpl(const Config& cfg) // (O(rows in sched/slice/counter)), then consider calling ShrinkToFit on // that table in TraceStorage::ShrinkToFitTables. RegisterDbTable(storage->arg_table()); + RegisterDbTable(storage->raw_table()); + RegisterDbTable(storage->ftrace_event_table()); RegisterDbTable(storage->thread_table()); RegisterDbTable(storage->process_table()); RegisterDbTable(storage->filedescriptor_table()); @@ -911,7 +743,7 @@ void TraceProcessorImpl::Flush() { context_.storage->InternString(TraceTypeToString(context_.trace_type)); context_.metadata_tracker->SetMetadata(metadata::trace_type, Variadic::String(trace_type_id)); - BuildBoundsTable(*db_, context_.storage->GetTraceTimestampBoundsNs()); + BuildBoundsTable(engine_.db(), context_.storage->GetTraceTimestampBoundsNs()); } void TraceProcessorImpl::NotifyEndOfFile() { @@ -949,7 +781,7 @@ void TraceProcessorImpl::NotifyEndOfFile() { // 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(*db_, context_.storage->GetTraceTimestampBoundsNs()); + BuildBoundsTable(engine_.db(), context_.storage->GetTraceTimestampBoundsNs()); TraceProcessorStorageImpl::DestroyContext(); } @@ -996,19 +828,20 @@ Iterator TraceProcessorImpl::ExecuteQuery(const std::string& sql) { ScopedStmt stmt; IteratorImpl::StmtMetadata metadata; base::Status status = - PrepareAndStepUntilLastValidStmt(*db_, sql, &stmt, &metadata); + PrepareAndStepUntilLastValidStmt(engine_.db(), sql, &stmt, &metadata); PERFETTO_DCHECK((status.ok() && stmt) || (!status.ok() && !stmt)); - std::unique_ptr<IteratorImpl> impl(new IteratorImpl( - this, *db_, status, std::move(stmt), std::move(metadata), sql_stats_row)); + std::unique_ptr<IteratorImpl> impl( + new IteratorImpl(this, engine_.db(), status, std::move(stmt), + std::move(metadata), sql_stats_row)); return Iterator(std::move(impl)); } void TraceProcessorImpl::InterruptQuery() { - if (!db_) + if (!engine_.db()) return; query_interrupted_.store(true); - sqlite3_interrupt(db_.get()); + sqlite3_interrupt(engine_.db()); } bool TraceProcessorImpl::IsRootMetricField(const std::string& metric_name) { @@ -1100,7 +933,7 @@ base::Status TraceProcessorImpl::RegisterMetric(const std::string& path, prev_path.c_str(), path.c_str(), metric.proto_field_name->c_str()); } - InsertIntoTraceMetricsTable(*db_, no_ext_name); + InsertIntoTraceMetricsTable(engine_.db(), no_ext_name); } sql_metrics_.emplace_back(metric); @@ -1128,7 +961,7 @@ base::Status TraceProcessorImpl::ExtendMetricsProto( auto fn_name = desc.full_name().substr(desc.package_name().size() + 1); std::replace(fn_name.begin(), fn_name.end(), '.', '_'); RegisterFunction<metrics::BuildProto>( - db_.get(), 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 37e7a2cf4..21cbd9289 100644 --- a/src/trace_processor/trace_processor_impl.h +++ b/src/trace_processor/trace_processor_impl.h @@ -36,6 +36,7 @@ #include "src/trace_processor/sqlite/db_sqlite_table.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" #include "src/trace_processor/trace_processor_storage_impl.h" #include "src/trace_processor/util/sql_modules.h" @@ -107,13 +108,11 @@ class TraceProcessorImpl : public TraceProcessor, template <typename Table> void RegisterDbTable(const Table& table) { - DbSqliteTable::RegisterTable(*db_, query_cache_.get(), &table, - Table::Name()); + engine_.RegisterTable(table, Table::Name()); } - void RegisterTableFunction(std::unique_ptr<TableFunction> generator) { - DbSqliteTable::RegisterTable(*db_, query_cache_.get(), - std::move(generator)); + void RegisterTableFunction(std::unique_ptr<TableFunction> fn) { + engine_.RegisterTableFunction(std::move(fn)); } template <typename View> @@ -121,15 +120,7 @@ class TraceProcessorImpl : public TraceProcessor, bool IsRootMetricField(const std::string& metric_name); - // Keep this first: we need this to be destroyed after we clean up - // everything else. - ScopedDb db_; - - // 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_; - - std::unique_ptr<QueryCache> query_cache_; + SqliteEngine engine_; DescriptorPool pool_; diff --git a/src/trace_processor/types/BUILD.gn b/src/trace_processor/types/BUILD.gn index ba26e2424..0605ea1bc 100644 --- a/src/trace_processor/types/BUILD.gn +++ b/src/trace_processor/types/BUILD.gn @@ -23,7 +23,6 @@ source_set("types") { "task_state.h", "tcp_state.h", "trace_processor_context.h", - "variadic.cc", "variadic.h", "version_number.h", ] diff --git a/src/trace_processor/util/status_macros.h b/src/trace_processor/util/status_macros.h index 50e2bb4d0..2acb79947 100644 --- a/src/trace_processor/util/status_macros.h +++ b/src/trace_processor/util/status_macros.h @@ -23,7 +23,7 @@ // error status, returns the status from the current function. #define RETURN_IF_ERROR(expr) \ do { \ - util::Status status_macro_internal_status = (expr); \ + base::Status status_macro_internal_status = (expr); \ if (!status_macro_internal_status.ok()) \ return status_macro_internal_status; \ } while (0) diff --git a/src/trace_processor/views/BUILD.gn b/src/trace_processor/views/BUILD.gn index 7cd540887..604a4bb24 100644 --- a/src/trace_processor/views/BUILD.gn +++ b/src/trace_processor/views/BUILD.gn @@ -12,6 +12,7 @@ # 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("views") { @@ -28,10 +29,15 @@ source_set("views") { ] } +perfetto_tp_tables("macros_unittest") { + sources = [ "macros_unittest.py" ] +} + source_set("unittests") { testonly = true sources = [ "macros_unittest.cc" ] deps = [ + ":macros_unittest", ":views", "../../../gn:default_deps", "../../../gn:gtest_and_gmock", diff --git a/src/trace_processor/views/macros_internal.h b/src/trace_processor/views/macros_internal.h index 69f4f9058..ca161372f 100644 --- a/src/trace_processor/views/macros_internal.h +++ b/src/trace_processor/views/macros_internal.h @@ -16,8 +16,6 @@ #include "src/trace_processor/db/view.h" -#include "src/trace_processor/tables/macros_internal.h" - #ifndef SRC_TRACE_PROCESSOR_VIEWS_MACROS_INTERNAL_H_ #define SRC_TRACE_PROCESSOR_VIEWS_MACROS_INTERNAL_H_ @@ -73,6 +71,15 @@ class ViewColumnBlueprint { #pragma GCC system_header #endif +// Basic helper macros. +#define PERFETTO_TP_NOOP(...) + +// Invokes FN on each column in the definition of the table. We define a +// recursive macro as we need to walk up the hierarchy until we hit the root. +// Currently, we hardcode 5 levels but this can be increased as necessary. +#define PERFETTO_TP_ALL_COLUMNS(DEF, arg) \ + DEF(PERFETTO_TP_NOOP, PERFETTO_TP_NOOP, arg) + // Invokes a View column function using data from a table column definition. #define PERFETTO_TP_VIEW_INVOKE_VIEW_COL_FN_FROM_TABLE(FN, col_name) \ FN(col_name, from_table, col_name) diff --git a/src/trace_processor/views/macros_unittest.cc b/src/trace_processor/views/macros_unittest.cc index 89c77343f..dc4d697ff 100644 --- a/src/trace_processor/views/macros_unittest.cc +++ b/src/trace_processor/views/macros_unittest.cc @@ -16,38 +16,30 @@ #include "src/trace_processor/views/macros.h" +#include "src/trace_processor/views/macros_unittest_py.h" #include "test/gtest_and_gmock.h" -#include "src/trace_processor/tables/macros.h" - namespace perfetto { namespace trace_processor { -namespace { - -#define PERFETTO_TP_TEST_THREAD_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestThreadTable, "thread") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ - C(StringPool::Id, name) \ - C(int64_t, start_ts, Column::Flag::kSorted) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_THREAD_TABLE_DEF); +namespace tables { #define PERFETTO_TP_TEST_EVENT_TABLE_DEF(NAME, PARENT, C) \ - NAME(TestEventTable, "event") \ - PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C) \ + NAME(MacrosEventTable, "event") \ C(int64_t, ts, Column::Flag::kSorted) \ - C(TestThreadTable::Id, thread_id) -PERFETTO_TP_TABLE(PERFETTO_TP_TEST_EVENT_TABLE_DEF); + C(MacrosThreadTable::Id, thread_id) -TestEventTable::~TestEventTable() = default; -TestThreadTable::~TestThreadTable() = default; +MacrosEventTable::~MacrosEventTable() = default; +MacrosThreadTable::~MacrosThreadTable() = default; + +namespace { #define PERFETTO_TP_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, FCOL) \ NAME(TestEventView, "event_view") \ PERFETTO_TP_VIEW_EXPORT_FROM_COLS(PERFETTO_TP_TEST_EVENT_TABLE_DEF, FCOL) \ COL(thread_name, thread, name) \ COL(thread_start_ts, thread, start_ts) \ - FROM(TestEventTable, event) \ - JOIN(TestThreadTable, thread, id, event, thread_id, View::kIdAlwaysPresent) + FROM(MacrosEventTable, event) \ + JOIN(MacrosThreadTable, thread, id, event, thread_id, View::kIdAlwaysPresent) PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_EVENT_VIEW_DEF); PERFETTO_TP_DEFINE_VIEW(TestEventView); @@ -72,8 +64,8 @@ TEST(ViewMacrosUnittest, ColIdx) { } TEST(ViewMacrosUnittest, Schema) { - TestThreadTable thread{nullptr, nullptr}; - TestEventTable event{nullptr, nullptr}; + MacrosThreadTable thread{nullptr}; + MacrosEventTable event{nullptr}; TestEventView view{&event, &thread}; auto schema = view.schema(); @@ -98,5 +90,6 @@ TEST(ViewMacrosUnittest, Schema) { } } // namespace +} // namespace tables } // namespace trace_processor } // namespace perfetto diff --git a/src/trace_processor/views/macros_unittest.py b/src/trace_processor/views/macros_unittest.py new file mode 100644 index 000000000..96606e0e9 --- /dev/null +++ b/src/trace_processor/views/macros_unittest.py @@ -0,0 +1,45 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains tables for unittesting.""" + +from python.generators.trace_processor_table.public import Column as C +from python.generators.trace_processor_table.public import ColumnFlag +from python.generators.trace_processor_table.public import CppInt64 +from python.generators.trace_processor_table.public import Table +from python.generators.trace_processor_table.public import CppString +from python.generators.trace_processor_table.public import CppTableId + +MACROS_THREAD_TABLE = Table( + python_module=__file__, + class_name="MacrosThreadTable", + sql_name="thread", + columns=[ + C("name", CppString()), + C("start_ts", CppInt64(), flags=ColumnFlag.SORTED), + ]) + +MACROS_EVENT_TABLE = Table( + python_module=__file__, + class_name="MacrosEventTable", + sql_name="event", + columns=[ + C("ts", CppInt64(), flags=ColumnFlag.SORTED), + C("thread_id", CppTableId(MACROS_THREAD_TABLE)), + ]) + +# Keep this list sorted. +ALL_TABLES = [ + MACROS_THREAD_TABLE, + MACROS_EVENT_TABLE, +] diff --git a/src/trace_processor/views/slice_views.h b/src/trace_processor/views/slice_views.h index 287fd02b2..53dd896ff 100644 --- a/src/trace_processor/views/slice_views.h +++ b/src/trace_processor/views/slice_views.h @@ -19,7 +19,6 @@ #include "src/trace_processor/db/view.h" #include "src/trace_processor/tables/metadata_tables_py.h" -#include "src/trace_processor/tables/slice_tables.h" #include "src/trace_processor/tables/slice_tables_py.h" #include "src/trace_processor/tables/track_tables_py.h" #include "src/trace_processor/views/macros.h" @@ -28,6 +27,23 @@ namespace perfetto { namespace trace_processor { namespace views { +#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C) \ + NAME(SliceTable, "internal_slice") \ + C(int64_t, ts, Column::Flag::kSorted) \ + C(int64_t, dur) \ + C(TrackTable::Id, track_id) \ + C(std::optional<StringPool::Id>, category) \ + C(std::optional<StringPool::Id>, name) \ + C(uint32_t, depth) \ + C(int64_t, stack_id) \ + C(int64_t, parent_stack_id) \ + C(std::optional<SliceTable::Id>, parent_id) \ + C(uint32_t, arg_set_id) \ + C(std::optional<int64_t>, thread_ts) \ + C(std::optional<int64_t>, thread_dur) \ + C(std::optional<int64_t>, thread_instruction_count) \ + C(std::optional<int64_t>, thread_instruction_delta) + // TODO(lalitm): add support in document generator for views. #define PERFETTO_TP_THREAD_SLICE_VIEW_DEF(NAME, FROM, JOIN, COL, FCOL) \ NAME(ThreadSliceView, "exp_thread_slice") \ diff --git a/src/traceconv/trace_to_systrace.cc b/src/traceconv/trace_to_systrace.cc index 6b2f3802e..e3494e837 100644 --- a/src/traceconv/trace_to_systrace.cc +++ b/src/traceconv/trace_to_systrace.cc @@ -33,9 +33,6 @@ #include "perfetto/trace_processor/trace_processor.h" #include "src/traceconv/utils.h" -#define FILTER_RAW_EVENTS \ - " where not (name like \"chrome_event.%\" or name like \"track_event.%\")" - namespace perfetto { namespace trace_to_text { @@ -168,8 +165,7 @@ int ExtractRawEvents(TraceWriter* trace_writer, Keep truncate_keep) { using trace_processor::Iterator; - static const char kRawEventsCountSql[] = - "select count(1) from raw" FILTER_RAW_EVENTS; + static const char kRawEventsCountSql[] = "select count(1) from ftrace_event"; uint32_t raw_events = 0; auto e_callback = [&raw_events](Iterator* it, base::StringWriter*) { raw_events = static_cast<uint32_t>(it->Get(0).long_value); @@ -235,7 +231,7 @@ int ExtractRawEvents(TraceWriter* trace_writer, const uint32_t max_ftrace_events = (140 * 1024 * 1024) / 130; static const char kRawEventsQuery[] = - "select to_ftrace(id) from raw" FILTER_RAW_EVENTS; + "select to_ftrace(id) from ftrace_event"; // 1. Write the appropriate header for the file type. if (wrapped_in_json) { diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc index 459b64081..859cc8bf1 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::Not; using testing::Pair; using testing::Property; using testing::Return; +using testing::SizeIs; using testing::StartsWith; namespace perfetto { @@ -3303,5 +3305,113 @@ TEST_F(CpuReaderParsePagePayloadTest, ZeroPaddedPageWorkaround) { 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 3551e9f7b..6a8cca30b 100644 --- a/src/traced/probes/ftrace/event_info.cc +++ b/src/traced/probes/ftrace/event_info.cc @@ -4266,9 +4266,6 @@ std::vector<Event> GetStaticEventInfo() { "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, @@ -6653,6 +6650,38 @@ std::vector<Event> GetStaticEventInfo() { kUnsetFtraceId, 475, kUnsetSize}, + {"mali_CSF_INTERRUPT_START", + "mali", + { + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "kctx_tgid", 1, ProtoSchemaType::kInt32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "kctx_id", 2, ProtoSchemaType::kUint32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "info_val", 3, ProtoSchemaType::kUint64, + TranslationStrategy::kInvalidTranslationStrategy}, + }, + kUnsetFtraceId, + 482, + kUnsetSize}, + {"mali_CSF_INTERRUPT_END", + "mali", + { + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "kctx_tgid", 1, ProtoSchemaType::kInt32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "kctx_id", 2, ProtoSchemaType::kUint32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "info_val", 3, ProtoSchemaType::kUint64, + TranslationStrategy::kInvalidTranslationStrategy}, + }, + kUnsetFtraceId, + 483, + kUnsetSize}, {"mdp_cmd_kickoff", "mdss", { 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 000000000..5588a9606 --- /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 000000000..2f5a8ab38 --- /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 000000000..276dce9d5 --- /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 000000000..5588a9606 --- /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 000000000..15f6a646b --- /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 000000000..276dce9d5 --- /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/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_END/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_END/format new file mode 100644 index 000000000..277128baa --- /dev/null +++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_END/format @@ -0,0 +1,13 @@ +name: mali_CSF_INTERRUPT_END +ID: 1679 +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:pid_t kctx_tgid; offset:8; size:4; signed:1; + field:u32 kctx_id; offset:12; size:4; signed:0; + field:u64 info_val; offset:16; size:8; signed:0; + +print fmt: "kctx=%d_%u info=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->info_val diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_START/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_START/format new file mode 100644 index 000000000..0c9e0db55 --- /dev/null +++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_CSF_INTERRUPT_START/format @@ -0,0 +1,13 @@ +name: mali_CSF_INTERRUPT_START +ID: 1678 +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:pid_t kctx_tgid; offset:8; size:4; signed:1; + field:u32 kctx_id; offset:12; size:4; signed:0; + field:u64 info_val; offset:16; size:8; signed:0; + +print fmt: "kctx=%d_%u info=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->info_val diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc index 8100e4264..9ce614cfc 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 @@ ProcessStatsDataSource::ProcessStatsDataSource( 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 @@ void ProcessStatsDataSource::WriteAllProcessStats() { 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 @@ bool ProcessStatsDataSource::WriteMemCounters(int32_t pid, 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 706ee2a19..4989b3cb7 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 @@ class ProcessStatsDataSource : public ProbesDataSource { 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 @@ class ProcessStatsDataSource : public ProbesDataSource { 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 c87f0bdfe..5a09ebefd 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 @@ TEST_F(ProcessStatsDataSourceTest, ProcessStats) { 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 @@ TEST_F(ProcessStatsDataSourceTest, NamespacedProcess) { 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 627d4bc97..9d3d7f74b 100644 --- a/src/traced/probes/statsd_client/statsd_binder_data_source.cc +++ b/src/traced/probes/statsd_client/statsd_binder_data_source.cc @@ -242,30 +242,20 @@ void StatsdBinderDataSource::OnData(uint32_t reason, const uint8_t* data, size_t sz) { ShellDataDecoder message(data, sz); - - 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); - } - - 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(); - } + if (message.has_atom()) { + TraceWriter::TracePacketHandle packet = writer_->NewTracePacket(); + + // 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())); + + // 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 e0770dbff..cb142e51a 100644 --- a/src/traced/service/BUILD.gn +++ b/src/traced/service/BUILD.gn @@ -43,6 +43,10 @@ source_set("service") { "../../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 72f19ac62..c9d633590 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 @@ int PERFETTO_EXPORT_ENTRYPOINT ServiceMain(int argc, char** argv) { 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 ea1cb903f..3fe90eae1 100644 --- a/src/tracing/core/BUILD.gn +++ b/src/tracing/core/BUILD.gn @@ -83,6 +83,21 @@ source_set("service") { } } +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 @@ perfetto_unittest_source_set("unittests") { "../../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 @@ perfetto_unittest_source_set("unittests") { "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/metatrace_writer.cc b/src/tracing/core/metatrace_writer.cc index c2706329b..8d121a6b7 100644 --- a/src/tracing/core/metatrace_writer.cc +++ b/src/tracing/core/metatrace_writer.cc @@ -26,11 +26,6 @@ namespace perfetto { -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr char MetatraceWriter::kDataSourceName[]; -#endif - MetatraceWriter::MetatraceWriter() : weak_ptr_factory_(this) {} MetatraceWriter::~MetatraceWriter() { diff --git a/src/tracing/core/shared_memory_abi.cc b/src/tracing/core/shared_memory_abi.cc index 0c4694b02..a9098dc24 100644 --- a/src/tracing/core/shared_memory_abi.cc +++ b/src/tracing/core/shared_memory_abi.cc @@ -69,16 +69,6 @@ inline void ClearChunkHeader(SharedMemoryABI::ChunkHeader* header) { } // namespace -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr uint32_t SharedMemoryABI::kNumChunksForLayout[]; -constexpr const char* SharedMemoryABI::kChunkStateStr[]; -constexpr const size_t SharedMemoryABI::kInvalidPageIdx; -constexpr const size_t SharedMemoryABI::kMinPageSize; -constexpr const size_t SharedMemoryABI::kMaxPageSize; -constexpr const size_t SharedMemoryABI::kPacketSizeDropPacket; -#endif - SharedMemoryABI::SharedMemoryABI() = default; SharedMemoryABI::SharedMemoryABI(uint8_t* start, diff --git a/src/tracing/core/shared_memory_arbiter_impl.cc b/src/tracing/core/shared_memory_arbiter_impl.cc index f1b830abf..61b3974c0 100644 --- a/src/tracing/core/shared_memory_arbiter_impl.cc +++ b/src/tracing/core/shared_memory_arbiter_impl.cc @@ -52,11 +52,6 @@ bool IsReservationTargetBufferId(MaybeUnboundBufferID buffer_id) { SharedMemoryABI::PageLayout SharedMemoryArbiterImpl::default_page_layout = SharedMemoryABI::PageLayout::kPageDiv1; -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr BufferID SharedMemoryArbiterImpl::kInvalidBufferId; -#endif - // static std::unique_ptr<SharedMemoryArbiter> SharedMemoryArbiter::CreateInstance( SharedMemory* shared_memory, diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc index 4f0f69f49..baa6a3486 100644 --- a/src/tracing/core/trace_buffer.cc +++ b/src/tracing/core/trace_buffer.cc @@ -42,9 +42,6 @@ constexpr uint8_t kChunkNeedsPatching = SharedMemoryABI::ChunkHeader::kChunkNeedsPatching; } // namespace. -#if !PERFETTO_IS_AT_LEAST_CPP17() -constexpr size_t TraceBuffer::ChunkRecord::kMaxSize; -#endif const size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord); // static diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc index b4ab055e7..b32a65854 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> @@ -301,25 +298,21 @@ void AppendOwnedSlicesToPacket(std::unique_ptr<uint8_t[]> data, } // namespace -#if !PERFETTO_IS_AT_LEAST_CPP17() -// These constants instead are defined in the header because are used by tests. -constexpr size_t TracingServiceImpl::kMaxShmSize; -constexpr uint32_t TracingServiceImpl::kDataSourceStopTimeoutMs; -constexpr uint8_t TracingServiceImpl::kSyncMarker[]; -#endif - // 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), @@ -587,8 +580,8 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, 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)) { @@ -610,6 +603,16 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, "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); @@ -617,8 +620,8 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, "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 @@ -630,8 +633,8 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, 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; @@ -853,6 +856,17 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, 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; @@ -943,7 +957,7 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, 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); @@ -960,6 +974,7 @@ base::Status TracingServiceImpl::EnableTracing(ConsumerEndpointImpl* consumer, 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. @@ -1245,7 +1260,7 @@ void TracingServiceImpl::StopOnDurationMsExpiry( // 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; @@ -1485,7 +1500,7 @@ void TracingServiceImpl::ActivateTriggers( 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 = @@ -1544,8 +1559,7 @@ void TracingServiceImpl::ActivateTriggers( 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(); @@ -1553,7 +1567,7 @@ void TracingServiceImpl::ActivateTriggers( {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 @@ -1600,6 +1614,24 @@ void TracingServiceImpl::ActivateTriggers( // 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; @@ -2306,6 +2338,8 @@ std::vector<TracePacket> TracingServiceImpl::ReadBuffers( 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 @@ -2358,6 +2392,16 @@ void TracingServiceImpl::MaybeFilterPackets(TracingSession* tracing_session, } } +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) { @@ -3497,7 +3541,7 @@ void TracingServiceImpl::FlushAndCloneSession(ConsumerEndpointImpl* consumer, 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; @@ -3510,15 +3554,18 @@ void TracingServiceImpl::FlushAndCloneSession(ConsumerEndpointImpl* consumer, 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_)); @@ -3569,6 +3616,7 @@ base::Status TracingServiceImpl::DoCloneSession(ConsumerEndpointImpl* consumer, 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; @@ -3589,6 +3637,7 @@ base::Status TracingServiceImpl::DoCloneSession(ConsumerEndpointImpl* consumer, 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( @@ -3814,6 +3863,15 @@ void TracingServiceImpl::ConsumerEndpointImpl::OnAllDataSourcesStarted() { 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_); @@ -3910,11 +3968,13 @@ void TracingServiceImpl::ConsumerEndpointImpl::QueryCapabilities( 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 7cefb9b6b..8d067fd78 100644 --- a/src/tracing/core/tracing_service_impl.h +++ b/src/tracing/core/tracing_service_impl.h @@ -87,8 +87,9 @@ class TracingServiceImpl : public TracingService { // 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 @@ class TracingServiceImpl : public TracingService { ~ConsumerEndpointImpl() override; void NotifyOnTracingDisabled(const std::string& error); + void NotifyCloneSnapshotTrigger(); // TracingService::ConsumerEndpoint implementation. void EnableTracing(const TraceConfig&, base::ScopedFile) override; @@ -264,7 +266,8 @@ class TracingServiceImpl : public TracingService { }; explicit TracingServiceImpl(std::unique_ptr<SharedMemory::Factory>, - base::TaskRunner*); + base::TaskRunner*, + InitOpts = {}); ~TracingServiceImpl() override; // Called by ProducerEndpointImpl. @@ -565,6 +568,9 @@ class TracingServiceImpl : public TracingService { // 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 @@ class TracingServiceImpl : public TracingService { 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 @@ class TracingServiceImpl : public TracingService { 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 @@ class TracingServiceImpl : public TracingService { 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 654272394..fb175e990 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 @@ MATCHER_P(HasTriggerMode, mode, "") { 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 @@ class TracingServiceImplTest : public testing::Test { 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 @@ TEST_F(TracingServiceImplTest, ProducerIDWrapping) { 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 @@ TEST_F(TracingServiceImplTest, CloneSession) { // 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 @@ TEST_F(TracingServiceImplTest, CloneSession) { } 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 @@ TEST_F(TracingServiceImplTest, CloneSession) { // 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 5b7534f88..d38a77613 100644 --- a/src/tracing/core/virtual_destructors.cc +++ b/src/tracing/core/virtual_destructors.cc @@ -38,11 +38,6 @@ SharedMemoryArbiter::~SharedMemoryArbiter() = default; // TODO(primiano): make pure virtual after various 3way patches. void ConsumerEndpoint::CloneSession(TracingSessionID) {} -void Consumer::OnSessionCloned(bool, const std::string&) {} - -#if !PERFETTO_IS_AT_LEAST_CPP17() -constexpr size_t TracingService::kDefaultShmSize; -constexpr size_t TracingService::kDefaultShmPageSize; -#endif +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 000000000..1a9689ec2 --- /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/tracing/core/zlib_compressor.h b/src/tracing/core/zlib_compressor.h new file mode 100644 index 000000000..1962c4831 --- /dev/null +++ b/src/tracing/core/zlib_compressor.h @@ -0,0 +1,33 @@ +/* + * 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_TRACING_CORE_ZLIB_COMPRESSOR_H_ +#define SRC_TRACING_CORE_ZLIB_COMPRESSOR_H_ + +#include <vector> + +#include "perfetto/ext/tracing/core/trace_packet.h" + +namespace perfetto { + +// Matches TracingServiceImpl::kMaxTracePacketSliceSize. Exposed for testing. +static constexpr size_t kZlibCompressSliceSize = 128 * 1024 - 512; + +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 000000000..2471c1b63 --- /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 181237fc2..5169a2bee 100644 --- a/src/tracing/internal/tracing_muxer_impl.cc +++ b/src/tracing/internal/tracing_muxer_impl.cc @@ -644,8 +644,7 @@ void TracingMuxerImpl::ConsumerImpl::OnObservableEvents( } 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 @@ void TracingMuxerImpl::RegisterInterceptor( } // 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 4c87bb9c1..54d2fc375 100644 --- a/src/tracing/internal/tracing_muxer_impl.h +++ b/src/tracing/internal/tracing_muxer_impl.h @@ -300,7 +300,7 @@ class TracingMuxerImpl : public TracingMuxer { 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/internal/tracing_muxer_impl_integrationtest.cc b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc index 5711d73c3..bc28cdef6 100644 --- a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc +++ b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc @@ -25,6 +25,12 @@ using ::testing::Property; class TracingMuxerImplIntegrationTest : public testing::Test { protected: + void SetUp() override { +#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) + GTEST_SKIP() << "Unix sockets not supported on windows"; +#endif + } + // Sets the environment variable `name` to `value`. Restores it to the // previous value when the test finishes. void SetEnvVar(const char* name, const char* value) { @@ -38,7 +44,7 @@ class TracingMuxerImplIntegrationTest : public testing::Test { base::SetEnv(name, value); } - ~TracingMuxerImplIntegrationTest() { + ~TracingMuxerImplIntegrationTest() override { perfetto::Tracing::ResetForTesting(); while (!prev_state_.empty()) { const EnvVar& var = prev_state_.top(); diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc index 7eecd09ed..cf34361be 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 @@ void ConsumerIPCClientImpl::CloneSession(TracingSessionID tsid) { // 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 2f457136e..ac52864ae 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::CloseObserveEventsResponseStream() { } 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 d570c519a..f1424d9e4 100644 --- a/src/tracing/ipc/service/consumer_ipc_service.h +++ b/src/tracing/ipc/service/consumer_ipc_service.h @@ -94,7 +94,7 @@ class ConsumerIPCService : public protos::gen::ConsumerPort { 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 85029a296..1df674ef7 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 @@ constexpr uint32_t kProducerSocketTxTimeoutMs = 10; // 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 @@ bool ServiceIPCHostImpl::DoStart() { 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 dda7e5bfa..4ccfa65f2 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 @@ class Host; // 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 @@ class ServiceIPCHostImpl : public ServiceIPCHost { 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/aligned_buffer_test.cc b/src/tracing/test/aligned_buffer_test.cc index 082e13051..fc9b8bc47 100644 --- a/src/tracing/test/aligned_buffer_test.cc +++ b/src/tracing/test/aligned_buffer_test.cc @@ -20,11 +20,6 @@ namespace perfetto { -#if !PERFETTO_IS_AT_LEAST_CPP17() -// static -constexpr size_t AlignedBufferTest::kNumPages; -#endif - void AlignedBufferTest::SetUp() { page_size_ = GetParam(); buf_.reset(new TestSharedMemory(page_size_ * kNumPages)); diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h index 3b6ad2b73..2253d93f5 100644 --- a/src/tracing/test/mock_consumer.h +++ b/src/tracing/test/mock_consumer.h @@ -83,7 +83,7 @@ class MockConsumer : public Consumer { 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 05fa12607..29949a853 100644 --- a/src/tracing/test/tracing_integration_test.cc +++ b/src/tracing/test/tracing_integration_test.cc @@ -97,7 +97,7 @@ class MockConsumer : public Consumer { 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 cbc355560..f8c7a43e6 100644 --- a/test/cmdline_integrationtest.cc +++ b/test/cmdline_integrationtest.cc @@ -46,6 +46,7 @@ namespace { 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 @@ TraceConfig CreateTraceConfigForBugreportTest() { 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 @@ class PerfettoCmdlineTest : public ::testing::Test { // 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 @@ class PerfettoCmdlineTest : public ::testing::Test { }, 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 @@ TEST_F(PerfettoCmdlineTest, StartTracingTrigger) { // (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 @@ TEST_F(PerfettoCmdlineTest, StopTracingTrigger) { // (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 @@ TEST_F(PerfettoCmdlineTest, AndroidOnly(NoDataNoFileWithoutTrigger)) { // (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 @@ TEST_F(PerfettoCmdlineTest, StopTracingTriggerFromConfig) { // (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 @@ TEST_F(PerfettoCmdlineTest, TriggerFromConfigStopsFileOpening) { // (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 @@ TEST_F(PerfettoCmdlineTest, AndroidOnly(CmdTriggerWithUploadFlag)) { // (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 @@ TEST_F(PerfettoCmdlineTest, AndroidOnly(CmdTriggerWithUploadFlag)) { 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 @@ TEST_F(PerfettoCmdlineTest, SaveForBugreport_WriteIntoFile) { 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 230fe0147..b56822bb6 100644 --- a/test/configs/BUILD.gn +++ b/test/configs/BUILD.gn @@ -40,6 +40,7 @@ action_foreach("configs") { "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 000000000..eb2c19786 --- /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 a384b5728..10d5b0d07 100644 --- a/test/configs/statsd.cfg +++ b/test/configs/statsd.cfg @@ -5,7 +5,7 @@ buffers { data_sources { config { - name: "android.statsd" + name: "android.statsd_binder" 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 f9294f4e5..f27d3daba 100644 --- a/test/cts/reporter/reporter_test_cts.cc +++ b/test/cts/reporter/reporter_test_cts.cc @@ -42,6 +42,7 @@ TEST(PerfettoReporterTest, TestEndToEndReport) { 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"); auto* ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("android.perfetto.FakeProducer"); @@ -72,6 +73,7 @@ TEST(PerfettoReporterTest, TestEndToEndReport) { auto perfetto_proc = Exec("perfetto", { "--upload", + "--no-guardrails", "-c", "-", }, diff --git a/test/data/fuchsia_events_and_args.fxt.sha256 b/test/data/fuchsia_events_and_args.fxt.sha256 new file mode 100644 index 000000000..cf71b6769 --- /dev/null +++ b/test/data/fuchsia_events_and_args.fxt.sha256 @@ -0,0 +1 @@ +1597b8fd935caedb1e319e423cff4dbfba68327cad6ec21f985c0378c2c76b20
\ 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 62feb86a5..adfa483e2 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 @@ -826cb6e532e4d342e02b6022917423bb481a3b8764bb697706ca890d0ad847c3
\ No newline at end of file +37ce86e92a72fe05c24acfcadf1393f3407a99e3dcdda6b3c883f380ffd00b41
\ 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 de95b69cf..ba42f3444 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 @@ -a56f7bcdebf30ae7da87417e1951867446ae7bd337e450d52ffa45d5d4c2ab65
\ No newline at end of file +0c7f3705f1d29c0b16756b66cb748635352fef3928a173b34d995356e6d3d58e
\ 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 d34a6fde8..4c65000da 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 @@ -e97284d62ddefdbd091e5e820881e81d5ad95a22edcf50d83cacc06b7b9bedde
\ No newline at end of file +c8277a777519e7c1bf762d4e0e299a79a66da88ce54ed26e6d27939dca6128c9
\ 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 5780168a2..f504fafc3 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 @@ -eb2d68c1fab251b50184431abb3a10b420f95e5eca2dc341028feb4a3ea03d40
\ No newline at end of file +dc246b0bf68834a63464c0c98e35a060cb3698947084cdb9d976207a37bf8156
\ 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 73891dc2e..0ee6b178d 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 @@ -fd17d4de0cae0cfbaaed535519b333d7ee13cac65ad14144e68335c8ca521216
\ No newline at end of file +e79c53f02bed0a629c76fd3d044492dc8e619fe9f58c597db5f4417cbe91f805
\ 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 d65717981..0e50d9b87 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 @@ -92aa98e5edad0293244bc0a94672a2e9b63e9b6aaa86ab0a58db8b106e912693
\ No newline at end of file +cc543d6db95f56863f5d6c90dbbb8b07e8dff539534c53d595b1faee807903da
\ 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 69e55e119..bf8f89b19 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 @@ -e7dc92a76eec326637bebaa176482197c621f48bc066fc761d0b1d19513c8c21
\ No newline at end of file +db689629c3ba9a51e74d48edd3e801bf062dd985ed5210e5a9b4f1bce50f29a7
\ 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 48395a155..6c3077884 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 @@ -36628de5b3af4cb6de10e6b4f1860ea8534b8b0db37ee994fddc94947947cb6e
\ No newline at end of file +eae8d6c700a16b5062736c54e4fe0ffab569ffd839715138e74cd257fdb014fa
\ 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 623260e39..5117a65d5 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 @@ -b51e0a5035eab217bc4339800e8a6c4025bfc7d5240844c152fbc867b28f75ba
\ No newline at end of file +5767e81834101bf14dcd1917544f1605b8dbb8aa747a7eefd1c52f4db891575f
\ 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 679bd3983..ff3aafa1a 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 @@ -99d3e463fe3812942825829a388bb2d5b11d73010c3213b11d09884dfc1663b5
\ No newline at end of file +ab1a1b7c948e008fa5d44a4471dc24977f3ab2d592c1ceeaa0ab4afac5bb2336
\ 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 e84c777d3..f4e56d2eb 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 @@ -c5c11d6515e27365eeaf50602243d918cc688ee18d1390649c786b28225e1d81
\ No newline at end of file +d7c89c766d8db408de06f79654e2f81d8c761c08c4451991447807468df0f3d5
\ 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 849be2d4c..46f012943 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 @@ -4b07a28b92a72568615003beb4eb086adc9be581a27baa2ea593c5e88802647b
\ No newline at end of file +e596f8037a578f1e58a33bbe08f5d5621b1d37e55456409e5f8799a6897eedb9
\ 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 eb2608789..23be44dae 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 @@ -9bac90702af629b8ecec00b99d86704f188b332695dea2eaf49d79aba3fc8670
\ No newline at end of file +b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ 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 b3581ce86..b3b161970 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 @@ -f64d01ccda59ec74395e00267d7adbf179f5dd962affeeef639ae583756bc65b
\ No newline at end of file +ae78e1f372a6a052a650974464f2e706ab8cb665c639ca4b0b15b48dce95d185
\ 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 2afc1a3ab..0abbaa782 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 @@ -04e0762424dae233fb13d5cfff51a2d3075ad62220538010d0ab376fc79022a9
\ No newline at end of file +d62fb2e2f4067552d4f9d4170b4ffa45c74171b4de750292ded957cb6b12b669
\ 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 4cfdf9f7e..9287876a7 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 @@ -3539c6d2a8ac9d9e15455be491aa171f75f00fdb39e36971b50a060d70732e81
\ No newline at end of file +30faf223bfc57acbbcca2308c716064d3447dee2673db6329ad9188bf08a59e6
\ 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 eb2608789..23be44dae 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 @@ -9bac90702af629b8ecec00b99d86704f188b332695dea2eaf49d79aba3fc8670
\ No newline at end of file +b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ 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 4cfdf9f7e..9287876a7 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 @@ -3539c6d2a8ac9d9e15455be491aa171f75f00fdb39e36971b50a060d70732e81
\ No newline at end of file +30faf223bfc57acbbcca2308c716064d3447dee2673db6329ad9188bf08a59e6
\ 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 d8da34611..9287876a7 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 @@ -150bfec1eb606a548dc39bbdf081efb82a529b7cc39500605c4e56a5d742fea6
\ No newline at end of file +30faf223bfc57acbbcca2308c716064d3447dee2673db6329ad9188bf08a59e6
\ 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 a494f13b2..4324721a4 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 @@ -8de36401497e509ab127d202b83a056c40c2f5f461254f1ea79fe7a4861b059e
\ No newline at end of file +9d502e8458f85884c7bf1a6411aa3425c63ccf102e6f537318733704e7a416fb
\ 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 6d882f60e..535b9b126 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 @@ -3898ba5cd5fdb63fdef0abced3a1174f5ee864c9b01c7e033e7e2f5e1f0280f2
\ No newline at end of file +b0d5928fbcf0b7adfc06386ea1961d301ee19f66c61b190f9d00fd5acef824c0
\ 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 eb2608789..23be44dae 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 @@ -9bac90702af629b8ecec00b99d86704f188b332695dea2eaf49d79aba3fc8670
\ No newline at end of file +b938d4dfcc109165feaedb9421276a355d03a383a0d21ab91ff72409b18a3ab5
\ 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 4cfdf9f7e..9287876a7 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 @@ -3539c6d2a8ac9d9e15455be491aa171f75f00fdb39e36971b50a060d70732e81
\ No newline at end of file +30faf223bfc57acbbcca2308c716064d3447dee2673db6329ad9188bf08a59e6
\ 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 4cfdf9f7e..9287876a7 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 @@ -3539c6d2a8ac9d9e15455be491aa171f75f00fdb39e36971b50a060d70732e81
\ No newline at end of file +30faf223bfc57acbbcca2308c716064d3447dee2673db6329ad9188bf08a59e6
\ No newline at end of file diff --git a/test/end_to_end_benchmark.cc b/test/end_to_end_benchmark.cc index ac7f8faa2..ce63780bc 100644 --- a/test/end_to_end_benchmark.cc +++ b/test/end_to_end_benchmark.cc @@ -232,7 +232,7 @@ void SaturateCpuProducerArgs(benchmark::internal::Benchmark* b) { 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 ConstantRateProducerArgs(benchmark::internal::Benchmark* b) { 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/ftrace_integrationtest.cc b/test/ftrace_integrationtest.cc index 318b308a9..ae1b1046e 100644 --- a/test/ftrace_integrationtest.cc +++ b/test/ftrace_integrationtest.cc @@ -244,7 +244,7 @@ TEST_F(PerfettoFtraceIntegrationTest, MAYBE_KernelAddressSymbolization) { helper.WaitForConsumerConnect(); TraceConfig trace_config; - trace_config.add_buffers()->set_size_kb(1024); + trace_config.add_buffers()->set_size_kb(64); auto* ds_config = trace_config.add_data_sources()->mutable_config(); ds_config->set_name("linux.ftrace"); diff --git a/test/test_helper.cc b/test/test_helper.cc index cbc3aca27..49a996188 100644 --- a/test/test_helper.cc +++ b/test/test_helper.cc @@ -290,7 +290,7 @@ void TestHelper::OnTraceStats(bool, const TraceStats&) {} 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 dd18b4c31..2cabd98d3 100644 --- a/test/test_helper.h +++ b/test/test_helper.h @@ -290,7 +290,7 @@ class TestHelper : public Consumer { 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 000000000..82cd36c91 --- /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 000000000..04ea9533d --- /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,"lte",3000 +4000,"battery_stats.data_conn",20,"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 8191521c9..09dc24888 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 @@ android_binder { 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 aa631943c..d75d44d3b 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 @@ android_blocking_calls_cuj_metric { process { name: "com.android.systemui" uid: 10001 + pid: 1000 } ts: 2000000 dur: 15000000 @@ -22,6 +23,7 @@ android_blocking_calls_cuj_metric { process { name: "com.google.android.apps.nexuslauncher" uid: 10002 + pid: 2000 } ts: 2000000 dur: 15000000 @@ -39,6 +41,7 @@ android_blocking_calls_cuj_metric { process { name: "com.google.android.third.process" uid: 10003 + pid: 3000 } ts: 2000000 dur: 150000000 @@ -147,6 +150,7 @@ android_blocking_calls_cuj_metric { process { name: "com.android.systemui" uid: 10001 + pid: 1000 } ts: 20000000 dur: 10000000 @@ -164,6 +168,7 @@ android_blocking_calls_cuj_metric { process { name: "com.android.systemui" uid: 10001 + pid: 1000 } ts: 22000000 dur: 10000000 @@ -175,4 +180,29 @@ android_blocking_calls_cuj_metric { 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 7573b76f6..1039e47f2 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 @@ -45,6 +45,19 @@ def add_async_trace(trace, ts, ts_end, buf, pid): 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 +161,31 @@ def add_overlapping_cujs_with_blocking_calls(trace, start_ts, pid): 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 +218,8 @@ add_all_blocking_calls_in_cuj(trace, pid=THIRD_PROCESS_PID) 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 68b3c9f81..89d7c63f7 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 @@ android_blocking_calls_cuj_metric { apk_version_code: 1 debuggable: false } + pid: 1000 } ts: 0 dur: 115000000 @@ -42,6 +43,7 @@ android_blocking_calls_cuj_metric { 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 02a64123e..163815d7a 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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 @@ android_monitor_contention { 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,7 +1209,10 @@ android_monitor_contention { 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 { @@ -1063,7 +1234,10 @@ android_monitor_contention { 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 +1264,10 @@ android_monitor_contention { 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 +1294,10 @@ android_monitor_contention { 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 +1324,10 @@ android_monitor_contention { 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,7 +1365,10 @@ android_monitor_contention { 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 { @@ -1210,7 +1396,10 @@ android_monitor_contention { 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 { @@ -1237,7 +1426,10 @@ android_monitor_contention { 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 { @@ -1269,7 +1461,10 @@ android_monitor_contention { 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 { @@ -1291,7 +1486,10 @@ android_monitor_contention { 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 +1506,10 @@ android_monitor_contention { 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 +1526,10 @@ android_monitor_contention { 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 +1546,10 @@ android_monitor_contention { 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 +1566,10 @@ android_monitor_contention { 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 +1586,10 @@ android_monitor_contention { 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 +1606,10 @@ android_monitor_contention { 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 +1626,10 @@ android_monitor_contention { 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 +1646,10 @@ android_monitor_contention { 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 +1666,10 @@ android_monitor_contention { 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 +1686,10 @@ android_monitor_contention { 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 +1706,10 @@ android_monitor_contention { 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 +1726,10 @@ android_monitor_contention { 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 +1746,10 @@ android_monitor_contention { 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 +1766,10 @@ android_monitor_contention { 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 +1786,10 @@ android_monitor_contention { 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 +1806,10 @@ android_monitor_contention { 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 +1826,10 @@ android_monitor_contention { 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 +1846,10 @@ android_monitor_contention { 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 +1876,10 @@ android_monitor_contention { 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 +1906,10 @@ android_monitor_contention { 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 +1936,10 @@ android_monitor_contention { 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 +1971,10 @@ android_monitor_contention { 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 +2001,10 @@ android_monitor_contention { 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 +2031,10 @@ android_monitor_contention { 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 +2061,10 @@ android_monitor_contention { 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 +2091,10 @@ android_monitor_contention { 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 +2121,10 @@ android_monitor_contention { 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 +2151,10 @@ android_monitor_contention { 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 +2181,10 @@ android_monitor_contention { 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 +2211,10 @@ android_monitor_contention { 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 +2241,10 @@ android_monitor_contention { 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 +2271,10 @@ android_monitor_contention { 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 +2301,10 @@ android_monitor_contention { 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 +2331,10 @@ android_monitor_contention { 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 +2361,10 @@ android_monitor_contention { 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 +2391,10 @@ android_monitor_contention { 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 +2421,10 @@ android_monitor_contention { 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 +2451,10 @@ android_monitor_contention { 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 +2481,10 @@ android_monitor_contention { 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 +2511,10 @@ android_monitor_contention { 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 +2541,10 @@ android_monitor_contention { 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 +2566,10 @@ android_monitor_contention { 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 +2598,10 @@ android_monitor_contention { 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 +2628,10 @@ android_monitor_contention { 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 +2658,10 @@ android_monitor_contention { 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 +2688,10 @@ android_monitor_contention { 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 +2718,10 @@ android_monitor_contention { 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 +2749,10 @@ android_monitor_contention { 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 +2784,10 @@ android_monitor_contention { 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 +2824,10 @@ android_monitor_contention { 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 +2844,10 @@ android_monitor_contention { 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 +2864,10 @@ android_monitor_contention { 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 +2884,10 @@ android_monitor_contention { 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 +2906,10 @@ android_monitor_contention { 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 +2926,10 @@ android_monitor_contention { 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 +2946,10 @@ android_monitor_contention { 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 +2966,10 @@ android_monitor_contention { 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 +2986,10 @@ android_monitor_contention { 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 +3006,10 @@ android_monitor_contention { 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 +3026,10 @@ android_monitor_contention { 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 +3046,10 @@ android_monitor_contention { 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 +3066,10 @@ android_monitor_contention { 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 +3086,10 @@ android_monitor_contention { 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 +3106,10 @@ android_monitor_contention { 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 +3126,10 @@ android_monitor_contention { 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 +3146,10 @@ android_monitor_contention { 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 +3166,10 @@ android_monitor_contention { 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 +3186,10 @@ android_monitor_contention { 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 +3206,10 @@ android_monitor_contention { 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 +3226,10 @@ android_monitor_contention { 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 +3246,10 @@ android_monitor_contention { 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 +3266,10 @@ android_monitor_contention { 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 +3296,10 @@ android_monitor_contention { 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 +3326,10 @@ android_monitor_contention { 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 +3356,10 @@ android_monitor_contention { 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 +3386,10 @@ android_monitor_contention { 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 +3416,10 @@ android_monitor_contention { 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 +3446,10 @@ android_monitor_contention { 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 +3476,10 @@ android_monitor_contention { 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 +3506,10 @@ android_monitor_contention { 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 +3536,10 @@ android_monitor_contention { 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 +3566,10 @@ android_monitor_contention { 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 +3596,10 @@ android_monitor_contention { 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 +3626,10 @@ android_monitor_contention { 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 +3656,10 @@ android_monitor_contention { 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 +3686,10 @@ android_monitor_contention { 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 +3716,10 @@ android_monitor_contention { 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 +3746,10 @@ android_monitor_contention { 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 +3778,10 @@ android_monitor_contention { 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 +3808,10 @@ android_monitor_contention { 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 +3838,10 @@ android_monitor_contention { 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 +3868,10 @@ android_monitor_contention { 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 +3898,10 @@ android_monitor_contention { 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 +3928,10 @@ android_monitor_contention { 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 +3958,10 @@ android_monitor_contention { 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 +3988,10 @@ android_monitor_contention { 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 +4018,10 @@ android_monitor_contention { 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 +4048,10 @@ android_monitor_contention { 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 +4078,10 @@ android_monitor_contention { 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 +4108,10 @@ android_monitor_contention { 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 +4138,10 @@ android_monitor_contention { 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 +4168,10 @@ android_monitor_contention { 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 +4198,10 @@ android_monitor_contention { 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 +4228,10 @@ android_monitor_contention { 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 +4258,10 @@ android_monitor_contention { 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 +4288,10 @@ android_monitor_contention { 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 +4318,10 @@ android_monitor_contention { 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 +4348,10 @@ android_monitor_contention { 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 +4378,10 @@ android_monitor_contention { 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 +4408,10 @@ android_monitor_contention { 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 +4438,10 @@ android_monitor_contention { 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 +4478,10 @@ android_monitor_contention { 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,7 +4498,10 @@ android_monitor_contention { 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 { @@ -3986,7 +4523,10 @@ android_monitor_contention { 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 { @@ -4008,7 +4548,10 @@ android_monitor_contention { 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 { @@ -4030,7 +4573,10 @@ android_monitor_contention { 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,7 +4608,10 @@ android_monitor_contention { 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 { @@ -4084,7 +4633,10 @@ android_monitor_contention { 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 +4653,10 @@ android_monitor_contention { 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 +4673,10 @@ android_monitor_contention { 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 +4693,10 @@ android_monitor_contention { 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 +4713,10 @@ android_monitor_contention { 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 +4733,10 @@ android_monitor_contention { 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 +4753,10 @@ android_monitor_contention { 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 +4773,10 @@ android_monitor_contention { 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 +4793,10 @@ android_monitor_contention { 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 +4813,10 @@ android_monitor_contention { 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 +4833,10 @@ android_monitor_contention { 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 +4853,10 @@ android_monitor_contention { 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 +4873,10 @@ android_monitor_contention { 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 +4893,10 @@ android_monitor_contention { 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 +4913,10 @@ android_monitor_contention { 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 +4933,10 @@ android_monitor_contention { 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 +4953,10 @@ android_monitor_contention { 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 +4983,10 @@ android_monitor_contention { 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 +5008,10 @@ android_monitor_contention { 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 +5038,10 @@ android_monitor_contention { 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 +5068,10 @@ android_monitor_contention { 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 +5093,10 @@ android_monitor_contention { 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 +5123,10 @@ android_monitor_contention { 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 +5148,10 @@ android_monitor_contention { 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 +5185,10 @@ android_monitor_contention { 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,7 +5215,10 @@ android_monitor_contention { 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 { @@ -4628,7 +5255,10 @@ android_monitor_contention { 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 { @@ -4650,7 +5280,10 @@ android_monitor_contention { 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 +5305,10 @@ android_monitor_contention { 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 +5335,10 @@ android_monitor_contention { 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 +5360,10 @@ android_monitor_contention { 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 +5385,10 @@ android_monitor_contention { 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 +5415,10 @@ android_monitor_contention { 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 +5445,10 @@ android_monitor_contention { 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 +5475,10 @@ android_monitor_contention { 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 +5505,10 @@ android_monitor_contention { 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 +5535,10 @@ android_monitor_contention { 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 +5565,10 @@ android_monitor_contention { 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 +5595,10 @@ android_monitor_contention { 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 +5625,10 @@ android_monitor_contention { 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 +5655,10 @@ android_monitor_contention { 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 +5685,10 @@ android_monitor_contention { 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 +5715,10 @@ android_monitor_contention { 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 +5745,10 @@ android_monitor_contention { 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 +5775,10 @@ android_monitor_contention { 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 +5805,10 @@ android_monitor_contention { 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 +5835,10 @@ android_monitor_contention { 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 +5865,10 @@ android_monitor_contention { 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 +5905,10 @@ android_monitor_contention { 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 +5925,10 @@ android_monitor_contention { 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 +5955,10 @@ android_monitor_contention { 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 +5985,10 @@ android_monitor_contention { 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 +6015,10 @@ android_monitor_contention { 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 +6045,10 @@ android_monitor_contention { 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 +6070,10 @@ android_monitor_contention { 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 +6100,10 @@ android_monitor_contention { 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 +6130,10 @@ android_monitor_contention { 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 +6160,10 @@ android_monitor_contention { 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 +6190,10 @@ android_monitor_contention { 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 +6220,10 @@ android_monitor_contention { 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 +6250,10 @@ android_monitor_contention { 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 +6280,10 @@ android_monitor_contention { 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 +6310,10 @@ android_monitor_contention { 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 +6340,10 @@ android_monitor_contention { 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 +6370,10 @@ android_monitor_contention { 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 +6400,10 @@ android_monitor_contention { 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 +6430,10 @@ android_monitor_contention { 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 +6460,10 @@ android_monitor_contention { 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 +6490,10 @@ android_monitor_contention { 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 +6520,10 @@ android_monitor_contention { 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 +6550,10 @@ android_monitor_contention { 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 +6580,10 @@ android_monitor_contention { 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 +6610,10 @@ android_monitor_contention { 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 +6640,10 @@ android_monitor_contention { 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 +6670,10 @@ android_monitor_contention { 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 +6700,10 @@ android_monitor_contention { 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 +6730,10 @@ android_monitor_contention { 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 +6760,10 @@ android_monitor_contention { 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 +6790,10 @@ android_monitor_contention { 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 +6820,10 @@ android_monitor_contention { 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,7 +6865,10 @@ android_monitor_contention { 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 @@ -6115,7 +6907,10 @@ android_monitor_contention { 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,7 +6929,10 @@ android_monitor_contention { 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 { @@ -6161,7 +6959,10 @@ android_monitor_contention { 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 +6979,10 @@ android_monitor_contention { 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 +7004,10 @@ android_monitor_contention { 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 +7034,10 @@ android_monitor_contention { 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 +7064,10 @@ android_monitor_contention { 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 +7094,10 @@ android_monitor_contention { 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 +7124,10 @@ android_monitor_contention { 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 +7154,10 @@ android_monitor_contention { 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 +7174,10 @@ android_monitor_contention { 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 +7194,10 @@ android_monitor_contention { 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,7 +7214,10 @@ android_monitor_contention { 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 @@ -6415,7 +7246,10 @@ android_monitor_contention { 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 +7266,10 @@ android_monitor_contention { 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 +7286,10 @@ android_monitor_contention { 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 +7306,10 @@ android_monitor_contention { 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 +7326,10 @@ android_monitor_contention { 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 +7346,10 @@ android_monitor_contention { 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 +7366,10 @@ android_monitor_contention { 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 +7386,10 @@ android_monitor_contention { 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 +7406,10 @@ android_monitor_contention { 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 +7426,10 @@ android_monitor_contention { 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 +7446,10 @@ android_monitor_contention { 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 +7481,10 @@ android_monitor_contention { 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 +7501,10 @@ android_monitor_contention { 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 +7521,10 @@ android_monitor_contention { 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 +7541,10 @@ android_monitor_contention { 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 +7561,10 @@ android_monitor_contention { 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 +7591,10 @@ android_monitor_contention { 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,7 +7623,10 @@ android_monitor_contention { 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 { @@ -6778,7 +7663,10 @@ android_monitor_contention { 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 { @@ -6800,7 +7688,10 @@ android_monitor_contention { 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,7 +7710,10 @@ android_monitor_contention { 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 { @@ -6841,7 +7735,10 @@ android_monitor_contention { 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 +7765,10 @@ android_monitor_contention { 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 +7795,10 @@ android_monitor_contention { 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 +7825,10 @@ android_monitor_contention { 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 +7855,10 @@ android_monitor_contention { 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 +7885,10 @@ android_monitor_contention { 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 +7922,10 @@ android_monitor_contention { 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 +7952,10 @@ android_monitor_contention { 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 +7984,10 @@ android_monitor_contention { 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 +8019,10 @@ android_monitor_contention { 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,7 +8039,10 @@ android_monitor_contention { 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 { @@ -7137,7 +8064,10 @@ android_monitor_contention { 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 +8084,10 @@ android_monitor_contention { 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 +8104,10 @@ android_monitor_contention { 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 +8124,10 @@ android_monitor_contention { 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 +8154,10 @@ android_monitor_contention { 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 +8184,10 @@ android_monitor_contention { 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 +8214,10 @@ android_monitor_contention { 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 +8244,10 @@ android_monitor_contention { 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 +8274,10 @@ android_monitor_contention { 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 +8304,10 @@ android_monitor_contention { 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 +8334,10 @@ android_monitor_contention { 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 +8364,10 @@ android_monitor_contention { 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 +8394,10 @@ android_monitor_contention { 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 +8424,10 @@ android_monitor_contention { 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 +8454,10 @@ android_monitor_contention { 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 +8484,10 @@ android_monitor_contention { 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 +8514,10 @@ android_monitor_contention { 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 +8544,10 @@ android_monitor_contention { 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 +8574,10 @@ android_monitor_contention { 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 +8604,10 @@ android_monitor_contention { 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 +8634,10 @@ android_monitor_contention { 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 +8664,10 @@ android_monitor_contention { 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 +8694,10 @@ android_monitor_contention { 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 +8724,10 @@ android_monitor_contention { 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 +8754,10 @@ android_monitor_contention { 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 +8784,10 @@ android_monitor_contention { 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 +8814,10 @@ android_monitor_contention { 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 +8844,10 @@ android_monitor_contention { 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 +8874,10 @@ android_monitor_contention { 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 +8904,10 @@ android_monitor_contention { 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 +8934,10 @@ android_monitor_contention { 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 +8964,10 @@ android_monitor_contention { 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 +8994,10 @@ android_monitor_contention { 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 +9024,10 @@ android_monitor_contention { 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 +9054,10 @@ android_monitor_contention { 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 +9084,10 @@ android_monitor_contention { 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 +9114,10 @@ android_monitor_contention { 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 +9144,10 @@ android_monitor_contention { 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 +9174,10 @@ android_monitor_contention { 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 +9204,10 @@ android_monitor_contention { 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 +9234,10 @@ android_monitor_contention { 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 +9264,10 @@ android_monitor_contention { 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 +9299,10 @@ android_monitor_contention { 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 +9329,10 @@ android_monitor_contention { 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 +9359,10 @@ android_monitor_contention { 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 +9389,10 @@ android_monitor_contention { 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 +9419,10 @@ android_monitor_contention { 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 +9449,10 @@ android_monitor_contention { 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 +9479,10 @@ android_monitor_contention { 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 +9509,10 @@ android_monitor_contention { 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 +9539,10 @@ android_monitor_contention { 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 +9569,10 @@ android_monitor_contention { 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 +9599,10 @@ android_monitor_contention { 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 +9629,10 @@ android_monitor_contention { 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 +9659,10 @@ android_monitor_contention { 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 +9689,10 @@ android_monitor_contention { 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 +9719,10 @@ android_monitor_contention { 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 +9749,10 @@ android_monitor_contention { 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 +9779,10 @@ android_monitor_contention { 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 +9809,10 @@ android_monitor_contention { 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 +9839,10 @@ android_monitor_contention { 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 +9869,10 @@ android_monitor_contention { 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 +9899,10 @@ android_monitor_contention { 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 +9929,10 @@ android_monitor_contention { 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 +9959,10 @@ android_monitor_contention { 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 +9989,10 @@ android_monitor_contention { 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 +10019,10 @@ android_monitor_contention { 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 +10049,10 @@ android_monitor_contention { 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 +10079,10 @@ android_monitor_contention { 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 +10109,10 @@ android_monitor_contention { 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 +10139,10 @@ android_monitor_contention { 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 +10164,10 @@ android_monitor_contention { 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 +10194,10 @@ android_monitor_contention { 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 000000000..14418e4cd --- /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,1010,2,300 diff --git a/test/trace_processor/diff_tests/android/android_system_property_slice.out b/test/trace_processor/diff_tests/android/android_system_property_slice.out index 8fe8d0782..2505d36d8 100644 --- a/test/trace_processor/diff_tests/android/android_system_property_slice.out +++ b/test/trace_processor/diff_tests/android/android_system_property_slice.out @@ -1,3 +1,3 @@ -"id","type","name","id","ts","dur","type","name" -1,"track","DeviceStateChanged",0,1000,0,"internal_slice","some_state_from_sysprops" -1,"track","DeviceStateChanged",1,3000,0,"internal_slice","some_state_from_atrace" +"type","name","id","ts","dur","type","name" +"track","DeviceStateChanged",0,1000,0,"internal_slice","some_state_from_sysprops" +"track","DeviceStateChanged",1,3000,0,"internal_slice","some_state_from_atrace" diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py index b808d1b36..84bafc1d1 100644 --- a/test/trace_processor/diff_tests/android/tests.py +++ b/test/trace_processor/diff_tests/android/tests.py @@ -58,14 +58,14 @@ class Android(TestSuite): } """), query=""" - SELECT t.id, t.type, t.name, c.id, c.ts, c.type, c.value + SELECT t.type, t.name, c.id, c.ts, c.type, c.value FROM counter_track t JOIN counter c ON t.id = c.track_id WHERE name = 'ScreenState'; """, out=Csv(""" - "id","type","name","id","ts","type","value" - 0,"counter_track","ScreenState",0,1000,"counter",2.000000 - 0,"counter_track","ScreenState",1,2000,"counter",1.000000 + "type","name","id","ts","type","value" + "counter_track","ScreenState",0,1000,"counter",2.000000 + "counter_track","ScreenState",1,2000,"counter",1.000000 """)) def test_android_system_property_slice(self): @@ -105,12 +105,163 @@ class Android(TestSuite): } """), query=""" - SELECT t.id, t.type, t.name, s.id, s.ts, s.dur, s.type, s.name + SELECT t.type, t.name, s.id, s.ts, s.dur, s.type, s.name FROM track t JOIN slice s ON s.track_id = t.id WHERE t.name = 'DeviceStateChanged'; """, 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 2015 (note: doesn't group with above due to name) + # * 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: 0 + network_packet_bundle { + ctx { + direction: DIR_EGRESS + interface: "wlan" + uid: 456 + } + packet_timestamps: [1005, 1015] + packet_lengths: [100, 200] + } + } + 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'), @@ -236,15 +387,18 @@ class Android(TestSuite): query=""" SELECT IMPORT('android.monitor_contention'); SELECT - * + blocking_method, + blocked_method, + short_blocking_method, + short_blocked_method FROM android_monitor_contention WHERE binder_reply_id IS NOT NULL ORDER BY dur DESC LIMIT 1; """, out=Csv(""" - "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","is_blocking_thread_main","binder_reply_id","binder_reply_ts","binder_reply_tid" - "boolean com.android.server.am.ActivityManagerService.forceStopPackageLocked(java.lang.String, int, boolean, boolean, boolean, boolean, boolean, int, java.lang.String)","boolean com.android.server.am.ActivityManagerService.isUidActive(int, java.lang.String)","com.android.server.am.ActivityManagerService.forceStopPackageLocked","com.android.server.am.ActivityManagerService.isUidActive","ActivityManagerService.java:4484","ActivityManagerService.java:7325",0,656,"binder:642_12",495,"binder:642_1",657,250,"system_server",291,1737056375519,37555955,1235,0,0,285,1737055785896,2720 + "blocking_method","blocked_method","short_blocking_method","short_blocked_method" + "boolean com.android.server.am.ActivityManagerService.forceStopPackageLocked(java.lang.String, int, boolean, boolean, boolean, boolean, boolean, int, java.lang.String)","boolean com.android.server.am.ActivityManagerService.isUidActive(int, java.lang.String)","com.android.server.am.ActivityManagerService.forceStopPackageLocked","com.android.server.am.ActivityManagerService.isUidActive" """)) def test_monitor_contention_chain_blocked_functions(self): @@ -305,7 +459,6 @@ class Android(TestSuite): id, ts, dur, - track_id, is_blocked_thread_main, is_blocking_thread_main, IIF(binder_reply_id IS NULL, "", binder_reply_id) AS binder_reply_id, @@ -316,8 +469,8 @@ class Android(TestSuite): 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","track_id","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,1238,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","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,"","","" """)) def test_monitor_contention_metric(self): diff --git a/test/trace_processor/diff_tests/android/tests_general.py b/test/trace_processor/diff_tests/android/tests_general.py deleted file mode 100644 index fd555a58f..000000000 --- a/test/trace_processor/diff_tests/android/tests_general.py +++ /dev/null @@ -1,71 +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 AndroidGeneral(DiffTestModule): - - def test_game_intervention_list(self): - return DiffTestBlueprint( - trace=Path('game_intervention_list_test.textproto'), - query=""" -SELECT - package_name, - uid, - current_mode, - standard_mode_supported, - standard_mode_downscale, - standard_mode_use_angle, - standard_mode_fps, - perf_mode_supported, - perf_mode_downscale, - perf_mode_use_angle, - perf_mode_fps, - battery_mode_supported, - battery_mode_downscale, - battery_mode_use_angle, - battery_mode_fps -FROM android_game_intervention_list -ORDER BY package_name; -""", - out=Path('game_intervention_list_test.out')) - - def test_android_system_property_counter(self): - return DiffTestBlueprint( - trace=Path('android_system_property.textproto'), - query=""" -SELECT t.id, t.type, t.name, c.id, c.ts, c.type, c.value -FROM counter_track t JOIN counter c ON t.id = c.track_id -WHERE name = 'ScreenState'; -""", - out=Csv(""" -"id","type","name","id","ts","type","value" -0,"counter_track","ScreenState",0,1000,"counter",2.000000 -0,"counter_track","ScreenState",1,2000,"counter",1.000000 -""")) - - def test_android_system_property_slice(self): - return DiffTestBlueprint( - trace=Path('android_system_property.textproto'), - query=""" -SELECT t.id, t.type, t.name, s.id, s.ts, s.dur, s.type, s.name -FROM track t JOIN slice s ON s.track_id = t.id -WHERE t.name = 'DeviceStateChanged'; -""", - out=Path('android_system_property_slice.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 000000000..4c51da444 --- /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 000000000..7b7cee113 --- /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_tasks.out b/test/trace_processor/diff_tests/chrome/chrome_tasks.out index ef3383735..7ca510e92 100644 --- a/test/trace_processor/diff_tests/chrome/chrome_tasks.out +++ b/test/trace_processor/diff_tests/chrome/chrome_tasks.out @@ -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,16 @@ "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 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 7e33ac21e..c9ba45ca4 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 @@ import synth_common 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_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py index c5fd3bcb8..62833c226 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,25 @@ class ChromeScrollJank(TestSuite): 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 + """)) diff --git a/test/trace_processor/diff_tests/fuchsia/tests.py b/test/trace_processor/diff_tests/fuchsia/tests.py index 54d0773b9..d7b8b4d0d 100644 --- a/test/trace_processor/diff_tests/fuchsia/tests.py +++ b/test/trace_processor/diff_tests/fuchsia/tests.py @@ -231,3 +231,27 @@ class Fuchsia(TestSuite): "Update time(ms)",21 "Vsync interval",900 """)) + + def test_fuchsia_args_import(self): + return DiffTestBlueprint( + trace=DataPath('fuchsia_events_and_args.fxt'), + query=""" + SELECT key,int_value,string_value,real_value,value_type,display_value + FROM args + LIMIT 12; + """, + out=Csv(""" + "key","int_value","string_value","real_value","value_type","display_value" + "SomeNullArg","[NULL]","null","[NULL]","string","null" + "Someuint32",2145,"[NULL]","[NULL]","int","2145" + "Someuint64",423621626134123415,"[NULL]","[NULL]","int","423621626134123415" + "Someint32",-7,"[NULL]","[NULL]","int","-7" + "Someint64",-234516543631231,"[NULL]","[NULL]","int","-234516543631231" + "Somedouble","[NULL]","[NULL]",3.141500,"real","3.1415" + "ping","[NULL]","pong","[NULL]","string","pong" + "somepointer",3285933758964,"[NULL]","[NULL]","pointer","0x2fd10ea19f4" + "someotherpointer",43981,"[NULL]","[NULL]","pointer","0xabcd" + "somekoid",18,"[NULL]","[NULL]","int","18" + "somebool",1,"[NULL]","[NULL]","bool","true" + "someotherbool",0,"[NULL]","[NULL]","bool","false" + """)) 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 41cf687d4..22045da50 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 @@ android_jank_cuj { apk_version_code: 1 debuggable: false } + pid: 1000 } ts: 0 dur: 123000000 @@ -27,6 +28,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 16000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 2 @@ -36,6 +39,8 @@ android_jank_cuj { app_missed: true sf_missed: true dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 3 @@ -45,6 +50,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 4 @@ -54,6 +61,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 5 @@ -63,6 +72,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 6 @@ -72,6 +83,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } sf_frame { frame_number: 1 @@ -127,6 +140,8 @@ android_jank_cuj { missed_app_frames: 5 missed_sf_frames: 1 frame_dur_max: 40000000 + sf_callback_missed_frames: 0 + hwui_callback_missed_frames: 0 } timeline_metrics { total_frames: 6 @@ -143,6 +158,8 @@ android_jank_cuj { frame_dur_ms_p90: 34.0 frame_dur_ms_p95: 37.0 frame_dur_ms_p99: 39.400000000000006 + sf_callback_missed_frames: 0 + hwui_callback_missed_frames: 0 } trace_metrics { total_frames: 6 @@ -159,6 +176,8 @@ android_jank_cuj { frame_dur_ms_p90: 34.0 frame_dur_ms_p95: 37.0 frame_dur_ms_p99: 39.400000000000006 + sf_callback_missed_frames: 0 + hwui_callback_missed_frames: 0 } } cuj { @@ -178,6 +197,7 @@ android_jank_cuj { apk_version_code: 1 debuggable: false } + pid: 1000 } ts: 0 dur: 901000010 @@ -189,6 +209,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 16000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 2 @@ -198,6 +220,8 @@ android_jank_cuj { app_missed: true sf_missed: true dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 3 @@ -207,6 +231,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 4 @@ -216,6 +242,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 5 @@ -225,6 +253,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 6 @@ -234,6 +264,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 7 @@ -243,6 +275,8 @@ android_jank_cuj { app_missed: false sf_missed: true dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 8 @@ -252,6 +286,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 9 @@ -261,6 +297,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 10 @@ -270,6 +308,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 11 @@ -279,6 +319,8 @@ android_jank_cuj { app_missed: true sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 12 @@ -288,6 +330,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 20000000 + sf_callback_missed: false + hwui_callback_missed: false } frame { frame_number: 13 @@ -297,6 +341,8 @@ android_jank_cuj { app_missed: false sf_missed: false dur_expected: 20000000 + sf_callback_missed: true + hwui_callback_missed: true } sf_frame { frame_number: 1 @@ -361,6 +407,8 @@ android_jank_cuj { missed_sf_frames: 2 missed_frames_max_successive: 5 frame_dur_max: 62000000 + sf_callback_missed_frames: 1 + hwui_callback_missed_frames: 1 } timeline_metrics { total_frames: 13 @@ -377,6 +425,8 @@ android_jank_cuj { frame_dur_ms_p90: 56.80000000000001 frame_dur_ms_p95: 61.000000 frame_dur_ms_p99: 61.000000 + sf_callback_missed_frames: 1 + hwui_callback_missed_frames: 1 } trace_metrics { total_frames: 13 @@ -393,6 +443,8 @@ android_jank_cuj { frame_dur_ms_p90: 60.000000 frame_dur_ms_p95: 61.000000 frame_dur_ms_p99: 61.000000 + sf_callback_missed_frames: 1 + hwui_callback_missed_frames: 1 } } } diff --git a/test/trace_processor/diff_tests/graphics/android_jank_cuj.py b/test/trace_processor/diff_tests/graphics/android_jank_cuj.py index 830d6ac46..beb39f72b 100644 --- a/test/trace_processor/diff_tests/graphics/android_jank_cuj.py +++ b/test/trace_processor/diff_tests/graphics/android_jank_cuj.py @@ -227,6 +227,17 @@ trace.add_track_event_slice_begin(ts=10, track=SHADE_CUJ_TRACK, name="J<SHADE_ROW_EXPAND>") trace.add_track_event_slice_end(ts=901_000_010, track=SHADE_CUJ_TRACK) add_instant_for_track(trace, ts=11, track=SHADE_CUJ_TRACK, name="FT#layerId#0") +add_instant_for_track( + trace, + ts=950_100_000, + track=SHADE_CUJ_TRACK, + name="FT#MissedHWUICallback#150") +add_instant_for_track( + trace, + ts=950_100_000, + track=SHADE_CUJ_TRACK, + name="FT#MissedSFCallback#150") + trace.add_track_event_slice_begin( ts=100_100_000, track=CANCELED_CUJ_TRACK, name="J<CANCELED>") trace.add_track_event_slice_end(ts=999_000_000, track=CANCELED_CUJ_TRACK) diff --git a/test/trace_processor/diff_tests/graphics/frame_missed.py b/test/trace_processor/diff_tests/graphics/frame_missed.py index d08191d68..04469b7d4 100644 --- a/test/trace_processor/diff_tests/graphics/frame_missed.py +++ b/test/trace_processor/diff_tests/graphics/frame_missed.py @@ -28,12 +28,12 @@ trace.add_process(11, 10, "child_process") trace.add_ftrace_packet(1) -trace.add_print(ts=99, tid=11, buf='C|10|PrevFrameMissed|0') -trace.add_print(ts=100, tid=11, buf='C|10|PrevFrameMissed|0') -trace.add_print(ts=101, tid=11, buf='C|10|PrevFrameMissed|1') -trace.add_print(ts=102, tid=11, buf='C|10|PrevFrameMissed|0') -trace.add_print(ts=103, tid=11, buf='C|10|PrevFrameMissed|1') -trace.add_print(ts=104, tid=11, buf='C|10|PrevFrameMissed|1') -trace.add_print(ts=105, tid=11, buf='C|10|PrevFrameMissed|0') +trace.add_print(ts=99, tid=11, buf='C|10|PrevFrameMissed 101|0') +trace.add_print(ts=100, tid=11, buf='C|10|PrevFrameMissed 102|0') +trace.add_print(ts=101, tid=11, buf='C|10|PrevFrameMissed 102|1') +trace.add_print(ts=102, tid=11, buf='C|10|PrevFrameMissed 102|0') +trace.add_print(ts=103, tid=11, buf='C|10|PrevFrameMissed 101|1') +trace.add_print(ts=104, tid=11, buf='C|10|PrevFrameMissed 101|1') +trace.add_print(ts=105, tid=11, buf='C|10|PrevFrameMissed 101|0') sys.stdout.buffer.write(trace.trace.SerializeToString()) diff --git a/test/trace_processor/diff_tests/graphics/tests.py b/test/trace_processor/diff_tests/graphics/tests.py index aea2818ee..7ab6c8901 100644 --- a/test/trace_processor/diff_tests/graphics/tests.py +++ b/test/trace_processor/diff_tests/graphics/tests.py @@ -111,6 +111,20 @@ class Graphics(TestSuite): missed_gpu_frames: 0 missed_frame_rate: 0.42857142857142855 # = 3/7 gpu_invocations: 0 + metrics_per_display: { + display_id: "101" + missed_frames: 2 + missed_hwc_frames: 0 + missed_gpu_frames: 0 + missed_frame_rate: 0.5 + } + metrics_per_display: { + display_id: "102" + missed_frames: 1 + missed_hwc_frames: 0 + missed_gpu_frames: 0 + missed_frame_rate: 0.33333333333333333 + } } """)) diff --git a/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql b/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql index 7fe9c9d8f..481f3f5f1 100644 --- a/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql +++ b/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql @@ -13,4 +13,7 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -SELECT ts, name, cpu, key, int_value, string_value FROM raw JOIN args ON raw.arg_set_id = args.arg_set_id WHERE name = "sched_waking" ORDER BY cpu ASC, ts ASC; +SELECT ts, name, cpu, key, int_value, string_value +FROM ftrace_event JOIN args USING(arg_set_id) +WHERE name = "sched_waking" +ORDER BY cpu ASC, ts ASC; diff --git a/test/trace_processor/diff_tests/parsing/tests.py b/test/trace_processor/diff_tests/parsing/tests.py index 609bdf182..41abe1cb9 100644 --- a/test/trace_processor/diff_tests/parsing/tests.py +++ b/test/trace_processor/diff_tests/parsing/tests.py @@ -175,7 +175,7 @@ class Parsing(TestSuite): trace=DataPath('lmk_userspace.pb'), query=""" SELECT to_ftrace(id) - FROM raw; + FROM ftrace_event; """, out=Path('print_systrace_lmk_userspace.out')) @@ -272,7 +272,7 @@ class Parsing(TestSuite): trace=Path('print_systrace_unsigned.py'), query=""" SELECT to_ftrace(id) - FROM raw; + FROM ftrace_event; """, out=Path('print_systrace_unsigned.out')) @@ -299,7 +299,7 @@ class Parsing(TestSuite): """), query=""" SELECT to_ftrace(id) - FROM raw; + FROM ftrace_event; """, out=Path('cgroup_attach_task_pre_s_print_systrace.out')) @@ -326,7 +326,7 @@ class Parsing(TestSuite): """), query=""" SELECT to_ftrace(id) - FROM raw; + FROM ftrace_event; """, out=Path('cgroup_attach_task_post_s_print_systrace.out')) @@ -919,7 +919,7 @@ class Parsing(TestSuite): trace=Path('sched_blocked_reason_symbolized.textproto'), query=""" SELECT to_ftrace(id) AS line - FROM raw; + FROM ftrace_event; """, out=Path('sched_blocked_reason_symbolized_to_systrace.out')) 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 eac9136a9..1a034f009 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 @@ android_frame_timeline_metric { process { process { name: "process1" + pid: 1001 } total_frames: 2 missed_frames: 2 @@ -27,6 +28,7 @@ android_frame_timeline_metric { process { process { name: "process2" + pid: 1002 } total_frames: 2 missed_frames: 1 @@ -48,6 +50,7 @@ android_frame_timeline_metric { process { process { name: "process3" + pid: 1003 } total_frames: 2 missed_frames: 2 @@ -69,6 +72,7 @@ android_frame_timeline_metric { 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 869604cb2..5085cd24c 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 @@ class PowerPowerRails(TestSuite): 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 b3decefb9..a6e314b77 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 @@ packet { 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 1ee09bf04..9a786535e 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 @@ java_heap_stats { process { name: "proc1" uid: 1000 + pid: 2 } samples { ts: 200000000 @@ -28,6 +29,7 @@ java_heap_stats { 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 206dc7ccb..c5aaf4924 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 @@ java_heap_histogram { 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 e56fbffae..4c5c2e313 100644 --- a/test/trace_processor/diff_tests/profiling/tests.py +++ b/test/trace_processor/diff_tests/profiling/tests.py @@ -116,6 +116,7 @@ class Profiling(TestSuite): 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 cb210cfea..9a4ff07c3 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 @@ class ProfilingMetrics(TestSuite): process { name: "system_server" uid: 1000 + pid: 2 } samples { ts: 10 @@ -81,6 +82,7 @@ class ProfilingMetrics(TestSuite): 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/startup/android_startup.out b/test/trace_processor/diff_tests/startup/android_startup.out index 48fa752ba..18b879c9d 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 @@ android_startup { 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 e55fe5574..ea96296b7 100644 --- a/test/trace_processor/diff_tests/startup/android_startup_attribution.out +++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.out @@ -25,8 +25,8 @@ android_startup { } dur_ms: 999.9999 time_dex_open { - dur_ns: 20 - dur_ms: 2e-05 + dur_ns: 499999845 + dur_ms: 499.999845 } time_verify_class { dur_ns: 40 @@ -45,6 +45,14 @@ android_startup { dur_ns: 50 dur_ms: 5e-05 } + time_dex_open_thread_main { + dur_ns: 499999845 + dur_ms: 499.999845 + } + time_dlopen_thread_main { + dur_ns: 2 + dur_ms: 2e-06 + } } activity_hosting_process_count: 1 process { @@ -60,6 +68,7 @@ android_startup { apk_version_code: 123 debuggable: false } + pid: 3 } event_timestamps { intent_received: 100 @@ -106,8 +115,11 @@ android_startup { 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*" slow_start_reason: "Main Thread - Binder transactions blocked" } } 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 b92120194..59301e05e 100644 --- a/test/trace_processor/diff_tests/startup/android_startup_attribution.py +++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.py @@ -78,13 +78,22 @@ trace.add_atrace_end(ts=165, pid=APP_PID, tid=APP_TID) trace.add_atrace_begin( ts=170, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something else)') -trace.add_atrace_end(ts=175, pid=APP_PID, tid=APP_TID) +trace.add_atrace_end(ts=5*10**8, pid=APP_PID, tid=APP_TID) # OpenDex slice outside the startup. trace.add_atrace_begin( 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 368b7f251..4bf3fff3e 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 @@ -45,6 +45,10 @@ android_startup { dur_ns: 50000000000 dur_ms: 50000 } + time_dex_open_thread_main { + dur_ns: 20000000000 + dur_ms: 20000.0 + } } activity_hosting_process_count: 1 process { @@ -60,6 +64,7 @@ android_startup { 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 36aab5fac..2f930d3f3 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 @@ android_startup { apk_version_code: 123 debuggable: false } + pid: 3 } activities { name: "com.google.android.calendar.MainActivity" @@ -107,7 +108,7 @@ android_startup { slow_start_reason: "Time spent in bindApplication" slow_start_reason: "Time spent in view inflation" slow_start_reason: "Time spent in ResourcesManager#getResources" - slow_start_reason: "Potential CPU contention with init" + slow_start_reason: "Potential CPU contention with another process" startup_type: "cold" } } 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 0b17116d0..441709154 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 @@ android_startup { apk_version_code: 123 debuggable: false } + pid: 3 } activities { name: "com.google.android.calendar.MainActivity" @@ -106,7 +107,7 @@ android_startup { slow_start_reason: "Time spent in bindApplication" slow_start_reason: "Time spent in view inflation" slow_start_reason: "Time spent in ResourcesManager#getResources" - slow_start_reason: "Potential CPU contention with init" + slow_start_reason: "Potential CPU contention with another process" startup_type: "cold" } } 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 e570b2212..ba1311c7f 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 @@ android_startup { 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 cd5dc6c83..aa4ec05bf 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 @@ android_startup { 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 1723d34fb..2a8307b07 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 @@ android_startup { 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 41b26e5e2..561fb0d2e 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 @@ android_startup { apk_version_code: 123 debuggable: false } + pid: 3 } event_timestamps { intent_received: 100 @@ -118,6 +119,7 @@ android_startup { 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 3b5d1ab18..f791fd655 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 @@ android_startup { apk_version_code: 123 debuggable: false } + pid: 3 } report_fully_drawn { dur_ns: 198000000000 @@ -66,6 +67,6 @@ android_startup { slow_start_reason: "Main Thread - Time spent in Runnable state" slow_start_reason: "Main Thread - Time spent in interruptible sleep state" slow_start_reason: "Main Thread - Time spent in Blocking I/O" - slow_start_reason: "Potential CPU contention with init" + slow_start_reason: "Potential CPU contention with another process" } } diff --git a/third_party/.gitignore b/third_party/.gitignore new file mode 100644 index 000000000..16428546e --- /dev/null +++ b/third_party/.gitignore @@ -0,0 +1,3 @@ +clang-format/ +gn/ +ninja/ diff --git a/tools/cpu_profile b/tools/cpu_profile index 07e795a19..1dfdbb762 100755 --- a/tools/cpu_profile +++ b/tools/cpu_profile @@ -37,18 +37,18 @@ import uuid # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACECONV_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'traceconv', 'file_size': - 7822272, + 7904536, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/traceconv', 'sha256': - '2b1bae4755ee0dd7f3a8e55653f8a7c344f688ea29700064ef8211c55bb4ae9f', + '037f84ac943f3f4d75447c668cc49c966fe3d85eca3a455c958b24fc6a9e314a', 'platform': 'darwin', 'machine': ['x86_64'] @@ -58,11 +58,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6604056, + 6554600, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/traceconv', 'sha256': - 'c0bd6d1ebe2c61ffeefbd4f01426e9b853c81daf70530be7e78c97a4d3af100c', + 'eda545ef4fa37fdfa1b47ced7cbbe0aa3c0df9bd161cacd7c78e6c55aef98d20', 'platform': 'darwin', 'machine': ['arm64'] @@ -72,11 +72,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 8122112, + 7664384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/traceconv', 'sha256': - 'c4c57d8e7b435822a1437b2dc7f7154f6ff2e197deff1f9284bbd36bbedb004f', + '24285e6e0e873d393fa5a993bac18ec8e1ab5fae6f4e3453214e095ef36e4c45', 'platform': 'linux', 'machine': ['x86_64'] @@ -86,11 +86,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6692016, + 5657944, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/traceconv', 'sha256': - 'da666eb9f80bcbec4c959f4adf493a59ff89e4106666fe1884291078dba0243b', + 'c9af3d976f849fc75e96c2c552cb14fcc9eacce6fe7c45c4a8289080b0f66706', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -100,11 +100,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 7575344, + 7184224, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/traceconv', 'sha256': - '8ca00c39c5ec7bd78576f64c4ab05e663d803b06b36fbddf968825edbe236fca', + 'c6dc936492d58a40cd8e0b58abc46bd479e0c1c387cd1ba29198a6c9b2000d7a', 'platform': 'linux', 'machine': ['aarch64'] @@ -114,55 +114,55 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 5376396, + 5325260, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/traceconv', 'sha256': - 'b77e7f0274ba45ff32d34df347845bc996763291fcc6b2a697f56c0c9a543150' + '963267dcb58cdde9f61a952e5cb7f3557833209d3251e7fdcefc3b52db54f77b' }, { 'arch': 'android-arm64', 'file_name': 'traceconv', 'file_size': - 6793744, + 6572688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/traceconv', 'sha256': - 'e83f3d43f8782cb57e9d3e8a3cd31826c9713da9f92bd8d8be2c48872ed423eb' + '87373c351fe5e947826cd957438cab8a37a352bf83b1cbbb15fe276eee9d873a' }, { 'arch': 'android-x86', 'file_name': 'traceconv', 'file_size': - 7694692, + 7303588, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/traceconv', 'sha256': - 'c9ee2c3c91d6c68cb7f52a626767bde5e267f34c6ddf987ff73eec3d813c0a2c' + 'dfc4e714963b5ed662d29d6028ffa69e67f8cd2f9a28223f715437a260fd456f' }, { 'arch': 'android-x64', 'file_name': 'traceconv', 'file_size': - 7940680, + 7482056, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/traceconv', 'sha256': - 'f75122ca3e6bbe393b705c3bc5514d81c57f38bf408d857d89c4268b79a39e08' + '79c666c629fcffd810635270b45e58b40ed253d22650f41550057e5d8f8c49a7' }, { 'arch': 'windows-amd64', 'file_name': 'traceconv.exe', 'file_size': - 7239168, + 7072768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/traceconv.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/traceconv.exe', 'sha256': - '5be5d698f69d44b4baa8ae1f21955becad9d0e6774e967f923b8386744002cfe', + '40fac80fdeae443a924e160650c94629e6463c1fb5a4f04f4ef6e9e5e72a3965', 'platform': 'win32', 'machine': ['amd64'] diff --git a/tools/gen_amalgamated b/tools/gen_amalgamated index c52915582..fac7f60f0 100755 --- a/tools/gen_amalgamated +++ b/tools/gen_amalgamated @@ -44,6 +44,7 @@ default_targets = [ # 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 d0eb99f9c..531791499 100755 --- a/tools/gen_android_bp +++ b/tools/gen_android_bp @@ -819,10 +819,9 @@ def create_tp_tables_module(blueprint: Blueprint, gn: GnParser, module.cmd = ' '.join([ f'$(location {bp_binary_module_name})', '--gen-dir=$(genDir)', + '--relative-input-dir=external/perfetto', '--inputs', '$(in)', - '--outputs', - '$(out)', ]) module.out.update(target.outputs) module.genrule_headers.add(module.name) diff --git a/tools/gen_bazel b/tools/gen_bazel index a514fe3e7..7461e4d47 100755 --- a/tools/gen_bazel +++ b/tools/gen_bazel @@ -76,16 +76,23 @@ public_targets = [ # 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': { @@ -582,6 +589,9 @@ def gen_cc_tp_tables(target: GnParser.Target): label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_cc_tp_tables') label.comment = target.name label.srcs += (gn_utils.label_to_path(x) for x in target.sources) + label.deps += sorted(':' + get_bazel_label_name(x.name) + for x in target.transitive_deps + if x.name not in default_python_targets) label.outs += target.outputs return [label] @@ -784,6 +794,10 @@ exports_files(["NOTICE"]) 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_binary_descriptors b/tools/gen_binary_descriptors index 952e809e6..e09a5c4de 100755 --- a/tools/gen_binary_descriptors +++ b/tools/gen_binary_descriptors @@ -21,7 +21,6 @@ import re import argparse import tempfile import subprocess -import hashlib from compat import iteritems SOURCE_TARGET = [ @@ -36,13 +35,6 @@ ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) SCRIPT_PATH = 'tools/gen_binary_descriptors' -def hash_path(path): - hash = hashlib.sha1() - with open(os.path.join(ROOT_DIR, path), 'rb') as f: - hash.update(f.read()) - return hash.hexdigest() - - def find_protoc(): for root, _, files in os.walk(os.path.join(ROOT_DIR, 'out')): if 'protoc' in files: @@ -50,26 +42,7 @@ def find_protoc(): return None -def check(source, target): - assert os.path.exists(os.path.join(ROOT_DIR, target)), \ - 'Output file {} does not exist and so cannot be checked'.format(target) - - sha1_file = target + '.sha1' - assert os.path.exists(sha1_file), \ - 'SHA1 file {} does not exist and so cannot be checked'.format(sha1_file) - - with open(sha1_file, 'rb') as f: - s = f.read() - - hashes = re.findall(r'// SHA1\((.*)\)\n// (.*)\n', s.decode()) - assert sorted([SCRIPT_PATH, source]) == sorted([key for key, _ in hashes]) - for path, expected_sha1 in hashes: - actual_sha1 = hash_path(os.path.join(ROOT_DIR, path)) - assert actual_sha1 == expected_sha1, \ - 'In {} hash given for {} did not match'.format(target, path) - - -def generate(source, target, protoc_path): +def generate(source, target, protoc_path, check_only): # delete=False + manual unlink is required for Windows. Otherwise the temp # file is kept locked exclusively and unaccassible until it's destroyed. with tempfile.NamedTemporaryFile(delete=False) as fdescriptor: @@ -86,23 +59,17 @@ def generate(source, target, protoc_path): s = fdescriptor.read() fdescriptor.close() os.remove(fdescriptor.name) + + if check_only: + with open(target, 'rb') as old: + old_content = old.read() + if (s != old_content): + raise AssertionError('Target {} does not match', target) + return + with open(target, 'wb') as out: out.write(s) - sha1_path = target + '.sha1' - with open(sha1_path, 'wb') as c: - c.write(""" -// SHA1({script_path}) -// {script_hash} -// SHA1({source_path}) -// {source_hash} - """.format( - script_path=SCRIPT_PATH, - script_hash=hash_path(__file__), - source_path=source, - source_hash=hash_path(os.path.join(source)), - ).encode()) - def main(): parser = argparse.ArgumentParser() @@ -112,15 +79,12 @@ def main(): try: for source, target in SOURCE_TARGET: - if args.check_only: - check(source, target) - else: - protoc = args.protoc or find_protoc() - assert protoc, 'protoc not found specific (--protoc PROTOC_PATH)' - assert os.path.exists(protoc), '{} does not exist'.format(protoc) - if protoc is not args.protoc: - print('Using protoc: {}'.format(protoc)) - generate(source, target, protoc) + protoc = args.protoc or find_protoc() + assert protoc, 'protoc not found specific (--protoc PROTOC_PATH)' + assert os.path.exists(protoc), '{} does not exist'.format(protoc) + if protoc is not args.protoc: + print('Using protoc: {}'.format(protoc)) + generate(source, target, protoc, args.check_only) except AssertionError as e: if not str(e): raise diff --git a/tools/gen_tp_table_docs.py b/tools/gen_tp_table_docs.py index 8b8565aff..04339bfa0 100755 --- a/tools/gen_tp_table_docs.py +++ b/tools/gen_tp_table_docs.py @@ -27,6 +27,7 @@ sys.path.append(os.path.join(ROOT_DIR)) #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 @@ def gen_json_for_column(table: ParsedTable, 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. @@ -59,7 +64,9 @@ def gen_json_for_column(table: ParsedTable, raise Exception('Unknown column documentation type ' f'{table.table.class_name}::{col.column.name}') - parsed_type = table.parse_type(col.column.type) + parsed_type = util.parse_type_with_cols(table.table, + [c.column for c in table.columns], + col.column.type) docs_type = parsed_type.cpp_type if docs_type == 'StringPool::Id': docs_type = 'string' @@ -88,11 +95,20 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--out', required=True) parser.add_argument('inputs', nargs='*') + parser.add_argument('--relative-input-dir') args = parser.parse_args() - tables = util.parse_tables_from_files(args.inputs) + def get_relin_path(in_path: str): + if not args.relative_input_dir: + return in_path + return os.path.relpath(in_path, args.relative_input_dir) + + modules = [ + os.path.splitext(get_relin_path(i).replace('/', '.'))[0] + for i in args.inputs + ] table_docs = [] - for parsed in tables: + for parsed in util.parse_tables_from_modules(modules): table = parsed.table doc = table.tabledoc assert doc diff --git a/tools/gen_tp_table_headers.py b/tools/gen_tp_table_headers.py index 1ce096354..f15245ef8 100755 --- a/tools/gen_tp_table_headers.py +++ b/tools/gen_tp_table_headers.py @@ -28,63 +28,71 @@ sys.path.append(os.path.join(ROOT_DIR)) #pylint: disable=wrong-import-position from python.generators.trace_processor_table.serialize import serialize_header +from python.generators.trace_processor_table.util import find_table_deps from python.generators.trace_processor_table.util import ParsedTable -from python.generators.trace_processor_table.util import parse_tables_from_files +from python.generators.trace_processor_table.util import parse_tables_from_modules #pylint: enable=wrong-import-position +# Suffix which replaces the .py extension for all input modules. +OUT_HEADER_SUFFIX = '_py.h' + @dataclass class Header: """Represents a Python module which will be converted to a header.""" - out_path: str - relout_path: str tables: List[ParsedTable] def main(): """Main function.""" parser = argparse.ArgumentParser() - parser.add_argument('--gen-dir', required=True) parser.add_argument('--inputs', required=True, nargs='*') - parser.add_argument('--outputs', required=True, nargs='*') - parser.add_argument('--header-prefix') + parser.add_argument('--gen-dir', required=True) + parser.add_argument('--relative-input-dir') + parser.add_argument('--import-prefix', default='') args = parser.parse_args() - if len(args.inputs) != len(args.outputs): - raise Exception('Number of inputs must match number of outputs') + def get_relin_path(in_path: str): + if not args.relative_input_dir: + return in_path + return os.path.relpath(in_path, args.relative_input_dir) - header_prefix = args.header_prefix if args.header_prefix else '' - in_to_out = dict(zip(args.inputs, args.outputs)) - headers: Dict[str, Header] = {} - for table in parse_tables_from_files(args.inputs): - out_path = in_to_out[table.input_path] - relout_path = os.path.join(header_prefix, - os.path.relpath(out_path, args.gen_dir)) + def get_relout_path(in_path: str): + return os.path.splitext(in_path)[0] + OUT_HEADER_SUFFIX - header = headers.get(table.input_path, Header(out_path, relout_path, [])) + def get_out_path(in_path: str): + return os.path.join(args.gen_dir, get_relout_path(in_path)) + + def get_header_path(in_path: str): + return os.path.join(args.import_prefix, get_relout_path(in_path)) + + modules = [ + os.path.splitext(get_relin_path(i).replace(os.sep, '.'))[0] + for i in args.inputs + ] + headers: Dict[str, Header] = {} + for table in parse_tables_from_modules(modules): + input_path = os.path.relpath(table.table.python_module, ROOT_DIR) + header = headers.get(input_path, Header([])) header.tables.append(table) - headers[table.input_path] = header + headers[input_path] = header - # Build a mapping from table class name to the output path of the header - # which will be generated for it. This is used to include one header into - # another for Id dependencies. - table_class_name_to_relout: Dict[str, str] = {} - for header in headers.values(): - for table in header.tables: - table_class_name_to_relout[table.table.class_name] = header.relout_path + for in_path, header in headers.items(): + out_path = get_out_path(in_path) + relout_path = get_relout_path(in_path) - for header in headers.values(): # Find all headers depended on by this table. These will be #include-ed when # generating the header file below so ensure we remove ourself. header_relout_deps: Set[str] = set() for table in header.tables: - header_relout_deps = header_relout_deps.union( - [table_class_name_to_relout[c] for c in table.find_table_deps()]) - header_relout_deps.discard(header.relout_path) + header_relout_deps = header_relout_deps.union([ + get_header_path(os.path.relpath(c.python_module, ROOT_DIR)) + for c in find_table_deps(table.table) + ]) + header_relout_deps.discard(relout_path) - with open(header.out_path, 'w', encoding='utf8') as out: - ifdef_guard = re.sub(r'[^a-zA-Z0-9_-]', '_', - header.relout_path).upper() + '_' + with open(out_path, 'w', encoding='utf8') as out: + ifdef_guard = re.sub(r'[^a-zA-Z0-9_-]', '_', relout_path).upper() + '_' out.write( serialize_header(ifdef_guard, header.tables, sorted(header_relout_deps))) diff --git a/tools/heap_profile b/tools/heap_profile index 40e7f403d..6e32c4bfd 100755 --- a/tools/heap_profile +++ b/tools/heap_profile @@ -34,18 +34,18 @@ import uuid # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACECONV_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'traceconv', 'file_size': - 7822272, + 7904536, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/traceconv', 'sha256': - '2b1bae4755ee0dd7f3a8e55653f8a7c344f688ea29700064ef8211c55bb4ae9f', + '037f84ac943f3f4d75447c668cc49c966fe3d85eca3a455c958b24fc6a9e314a', 'platform': 'darwin', 'machine': ['x86_64'] @@ -55,11 +55,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6604056, + 6554600, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/traceconv', 'sha256': - 'c0bd6d1ebe2c61ffeefbd4f01426e9b853c81daf70530be7e78c97a4d3af100c', + 'eda545ef4fa37fdfa1b47ced7cbbe0aa3c0df9bd161cacd7c78e6c55aef98d20', 'platform': 'darwin', 'machine': ['arm64'] @@ -69,11 +69,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 8122112, + 7664384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/traceconv', 'sha256': - 'c4c57d8e7b435822a1437b2dc7f7154f6ff2e197deff1f9284bbd36bbedb004f', + '24285e6e0e873d393fa5a993bac18ec8e1ab5fae6f4e3453214e095ef36e4c45', 'platform': 'linux', 'machine': ['x86_64'] @@ -83,11 +83,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6692016, + 5657944, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/traceconv', 'sha256': - 'da666eb9f80bcbec4c959f4adf493a59ff89e4106666fe1884291078dba0243b', + 'c9af3d976f849fc75e96c2c552cb14fcc9eacce6fe7c45c4a8289080b0f66706', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -97,11 +97,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 7575344, + 7184224, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/traceconv', 'sha256': - '8ca00c39c5ec7bd78576f64c4ab05e663d803b06b36fbddf968825edbe236fca', + 'c6dc936492d58a40cd8e0b58abc46bd479e0c1c387cd1ba29198a6c9b2000d7a', 'platform': 'linux', 'machine': ['aarch64'] @@ -111,55 +111,55 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 5376396, + 5325260, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/traceconv', 'sha256': - 'b77e7f0274ba45ff32d34df347845bc996763291fcc6b2a697f56c0c9a543150' + '963267dcb58cdde9f61a952e5cb7f3557833209d3251e7fdcefc3b52db54f77b' }, { 'arch': 'android-arm64', 'file_name': 'traceconv', 'file_size': - 6793744, + 6572688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/traceconv', 'sha256': - 'e83f3d43f8782cb57e9d3e8a3cd31826c9713da9f92bd8d8be2c48872ed423eb' + '87373c351fe5e947826cd957438cab8a37a352bf83b1cbbb15fe276eee9d873a' }, { 'arch': 'android-x86', 'file_name': 'traceconv', 'file_size': - 7694692, + 7303588, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/traceconv', 'sha256': - 'c9ee2c3c91d6c68cb7f52a626767bde5e267f34c6ddf987ff73eec3d813c0a2c' + 'dfc4e714963b5ed662d29d6028ffa69e67f8cd2f9a28223f715437a260fd456f' }, { 'arch': 'android-x64', 'file_name': 'traceconv', 'file_size': - 7940680, + 7482056, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/traceconv', 'sha256': - 'f75122ca3e6bbe393b705c3bc5514d81c57f38bf408d857d89c4268b79a39e08' + '79c666c629fcffd810635270b45e58b40ed253d22650f41550057e5d8f8c49a7' }, { 'arch': 'windows-amd64', 'file_name': 'traceconv.exe', 'file_size': - 7239168, + 7072768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/traceconv.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/traceconv.exe', 'sha256': - '5be5d698f69d44b4baa8ae1f21955becad9d0e6774e967f923b8386744002cfe', + '40fac80fdeae443a924e160650c94629e6463c1fb5a4f04f4ef6e9e5e72a3965', 'platform': 'win32', 'machine': ['amd64'] diff --git a/tools/install-build-deps b/tools/install-build-deps index 255be950a..c72eae971 100755 --- a/tools/install-build-deps +++ b/tools/install-build-deps @@ -23,6 +23,7 @@ import subprocess import stat import sys import tempfile +import time import zipfile from collections import namedtuple @@ -60,6 +61,17 @@ CLEANUP_OLD_DIRS = [ '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 @@ BUILD_DEPS_TOOLCHAIN_HOST = [ # 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 @@ BUILD_DEPS_TOOLCHAIN_HOST = [ # 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 @@ BUILD_DEPS_TOOLCHAIN_HOST = [ # 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 @@ NODE_MODULES_STATUS_FILE = os.path.join(UI_DIR, 'node_modules', '.last_install') 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 @@ def RmtreeIfExists(path): 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 @@ def CheckoutGitRepo(path, git_url, revision, check_only): 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 @@ def CheckHashes(): 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 @@ def Main(): 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 65f215da3..a9db452ff 100755 --- a/tools/record_android_trace +++ b/tools/record_android_trace @@ -33,18 +33,18 @@ import webbrowser # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACEBOX_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'tracebox', 'file_size': - 1415776, + 1432064, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/tracebox', 'sha256': - '860cccef002f1a7216d301a09b97d7276b8a57c8d85ad1c3aa4697bb115ffca7', + '4ceb7646cd99303224ab5e7ff0a9f84c04f3c5466fff65a55dab65171ae9d482', 'platform': 'darwin', 'machine': ['x86_64'] @@ -54,11 +54,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1309272, + 1325704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/tracebox', 'sha256': - '9c079ac561064c33e9bdfe2e23e92fb95c025603e545c1aae31b2bd7de0398ad', + '2c560fcce5e19eb692e50487af134e2078347cdb79decba0c572917860528388', 'platform': 'darwin', 'machine': ['arm64'] @@ -68,11 +68,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2137040, + 2155496, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/tracebox', 'sha256': - '9eb9ce1a14432c284fecce7886786bb2555bcb6dfb4f00a2df2885984961a5fc', + '10b92180bb461a7e21be3f8b3d4640430a98d0547238ce095709213b378217d2', 'platform': 'linux', 'machine': ['x86_64'] @@ -82,11 +82,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1277896, + 1288764, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/tracebox', 'sha256': - '60c71b39be7e04d9d0278e36e7e4d33c32a03d6cc8a3782a9e5ed2484f3f2082', + 'fa28950ce2b7a9345fbb9272f2dd04d3d4eb2a87f021df25e1e649840eae60b5', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -96,11 +96,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2065704, + 2082704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/tracebox', 'sha256': - 'a8d9e9e186b5daf45ec80b975b9a3ad04cb578890beda136b821c80b9cc74995', + '85c371d79b8e23d22a293c29e6399dc311d891a6bd85d7eeaf2cb0179c69eb27', 'platform': 'linux', 'machine': ['aarch64'] @@ -110,44 +110,44 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1161172, + 1169364, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/tracebox', 'sha256': - 'f9dac5df26d471d1cf0aff942d7249da6b4122543e003813203ef128a15f93fd' + '40a3f31600f02dea10e290134d5c862e0e717f4f039756889a4e72c60f1591b6' }, { 'arch': 'android-arm64', 'file_name': 'tracebox', 'file_size': - 1764008, + 1776296, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/tracebox', 'sha256': - '3b325a09b6efae0939b73d4d74e6e01e3735508ed31b774f3a21765efab95099' + '562505fca18b34a97687dc002aeebcbf20acef68c8a8e48bed6d618c20e07c92' }, { 'arch': 'android-x86', 'file_name': 'tracebox', 'file_size': - 1755052, + 1767340, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/tracebox', 'sha256': - '86e31fa7e2b476187a0222ac2cf6a4ee7e5f8fb5b0e019c1349d14534343a581' + 'eb47eb43ba93403557dd15a61196799e945ec324d96109db2f155fb131f9996a' }, { 'arch': 'android-x64', 'file_name': 'tracebox', 'file_size': - 2034344, + 2054824, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/tracebox', 'sha256': - '7692f6ceaa5d2eb9da42486262610a1820a9b31d46255f624407bf712eff021d' + 'a3ae6d108e041ba368a9770f952772f111865d4eff7c8e4e4e2f653f45017948' }] # ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py diff --git a/tools/run_buildtools_binary.py b/tools/run_buildtools_binary.py index 6fcfe1a30..1ce25a748 100644 --- a/tools/run_buildtools_binary.py +++ b/tools/run_buildtools_binary.py @@ -46,7 +46,13 @@ def run_buildtools_binary(args): 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/tools/trace_processor b/tools/trace_processor index 96d5fa337..1ed7c63fc 100755 --- a/tools/trace_processor +++ b/tools/trace_processor @@ -30,18 +30,18 @@ exec python3 - "$@" <<'#'EOF # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/trace_processor_shell.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'trace_processor_shell', 'file_size': - 8583152, + 8714576, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/trace_processor_shell', 'sha256': - '35673d3546dec894b5d55147da2fad523a8f5917b42ec1c327c940b82d3ce565', + '9bdb89493f0f00db5d3a73166450ac2f6ee830de16415e79c5a0234990caa644', 'platform': 'darwin', 'machine': ['x86_64'] @@ -51,11 +51,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 7303384, + 7286968, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell', 'sha256': - 'a4d301cf8c0c01d328a9253d5ba78f4249333d4b04236cf8be0c7dad2a65e7e0', + '948536035fbe680b47b94a99d320ff459450738e4aeeb16cef18364f0023622b', 'platform': 'darwin', 'machine': ['arm64'] @@ -65,11 +65,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 8991600, + 8576688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/trace_processor_shell', 'sha256': - 'e8dd82c1ec73fbbf4165ab0d9cbbb750cff5bcf723a1eab51adc9382bf652361', + '493698c81fffcabc340c72831b175962dba5a31dfe8572a6d5af083a116af4f8', 'platform': 'linux', 'machine': ['x86_64'] @@ -79,11 +79,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 7117104, + 6125384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/trace_processor_shell', 'sha256': - 'bbfde44ec004815a36cecdc1dbc135f815f46ac6a3989c87cb0c577510c1c8fe', + '53f1e27603695cf92d22519993b6eafa9c60957d9cb33bd0b300df8573b87ebb', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -93,11 +93,11 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 8384816, + 8036288, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/trace_processor_shell', 'sha256': - 'c4f9a499e8c443961725448aabc04cd7dc18cb79883f6b8b615fd8f4ed7c8c16', + '2a2cda222c9d5e18b638057688babb00a3a975ccd4b7dd65f26211c2cb7767f9', 'platform': 'linux', 'machine': ['aarch64'] @@ -107,55 +107,55 @@ TRACE_PROCESSOR_SHELL_MANIFEST = [{ 'file_name': 'trace_processor_shell', 'file_size': - 5823560, + 5813384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/trace_processor_shell', 'sha256': - 'ec5d23fc761021fe10a7cdb66d35590dab0216b2305f5163ace98da28b535fb8' + 'f3ec4c194d0b06af5b296c1c479e6b29090e6b7cc7e58fbd55ca2919a126f0ee' }, { 'arch': 'android-arm64', 'file_name': 'trace_processor_shell', 'file_size': - 7474864, + 7294768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/trace_processor_shell', 'sha256': - 'f4877f51d0fbb8e9ead576e746a7adf7806b5cb2dffc4373a55ceeec21f615ff' + 'f44f47d4b873ec68b6fa4f4c69a3e5a13d58b4d9cb2ec591fa687d4480c1950b' }, { 'arch': 'android-x86', 'file_name': 'trace_processor_shell', 'file_size': - 8436764, + 8090716, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/trace_processor_shell', 'sha256': - '68ad60af32890f903afb7cbee7cc8f0f4f4b18dea7ab077cb1d807ea80053dcb' + '5636d8251747376787640bc3a4894ecf3091e4bf3d38b007003e1992fc5792df' }, { 'arch': 'android-x64', 'file_name': 'trace_processor_shell', 'file_size': - 8781544, + 8359784, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/trace_processor_shell', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/trace_processor_shell', 'sha256': - 'edf5efca4cf46ffbd3586592490b14d61758198c7d46c1bc8e083b1ab19382f5' + '50440fa055ab998f6cf24f9a9a7388520cc854708735521505e10291bc52f3d0' }, { 'arch': 'windows-amd64', 'file_name': 'trace_processor_shell.exe', 'file_size': - 8252928, + 8130560, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/trace_processor_shell.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/trace_processor_shell.exe', 'sha256': - '323a210f857ce840c4d69dfa7f9b0a32501ffa4a856e5667a0916e3f8006a5d0', + '5cbcf98e29a2d989523235e11e4e0dade692a295ebf47a6c93a09a050ce9bc91', 'platform': 'win32', 'machine': ['amd64'] diff --git a/tools/tracebox b/tools/tracebox index d788baf39..bf03fa670 100755 --- a/tools/tracebox +++ b/tools/tracebox @@ -30,18 +30,18 @@ exec python3 - "$@" <<'#'EOF # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACEBOX_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'tracebox', 'file_size': - 1415776, + 1432064, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/tracebox', 'sha256': - '860cccef002f1a7216d301a09b97d7276b8a57c8d85ad1c3aa4697bb115ffca7', + '4ceb7646cd99303224ab5e7ff0a9f84c04f3c5466fff65a55dab65171ae9d482', 'platform': 'darwin', 'machine': ['x86_64'] @@ -51,11 +51,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1309272, + 1325704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/tracebox', 'sha256': - '9c079ac561064c33e9bdfe2e23e92fb95c025603e545c1aae31b2bd7de0398ad', + '2c560fcce5e19eb692e50487af134e2078347cdb79decba0c572917860528388', 'platform': 'darwin', 'machine': ['arm64'] @@ -65,11 +65,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2137040, + 2155496, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/tracebox', 'sha256': - '9eb9ce1a14432c284fecce7886786bb2555bcb6dfb4f00a2df2885984961a5fc', + '10b92180bb461a7e21be3f8b3d4640430a98d0547238ce095709213b378217d2', 'platform': 'linux', 'machine': ['x86_64'] @@ -79,11 +79,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1277896, + 1288764, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/tracebox', 'sha256': - '60c71b39be7e04d9d0278e36e7e4d33c32a03d6cc8a3782a9e5ed2484f3f2082', + 'fa28950ce2b7a9345fbb9272f2dd04d3d4eb2a87f021df25e1e649840eae60b5', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -93,11 +93,11 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 2065704, + 2082704, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/tracebox', 'sha256': - 'a8d9e9e186b5daf45ec80b975b9a3ad04cb578890beda136b821c80b9cc74995', + '85c371d79b8e23d22a293c29e6399dc311d891a6bd85d7eeaf2cb0179c69eb27', 'platform': 'linux', 'machine': ['aarch64'] @@ -107,44 +107,44 @@ TRACEBOX_MANIFEST = [{ 'file_name': 'tracebox', 'file_size': - 1161172, + 1169364, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/tracebox', 'sha256': - 'f9dac5df26d471d1cf0aff942d7249da6b4122543e003813203ef128a15f93fd' + '40a3f31600f02dea10e290134d5c862e0e717f4f039756889a4e72c60f1591b6' }, { 'arch': 'android-arm64', 'file_name': 'tracebox', 'file_size': - 1764008, + 1776296, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/tracebox', 'sha256': - '3b325a09b6efae0939b73d4d74e6e01e3735508ed31b774f3a21765efab95099' + '562505fca18b34a97687dc002aeebcbf20acef68c8a8e48bed6d618c20e07c92' }, { 'arch': 'android-x86', 'file_name': 'tracebox', 'file_size': - 1755052, + 1767340, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/tracebox', 'sha256': - '86e31fa7e2b476187a0222ac2cf6a4ee7e5f8fb5b0e019c1349d14534343a581' + 'eb47eb43ba93403557dd15a61196799e945ec324d96109db2f155fb131f9996a' }, { 'arch': 'android-x64', 'file_name': 'tracebox', 'file_size': - 2034344, + 2054824, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/tracebox', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/tracebox', 'sha256': - '7692f6ceaa5d2eb9da42486262610a1820a9b31d46255f624407bf712eff021d' + 'a3ae6d108e041ba368a9770f952772f111865d4eff7c8e4e4e2f653f45017948' }] # ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py diff --git a/tools/traceconv b/tools/traceconv index f838c6900..9298eb373 100755 --- a/tools/traceconv +++ b/tools/traceconv @@ -30,18 +30,18 @@ exec python3 - "$@" <<'#'EOF # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py -# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v33.1 +# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0 TRACECONV_MANIFEST = [{ 'arch': 'mac-amd64', 'file_name': 'traceconv', 'file_size': - 7822272, + 7904536, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/traceconv', 'sha256': - '2b1bae4755ee0dd7f3a8e55653f8a7c344f688ea29700064ef8211c55bb4ae9f', + '037f84ac943f3f4d75447c668cc49c966fe3d85eca3a455c958b24fc6a9e314a', 'platform': 'darwin', 'machine': ['x86_64'] @@ -51,11 +51,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6604056, + 6554600, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/mac-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/traceconv', 'sha256': - 'c0bd6d1ebe2c61ffeefbd4f01426e9b853c81daf70530be7e78c97a4d3af100c', + 'eda545ef4fa37fdfa1b47ced7cbbe0aa3c0df9bd161cacd7c78e6c55aef98d20', 'platform': 'darwin', 'machine': ['arm64'] @@ -65,11 +65,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 8122112, + 7664384, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-amd64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/traceconv', 'sha256': - 'c4c57d8e7b435822a1437b2dc7f7154f6ff2e197deff1f9284bbd36bbedb004f', + '24285e6e0e873d393fa5a993bac18ec8e1ab5fae6f4e3453214e095ef36e4c45', 'platform': 'linux', 'machine': ['x86_64'] @@ -79,11 +79,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 6692016, + 5657944, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/traceconv', 'sha256': - 'da666eb9f80bcbec4c959f4adf493a59ff89e4106666fe1884291078dba0243b', + 'c9af3d976f849fc75e96c2c552cb14fcc9eacce6fe7c45c4a8289080b0f66706', 'platform': 'linux', 'machine': ['armv6l', 'armv7l', 'armv8l'] @@ -93,11 +93,11 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 7575344, + 7184224, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/linux-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/traceconv', 'sha256': - '8ca00c39c5ec7bd78576f64c4ab05e663d803b06b36fbddf968825edbe236fca', + 'c6dc936492d58a40cd8e0b58abc46bd479e0c1c387cd1ba29198a6c9b2000d7a', 'platform': 'linux', 'machine': ['aarch64'] @@ -107,55 +107,55 @@ TRACECONV_MANIFEST = [{ 'file_name': 'traceconv', 'file_size': - 5376396, + 5325260, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/traceconv', 'sha256': - 'b77e7f0274ba45ff32d34df347845bc996763291fcc6b2a697f56c0c9a543150' + '963267dcb58cdde9f61a952e5cb7f3557833209d3251e7fdcefc3b52db54f77b' }, { 'arch': 'android-arm64', 'file_name': 'traceconv', 'file_size': - 6793744, + 6572688, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-arm64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/traceconv', 'sha256': - 'e83f3d43f8782cb57e9d3e8a3cd31826c9713da9f92bd8d8be2c48872ed423eb' + '87373c351fe5e947826cd957438cab8a37a352bf83b1cbbb15fe276eee9d873a' }, { 'arch': 'android-x86', 'file_name': 'traceconv', 'file_size': - 7694692, + 7303588, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x86/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/traceconv', 'sha256': - 'c9ee2c3c91d6c68cb7f52a626767bde5e267f34c6ddf987ff73eec3d813c0a2c' + 'dfc4e714963b5ed662d29d6028ffa69e67f8cd2f9a28223f715437a260fd456f' }, { 'arch': 'android-x64', 'file_name': 'traceconv', 'file_size': - 7940680, + 7482056, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/android-x64/traceconv', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/traceconv', 'sha256': - 'f75122ca3e6bbe393b705c3bc5514d81c57f38bf408d857d89c4268b79a39e08' + '79c666c629fcffd810635270b45e58b40ed253d22650f41550057e5d8f8c49a7' }, { 'arch': 'windows-amd64', 'file_name': 'traceconv.exe', 'file_size': - 7239168, + 7072768, 'url': - 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v33.1/windows-amd64/traceconv.exe', + 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/traceconv.exe', 'sha256': - '5be5d698f69d44b4baa8ae1f21955becad9d0e6774e967f923b8386744002cfe', + '40fac80fdeae443a924e160650c94629e6463c1fb5a4f04f4ef6e9e5e72a3965', 'platform': 'win32', 'machine': ['amd64'] diff --git a/ui/package-lock.json b/ui/package-lock.json index 7dc51bd84..1bd97da88 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 0f4d69bb4..1257ab7b2 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 fb05de45d..09f14f427 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 79ba00752..043542250 100644 --- a/ui/src/assets/common.scss +++ b/ui/src/assets/common.scss @@ -42,17 +42,14 @@ } @mixin track_shell_title() { - // line-height is deliberately 1px larger than font-size. Roboto seems to - // overflow on the bottom on "g"s otherwise. font-size: 14px; - line-height: 15px; max-height: 30px; overflow: hidden; text-align: left; overflow-wrap: break-word; font-family: "Roboto Condensed", sans-serif; font-weight: 300; - letter-spacing: -0.5px; + line-break: anywhere; } * { @@ -388,6 +385,7 @@ $bottom-tab-padding: 10px; width: var(--track-shell-width); background: #fff; border-right: 1px solid #c7d0db; + overflow: hidden; &.drag { background-color: #eee; @@ -462,7 +460,7 @@ $bottom-tab-padding: 10px; .scrolling-panel-container { position: relative; overflow-x: hidden; - overflow-y: auto; + overflow-y: scroll; // Always show vertical scrollbar flex: 1 1 auto; will-change: transform; // Force layer creation. display: grid; diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss index 9ba93b3b9..b5c131b86 100644 --- a/ui/src/assets/details.scss +++ b/ui/src/assets/details.scss @@ -293,6 +293,7 @@ .details-table-multicolumn { display: flex; + user-select: 'text'; } .flow-link:hover { diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss index 7a0057618..e4da14727 100644 --- a/ui/src/assets/perfetto.scss +++ b/ui/src/assets/perfetto.scss @@ -38,3 +38,4 @@ @import "widgets/spinner"; @import "widgets/tree"; @import "widgets/switch"; +@import "widgets/form"; diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss index e4820d8c4..187499f6e 100644 --- a/ui/src/assets/record.scss +++ b/ui/src/assets/record.scss @@ -399,6 +399,8 @@ line-height: 12.5px; margin-top: -5px; opacity: 0; + text-overflow: ellipsis; + white-space: nowrap; } &:hover { diff --git a/ui/src/base/math_utils.ts b/ui/src/assets/widgets/form.scss index f1c18165b..569df72fc 100644 --- a/ui/src/base/math_utils.ts +++ b/ui/src/assets/widgets/form.scss @@ -12,12 +12,27 @@ // 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); -} +.pf-form { + display: grid; + grid-template-columns: auto auto; + margin: 6px; + row-gap: 8px; + column-gap: 8px; + + .pf-form-button-bar { + grid-column: span 2; + margin-top: 6px; + display: flex; + justify-content: right; + flex-direction: row-reverse; + align-items: center; + + .pf-button { + margin-right: 4px; + } + } -// Round a number down to the nearest stepsize. -export function roundDownNearest(val: number, stepsize: number): number { - return stepsize * Math.floor(val / stepsize); + .pf-form-label { + font-weight: 600; + } } diff --git a/ui/src/assets/widgets/menu.scss b/ui/src/assets/widgets/menu.scss index e161d4cab..5bc6a9f55 100644 --- a/ui/src/assets/widgets/menu.scss +++ b/ui/src/assets/widgets/menu.scss @@ -22,10 +22,10 @@ .pf-menu-item { font-family: $pf-font; - font-size: inherit; + font-size: 14px; user-select: none; text-align: left; - padding: 6px 12px; + padding: 5px 10px; white-space: nowrap; min-width: max-content; cursor: pointer; diff --git a/ui/src/base/bigint_math.ts b/ui/src/base/bigint_math.ts new file mode 100644 index 000000000..1860afedd --- /dev/null +++ b/ui/src/base/bigint_math.ts @@ -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. + +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 multiple of step which is not larger than n. + // If step is less than or equal to 0, returns n. + static quantizeFloor(n: bigint, step: bigint): bigint { + step = BigintMath.max(1n, step); + return step * (n / step); + } + + // Return the integral multiple of step which is closest to n. + // If step is less than or equal to 0, returns n. + static quantize(n: bigint, step: bigint): bigint { + step = BigintMath.max(1n, step); + const halfStep = step / 2n; + return step * ((n + halfStep) / step); + } + + // Return the greater of a and b + static max(a: bigint, b: bigint): bigint { + return a > b ? a : b; + } + + // Return 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; + } +} diff --git a/ui/src/base/bigint_math_unittest.ts b/ui/src/base/bigint_math_unittest.ts new file mode 100644 index 000000000..291eb32ad --- /dev/null +++ b/ui/src/base/bigint_math_unittest.ts @@ -0,0 +1,141 @@ +// 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.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('BigIntMath.bigFloor', () => { + 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('quantize', () => { + it('should quantize a number to the nearest multiple of a stepsize', () => { + expect(BIM.quantizeFloor(10n, 2n)).toEqual(10n); + expect(BIM.quantizeFloor(11n, 2n)).toEqual(10n); + expect(BIM.quantizeFloor(12n, 2n)).toEqual(12n); + expect(BIM.quantizeFloor(13n, 2n)).toEqual(12n); + + expect(BIM.quantizeFloor(9n, 4n)).toEqual(8n); + expect(BIM.quantizeFloor(10n, 4n)).toEqual(8n); + expect(BIM.quantizeFloor(11n, 4n)).toEqual(8n); + expect(BIM.quantizeFloor(12n, 4n)).toEqual(12n); + expect(BIM.quantizeFloor(13n, 4n)).toEqual(12n); + }); + + it('should return value if stepsize is smaller than 1', () => { + expect(BIM.quantizeFloor(123n, 0n)).toEqual(123n); + expect(BIM.quantizeFloor(123n, -10n)).toEqual(123n); + }); +}); + +describe('quantizeRound', () => { + it('should quantize a number to the nearest multiple of a stepsize', () => { + expect(BIM.quantize(0n, 2n)).toEqual(0n); + expect(BIM.quantize(1n, 2n)).toEqual(2n); + expect(BIM.quantize(2n, 2n)).toEqual(2n); + expect(BIM.quantize(3n, 2n)).toEqual(4n); + expect(BIM.quantize(4n, 2n)).toEqual(4n); + + expect(BIM.quantize(0n, 3n)).toEqual(0n); + expect(BIM.quantize(1n, 3n)).toEqual(0n); + expect(BIM.quantize(2n, 3n)).toEqual(3n); + expect(BIM.quantize(3n, 3n)).toEqual(3n); + expect(BIM.quantize(4n, 3n)).toEqual(3n); + expect(BIM.quantize(5n, 3n)).toEqual(6n); + expect(BIM.quantize(6n, 3n)).toEqual(6n); + }); + + it('should return value if stepsize is smaller than 1', () => { + expect(BIM.quantize(123n, 0n)).toEqual(123n); + expect(BIM.quantize(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'); + }); +}); diff --git a/ui/src/base/math_utils_unittest.ts b/ui/src/base/math_utils_unittest.ts deleted file mode 100644 index 169b793e8..000000000 --- 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 1e602fcd7..286f82f72 100644 --- a/ui/src/common/actions.ts +++ b/ui/src/common/actions.ts @@ -61,7 +61,7 @@ import { 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 PostedTrace { } export interface PostedScrollToRange { - timeStart: number; - timeEnd: number; + timeStart: TPTime; + timeEnd: TPTime; viewPercentage?: number; } @@ -552,7 +552,7 @@ export const StateActions = { 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 +563,7 @@ export const StateActions = { }; }, - 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 +606,10 @@ export const StateActions = { }, 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 +663,7 @@ export const StateActions = { 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 +676,7 @@ export const StateActions = { 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 +686,8 @@ export const StateActions = { }; 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 +696,8 @@ export const StateActions = { selectPerfSamples(state: StateDraft, args: { id: number, upid: number, - leftTs: number, - rightTs: number, + leftTs: TPTime, + rightTs: TPTime, type: ProfileType }): void { state.currentSelection = { @@ -714,8 +710,8 @@ export const StateActions = { }; 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 +719,16 @@ export const StateActions = { 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: '', @@ -784,16 +780,33 @@ export const StateActions = { 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 +906,17 @@ export const StateActions = { }, 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 +1036,11 @@ export const StateActions = { 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 6702e723c..b613777a0 100644 --- a/ui/src/common/actions_unittest.ts +++ b/ui/src/common/actions_unittest.ts @@ -446,9 +446,13 @@ test('perf samples open flamegraph', () => { 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 @@ test('heap profile opens flamegraph', () => { 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/canvas_utils.ts b/ui/src/common/canvas_utils.ts index aef36a0dd..d086f5b6b 100644 --- a/ui/src/common/canvas_utils.ts +++ b/ui/src/common/canvas_utils.ts @@ -86,5 +86,11 @@ export function drawIncompleteSlice( ctx.lineTo(x + width - 3, y + (triangleSize * 3.5)); ctx.lineTo(x + width, y + 4 * triangleSize); ctx.lineTo(x, y + height); + + const gradient = ctx.createLinearGradient(x, y, x + width, y + height); + gradient.addColorStop(0.66, ctx.fillStyle as string); + gradient.addColorStop(1, '#FFFFFF'); + ctx.fillStyle = gradient; + ctx.fill(); } diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts index bd1e6b6d6..6d806a298 100644 --- a/ui/src/common/empty_state.ts +++ b/ui/src/common/empty_state.ts @@ -110,7 +110,7 @@ export function createEmptyState(): State { visibleState: { ...defaultTraceTime, lastUpdate: 0, - resolution: 0, + resolution: 0n, }, }, @@ -142,8 +142,8 @@ export function createEmptyState(): State { 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 8b6cb465c..861601fe7 100644 --- a/ui/src/common/engine.ts +++ b/ui/src/common/engine.ts @@ -24,18 +24,20 @@ import { 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 @@ export abstract class Engine { 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 000000000..927b7df0c --- /dev/null +++ b/ui/src/common/high_precision_time.ts @@ -0,0 +1,258 @@ +// 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'; + +// 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.isGreaterThan(b) ? a : b; + } + + static min(a: HighPrecisionTime, b: HighPrecisionTime): HighPrecisionTime { + return a.isLessThan(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); + } + + subtract(other: HighPrecisionTime): HighPrecisionTime { + return new HighPrecisionTime( + this.base - other.base, this.offset - other.offset); + } + + subtractTPTime(ts: TPTime): HighPrecisionTime { + return this.addTPTime(-ts); + } + + subtractNanos(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 + equals(other: HighPrecisionTime, epsilon: number = 1e-6): boolean { + return Math.abs(this.subtract(other).nanos) < epsilon; + } + + isLessThan(other: HighPrecisionTime): boolean { + if (this.base < other.base) { + return true; + } else if (this.base === other.base) { + return this.offset < other.offset; + } else { + return false; + } + } + + isLessThanOrEqual(other: HighPrecisionTime): boolean { + if (this.equals(other)) { + return true; + } else { + return this.isLessThan(other); + } + } + + isGreaterThan(other: HighPrecisionTime): boolean { + return !this.isLessThanOrEqual(other); + } + + isGreaterThanOrEqual(other: HighPrecisionTime): boolean { + return !this.isLessThan(other); + } + + clamp(lower: HighPrecisionTime, upper: HighPrecisionTime): HighPrecisionTime { + if (this.isLessThan(lower)) { + return lower; + } else if (this.isGreaterThan(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)}`; + } + } +} + +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.isLessThanOrEqual(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.subtract(this.start); + } + + get midpoint(): HighPrecisionTime { + return this.start.add(this.end).divide(2); + } + + equals(other: Span<HighPrecisionTime>): boolean { + return this.start.equals(other.start) && this.end.equals(other.end); + } + + contains(x: HighPrecisionTime|Span<HighPrecisionTime>): boolean { + if (x instanceof HighPrecisionTime) { + return this.start.isLessThanOrEqual(x) && x.isLessThan(this.end); + } else { + return this.start.isLessThanOrEqual(x.start) && + x.end.isLessThanOrEqual(this.end); + } + } + + intersects(x: Span<HighPrecisionTime>): boolean { + return !( + x.end.isLessThanOrEqual(this.start) || + x.start.isGreaterThanOrEqual(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.subtract(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 000000000..c3a2033d9 --- /dev/null +++ b/ui/src/common/high_precision_time_unittest.ts @@ -0,0 +1,308 @@ +// 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.subtract(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 gt comparisions', () => { + const time = mkTime('1.2'); + expect(time.isGreaterThanOrEqual(mkTime('0.5'))).toBeTruthy(); + expect(time.isGreaterThanOrEqual(mkTime('1.1'))).toBeTruthy(); + expect(time.isGreaterThanOrEqual(mkTime('1.2'))).toBeTruthy(); + expect(time.isGreaterThanOrEqual(mkTime('1.5'))).toBeFalsy(); + expect(time.isGreaterThanOrEqual(mkTime('5.5'))).toBeFalsy(); + }); + + it('should perform gte comparisions', () => { + const time = mkTime('1.2'); + expect(time.isGreaterThan(mkTime('0.5'))).toBeTruthy(); + expect(time.isGreaterThan(mkTime('1.1'))).toBeTruthy(); + expect(time.isGreaterThan(mkTime('1.2'))).toBeFalsy(); + expect(time.isGreaterThan(mkTime('1.5'))).toBeFalsy(); + expect(time.isGreaterThan(mkTime('5.5'))).toBeFalsy(); + }); + + it('should perform lt comparisions', () => { + const time = mkTime('1.2'); + expect(time.isLessThan(mkTime('0.5'))).toBeFalsy(); + expect(time.isLessThan(mkTime('1.1'))).toBeFalsy(); + expect(time.isLessThan(mkTime('1.2'))).toBeFalsy(); + expect(time.isLessThan(mkTime('1.5'))).toBeTruthy(); + expect(time.isLessThan(mkTime('5.5'))).toBeTruthy(); + }); + + it('should perform lte comparisions', () => { + const time = mkTime('1.2'); + expect(time.isLessThanOrEqual(mkTime('0.5'))).toBeFalsy(); + expect(time.isLessThanOrEqual(mkTime('1.1'))).toBeFalsy(); + expect(time.isLessThanOrEqual(mkTime('1.2'))).toBeTruthy(); + expect(time.isLessThanOrEqual(mkTime('1.5'))).toBeTruthy(); + expect(time.isLessThanOrEqual(mkTime('5.5'))).toBeTruthy(); + }); + + it('should detect equality', () => { + const time = new HPTime(1n, 0.2); + expect(time.equals(new HPTime(1n, 0.2))).toBeTruthy(); + expect(time.equals(new HPTime(0n, 1.2))).toBeTruthy(); + expect(time.equals(new HPTime(-100n, 101.2))).toBeTruthy(); + expect(time.equals(new HPTime(1n, 0.3))).toBeFalsy(); + expect(time.equals(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'); + }); +}); + +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 000000000..478cd029c --- /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 0fc2fae9e..04cf06599 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 const LogEntriesKey = 'log-entries'; 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 fa8a9fcd8..0dc66d618 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 @@ export interface PluginContext { // 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 aa414bc74..5898c5f4b 100644 --- a/ui/src/common/plugins.ts +++ b/ui/src/common/plugins.ts @@ -27,12 +27,14 @@ import { 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 @@ export class PluginContextImpl implements PluginContext { registerTrackProvider(provider: TrackProvider) { this.trackProviders.push(provider); } + + registerOnDetailsPanelSelectionChange( + onDetailsPanelSelectionChange: (newSelection?: Selection) => void) { + this.onDetailsPanelSelectionChange = onDetailsPanelSelectionChange; + } // ================================================================== // ================================================================== @@ -123,6 +130,14 @@ export class PluginManager { } 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/state.ts b/ui/src/common/state.ts index 995f6438b..91ca35835 100644 --- a/ui/src/common/state.ts +++ b/ui/src/common/state.ts @@ -18,7 +18,10 @@ import { 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 OmniboxState { } 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 interface AreaSelection { export type AreaById = Area&{id: string}; export interface Area { - startSec: number; - endSec: number; + start: TPTime; + end: TPTime; tracks: string[]; } @@ -100,7 +103,8 @@ export const MAX_TIME = 180; // 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. +export const STATE_VERSION = 31; export const SCROLLING_TRACK_GROUP = 'ScrollingTracks'; @@ -268,8 +272,8 @@ export interface PermalinkConfig { } export interface TraceTime { - startSec: number; - endSec: number; + start: TPTime; + end: TPTime; } export interface FrontendLocalState { @@ -284,7 +288,7 @@ export interface Status { export interface Note { noteType: 'DEFAULT'; id: string; - timestamp: number; + timestamp: TPTime; color: string; text: string; } @@ -311,14 +315,14 @@ export interface DebugSliceSelection { 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 +330,7 @@ export interface HeapProfileSelection { kind: 'HEAP_PROFILE'; id: number; upid: number; - ts: number; + ts: TPTime; type: ProfileType; } @@ -334,16 +338,16 @@ export interface PerfSamplesSelection { 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; @@ -377,8 +381,8 @@ export interface LogSelection { 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 { @@ -570,8 +574,8 @@ export interface State { // Hovered and focused events hoveredUtid: number; hoveredPid: number; - hoverCursorTimestamp: number; - hoveredNoteTimestamp: number; + hoverCursorTimestamp: TPTime; + hoveredNoteTimestamp: TPTime; highlightedSliceId: number; focusedFlowIdLeft: number; focusedFlowIdRight: number; @@ -608,8 +612,8 @@ export interface State { } export const defaultTraceTime = { - startSec: 0, - endSec: 10, + start: 0n, + end: BigInt(10e9), }; export declare type RecordMode = @@ -661,9 +665,9 @@ export function hasActiveProbes(config: RecordConfig) { export function getDefaultRecordingTargets(): RecordingTarget[] { return [ - {os: 'Q', name: 'Android Q+'}, - {os: 'P', name: 'Android P'}, - {os: 'O', name: 'Android O-'}, + {os: 'Q', name: 'Android Q+ / 10+'}, + {os: 'P', name: 'Android P / 9'}, + {os: 'O', name: 'Android O- / 8-'}, {os: 'C', name: 'Chrome'}, {os: 'CrOS', name: 'Chrome OS (system trace)'}, {os: 'L', name: 'Linux desktop'}, diff --git a/ui/src/common/state_unittest.ts b/ui/src/common/state_unittest.ts index 58610dc99..9b5a064fb 100644 --- a/ui/src/common/state_unittest.ts +++ b/ui/src/common/state_unittest.ts @@ -14,6 +14,7 @@ import {createEmptyState} from './empty_state'; import {getContainingTrackId, PrimaryTrackSortKey, State} from './state'; +import {deserializeStateObject, serializeStateObject} from './upload_utils'; test('createEmptyState', () => { const state: State = createEmptyState(); @@ -46,20 +47,10 @@ test('getContainingTrackId', () => { expect(getContainingTrackId(state, 'b')).toEqual('containsB'); }); -function serializeState(state: State): string { - return JSON.stringify(state, (key, value) => { - return key === 'nonSerializableState' ? undefined : value; - }); -} - -function deserializeState(json: string): State { - return JSON.parse(json); -} - test('state is serializable', () => { const state: State = createEmptyState(); - const json = serializeState(state); - const restored: State = deserializeState(json); + const json = serializeStateObject(state); + const restored: State = deserializeStateObject(json); // Remove nonSerializableState from original const serializableState: any = state as any; diff --git a/ui/src/common/time.ts b/ui/src/common/time.ts index ea5e9d8a6..5608d3f91 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,10 @@ export function timeToString(sec: number) { return `${sign < 0 ? '-' : ''}${Math.round(n * 10) / 10} ${units[u]}`; } +export function tpTimeToString(time: TPTime) { + return timeToString(tpTimeToSeconds(time)); +} + export function fromNs(ns: number) { return ns / 1e9; } @@ -52,6 +55,10 @@ export function formatTimestamp(sec: number) { return parts.join('.'); } +export function formatTPTime(time: TPTime) { + return formatTimestamp(tpTimeToSeconds(time)); +} + // TODO(hjd): Rename to formatTimestampWithUnits // 1000000023ns -> "1s 23ns" export function timeToCode(sec: number): string { @@ -77,44 +84,128 @@ export function timeToCode(sec: number): string { return result.slice(0, -1); } +export function tpTimeToCode(time: TPTime) { + 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; + +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>; +} - constructor(start: number, end: number) { - assertTrue(start <= end); +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); + get duration(): TPDuration { + return this.end - this.start; } - equals(other: TimeSpan): boolean { - return Math.abs(this.start - other.start) < EPSILON && - Math.abs(this.end - other.end) < EPSILON; + get midpoint(): TPTime { + return (this.start + this.end) / 2n; } - get duration() { - return this.end - this.start; + 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; + } + } + + intersects(x: Span<TPTime, TPDuration>): boolean { + return !(x.end <= this.start || x.start >= this.end); } - isInBounds(sec: number) { - return this.start <= sec && sec <= this.end; + equals(span: Span<TPTime, TPDuration>): boolean { + return this.start === span.start && this.end === span.end; } - add(sec: number): TimeSpan { - return new TimeSpan(this.start + sec, this.end + sec); + add(x: TPTime): Span<TPTime, TPDuration> { + return new TPTimeSpan(this.start + x, this.end + x); } - contains(other: TimeSpan) { - return this.start <= other.start && other.end <= this.end; + 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 7bfead11e..b9e6bd900 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 @@ test('seconds to code', () => { 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 9af7fff5e..a49a56d76 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/common/upload_utils.ts b/ui/src/common/upload_utils.ts index 875b072a4..0c9792178 100644 --- a/ui/src/common/upload_utils.ts +++ b/ui/src/common/upload_utils.ts @@ -34,11 +34,53 @@ export async function saveTrace(trace: File|ArrayBuffer): Promise<string> { return `https://storage.googleapis.com/${BUCKET_NAME}/${name}`; } -export async function saveState(stateOrConfig: State| - RecordConfig): Promise<string> { - const text = JSON.stringify(stateOrConfig, (key, value) => { +// Bigint's are not serializable using JSON.stringify, so we use a special +// object when serialising +export type SerializedBigint = { + __kind: 'bigint', + value: string +}; + +// Check if a value looks like a serialized bigint +export function isSerializedBigint(value: unknown): value is SerializedBigint { + if (value === null) { + return false; + } + if (typeof value !== 'object') { + return false; + } + if ('__kind' in value && 'value' in value) { + return value.__kind === 'bigint' && typeof value.value === 'string'; + } + return false; +} + +export function serializeStateObject(object: unknown): string { + const json = JSON.stringify(object, (key, value) => { + if (typeof value === 'bigint') { + return { + __kind: 'bigint', + value: value.toString(), + }; + } return key === 'nonSerializableState' ? undefined : value; }); + return json; +} + +export function deserializeStateObject(json: string): any { + const object = JSON.parse(json, (_key, value) => { + if (isSerializedBigint(value)) { + return BigInt(value.value); + } + return value; + }); + return object; +} + +export async function saveState(stateOrConfig: State| + RecordConfig): Promise<string> { + const text = serializeStateObject(stateOrConfig); const hash = await toSha256(text); const url = 'https://www.googleapis.com/upload/storage/v1/b/' + `${BUCKET_NAME}/o?uploadType=media` + diff --git a/ui/src/common/upload_utils_unittest.ts b/ui/src/common/upload_utils_unittest.ts new file mode 100644 index 000000000..4c0b8ce8d --- /dev/null +++ b/ui/src/common/upload_utils_unittest.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. + +import { + deserializeStateObject, + isSerializedBigint, + serializeStateObject, +} from './upload_utils'; + +describe('isSerializedBigint', () => { + it('should return true for a valid serialized bigint', () => { + const value = { + __kind: 'bigint', + value: '1234567890', + }; + expect(isSerializedBigint(value)).toBeTruthy(); + }); + + it('should return false for a null value', () => { + expect(isSerializedBigint(null)).toBeFalsy(); + }); + + it('should return false for a non-object value', () => { + expect(isSerializedBigint(123)).toBeFalsy(); + }); + + it('should return false for a non-serialized bigint value', () => { + const value = { + __kind: 'not-bigint', + value: '1234567890', + }; + expect(isSerializedBigint(value)).toBeFalsy(); + }); +}); + +describe('serializeStateObject', () => { + it('should serialize a simple object', () => { + const object = { + a: 1, + b: 2, + c: 3, + }; + const expectedJson = `{"a":1,"b":2,"c":3}`; + expect(serializeStateObject(object)).toEqual(expectedJson); + }); + + it('should serialize a bigint', () => { + const object = { + a: 123456789123456789n, + }; + const expectedJson = + `{"a":{"__kind":"bigint","value":"123456789123456789"}}`; + expect(serializeStateObject(object)).toEqual(expectedJson); + }); + + it('should not serialize a non-serializable property', () => { + const object = { + a: 1, + b: 2, + c: 3, + nonSerializableState: 4, + }; + const expectedJson = `{"a":1,"b":2,"c":3}`; + expect(serializeStateObject(object)).toEqual(expectedJson); + }); +}); + +describe('deserializeStateObject', () => { + it('should deserialize a simple object', () => { + const json = `{"a":1,"b":2,"c":3}`; + const expectedObject = { + a: 1, + b: 2, + c: 3, + }; + expect(deserializeStateObject(json)).toEqual(expectedObject); + }); + + it('should deserialize a bigint', () => { + const json = `{"a":{"__kind":"bigint","value":"123456789123456789"}}`; + const expectedObject = { + a: 123456789123456789n, + }; + expect(deserializeStateObject(json)).toEqual(expectedObject); + }); + + it('should deserialize a null', () => { + const json = `{"a":null}`; + const expectedObject = { + a: null, + }; + expect(deserializeStateObject(json)).toEqual(expectedObject); + }); +}); diff --git a/ui/src/controller/aggregation/counter_aggregation_controller.ts b/ui/src/controller/aggregation/counter_aggregation_controller.ts index dd7f14aad..e632f8e29 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 @@ export class CounterAggregationController extends AggregationController { } } 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 @@ export class CounterAggregationController extends AggregationController { 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 94d3950a0..452ca758f 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 @@ export class CpuAggregationController extends AggregationController { 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 b28e49656..fc24d1ed6 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 @@ export class CpuByProcessAggregationController extends AggregationController { 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 97e1f86f0..a22a83dbd 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 @@ export class FrameAggregationController extends AggregationController { 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 ebb80007b..8dcaccc0f 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 @@ export class SliceAggregationController extends AggregationController { 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 ddfadaea0..6e288d5dd 100644 --- a/ui/src/controller/aggregation/thread_aggregation_controller.ts +++ b/ui/src/controller/aggregation/thread_aggregation_controller.ts @@ -17,7 +17,6 @@ import {Engine} from '../../common/engine'; 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 @@ export class ThreadAggregationController extends AggregationController { 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 @@ export class ThreadAggregationController extends AggregationController { 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 b1d1c7e71..32dcb11bb 100644 --- a/ui/src/controller/area_selection_handler.ts +++ b/ui/src/controller/area_selection_handler.ts @@ -36,10 +36,10 @@ export class AreaSelectionHandler { // 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 c5a27c032..caac6780e 100644 --- a/ui/src/controller/area_selection_handler_unittest.ts +++ b/ui/src/controller/area_selection_handler_unittest.ts @@ -20,7 +20,7 @@ import {AreaSelectionHandler} from './area_selection_handler'; 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('validAreaAfterUndefinedArea', () => { 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('UndefinedAreaAfterUndefinedArea', () => { 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 @@ test('validAreaAfterValidArea', () => { 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('validAreaAfterValidArea', () => { 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 @@ test('sameAreaSelected', () => { 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 @@ test('NonAreaSelectionAfterUndefinedArea', () => { 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 e94531dbe..31a74c369 100644 --- a/ui/src/controller/flamegraph_controller.ts +++ b/ui/src/controller/flamegraph_controller.ts @@ -27,7 +27,7 @@ import { } 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 @@ export class FlamegraphController extends Controller<'main'> { } 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { } 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 @@ export class FlamegraphController extends Controller<'main'> { // 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 @@ export class FlamegraphController extends Controller<'main'> { } 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { 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 @@ export class FlamegraphController extends Controller<'main'> { } 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 @@ export class FlamegraphController extends Controller<'main'> { 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 d89b514ff..07125e125 100644 --- a/ui/src/controller/flow_events_controller.ts +++ b/ui/src/controller/flow_events_controller.ts @@ -16,7 +16,7 @@ import {Engine} from '../common/engine'; import {featureFlags} from '../common/feature_flags'; import {NUM, STR_NULL} from '../common/query_result'; import {Area} from '../common/state'; -import {fromNs, toNs} from '../common/time'; +import {fromNs} from '../common/time'; import {Flow, globals} from '../frontend/globals'; import {publishConnectedFlows, publishSelectedFlows} from '../frontend/publish'; import { @@ -241,8 +241,8 @@ export class FlowEventsController extends Controller<'main'> { 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 +268,8 @@ export class FlowEventsController extends Controller<'main'> { 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 132eda20e..a071ec542 100644 --- a/ui/src/controller/ftrace_controller.ts +++ b/ui/src/controller/ftrace_controller.ts @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {FtraceFilterState} from 'src/common/state'; - import {Engine} from '../common/engine'; -import {NUM, STR, STR_NULL} from '../common/query_result'; -import {TimeSpan, toNsCeil, toNsFloor} from '../common/time'; -import {FtraceEvent, globals as frontendGlobals} from '../frontend/globals'; -import {globals} from '../frontend/globals'; +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 {Span} from '../common/time'; +import {FtraceEvent, globals} from '../frontend/globals'; import {publishFtracePanelData} from '../frontend/publish'; import {ratelimit} from '../frontend/rate_limiters'; @@ -34,30 +36,12 @@ interface RetVal { numEvents: number; } -function cloneFtraceFilterState(other: FtraceFilterState): FtraceFilterState { - return { - excludedNames: [...other.excludedNames], - }; -} - -function ftraceFilterStateEq( - a: FtraceFilterState, b: FtraceFilterState): boolean { - if (a.excludedNames === b.excludedNames) return true; - if (a.excludedNames.length !== b.excludedNames.length) return false; - - for (let i = 0; i < a.excludedNames.length; ++i) { - if (a.excludedNames[i] !== b.excludedNames[i]) return false; - } - - return true; -} - export class FtraceController extends Controller<'main'> { private engine: Engine; - private oldSpan: TimeSpan = new TimeSpan(0, 0); - private oldFtraceFilter: FtraceFilterState = { - excludedNames: [], - }; + private oldSpan: Span<HighPrecisionTime> = HighPrecisionTimeSpan.ZERO; + private oldFtraceFilter?: FtraceFilterState; + private oldPagination?: Pagination; + constructor({engine}: FtraceControllerArgs) { super('main'); this.engine = engine; @@ -65,44 +49,38 @@ export class FtraceController extends Controller<'main'> { run() { if (this.shouldUpdate()) { - this.updateEverything(); + this.oldSpan = globals.frontendLocalState.visibleWindowTime; + this.oldFtraceFilter = globals.state.ftraceFilter; + this.oldPagination = globals.state.ftracePagination; + if (globals.state.ftracePagination.count > 0) { + this.lookupFtraceEventsRateLimited(); + } } } - private updateEverything = ratelimit(() => { + private lookupFtraceEventsRateLimited = ratelimit(() => { const {offset, count} = globals.state.ftracePagination; - this.oldSpan = frontendGlobals.frontendLocalState.visibleWindowTime; - this.oldFtraceFilter = - cloneFtraceFilterState(frontendGlobals.state.ftraceFilter); - this.lookupFtraceEvents(offset, count).then(({events, - offset, - numEvents}: RetVal) => { + // The formatter doesn't like formatted chained methods :( + const promise = this.lookupFtraceEvents(offset, count); + promise.then(({events, offset, numEvents}: RetVal) => { publishFtracePanelData({events, offset, numEvents}); }); }, 250); private shouldUpdate(): boolean { - if (this.oldSpan != frontendGlobals.frontendLocalState.visibleWindowTime) { - // The visible window has changed, definitely update - return true; - } - - const globalPanelData = frontendGlobals.ftracePanelData; - if (!globalPanelData) { - // No state has been written yet, so we definitely need to update + // Has the visible window moved? + const visibleWindow = globals.frontendLocalState.visibleWindowTime; + if (!this.oldSpan.equals(visibleWindow)) { return true; } - // Work out whether we've scrolled near our rendered bounds - const {offset, count} = globals.state.ftracePagination; - if (offset != globalPanelData.offset || - count != globalPanelData.events.length) { + // Has the pagination changed? + if (this.oldPagination !== globals.state.ftracePagination) { return true; } - // Work out of the ftrace filter has changed - const filter = frontendGlobals.state.ftraceFilter; - if (!ftraceFilterStateEq(this.oldFtraceFilter, filter)) { + // Has the filter changed? + if (this.oldFtraceFilter !== globals.state.ftraceFilter) { return true; } @@ -110,16 +88,21 @@ export class FtraceController extends Controller<'main'> { } async lookupFtraceEvents(offset: number, count: number): Promise<RetVal> { - const appState = frontendGlobals.state; - const frontendState = frontendGlobals.frontendLocalState; + const appState = globals.state; + const frontendState = globals.frontendLocalState; const {start, end} = frontendState.visibleWindowTime; - const startNs = toNsFloor(start); - const endNs = toNsCeil(end); + const startNs = start.nanos; + const endNs = end.nanos; const excludeList = appState.ftraceFilter.excludedNames; const excludeListSql = excludeList.map((s) => `'${s}'`).join(','); + // TODO(stevegolton): This query can be slow when traces are huge. + // The number of events is only used for correctly sizing the panel's + // scroll container so that the scrollbar works as if the panel were fully + // populated. + // Perhaps we could work out some UX that doesn't need this. let queryRes = await this.engine.query(` select count(id) as numEvents from ftrace_event @@ -152,7 +135,7 @@ export class FtraceController extends Controller<'main'> { 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 d8f9b3a96..c931ce0dc 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 @@ import { 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); - - 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 countRow = countResult.firstRow({minTs: NUM, maxTs: NUM, countTs: NUM}); - - const firstRowNs = countRow.minTs; - const lastRowNs = countRow.maxTs; - const total = countRow.countTs; - - 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 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, + engine: Engine, span: Span<TPTime>): Promise<LogBounds> { + const vizStartNs = span.start; + const vizEndNs = span.end; + + const vizFilter = `ts between ${vizStartNs} and ${vizEndNs}`; + + 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 data = result.firstRow({ + minTs: LONG_NULL, + maxTs: LONG_NULL, + minVizTs: LONG_NULL, + maxVizTs: LONG_NULL, + countTs: NUM, + }); + + const firstLogTs = data.minTs ?? 0n; + const lastLogTs = data.maxTs ?? BigintMath.INT64_MAX; + + 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 @@ async function updateLogEntries( const processName = []; const it = rowsResult.iter({ - ts: NUM, + ts: LONG, prio: NUM, tag: STR, msg: STR, @@ -179,7 +179,7 @@ export interface LogsControllerArgs { */ 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 @@ export class LogsController extends Controller<'main'> { 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 @@ export class LogsController extends Controller<'main'> { } 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/permalink_controller.ts b/ui/src/controller/permalink_controller.ts index a382a406d..ad58b27ae 100644 --- a/ui/src/controller/permalink_controller.ts +++ b/ui/src/controller/permalink_controller.ts @@ -26,6 +26,7 @@ import {STATE_VERSION} from '../common/state'; import { BUCKET_NAME, buggyToSha256, + deserializeStateObject, saveState, saveTrace, toSha256, @@ -195,7 +196,7 @@ export class PermalinkController extends Controller<'main'> { } const text = await response.text(); const stateHash = await toSha256(text); - const state = JSON.parse(text); + const state = deserializeStateObject(text); if (stateHash !== id) { // Old permalinks incorrectly dropped some digits from the // hexdigest of the SHA256. We don't want to invalidate those diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts index 598c85e97..5982af5c1 100644 --- a/ui/src/controller/record_controller.ts +++ b/ui/src/controller/record_controller.ts @@ -128,7 +128,8 @@ export function toPbtxt(configBuffer: Uint8Array): string { return value.startsWith('MEMINFO_') || value.startsWith('VMSTAT_') || value.startsWith('STAT_') || value.startsWith('LID_') || value.startsWith('BATTERY_COUNTER_') || value === 'DISCARD' || - value === 'RING_BUFFER'; + value === 'RING_BUFFER' || value === 'BACKGROUND' || + value === 'USER_INITIATED'; } // Since javascript doesn't have 64 bit numbers when converting protos to // json the proto library encodes them as strings. This is lossy since @@ -139,9 +140,10 @@ export function toPbtxt(configBuffer: Uint8Array): string { function is64BitNumber(key: string): boolean { return [ 'maxFileSizeBytes', + 'pid', 'samplingIntervalBytes', 'shmemSizeBytes', - 'pid', + 'timestampUnitMultiplier', ].includes(key); } function* message(msg: {}, indent: number): IterableIterator<string> { diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts index d1df094d2..c5968c278 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 {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 interface SearchControllerArgs { 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 @@ export class SearchController extends Controller<'main'> { 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 @@ export class SearchController extends Controller<'main'> { 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,9 +88,8 @@ export class SearchController extends Controller<'main'> { // 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) { @@ -105,25 +109,12 @@ export class SearchController extends Controller<'main'> { 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 @@ export class SearchController extends Controller<'main'> { 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.quantizeFloor(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}, diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts index 66cf3d005..b5a390769 100644 --- a/ui/src/controller/selection_controller.ts +++ b/ui/src/controller/selection_controller.ts @@ -16,14 +16,23 @@ import {assertTrue} from '../base/logging'; 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { } } - 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { 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 @@ export class SelectionController extends Controller<'main'> { // 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 @@ export class SelectionController extends Controller<'main'> { } 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 f57105d2d..f0e2d02b0 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,30 @@ import { 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 {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 +55,7 @@ import { } from '../frontend/globals'; import {showModal} from '../frontend/modal'; import { + clearOverviewData, publishFtraceCounters, publishMetricError, publishOverviewData, @@ -113,7 +130,6 @@ const METRICS = [ 'android_dma_heap', 'android_surfaceflinger', 'android_batt', - 'android_camera', 'android_other_traces', 'chrome_dropped_frames', 'chrome_long_latency', @@ -416,11 +432,11 @@ export class TraceController extends Controller<States> { 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 = @@ -453,16 +469,16 @@ export class TraceController extends Controller<States> { 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); @@ -486,7 +502,7 @@ export class TraceController extends Controller<States> { // Pull out the counts ftrace events by name const query = `select name, - count(*) as cnt + count(name) as cnt from ftrace_event group by name order by cnt desc`; @@ -540,8 +556,8 @@ export class TraceController extends Controller<States> { 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})); } @@ -560,7 +576,7 @@ export class TraceController extends Controller<States> { 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; @@ -610,31 +626,32 @@ export class TraceController extends Controller<States> { 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); @@ -645,16 +662,15 @@ export class TraceController extends Controller<States> { } // 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 @@ -665,21 +681,21 @@ export class TraceController extends Controller<States> { 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); } @@ -890,48 +906,48 @@ export class TraceController extends Controller<States> { } } -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 46d1f1f26..73b98969e 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 {BigintMath} from '../base/bigint_math'; import {assertExists, assertTrue} 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'; @@ -70,7 +76,7 @@ export abstract class TrackController< // 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 +135,7 @@ export abstract class TrackController< } 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 +144,14 @@ export abstract class TrackController< 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; @@ -162,8 +168,7 @@ export abstract class TrackController< return undefined; } - const bounds = globals.state.traceTime; - const traceDurNs = toNs(bounds.endSec - bounds.startSec); + const traceDuration = globals.stateTraceTime().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 @@ -198,7 +203,7 @@ export abstract class TrackController< // Compute the outermost bucket size. This acts as a starting point for // computing the cached size. const outermostResolutionLevel = - Math.ceil(Math.log2(traceDurNs / approxWidthPx)); + Math.ceil(Math.log2(traceDuration.nanos / approxWidthPx)); const outermostBucketNs = Math.pow(2, outermostResolutionLevel); // This constant decides how many resolution levels down from our outermost @@ -232,11 +237,11 @@ export abstract class TrackController< 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 +258,14 @@ export abstract class TrackController< .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 241e24865..488240082 100644 --- a/ui/src/controller/track_decider.ts +++ b/ui/src/controller/track_decider.ts @@ -25,6 +25,7 @@ import {Engine, EngineProxy} from '../common/engine'; 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 @@ import { 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({ @@ -207,6 +214,25 @@ class TrackDecider { } } + 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(); @@ -265,20 +291,21 @@ class TrackDecider { async addGlobalAsyncTracks(engine: EngineProxy): Promise<void> { const rawGlobalAsyncTracks = await engine.query(` - with global_tracks as materialized ( + with tracks_with_slices as materialized ( + select distinct track_id + from slice + ), + global_tracks as ( select track.parent_id as parent_id, track.id as track_id, - track.name as name, - count(1) cnt + track.name as name from track - join slice on slice.track_id = track.id + join tracks_with_slices on tracks_with_slices.track_id = track.id where track.type = "track" or track.type = "gpu_track" or track.type = "cpu_track" - group by track_id - having cnt > 0 ), global_tracks_grouped as ( select @@ -308,6 +335,7 @@ class TrackDecider { }); const parentIdToGroupId = new Map<number, string>(); + let scrollJankRendered = false; for (; it.valid(); it.next()) { const kind = ASYNC_SLICE_TRACK_KIND; @@ -352,6 +380,13 @@ class TrackDecider { } } + 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, @@ -405,45 +440,6 @@ class TrackDecider { } } - async addGlobalCounterTracks(engine: EngineProxy): Promise<void> { - // Add global or GPU counter tracks that are not bound to any pid/tid. - const globalCounters = await engine.query(` - select name, id - from ( - select name, id - from counter_track - where type = 'counter_track' - union - select name, id - from gpu_counter_track - where name != 'gpufreq' - ) - order by name - `); - - const it = globalCounters.iter({ - name: STR, - id: NUM, - }); - - for (; it.valid(); it.next()) { - const name = it.name; - const trackId = it.id; - this.tracksToAdd.push({ - engineId: this.engineId, - kind: COUNTER_TRACK_KIND, - name, - trackSortKey: PrimaryTrackSortKey.COUNTER_TRACK, - trackGroup: SCROLLING_TRACK_GROUP, - config: { - name, - trackId, - scale: getCounterScale(name), - }, - }); - } - } - async addCpuPerfCounterTracks(engine: EngineProxy): Promise<void> { // Perf counter tracks are bound to CPUs, follow the scheduling and // frequency track naming convention ("Cpu N ..."). @@ -720,15 +716,12 @@ class TrackDecider { } async addFtraceTrack(engine: EngineProxy): Promise<void> { - const query = `select - cpu, - count(*) as cnt + const query = `select distinct cpu from ftrace_event - where cpu + 1 > 1 or utid + 1 > 1 - group by cpu`; + where cpu + 1 > 1 or utid + 1 > 1`; const result = await engine.query(query); - const it = result.iter({cpu: NUM, cnt: NUM}); + const it = result.iter({cpu: NUM}); let groupUuid = undefined; let summaryTrackId = undefined; @@ -1000,9 +993,9 @@ class TrackDecider { 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; @@ -1317,8 +1310,8 @@ class TrackDecider { 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/frontend/aggregation_panel.ts b/ui/src/frontend/aggregation_panel.ts index ddcb6baf5..6bd642269 100644 --- a/ui/src/frontend/aggregation_panel.ts +++ b/ui/src/frontend/aggregation_panel.ts @@ -22,6 +22,7 @@ import { } 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 @@ export class AggregationPanel extends Panel<AggregationPanelAttrs> { 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 ac3cad07b..77c6e6d9a 100644 --- a/ui/src/frontend/base_slice_track.ts +++ b/ui/src/frontend/base_slice_track.ts @@ -22,7 +22,12 @@ import { } from '../common/colorizer'; import {NUM} from '../common/query_result'; import {Selection, SelectionKind} from '../common/state'; -import {fromNs, toNs} from '../common/time'; +import { + fromNs, + tpDurationFromNanos, + TPTime, + tpTimeFromNanos, +} from '../common/time'; import {checkerboardExcept} from './checkerboard'; import {globals} from './globals'; @@ -45,7 +50,7 @@ const DEFAULT_SLICE_COLOR = UNEXPECTED_PINK_COLOR; // 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 @@ function filterVisibleSlices<S extends Slice>( // 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 @@ function filterVisibleSlices<S extends Slice>( 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,7 +186,7 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = private cache: TrackCache<Array<CastInternal<T['slice']>>> = new TrackCache(5); - private readonly tableName: string; + protected readonly tableName: string; private maxDurNs = 0; private sqlState: 'UNINITIALIZED'|'INITIALIZING'|'QUERY_PENDING'| 'QUERY_DONE' = 'UNINITIALIZED'; @@ -272,13 +277,16 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = 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); + // TODO(stevegolton): Keep these guys as bigints + const rawStartNs = vizTime.start.nanos; + const rawEndNs = vizTime.end.nanos; const rawSlicesKey = CacheKey.create(rawStartNs, rawEndNs, windowSizePx); // If the visible time range is outside the cached area, requests @@ -298,7 +306,8 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = // 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 +330,15 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = // 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 +438,10 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = 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.secondsToPx(fromNs(this.slicesKey.startNs)), + timeScale.secondsToPx(fromNs(this.slicesKey.endNs))); // TODO(hjd): Remove this. // The only thing this does is drawing the sched latency arrow. We should @@ -623,8 +632,8 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = 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 +710,10 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = 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() { diff --git a/ui/src/frontend/base_slice_track_unittest.ts b/ui/src/frontend/base_slice_track_unittest.ts index 7dd109dee..e9202a2a7 100644 --- a/ui/src/frontend/base_slice_track_unittest.ts +++ b/ui/src/frontend/base_slice_track_unittest.ts @@ -19,11 +19,11 @@ import { } 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 @@ function slice(startS: number, durationS: number): Slice { 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 @@ test('filterVisibleSlices', () => { s(1, 9), s(6, 3), ], - 10, - 90)) + 10n, + 90n)) .toContainEqual(s(1, 9)); expect(filterVisibleSlices( [ @@ -73,16 +73,16 @@ test('filterVisibleSlices', () => { 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 @@ test('filterVisibleSlices', () => { s(0, 100), ], - 10, - 90)) + 10n, + 90n)) .toEqual([ s(0, 100), ]); @@ -109,7 +109,7 @@ test('filterVisibleSlices', () => { 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 6c982cfe9..50e3d1cd2 100644 --- a/ui/src/frontend/chrome_slice_panel.ts +++ b/ui/src/frontend/chrome_slice_panel.ts @@ -17,7 +17,9 @@ import m from 'mithril'; 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 {timeToCode, tpDurationToSeconds, tpTimeToCode} from '../common/time'; import {FlowPoint, globals, SliceDetails} from './globals'; import {PanelSize} from './panel'; @@ -54,6 +56,29 @@ const ITEMS: ContextMenuItem[] = [ ), }, { + 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 getSliceContextMenuItems(slice: SliceDetails): PopupMenuItem[] { }); } +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 @@ export class ChromeSliceDetailsPanel extends SlicePanel { !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 @@ export class ChromeSliceDetailsPanel extends SlicePanel { 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) + diff --git a/ui/src/frontend/counter_panel.ts b/ui/src/frontend/counter_panel.ts index 99d384174..42377735f 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 @@ export class CounterDetailsPanel extends Panel<CounterDetailsPanelAttrs> { 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 @@ export class CounterDetailsPanel extends Panel<CounterDetailsPanelAttrs> { 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 fae7a832f..7e9c9e523 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 {getSchema} from '../common/schema'; 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 @@ export function registerDebugGlobals() { 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 6e5a985f1..e3a75859e 100644 --- a/ui/src/frontend/details_panel.ts +++ b/ui/src/frontend/details_panel.ts @@ -17,9 +17,12 @@ import m from 'mithril'; 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'; @@ -45,6 +48,8 @@ const UP_ICON = 'keyboard_arrow_up'; 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; @@ -178,7 +183,7 @@ class DragHandle implements m.ClassComponent<DragHandleAttrs> { } 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 @@ function handleSelectionChange(newSelection?: Selection, _?: Selection): void { }, }); break; + case TOP_LEVEL_SCROLL_KIND: + pluginManager.onDetailsPanelSelectionChange( + SCROLL_JANK_PLUGIN_ID, newSelection); + break; default: bottomTabList.closeTabByTag(currentSelectionTag); } diff --git a/ui/src/frontend/drag/border_drag_strategy.ts b/ui/src/frontend/drag/border_drag_strategy.ts index df450fc2d..564ffc3fb 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.isGreaterThan(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 289684987..afb83e138 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 2af1b391f..7be7f7bc0 100644 --- a/ui/src/frontend/drag/inner_drag_strategy.ts +++ b/ui/src/frontend/drag/inner_drag_strategy.ts @@ -23,8 +23,8 @@ export class InnerDragStrategy extends DragStrategy { 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 648b50d4b..f8269fcf0 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 30c0a3855..4c547aa42 100644 --- a/ui/src/frontend/drag_gesture_handler.ts +++ b/ui/src/frontend/drag_gesture_handler.ts @@ -33,8 +33,6 @@ export class DragGestureHandler { 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 @@ export class DragGestureHandler { 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 b8246b3c3..0ac5f7930 100644 --- a/ui/src/frontend/flamegraph_panel.ts +++ b/ui/src/frontend/flamegraph_panel.ts @@ -28,10 +28,9 @@ import { 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 {Router} from './router'; 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 @@ const RENDER_OBJ_COUNT: NodeRendering = { 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 @@ export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> { 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 @@ export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> { 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 @@ export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> { }), ]), ]), - 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 @@ export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> { 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 @@ export class FlamegraphDetailsPanel extends Panel<FlamegraphDetailsPanelAttrs> { 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_renderer.ts b/ui/src/frontend/flow_events_renderer.ts index 909dcb7f3..fdb91e0e1 100644 --- a/ui/src/frontend/flow_events_renderer.ts +++ b/ui/src/frontend/flow_events_renderer.ts @@ -140,7 +140,7 @@ export class FlowEventsRenderer { } private getXCoordinate(ts: number): number { - return globals.frontendLocalState.timeScale.timeToPx(ts); + return globals.frontendLocalState.visibleTimeScale.secondsToPx(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 b24f69a33..f4d95ca42 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 @@ import { Timestamped, VisibleState, } from '../common/state'; -import {TimeSpan} from '../common/time'; +import {Span} 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 @@ function chooseLatest<T extends Timestamped>(current: T, next: T): T { 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,99 @@ function calculateScrollbarWidth() { 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) { + // TODO(stevegolton): Handle case where trace time < MIN_DURATION_NS + + 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> { + 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.isLessThan(traceTimeSpan.start)) { + this._start = traceTimeSpan.start; + } + + const end = this._start.addNanos(this._durationNanos); + if (end.isGreaterThan(traceTimeSpan.end)) { + this._start = traceTimeSpan.end.subtractNanos(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 +172,9 @@ export class FrontendLocalState { private _visibleState: VisibleState = { lastUpdate: 0, - startSec: 0, - endSec: 10, - resolution: 1, + start: 0n, + end: BigInt(10e9), + resolution: 1n, }; private _selectedArea?: Area; @@ -125,6 +215,16 @@ export class FrontendLocalState { } } + 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,17 +237,22 @@ export class FrontendLocalState { 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); + assertTrue( + 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(); } @@ -164,12 +269,11 @@ export class FrontendLocalState { 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(); } @@ -179,17 +283,17 @@ export class FrontendLocalState { 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 @@ -200,7 +304,28 @@ export class FrontendLocalState { 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; + } } diff --git a/ui/src/frontend/ftrace_panel.ts b/ui/src/frontend/ftrace_panel.ts index 29d020764..e45b5fe7e 100644 --- a/ui/src/frontend/ftrace_panel.ts +++ b/ui/src/frontend/ftrace_panel.ts @@ -18,7 +18,7 @@ import {StringListPatch} from 'src/common/state'; 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'; @@ -105,6 +105,11 @@ export class FtracePanel extends Panel<{}> { onremove({dom}: m.CVnodeDOM) { const sc = this.scrollContainer(dom); sc.removeEventListener('scroll', this.onScroll); + + globals.dispatch(Actions.updateFtracePagination({ + offset: 0, + count: 0, + })); } onScroll = (e: Event) => { @@ -112,12 +117,12 @@ export class FtracePanel extends Panel<{}> { 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() { @@ -183,8 +188,7 @@ export class FtracePanel extends Panel<{}> { 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; @@ -199,7 +203,7 @@ export class FtracePanel extends Panel<{}> { `.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 0ca10a077..79ca58a96 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 @@ import { } 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 {FrontendLocalState} from './frontend_local_state'; 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 AggregateDataStore = Map<string, AggregateData>; 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; @@ -104,23 +115,23 @@ export interface Flow { } 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[]; @@ -143,8 +154,8 @@ export interface CpuProfileDetails { } export interface QuantizedLoad { - startSec: number; - endSec: number; + start: TPTime; + end: TPTime; load: number; } type OverviewStore = Map<string, QuantizedLoad[]>; @@ -161,7 +172,7 @@ type ThreadMap = Map<number, ThreadDesc>; export interface FtraceEvent { id: number; - ts: number; + ts: TPTime; name: string; cpu: number; thread: string|null; @@ -530,7 +541,7 @@ class Globals { 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,18 @@ class Globals { // 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 = HighPrecisionTime.max( + timeScale.pxDeltaToDuration(1), new HighPrecisionTime(1n)); + + const resolutionBig = BigintMath.bitFloor(timePerPx.toTPTime()); + return resolutionBig; } getCurrentEngine(): EngineConfig|undefined { @@ -637,6 +642,30 @@ class Globals { 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 new TimeScale(traceTime.start, traceTime.duration.nanos, 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); + } } export const globals = new Globals(); diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts index 1c9dbfec8..6581c8121 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); - - // 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, '|....:....']]; - - for (const [multiplier, pattern] of stepSizeMultipliers) { - const newStepSize = multiplier * initialStepSize; - const numberOfNewSteps = range / newStepSize; - if (numberOfNewSteps <= maxNumberOfSteps) { - return [newStepSize, pattern]; +const micros = 1000n; +const millis = 1000n * micros; +const seconds = 1000n * millis; +const minutes = 60n * seconds; +const hours = 60n * minutes; +const days = 24n * hours; + +// 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, '|.:.'], +]; + +// 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 @@ function tickPatternToArray(pattern: string): TickType[] { }); } -// 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 enum TickType { 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; - - 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'); - - const desiredSteps = scale.widthPx / minLabelPx; - const [size, pattern] = getStepSize(scale.timeSpan.duration, desiredSteps); + private _patternSize: TPDuration; + private _timeSpan: Span<TPTime>; + private _offset: TPTime; + + 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'); + + 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); - - // 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}; + const stepSize = this._patternSize / BigInt(this._tickPattern.length); + const start = roundDownNearest(this._timeSpan.start, this._patternSize); + const end = this._timeSpan.end; + let patternIndex = 0; + + 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 @@ export class TickGenerator implements Iterable<Tick> { // 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 @@ export function drawGridLines( 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 3b6dcacad..2454680f6 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(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(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]); -}); - -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]); -}); - -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); - } -}); - -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); - }); + 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(getPattern(10n)).toEqual([10n, '|....:....']); + expect(getPattern(20n)).toEqual([20n, '|.:.']); + expect(getPattern(50n)).toEqual([50n, '|....']); + + expect(getPattern(100n)).toEqual([100n, '|....:....']); }); -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}); +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); }); - 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('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); }); - 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('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 timespan duration is 0', () => { + expect(() => { + new TickGenerator(new TPTimeSpan(0n, 0n), 1); + }).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); + it('throws an error when max ticks is 0', () => { + expect(() => { + new TickGenerator(new TPTimeSpan(0n, 1n), 0); + }).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/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts index 7da04de6a..f25bf5274 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 @@ import { } 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 @@ export function handleKey(e: KeyboardEvent, down: boolean): boolean { 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 @@ export function handleKey(e: KeyboardEvent, down: boolean): boolean { // 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 moveByFocusedFlow(direction: Direction): void { } } -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 @@ function findTimeRangeOfSelection(): {startTs: number, endTs: number} { } 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 @@ function findTimeRangeOfSelection(): {startTs: number, endTs: number} { // 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 findTimeRangeOfSelection(): {startTs: number, endTs: number} { 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 @@ export function findCurrentSelection() { 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 18ed325ba..88baa1ccd 100644 --- a/ui/src/frontend/logs_panel.ts +++ b/ui/src/frontend/logs_panel.ts @@ -16,14 +16,14 @@ import m from 'mithril'; 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'; @@ -62,12 +62,16 @@ export class LogPanel extends Panel<{}> { dom.parentElement!.parentElement!.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 +83,12 @@ export class LogPanel extends Panel<{}> { 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 +96,19 @@ export class LogPanel extends Panel<{}> { 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 +152,10 @@ export class LogPanel extends Panel<{}> { { '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 d3eec0733..27d9ec8ae 100644 --- a/ui/src/frontend/notes_panel.ts +++ b/ui/src/frontend/notes_panel.ts @@ -17,7 +17,9 @@ import m from 'mithril'; 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 {TRACK_SHELL_WIDTH} from './css_constants'; import {PerfettoMouseEvent} from './events'; import {globals} from './globals'; import { + getMaxMajorTicks, TickGenerator, TickType, timeScaleForVisibleWindow, @@ -46,7 +49,7 @@ function toSummary(s: string) { 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 @@ export class NotesPanel extends Panel { }); dom.addEventListener('mouseout', () => { this.hoveredX = null; - globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1})); + globals.dispatch(Actions.setHoveredNoteTimestamp({ts: -1n})); }, {passive: true}); } @@ -110,15 +113,27 @@ export class NotesPanel extends Panel { } 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 @@ export class NotesPanel extends Panel { 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 @@ export class NotesPanel extends Panel { 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 @@ export class NotesPanel extends Panel { 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 @@ export class NotesPanel extends Panel { // 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 @@ export class NotesPanel extends Panel { 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 @@ export class NotesPanel extends Panel { // 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 @@ export class NotesPanel extends Panel { 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 @@ export class NotesPanel extends Panel { } 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 @@ export class NotesEditorTab extends BottomTab<NotesEditorTabConfig> { 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 76b451582..54f932e54 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 {InnerDragStrategy} from './drag/inner_drag_strategy'; 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 @@ export class OverviewTimelinePanel extends Panel { 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,11 @@ export class OverviewTimelinePanel extends Panel { // 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 = + new TimeScale(traceTime.start, traceTime.duration.nanos, pxSpan); if (this.gesture === undefined) { this.gesture = new DragGestureHandler( dom as HTMLElement, @@ -78,26 +81,27 @@ export class OverviewTimelinePanel extends Panel { 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 +118,8 @@ export class OverviewTimelinePanel extends Panel { 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 +214,10 @@ export class OverviewTimelinePanel extends Panel { } 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 4c6576fde..b7841d7a5 100644 --- a/ui/src/frontend/panel_container.ts +++ b/ui/src/frontend/panel_container.ts @@ -135,11 +135,13 @@ export class PanelContainer implements m.ClassComponent<Attrs> { 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 @@ export class PanelContainer implements m.ClassComponent<Attrs> { } } } - 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 @@ export class PanelContainer implements m.ClassComponent<Attrs> { 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_query_generator.ts b/ui/src/frontend/pivot_table_query_generator.ts index 0c61f5618..dffa6e405 100644 --- a/ui/src/frontend/pivot_table_query_generator.ts +++ b/ui/src/frontend/pivot_table_query_generator.ts @@ -20,7 +20,6 @@ import { PivotTableQuery, PivotTableState, } from '../common/state'; -import {toNs} from '../common/time'; import { getSelectedTrackIds, } from '../controller/aggregation/slice_aggregation_controller'; @@ -100,8 +99,8 @@ function aggregationAlias(aggregationIndex: number): string { 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 7632e539c..0cc560439 100644 --- a/ui/src/frontend/publish.ts +++ b/ui/src/frontend/publish.ts @@ -54,6 +54,11 @@ export function publishOverviewData( 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 c27ecc29a..a58d1d6bf 100644 --- a/ui/src/frontend/query_table.ts +++ b/ui/src/frontend/query_table.ts @@ -14,13 +14,15 @@ 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 {TPTime, tpTimeFromNanos} from '../common/time'; +import {TPDuration} from '../common/time'; +import {Anchor} from './anchor'; import {copyToClipboard, queryResponseToClipboard} from './clipboard'; import {downloadData} from './download_utils'; import {globals} from './globals'; @@ -38,6 +40,16 @@ interface QueryTableRowAttrs { } // Convert column value to number if it's a bigint or a number, otherwise throw +function colToTimestamp(colValue: ColumnType): TPTime { + if (typeof colValue === 'bigint') { + return colValue; + } else if (typeof colValue === 'number') { + return tpTimeFromNanos(colValue); + } else { + throw Error('Value is not a number or a bigint'); + } +} + function colToNumber(colValue: ColumnType): number { if (typeof colValue === 'bigint') { return Number(colValue); @@ -48,6 +60,15 @@ function colToNumber(colValue: ColumnType): number { } } +function colToDuration(colValue: ColumnType): TPDuration { + return colToTimestamp(colValue); +} + +function clampDurationLower( + dur: TPDuration, lowerClamp: TPDuration): TPDuration { + return BigintMath.max(dur, lowerClamp); +} + class QueryTableRow implements m.ClassComponent<QueryTableRowAttrs> { static columnsContainsSliceLocation(columns: string[]) { const requiredColumns = ['ts', 'dur', 'track_id']; @@ -65,15 +86,14 @@ class QueryTableRow implements m.ClassComponent<QueryTableRowAttrs> { // the slice. event.stopPropagation(); - const sliceStart = fromNs(colToNumber(row.ts)); + const sliceStart = colToTimestamp(row.ts); // row.dur can be negative. Clamp to 1ns. - const sliceDur = fromNs(Math.max(colToNumber(row.dur), 1)); + const sliceDur = clampDurationLower(colToDuration(row.dur), 1n); const sliceEnd = sliceStart + sliceDur; - const trackId: number = colToNumber(row.track_id); + const trackId = 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; diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts index 18a9a7838..f177a6524 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) { +// TODO(stevegolton): Remove me! +export function horizontalScrollToTs(ts: TPTime) { + console.log('horizontalScrollToTs', ts); + const time = HighPrecisionTime.fromTPTime(ts); + const {start, end, duration} = globals.frontendLocalState.visibleWindowTime; + const halfDuration = duration.nanos / 2; + if (time.isLessThan(start) || time.isGreaterThan(end)) { // 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))); + globals.frontendLocalState.updateVisibleTime(new HighPrecisionTimeSpan( + time.subtractNanos(halfDuration), time.addNanos(halfDuration))); } } @@ -46,16 +51,11 @@ export function horizontalScrollToTs(ts: number) { // 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 @@ export function focusHorizontalRange( 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.isGreaterThan(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.subtract(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.isGreaterThan(trace.end)) { + newStart = trace.end.subtract(visible.duration); + newEnd = trace.end; } - if (newStartTs < globals.state.traceTime.startSec) { - newStartTs = globals.state.traceTime.startSec; - newEndTs = globals.state.traceTime.startSec + visibleDur; + if (newStart.isLessThan(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.equals(visible.start) && view.end.equals(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,7 +147,7 @@ export function verticalScrollToTrack( // 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); } diff --git a/ui/src/frontend/search_handler.ts b/ui/src/frontend/search_handler.ts index 1622a7efe..f99561713 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 @@ function setToNext(current: number) { 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.frontendLocalState.visibleWindowTime; + const startNs = vizWindow.start.nanos; + const endNs = vizWindow.end.nanos; 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 5b660ef0e..7587a27e9 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 9b018dd9b..13e3dd572 100644 --- a/ui/src/frontend/slice_details_panel.ts +++ b/ui/src/frontend/slice_details_panel.ts @@ -16,7 +16,7 @@ import m from 'mithril'; 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 @@ export class SliceDetailsPanel extends SlicePanel { 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 @@ export class SliceDetailsPanel extends SlicePanel { 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 @@ export class SliceDetailsPanel extends SlicePanel { {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 @@ export class SliceDetailsPanel extends SlicePanel { 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 17b4aeb31..9d6f542e1 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 @@ function getDisplayName(name: string|undefined, id: number|undefined): string| } 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 f7135faaf..b7e2df20c 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 @@ import {globals} from './globals'; // 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 c7031f229..0bc40827c 100644 --- a/ui/src/frontend/thread_state.ts +++ b/ui/src/frontend/thread_state.ts @@ -16,7 +16,11 @@ import {Actions} from '../common/actions'; 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 @@ import { asUtid, SchedSqlId, ThreadStateSqlId, - timestampFromNanos, - toTraceTime, - TPTimestamp, } from './sql_types'; import { constraintsToQueryFragment, @@ -50,10 +51,10 @@ export interface ThreadState { 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 @@ export async function getThreadStateFromConstraints( threadStateSqlId: NUM, schedSqlId: NUM_NULL, ts: LONG, - dur: NUM, + dur: LONG, cpu: NUM_NULL, state: STR_NULL, blockedFunction: STR_NULL, @@ -110,7 +111,7 @@ export async function getThreadStateFromConstraints( 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 @@ export async function getThreadState( 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 @@ export function goToSchedSlice(cpu: number, id: SchedSqlId, ts: TPTimestamp) { 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 @@ function stateToValue( 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 00612eb7a..f1579b9b8 100644 --- a/ui/src/frontend/tickmark_panel.ts +++ b/ui/src/frontend/tickmark_panel.ts @@ -19,6 +19,7 @@ import {fromNs} from '../common/time'; import {TRACK_SHELL_WIDTH} from './css_constants'; import {globals} from './globals'; import { + getMaxMajorTicks, TickGenerator, TickType, timeScaleForVisibleWindow, @@ -32,14 +33,26 @@ export class TickmarkPanel extends Panel { } renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) { - const {timeScale, visibleWindowTime} = globals.frontendLocalState; + const {visibleWindowTime, 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 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(px, 0, 1, size.height); + } } } @@ -47,12 +60,13 @@ export class TickmarkPanel extends Panel { 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) { + if (tEnd <= visibleWindowTime.start.seconds || + tStart >= visibleWindowTime.end.seconds) { continue; } const rectStart = - Math.max(timeScale.timeToPx(tStart), 0) + TRACK_SHELL_WIDTH; - const rectEnd = timeScale.timeToPx(tEnd) + TRACK_SHELL_WIDTH; + Math.max(visibleTimeScale.secondsToPx(tStart), 0) + TRACK_SHELL_WIDTH; + const rectEnd = visibleTimeScale.secondsToPx(tEnd) + TRACK_SHELL_WIDTH; ctx.fillStyle = '#ffe263'; ctx.fillRect( Math.floor(rectStart), @@ -61,16 +75,21 @@ export class TickmarkPanel extends Panel { 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) { + const startSec = fromNs(globals.currentSearchResults.tsStarts[index]); + const triangleStart = + Math.max(visibleTimeScale.secondsToPx(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(); + } + + ctx.restore(); } } diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts index 3f6dc6499..4819d54a9 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 @@ export class TimeAxisPanel extends Panel { 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 6f0307af8..7804348c8 100644 --- a/ui/src/frontend/time_scale.ts +++ b/ui/src/frontend/time_scale.ts @@ -12,95 +12,92 @@ // 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; - - constructor(timeBounds: TimeSpan, boundsPx: [number, number]) { - this.timeBounds = timeBounds; - this._startPx = boundsPx[0]; - this._endPx = boundsPx[1]; - this.updateSlope(); - } - - private updateSlope() { - this.secPerPx = this.timeBounds.duration / (this._endPx - this._startPx); + private _start: HighPrecisionTime; + private _durationNanos: number; + readonly pxSpan: PxSpan; + private _nanosPerPx = 0; + private _startSec: number; + + constructor(start: HighPrecisionTime, durationNanos: number, pxSpan: PxSpan) { + // TODO(stevegolton): Ensure duration & pxSpan > 0. + // assertTrue(pxSpan.start < pxSpan.end, 'Px start >= end'); + // assertTrue(durationNanos < 0, 'Duration <= 0'); + 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.subtract(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); + pxDeltaToDuration(pxDelta: number): HighPrecisionTime { + const time = pxDelta * this._nanosPerPx; + return HighPrecisionTime.fromNanos(time); } +} - get startPx(): number { - return this._startPx; +export class PxSpan { + constructor(private _start: number, private _end: number) { + assertTrue(_start <= _end, 'PxSpan start > end'); } - get endPx(): number { - return this._endPx; + get start(): number { + return this._start; } - get widthPx(): number { - return this._endPx - this._startPx; + get end(): number { + return this._end; } - get timeSpan(): TimeSpan { - return this.timeBounds; + get delta(): number { + return this._end - this._start; } } - -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); -} diff --git a/ui/src/frontend/time_scale_unittest.ts b/ui/src/frontend/time_scale_unittest.ts index 7a1be031d..f7046ded3 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); -}); - -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); -}); + result = ts.pxDeltaToDuration(1); + expect(result.base).toEqual(0n); + expect(result.offset).toBeCloseTo(0.125); -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 20f1c53f1..5711e6a31 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 @@ import { } from './css_constants'; import {globals} from './globals'; import { + getMaxMajorTicks, TickGenerator, TickType, timeScaleForVisibleWindow, @@ -48,7 +53,7 @@ function drawHBar( 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 @@ export class TimeSelectionPanel extends Panel { 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 @@ export class TimeSelectionPanel extends Panel { 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 @@ export class TimeSelectionPanel extends Panel { 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 ca4ced368..0f5ca69cb 100644 --- a/ui/src/frontend/trace_converter.ts +++ b/ui/src/frontend/trace_converter.ts @@ -17,6 +17,7 @@ import { 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 convertToJson(trace: Blob, truncate?: 'start'|'end') { } 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/track.ts b/ui/src/frontend/track.ts index 2d5ab81d0..e82631799 100644 --- a/ui/src/frontend/track.ts +++ b/ui/src/frontend/track.ts @@ -130,9 +130,11 @@ export abstract class Track<Config = {}, Data extends TrackData = TrackData> { 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 +177,7 @@ export abstract class Track<Config = {}, Data extends TrackData = TrackData> { 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; } diff --git a/ui/src/frontend/track_cache.ts b/ui/src/frontend/track_cache.ts index 049cbf43c..2adbd0c7d 100644 --- a/ui/src/frontend/track_cache.ts +++ b/ui/src/frontend/track_cache.ts @@ -52,6 +52,7 @@ export const BUCKETS_PER_PIXEL = 2; // In other words the normal window is a superset of the data of the // non-normal window at a higher resolution. Normalization is used to // avoid re-fetching data on tiny zooms/moves/resizes. +// TODO(stevegolton): Convert to bigint timestamps. export class CacheKey { readonly startNs: number; readonly endNs: number; diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts index c9d109f72..dbab7f7e5 100644 --- a/ui/src/frontend/track_group_panel.ts +++ b/ui/src/frontend/track_group_panel.ts @@ -187,18 +187,17 @@ export class TrackGroupPanel extends Panel<Attrs> { } 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 @@ export class TrackGroupPanel extends Panel<Attrs> { 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 @@ export class TrackGroupPanel extends Panel<Attrs> { globals.sliceDetails.wakeupTs !== undefined) { drawVerticalLineAtTime( ctx, - localState.timeScale, + visibleTimeScale, globals.sliceDetails.wakeupTs, size.height, `black`); @@ -265,21 +264,21 @@ export class TrackGroupPanel extends Panel<Attrs> { '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 9b3c3cc4f..278b73d01 100644 --- a/ui/src/frontend/track_panel.ts +++ b/ui/src/frontend/track_panel.ts @@ -31,6 +31,26 @@ import { drawVerticalLineAtTime, } from './vertical_line_helper'; +function getTitleSize(title: string): string|undefined { + const length = title.length; + if (length > 55) { + return '9px'; + } + if (length > 50) { + return '10px'; + } + if (length > 45) { + return '11px'; + } + if (length > 40) { + return '12px'; + } + if (length > 35) { + return '13px'; + } + return undefined; +} + function isPinned(id: string) { return globals.state.pinnedTracks.indexOf(id) !== -1; } @@ -75,7 +95,6 @@ class TrackShell implements m.ClassComponent<TrackShellAttrs> { `.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), @@ -86,6 +105,9 @@ class TrackShell implements m.ClassComponent<TrackShellAttrs> { 'h1', { title: attrs.trackState.name, + style: { + 'font-size': getTitleSize(attrs.trackState.name), + }, }, attrs.trackState.name, ('namespace' in attrs.trackState.config) && @@ -122,12 +144,6 @@ class TrackShell implements m.ClassComponent<TrackShellAttrs> { '')); } - 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; @@ -135,7 +151,6 @@ class TrackShell implements m.ClassComponent<TrackShellAttrs> { globals.rafScheduler.scheduleFullRedraw(); dataTransfer.setData('perfetto/track', `${this.attrs!.trackState.id}`); dataTransfer.setDragImage(new Image(), 0, 0); - e.stopImmediatePropagation(); } ondragend() { @@ -347,20 +362,20 @@ export class TrackPanel extends Panel<TrackPanelAttrs> { } 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); } } @@ -381,20 +396,20 @@ export class TrackPanel extends Panel<TrackPanelAttrs> { 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`); @@ -405,7 +420,7 @@ export class TrackPanel extends Panel<TrackPanelAttrs> { globals.sliceDetails.wakeupTs !== undefined) { drawVerticalLineAtTime( ctx, - localState.timeScale, + visibleTimeScale, globals.sliceDetails.wakeupTs, size.height, `black`); @@ -419,21 +434,21 @@ export class TrackPanel extends Panel<TrackPanelAttrs> { '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/vertical_line_helper.ts b/ui/src/frontend/vertical_line_helper.ts index b1e2cfc8c..353d166cb 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 996cae6af..1a9e0962a 100644 --- a/ui/src/frontend/viewer_page.ts +++ b/ui/src/frontend/viewer_page.ts @@ -13,9 +13,10 @@ // limitations under the License. import m from 'mithril'; +import {BigintMath} from '../base/bigint_math'; import {Actions} from '../common/actions'; -import {TimeSpan} from '../common/time'; +import {featureFlags} from '../common/feature_flags'; import {TRACK_SHELL_WIDTH} from './css_constants'; import {DetailsPanel} from './details_panel'; @@ -27,7 +28,6 @@ import {PanAndZoomHandler} from './pan_and_zoom_handler'; 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'; @@ -35,6 +35,13 @@ import {TrackPanel} from './track_panel'; const SIDEBAR_WIDTH = 256; +const OVERVIEW_PANEL_FLAG = featureFlags.register({ + id: 'overviewVisible', + name: 'Overview Panel', + description: 'Show the panel providing an overview of the trace', + defaultValue: true, +}); + // Checks if the mousePos is within 3px of the start or end of the // current selected time range. function onTimeRangeBoundary(mousePos: number): 'START'|'END'|null { @@ -45,8 +52,9 @@ function onTimeRangeBoundary(mousePos: number): 'START'|'END'|null { 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); @@ -111,21 +119,14 @@ class TraceViewer implements m.ClassComponent { 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(); @@ -133,11 +134,10 @@ class TraceViewer implements m.ClassComponent { 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) => { @@ -151,7 +151,7 @@ class TraceViewer implements m.ClassComponent { 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; @@ -159,33 +159,40 @@ class TraceViewer implements m.ClassComponent { 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; } @@ -252,12 +259,20 @@ class TraceViewer implements m.ClassComponent { } as TrackGroupAttrs)); } + const overviewPanel = []; + if (OVERVIEW_PANEL_FLAG.get()) { + overviewPanel.push(m(OverviewTimelinePanel, {key: 'overview'})); + } + return m( '.page', m('.split-panel', m('.pan-and-zoom-content', { onclick: () => { + // TODO(stevegolton): Make it possible to click buttons and + // things on this element without deselecting the selected + // element! // We don't want to deselect when panning/drag selecting. if (this.keepCurrentSelection) { this.keepCurrentSelection = false; @@ -269,7 +284,7 @@ class TraceViewer implements m.ClassComponent { m('.pinned-panel-container', m(PanelContainer, { doesScroll: false, panels: [ - m(OverviewTimelinePanel, {key: 'overview'}), + ...overviewPanel, m(TimeAxisPanel, {key: 'timeaxis'}), m(TimeSelectionPanel, {key: 'timeselection'}), m(NotesPanel, {key: 'notes'}), diff --git a/ui/src/frontend/widgets/button.ts b/ui/src/frontend/widgets/button.ts index 28df66a23..ac86e135d 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 @@ -35,6 +36,11 @@ interface CommonAttrs { disabled?: boolean; // Optional right icon. 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; } @@ -63,6 +69,8 @@ export class Button implements m.ClassComponent<ButtonAttrs> { minimal = false, disabled = false, rightIcon, + className, + dismissPopup = false, ...htmlAttrs } = attrs; @@ -72,6 +80,8 @@ export class Button implements m.ClassComponent<ButtonAttrs> { compact && 'pf-compact', minimal && 'pf-minimal', (icon && !label) && 'pf-icon-only', + dismissPopup && Popup.DISMISS_POPUP_GROUP_CLASS, + className, ); return m( diff --git a/ui/src/frontend/widgets/duration.ts b/ui/src/frontend/widgets/duration.ts index 6f36c95bd..cd18e029e 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/form.ts b/ui/src/frontend/widgets/form.ts new file mode 100644 index 000000000..64bf1ee77 --- /dev/null +++ b/ui/src/frontend/widgets/form.ts @@ -0,0 +1,61 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import m from 'mithril'; +import {classNames} from '../classnames'; + +interface FormAttrs { + // List of space separated class names forwarded to the icon. + className?: string; + // Remaining attributes forwarded to the underlying HTML <button>. + [htmlAttrs: string]: any; +} + +export class Form implements m.ClassComponent<FormAttrs> { + view({attrs, children}: m.CVnode<FormAttrs>) { + const {className, ...htmlAttrs} = attrs; + + const classes = classNames( + 'pf-form', + className, + ); + + return m( + 'form.pf-form', + { + class: classes, + ...htmlAttrs, + }, + children, + ); + } +} + +export class FormButtonBar implements m.ClassComponent<{}> { + view({children}: m.CVnode<{}>) { + return m('.pf-form-button-bar', children); + } +} + +interface FormLabelAttrs { + for: string; + // Remaining attributes forwarded to the underlying HTML <button>. + [htmlAttrs: string]: any; +} + +export class FormLabel implements m.ClassComponent<FormLabelAttrs> { + view({attrs, children}: m.CVnode<FormLabelAttrs>) { + return m('label.pf-form-label', attrs, children); + } +} diff --git a/ui/src/frontend/widgets/menu.ts b/ui/src/frontend/widgets/menu.ts index 582e6fbf1..b521dabb6 100644 --- a/ui/src/frontend/widgets/menu.ts +++ b/ui/src/frontend/widgets/menu.ts @@ -68,6 +68,7 @@ export class MenuItem implements m.ClassComponent<MenuItemAttrs> { ...rest, }), showArrow: false, + createNewGroup: false, }, children, ); @@ -86,7 +87,7 @@ export class MenuItem implements m.ClassComponent<MenuItemAttrs> { 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 @@ interface PopupMenu2Attrs { // 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 @@ export class PopupMenu2 implements m.ClassComponent<PopupMenu2Attrs> { { 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 ca6feb8fe..b258a115a 100644 --- a/ui/src/frontend/widgets/popup.ts +++ b/ui/src/frontend/widgets/popup.ts @@ -65,14 +65,19 @@ export interface PopupAttrs { 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 @@ export class Popup implements m.ClassComponent<PopupAttrs> { 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 @@ export class Popup implements m.ClassComponent<PopupAttrs> { const { className, showArrow = true, + createNewGroup = true, } = attrs; const portalAttrs: PortalAttrs = { @@ -168,7 +178,8 @@ export class Popup implements m.ClassComponent<PopupAttrs> { 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 @@ export class Popup implements m.ClassComponent<PopupAttrs> { }; 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 455227535..d8cc841d2 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 @@ interface TimestampAttrs { 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 0428a482a..e79734386 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 @@ export class Tree implements m.ClassComponent<TreeAttrs> { } 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 @@ export class TreeNode implements m.ClassComponent<TreeNodeAttrs> { ); } - 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 @@ export class TreeNode implements m.ClassComponent<TreeNodeAttrs> { 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 2c2ca9b7b..989be89be 100644 --- a/ui/src/frontend/widgets_page.ts +++ b/ui/src/frontend/widgets_page.ts @@ -24,6 +24,7 @@ import {TableShowcase} from './tables/table_showcase'; import {Button} from './widgets/button'; import {Checkbox} from './widgets/checkbox'; import {EmptyState} from './widgets/empty_state'; +import {Form, FormButtonBar, FormLabel} from './widgets/form'; import {Icon} from './widgets/icon'; import {Menu, MenuDivider, MenuItem, PopupMenu2} from './widgets/menu'; import {MultiSelect, MultiSelectDiff} from './widgets/multiselect'; @@ -33,7 +34,7 @@ import {Select} from './widgets/select'; 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, @@ -242,6 +243,18 @@ class WidgetShowcase implements m.ClassComponent<WidgetShowcaseAttrs> { } } +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() { @@ -540,7 +553,7 @@ export const WidgetsPage = createPage({ PopupMenu2, { trigger: m(Anchor, { - text: 'SELECT * FROM raw WHERE id = 123', + text: 'SELECT * FROM ftrace_event WHERE id = 123', icon: 'unfold_more', }), }, @@ -562,9 +575,14 @@ export const WidgetsPage = createPage({ 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( @@ -585,6 +603,45 @@ export const WidgetsPage = createPage({ }, wide: true, }), + m('h2', 'Form'), + m( + WidgetShowcase, { + renderWidget: () => m( + Form, + m(FormLabel, {for: 'foo'}, 'Foo'), + m(TextInput, {id: 'foo'}), + m(FormLabel, {for: 'bar'}, 'Bar'), + m(Select, {id: 'bar'}, [ + m('option', {value: 'foo', label: 'Foo'}), + m('option', {value: 'bar', label: 'Bar'}), + m('option', {value: 'baz', label: 'Baz'}), + ]), + m(FormButtonBar, + m(Button, {label: 'Submit', rightIcon: 'chevron_right'}), + 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 714666a08..df3e8a000 100644 --- a/ui/src/traceconv/index.ts +++ b/ui/src/traceconv/index.ts @@ -18,6 +18,7 @@ import { 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 @@ interface ConvertTraceToPprofArgs { kind: 'ConvertTraceToPprof'; trace: Blob; pid: number; - ts: number; + ts: TPTime; } function isConvertTraceToPprof(msg: Args): msg is ConvertTraceToPprofArgs { @@ -186,8 +187,7 @@ function isConvertTraceToPprof(msg: Args): msg is ConvertTraceToPprofArgs { 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 927dea442..7f5afb0fc 100644 --- a/ui/src/tracks/actual_frames/index.ts +++ b/ui/src/tracks/actual_frames/index.ts @@ -14,7 +14,7 @@ import {PluginContext} from '../../common/plugin_api'; import {NUM, NUM_NULL, STR} from '../../common/query_result'; -import {fromNs, toNs} from '../../common/time'; +import {fromNs, TPDuration, TPTime, tpTimeToNanos} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {TrackController} from '../../controller/track_controller'; import {NewTrackArgs, Track} from '../../frontend/track'; @@ -51,16 +51,17 @@ class ActualFramesSliceTrackController extends TrackController<Config, Data> { static readonly kind = ACTUAL_FRAMES_SLICE_TRACK_KIND; private maxDurNs = 0; - 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 startNs = tpTimeToNanos(start); + const endNs = tpTimeToNanos(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 bucketNs = + Math.max(Math.round(Number(resolution) * pxSize / 2) * 2, 1); if (this.maxDurNs === 0) { const maxDurResult = await this.query(` diff --git a/ui/src/tracks/android_log/index.ts b/ui/src/tracks/android_log/index.ts index b2a824bf2..6c8a1bc03 100644 --- a/ui/src/tracks/android_log/index.ts +++ b/ui/src/tracks/android_log/index.ts @@ -14,7 +14,7 @@ import {PluginContext} from '../../common/plugin_api'; import {NUM} from '../../common/query_result'; -import {fromNs, toNsCeil, toNsFloor} from '../../common/time'; +import {fromNs, TPDuration, TPTime} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {LIMIT} from '../../common/track_data'; import { @@ -61,21 +61,15 @@ const EVT_PX = 2; // Width of an event tick in pixels. 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};`); @@ -113,16 +107,16 @@ class AndroidLogTrack extends Track<Config, Data> { } 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 +127,7 @@ class AndroidLogTrack extends Track<Config, Data> { 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 +137,7 @@ class AndroidLogTrack extends Track<Config, Data> { } if (!hasEventsForCurColor) continue; ctx.fillStyle = LEVELS[lev].color; - const px = Math.floor(timeScale.timeToPx(data.timestamps[i])); + const px = Math.floor(visibleTimeScale.secondsToPx(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 338e02a22..997577f71 100644 --- a/ui/src/tracks/async_slices/index.ts +++ b/ui/src/tracks/async_slices/index.ts @@ -13,8 +13,8 @@ // limitations under the License. import {PluginContext} from '../../common/plugin_api'; -import {NUM, NUM_NULL, STR} from '../../common/query_result'; -import {fromNs, toNs} from '../../common/time'; +import {LONG_NULL, NUM, STR} from '../../common/query_result'; +import {fromNs, TPDuration, TPTime} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -43,26 +43,24 @@ export interface Data extends TrackData { 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); + const bucketNs = + Math.max(Math.round(Number(resolution) * 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(` @@ -78,8 +76,8 @@ class AsyncSliceTrackController extends TrackController<Config, Data> { 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 `); diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts index 23f9a5e1a..4e7050782 100644 --- a/ui/src/tracks/chrome_slices/index.ts +++ b/ui/src/tracks/chrome_slices/index.ts @@ -15,9 +15,16 @@ 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_NULL, NUM, NUM_NULL, STR} from '../../common/query_result'; +import { + fromNs, + TPDuration, + TPTime, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -60,27 +67,25 @@ export interface Data extends TrackData { 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 bucketNs = + Math.max(Math.round(Number(resolution) * 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; + this.maxDurNs = queryRes.firstRow({maxDur: LONG_NULL}).maxDur || 0n; } // Buckets are always even and positive, don't quantize once we zoom to @@ -103,8 +108,8 @@ export class ChromeSliceTrackController extends TrackController<Config, Data> { 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); @@ -209,7 +214,7 @@ export class ChromeSliceTrack extends Track<Config, Data> { 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 +224,10 @@ export class ChromeSliceTrack extends Track<Config, Data> { 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 +250,7 @@ export class ChromeSliceTrack extends Track<Config, Data> { const title = data.strings[titleId]; const colorOverride = data.colors && data.strings[data.colors[i]]; if (isIncomplete) { // incomplete slice - tEnd = visibleWindowTime.end; + tEnd = visibleWindowTime.end.seconds; } const rect = this.getSliceRect(tStart, tEnd, depth); @@ -369,10 +374,14 @@ export class ChromeSliceTrack extends Track<Config, Data> { 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 instantWidthTimeSec = instantWidthTime.seconds; + const t = timeScale.pxToHpTime(x).seconds; const depth = Math.floor((y - TRACK_PADDING) / SLICE_HEIGHT); for (let i = 0; i < data.starts.length; i++) { if (depth !== data.depths[i]) { @@ -380,13 +389,13 @@ export class ChromeSliceTrack extends Track<Config, Data> { } const tStart = data.starts[i]; if (data.isInstant[i]) { - if (Math.abs(tStart - t) < instantWidthTime) { + if (Math.abs(tStart - t) < instantWidthTimeSec) { return i; } } else { let tEnd = data.ends[i]; if (data.isIncomplete[i]) { - tEnd = globals.frontendLocalState.visibleWindowTime.end; + tEnd = visibleWindowTime.end.seconds; } if (tStart <= t && t <= tEnd) { return i; @@ -435,17 +444,28 @@ export class ChromeSliceTrack extends Track<Config, Data> { getSliceRect(tStart: number, tEnd: number, 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.secondsToPx(tStart), 0); + const right = Math.min(timeScale.secondsToPx(tEnd), pxEnd); + + const visible = + !(visibleWindowTime.start.isGreaterThan( + HighPrecisionTime.fromSeconds(tEnd)) || + visibleWindowTime.end.isLessThan( + HighPrecisionTime.fromSeconds(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 e49549945..f22e8c7c5 100644 --- a/ui/src/tracks/counter/index.ts +++ b/ui/src/tracks/counter/index.ts @@ -19,21 +19,27 @@ import {assertTrue} from '../../base/logging'; import {Actions} from '../../common/actions'; import { EngineProxy, + LONG_NULL, NUM, - NUM_NULL, PluginContext, STR, TrackInfo, } from '../../common/plugin_api'; -import {fromNs, toNs} from '../../common/time'; +import { + fromNs, + TPDuration, + TPTime, + tpTimeFromSeconds, +} 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 {PopupMenuButton, PopupMenuItem} from '../../frontend/popup_menu'; import {NewTrackArgs, Track} from '../../frontend/track'; +import {Button} from '../../frontend/widgets/button'; +import {MenuItem, PopupMenu2} from '../../frontend/widgets/menu'; export const COUNTER_TRACK_KIND = 'CounterTrack'; @@ -61,8 +67,8 @@ export interface Config { name: string; maximumValue?: number; minimumValue?: number; - startTs?: number; - endTs?: number; + startTs?: TPTime; + endTs?: TPTime; namespace: string; trackId: number; scale?: CounterScaleOptions; @@ -75,18 +81,16 @@ class CounterTrackController extends TrackController<Config, Data> { 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); + const bucketNs = + Math.max(Math.round(Number(resolution) * pxSize / 2) * 2, 1); if (!this.setup) { if (this.config.namespace === undefined) { @@ -122,7 +126,7 @@ class CounterTrackController extends TrackController<Config, Data> { ) 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 @@ -150,7 +154,7 @@ class CounterTrackController extends TrackController<Config, Data> { 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 `); @@ -259,18 +263,11 @@ class CounterTrack extends Track<Config, Data> { {name: 'DELTA_FROM_PREVIOUS', humanName: 'Delta'}, {name: 'RATE', humanName: 'Rate'}, ]; - const items: PopupMenuItem[] = []; - for (const scale of scales) { - let text; - if (currentScale === scale.name) { - text = `*${scale.humanName}*`; - } else { - text = scale.humanName; - } - items.push({ - itemType: 'regular', - text, - callback: () => { + const menuItems = scales.map((scale) => { + return m(MenuItem, { + label: scale.humanName, + active: currentScale === scale.name, + onclick: () => { this.config.scale = scale.name; Actions.updateTrackConfig({ id: this.trackState.id, @@ -278,16 +275,23 @@ class CounterTrack extends Track<Config, Data> { }); }, }); - } - return m(PopupMenuButton, { - icon: 'show_chart', - items, }); + + return m( + PopupMenu2, + { + trigger: m(Button, {icon: 'show_chart', minimal: true}), + }, + menuItems, + ); } 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. @@ -323,7 +327,7 @@ class CounterTrack extends Track<Config, Data> { 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). @@ -368,7 +372,7 @@ class CounterTrack extends Track<Config, Data> { ctx.strokeStyle = `hsl(${hue}, 45%, 45%)`; const calculateX = (ts: number) => { - return Math.floor(timeScale.timeToPx(ts)); + return Math.floor(timeScale.secondsToPx(ts)); }; const calculateY = (value: number) => { return MARGIN_TOP + RECT_HEIGHT - @@ -429,10 +433,10 @@ class CounterTrack extends Track<Config, Data> { 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.secondsToPx(this.hoveredTs)); const xEnd = this.hoveredTsEnd === undefined ? endPx : - Math.floor(timeScale.timeToPx(this.hoveredTsEnd)); + Math.floor(timeScale.secondsToPx(this.hoveredTsEnd)); const y = MARGIN_TOP + RECT_HEIGHT - Math.round(((this.hoveredValue - yMin) / yRange) * RECT_HEIGHT); @@ -465,9 +469,10 @@ class CounterTrack extends Track<Config, Data> { // 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) { @@ -481,18 +486,18 @@ class CounterTrack extends Track<Config, Data> { 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).seconds; const values = this.config.scale === 'DELTA_FROM_PREVIOUS' ? data.totalDeltas : @@ -511,8 +516,8 @@ class CounterTrack extends Track<Config, Data> { onMouseClick({x}: {x: number}) { const data = this.data(); if (data === undefined) return false; - const {timeScale} = globals.frontendLocalState; - const time = timeScale.pxToTime(x); + const {visibleTimeScale} = globals.frontendLocalState; + const time = visibleTimeScale.pxToHpTime(x).seconds; const [left, right] = searchSegment(data.timestamps, time); if (left === -1) { return false; @@ -520,8 +525,8 @@ class CounterTrack extends Track<Config, Data> { 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: tpTimeFromSeconds(data.timestamps[left]), + rightTs: tpTimeFromSeconds(right !== -1 ? data.timestamps[right] : -1), 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 8bb2e3b58..795617281 100644 --- a/ui/src/tracks/cpu_freq/index.ts +++ b/ui/src/tracks/cpu_freq/index.ts @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {BigintMath} 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 {fromNs, TPDuration, TPTime, tpTimeToNanos} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -96,15 +97,17 @@ class CpuFreqTrackController extends TrackController<Config, Data> { this.cachedBucketNs = bucketNs; } - 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( + BigintMath.popcount(resolution) === 1, + `${resolution} is not a power of 2`); + const resolutionNs = Number(resolution); - const startNs = toNs(start); - const endNs = toNs(end); + const startNs = tpTimeToNanos(start); + const endNs = tpTimeToNanos(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. @@ -289,7 +292,11 @@ class CpuFreqTrack extends Track<Config, Data> { 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 +309,7 @@ class CpuFreqTrack extends Track<Config, Data> { 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). @@ -326,17 +333,18 @@ class CpuFreqTrack extends Track<Config, Data> { ctx.strokeStyle = `hsl(${hue}, ${saturation}%, 55%)`; const calculateX = (timestamp: number) => { - return Math.floor(timeScale.timeToPx(timestamp)); + return Math.floor(visibleTimeScale.secondsToPx(timestamp)); }; const calculateY = (value: number) => { return zeroY - Math.round((value / yMax) * RECT_HEIGHT); }; - const [rawStartIdx] = - searchSegment(data.timestamps, visibleWindowTime.start); + const startSec = visibleWindowTime.start.seconds; + const endSec = visibleWindowTime.end.seconds; + const [rawStartIdx] = searchSegment(data.timestamps, startSec); const startIdx = rawStartIdx === -1 ? 0 : rawStartIdx; - const [, rawEndIdx] = searchSegment(data.timestamps, visibleWindowTime.end); + const [, rawEndIdx] = searchSegment(data.timestamps, endSec); const endIdx = rawEndIdx === -1 ? data.timestamps.length : rawEndIdx; ctx.beginPath(); @@ -383,10 +391,10 @@ class CpuFreqTrack extends Track<Config, Data> { // 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.secondsToPx(data.timestamps[i]); const xEnd = i === data.lastIdleValues.length - 1 ? finalX : - timeScale.timeToPx(data.timestamps[i + 1]); + visibleTimeScale.secondsToPx(data.timestamps[i + 1]); const width = xEnd - x; const height = calculateY(data.lastFreqKHz[i]) - zeroY; @@ -402,10 +410,10 @@ class CpuFreqTrack extends Track<Config, Data> { 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.secondsToPx(this.hoveredTs)); const xEnd = this.hoveredTsEnd === undefined ? endPx : - Math.floor(timeScale.timeToPx(this.hoveredTsEnd)); + Math.floor(visibleTimeScale.secondsToPx(this.hoveredTsEnd)); const y = zeroY - Math.round((this.hoveredValue / yMax) * RECT_HEIGHT); // Highlight line. @@ -446,18 +454,18 @@ class CpuFreqTrack extends Track<Config, Data> { 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).seconds; const [left, right] = searchSegment(data.timestamps, time); this.hoveredTs = left === -1 ? undefined : data.timestamps[left]; diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts index eee7b17fc..028d5b112 100644 --- a/ui/src/tracks/cpu_profile/index.ts +++ b/ui/src/tracks/cpu_profile/index.ts @@ -18,7 +18,7 @@ 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 {fromNs, TPDuration, TPTime} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -46,7 +46,7 @@ export interface Config { 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, @@ -105,7 +105,7 @@ class CpuProfileTrack extends Track<Config, Data> { renderCanvas(ctx: CanvasRenderingContext2D): void { const { - timeScale, + visibleTimeScale: timeScale, } = globals.frontendLocalState; const data = this.data(); @@ -120,7 +120,7 @@ class CpuProfileTrack extends Track<Config, Data> { const strokeWidth = isSelected ? 3 : 0; this.drawMarker( ctx, - timeScale.timeToPx(fromNs(centerX)), + timeScale.secondsToPx(fromNs(centerX)), this.centerY, isHovered, strokeWidth, @@ -146,8 +146,8 @@ class CpuProfileTrack extends Track<Config, Data> { 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.secondsToPx(fromNs(startX)) - this.markerWidth; + const rightPx = timeScale.secondsToPx(fromNs(endX)) + this.markerWidth; const width = rightPx - leftPx; ctx.fillStyle = colorForSample(callsiteId, false); ctx.fillRect(leftPx, MARGIN_TOP, width, BAR_HEIGHT); @@ -179,8 +179,10 @@ class CpuProfileTrack extends Track<Config, Data> { 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 { + visibleTimeScale: timeScale, + } = globals.frontendLocalState; + const time = timeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStarts, time); const index = this.findTimestampIndex(left, timeScale, data, x, y, right); this.hoveredTs = index === -1 ? undefined : data.tsStarts[index]; @@ -193,9 +195,11 @@ class CpuProfileTrack extends Track<Config, Data> { 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 time = timeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStarts, time); const index = this.findTimestampIndex(left, timeScale, data, x, y, right); @@ -217,13 +221,13 @@ class CpuProfileTrack extends Track<Config, Data> { right: number): number { let index = -1; if (left !== -1) { - const centerX = timeScale.timeToPx(fromNs(data.tsStarts[left])); + const centerX = timeScale.secondsToPx(fromNs(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.secondsToPx(fromNs(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 0953d2ff1..4264069a9 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} from '../../base/bigint_math'; import {search, searchEq, searchSegment} from '../../base/binary_search'; import {assertTrue} from '../../base/logging'; import {Actions} from '../../common/actions'; @@ -23,7 +24,15 @@ import { 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 { + fromNs, + toNs, + TPDuration, + TPTime, + tpTimeFromSeconds, + tpTimeToNanos, + tpTimeToString, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -102,16 +111,19 @@ class CpuSliceTrackController extends TrackController<Config, Data> { this.cachedBucketNs = bucketNs; } - async onBoundsChange(start: number, end: number, resolution: number): + async onBoundsChange(start: TPTime, end: TPTime, resolution: TPDuration): Promise<Data> { - const resolutionNs = toNs(resolution); + assertTrue( + BigintMath.popcount(resolution) === 1, + `${resolution} is not a power of 2`); + const resolutionNs = Number(resolution); // 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); + const boundStartNs = tpTimeToNanos(start); + const boundEndNs = tpTimeToNanos(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. @@ -223,7 +235,7 @@ class CpuSliceTrack extends Track<Config, Data> { 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 +245,35 @@ class CpuSliceTrack extends Track<Config, Data> { 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, + 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 startSec = visibleWindowTime.start.seconds; + const endSec = visibleWindowTime.end.seconds; + + const rawStartIdx = data.ends.findIndex((end) => end >= startSec); const startIdx = rawStartIdx === -1 ? 0 : rawStartIdx; - const [, rawEndIdx] = searchSegment(data.starts, visibleWindowTime.end); + const [, rawEndIdx] = searchSegment(data.starts, endSec); const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx; for (let i = startIdx; i < endIdx; i++) { @@ -266,10 +285,10 @@ class CpuSliceTrack extends Track<Config, Data> { // 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 = visibleWindowTime.end.seconds; } - const rectStart = timeScale.timeToPx(tStart); - const rectEnd = timeScale.timeToPx(tEnd); + const rectStart = visibleTimeScale.secondsToPx(tStart); + const rectEnd = visibleTimeScale.secondsToPx(tEnd); const rectWidth = Math.max(1, rectEnd - rectStart); const threadInfo = globals.threads.get(utid); @@ -317,8 +336,7 @@ class CpuSliceTrack extends Track<Config, Data> { 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 +359,8 @@ class CpuSliceTrack extends Track<Config, Data> { 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.secondsToPx(tStart); + const rectEnd = visibleTimeScale.secondsToPx(tEnd); const rectWidth = Math.max(1, rectEnd - rectStart); // Draw a rectangle around the slice that is currently selected. @@ -353,7 +371,7 @@ class CpuSliceTrack extends Track<Config, Data> { 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 +380,8 @@ class CpuSliceTrack extends Track<Config, Data> { latencyWidth, latencyWidth >= 20); // Latency time with a white semi-transparent background. - const displayText = timeToString(tStart - details.wakeupTs); + const latency = tpTimeFromSeconds(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 +402,8 @@ class CpuSliceTrack extends Track<Config, Data> { // 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,13 +431,13 @@ class CpuSliceTrack extends Track<Config, Data> { 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).seconds; let hoveredUtid = -1; for (let i = 0; i < data.starts.length; i++) { @@ -445,8 +465,8 @@ class CpuSliceTrack extends Track<Config, Data> { onMouseClick({x}: {x: number}) { const data = this.data(); if (data === undefined) return false; - const {timeScale} = globals.frontendLocalState; - const time = timeScale.pxToTime(x); + const {visibleTimeScale} = globals.frontendLocalState; + const time = visibleTimeScale.pxToHpTime(x).seconds; const index = search(data.starts, time); const id = index === -1 ? undefined : data.ids[index]; if (!id || this.utidHoveredInThisTrack === -1) return false; diff --git a/ui/src/tracks/debug/add_debug_track_menu.ts b/ui/src/tracks/debug/add_debug_track_menu.ts index 8b0441562..d14bfd85d 100644 --- a/ui/src/tracks/debug/add_debug_track_menu.ts +++ b/ui/src/tracks/debug/add_debug_track_menu.ts @@ -16,9 +16,10 @@ import m from 'mithril'; import {EngineProxy} from '../../common/engine'; import {Button} from '../../frontend/widgets/button'; +import {Form, FormButtonBar, FormLabel} from '../../frontend/widgets/form'; import {Select} from '../../frontend/widgets/select'; import {TextInput} from '../../frontend/widgets/text_input'; -import {Tree, TreeNode} from '../../frontend/widgets/tree'; + import {addDebugTrack, SliceColumns} from './slice_track'; export const ARG_PREFIX = 'arg_'; @@ -67,51 +68,59 @@ export class AddDebugTrackMenu implements }, column)); } - return m(TreeNode, { - left: name, - right: m( - Select, - { - oninput: (e: Event) => { - if (!e.target) return; - this.sliceColumns[name] = (e.target as HTMLSelectElement).value; - }, + return [ + m(FormLabel, + {for: name, + }, + name), + m(Select, + { + id: name, + oninput: (e: Event) => { + if (!e.target) return; + this.sliceColumns[name] = (e.target as HTMLSelectElement).value; }, - options), - }); + }, + options), + ]; }; - return [ - m( - Tree, - m(TreeNode, { - left: 'Name', - right: m(TextInput, { - onkeydown: (e: KeyboardEvent) => { - // Allow Esc to close popup. - if (e.key === 'Escape') return; - e.stopPropagation(); - }, - oninput: (e: KeyboardEvent) => { - if (!e.target) return; - this.name = (e.target as HTMLInputElement).value; + return m( + Form, + m(FormLabel, + {for: 'track_name', + }, + 'Name'), + m(TextInput, { + id: 'track_name', + onkeydown: (e: KeyboardEvent) => { + // Allow Esc to close popup. + if (e.key === 'Escape') return; + e.stopPropagation(); + }, + oninput: (e: KeyboardEvent) => { + if (!e.target) return; + this.name = (e.target as HTMLInputElement).value; + }, + }), + renderSelect('ts'), + renderSelect('dur'), + renderSelect('name'), + m( + FormButtonBar, + m(Button, { + label: 'Show', + dismissPopup: true, + onclick: (e: Event) => { + e.preventDefault(); + addDebugTrack( + vnode.attrs.engine, + vnode.attrs.sqlViewName, + this.name, + this.sliceColumns, + vnode.attrs.columns); }, }), - }), - renderSelect('ts'), - renderSelect('dur'), - renderSelect('name'), - ), - m(Button, { - label: 'Show', - onclick: () => { - addDebugTrack( - vnode.attrs.engine, - vnode.attrs.sqlViewName, - this.name, - this.sliceColumns, - vnode.attrs.columns); - }, - }), - ]; + ), + ); } } diff --git a/ui/src/tracks/debug/details_tab.ts b/ui/src/tracks/debug/details_tab.ts index c168eb6c4..50c231985 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 @@ function SqlValueToString(val: ColumnType) { 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 @@ export class DebugSliceDetailsTab extends BottomTab<DebugSliceDetalsTabConfig> { 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 @@ export class DebugSliceDetailsTab extends BottomTab<DebugSliceDetalsTabConfig> { } } 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 a871a9fae..664840dd4 100644 --- a/ui/src/tracks/debug/slice_track.ts +++ b/ui/src/tracks/debug/slice_track.ts @@ -78,8 +78,8 @@ export class DebugTrackV2 extends NamedSliceTrack<DebugTrackV2Types> { 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 f2ab086b4..f7e5121d8 100644 --- a/ui/src/tracks/expected_frames/index.ts +++ b/ui/src/tracks/expected_frames/index.ts @@ -19,8 +19,8 @@ export const EXPECTED_FRAMES_SLICE_TRACK_KIND = 'ExpectedFramesSliceTrack'; 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_NULL, NUM, STR} from '../../common/query_result'; +import {TPDuration, TPTime, fromNs} from '../../common/time'; import { TrackController, } from '../../controller/track_controller'; @@ -46,27 +46,25 @@ export interface Data extends TrackData { 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); + const bucketNs = + Math.max(Math.round(Number(resolution) * 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(` @@ -82,8 +80,8 @@ class ExpectedFramesSliceTrackController extends TrackController<Config, Data> { 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 `); diff --git a/ui/src/tracks/ftrace/index.ts b/ui/src/tracks/ftrace/index.ts index e82934dae..8cd40f330 100644 --- a/ui/src/tracks/ftrace/index.ts +++ b/ui/src/tracks/ftrace/index.ts @@ -17,7 +17,8 @@ import {Vnode} from 'mithril'; 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 {fromNs, TPDuration} from '../../common/time'; +import {TPTime} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {LIMIT} from '../../common/track_data'; import { @@ -29,12 +30,7 @@ import {NewTrackArgs, Track} from '../../frontend/track'; export interface Data extends TrackData { - // Total number of events within [start, end], before any quantization. - numEvents: number; - - // Below: data quantized by resolution and aggregated by event priority. timestamps: Float64Array; - names: string[]; } @@ -51,14 +47,8 @@ const TRACK_HEIGHT = (RECT_HEIGHT) + (2 * MARGIN); 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 = @@ -66,35 +56,32 @@ class FtraceRawTrackController extends TrackController<Config, Data> { const queryRes = await this.query(` select - cast(ts / ${quantNs} as integer) * ${quantNs} as tsQuant, + cast(ts / ${resolution} as integer) * ${resolution} as tsQuant, type, - count(type) as numEvents, 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};`); const rowCount = queryRes.numRows(); - const result = { + const result: Data = { start, end, resolution, length: rowCount, - numEvents: 0, timestamps: new Float64Array(rowCount), names: [], - } as Data; + }; const it = queryRes.iter( - {tsQuant: NUM, type: STR, numEvents: NUM, name: STR}, + {tsQuant: NUM, type: STR, name: STR}, ); for (let row = 0; it.valid(); it.next(), row++) { result.timestamps[row] = fromNs(it.tsQuant); result.names[row] = it.name; - result.numEvents += it.numEvents; } return result; } @@ -115,16 +102,19 @@ export class FtraceRawTrack extends Track<Config, Data> { } 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, @@ -145,7 +135,7 @@ export class FtraceRawTrack extends Track<Config, Data> { ${Math.min(color.l + 10, 60)}% )`; ctx.fillStyle = hsl; - const xPos = Math.floor(timeScale.timeToPx(data.timestamps[i])); + const xPos = Math.floor(visibleTimeScale.secondsToPx(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 b3eb40ae4..3e656f03b 100644 --- a/ui/src/tracks/heap_profile/index.ts +++ b/ui/src/tracks/heap_profile/index.ts @@ -18,7 +18,13 @@ 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 { + fromNs, + TPDuration, + TPTime, + tpTimeFromNanos, + tpTimeFromSeconds, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {profileType} from '../../controller/flamegraph_controller'; import { @@ -42,7 +48,7 @@ export interface Config { 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 { @@ -111,7 +117,7 @@ class HeapProfileTrack extends Track<Config, Data> { renderCanvas(ctx: CanvasRenderingContext2D): void { const { - timeScale, + visibleTimeScale: timeScale, } = globals.frontendLocalState; const data = this.data(); @@ -122,11 +128,12 @@ class HeapProfileTrack extends Track<Config, Data> { const selection = globals.state.currentSelection; const isHovered = this.hoveredTs === centerX; const isSelected = selection !== null && - selection.kind === 'HEAP_PROFILE' && selection.ts === centerX; + selection.kind === 'HEAP_PROFILE' && + selection.ts === tpTimeFromSeconds(centerX); const strokeWidth = isSelected ? 3 : 0; this.drawMarker( ctx, - timeScale.timeToPx(fromNs(centerX)), + timeScale.secondsToPx(fromNs(centerX)), this.centerY, isHovered, strokeWidth); @@ -155,8 +162,10 @@ class HeapProfileTrack extends Track<Config, Data> { 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 { + visibleTimeScale: timeScale, + } = globals.frontendLocalState; + const time = timeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStarts, time); const index = this.findTimestampIndex(left, timeScale, data, x, y, right); this.hoveredTs = index === -1 ? undefined : data.tsStarts[index]; @@ -169,15 +178,18 @@ class HeapProfileTrack extends Track<Config, Data> { 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 time = timeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStarts, time); const index = this.findTimestampIndex(left, timeScale, data, x, y, right); if (index !== -1) { - const ts = data.tsStarts[index]; + // TODO(stevegolton): Remove conversion from number to bigint. + const ts = tpTimeFromNanos(data.tsStarts[index]); const type = data.types[index]; globals.makeSelection(Actions.selectHeapProfile( {id: index, upid: this.config.upid, ts, type})); @@ -192,13 +204,13 @@ class HeapProfileTrack extends Track<Config, Data> { right: number): number { let index = -1; if (left !== -1) { - const centerX = timeScale.timeToPx(fromNs(data.tsStarts[left])); + const centerX = timeScale.secondsToPx(fromNs(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.secondsToPx(fromNs(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 cfc73dd32..693e2953e 100644 --- a/ui/src/tracks/perf_samples_profile/index.ts +++ b/ui/src/tracks/perf_samples_profile/index.ts @@ -17,7 +17,12 @@ import {Actions} from '../../common/actions'; import {PluginContext} from '../../common/plugin_api'; import {NUM} from '../../common/query_result'; import {ProfileType} from '../../common/state'; -import {fromNs, toNs} from '../../common/time'; +import { + fromNs, + TPDuration, + TPTime, + tpTimeFromSeconds, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -39,7 +44,7 @@ export interface Config { 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 { @@ -99,7 +104,7 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { renderCanvas(ctx: CanvasRenderingContext2D): void { const { - timeScale, + visibleTimeScale, } = globals.frontendLocalState; const data = this.data(); @@ -115,7 +120,7 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { const strokeWidth = isSelected ? 3 : 0; this.drawMarker( ctx, - timeScale.timeToPx(fromNs(centerX)), + visibleTimeScale.secondsToPx(fromNs(centerX)), this.centerY, isHovered, strokeWidth); @@ -144,10 +149,11 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { 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 {visibleTimeScale} = globals.frontendLocalState; + const time = visibleTimeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStartsNs, time); - const index = this.findTimestampIndex(left, timeScale, data, x, y, right); + const index = + this.findTimestampIndex(left, visibleTimeScale, data, x, y, right); this.hoveredTs = index === -1 ? undefined : data.tsStartsNs[index]; } @@ -158,9 +164,11 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { 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 time = timeScale.pxToHpTime(x).nanos; const [left, right] = searchSegment(data.tsStartsNs, time); const index = this.findTimestampIndex(left, timeScale, data, x, y, right); @@ -170,8 +178,8 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { globals.makeSelection(Actions.selectPerfSamples({ id: index, upid: this.config.upid, - leftTs: ts, - rightTs: ts, + leftTs: tpTimeFromSeconds(ts), + rightTs: tpTimeFromSeconds(ts), type: ProfileType.PERF_SAMPLE, })); return true; @@ -185,13 +193,13 @@ class PerfSamplesProfileTrack extends Track<Config, Data> { right: number): number { let index = -1; if (left !== -1) { - const centerX = timeScale.timeToPx(fromNs(data.tsStartsNs[left])); + const centerX = timeScale.secondsToPx(fromNs(data.tsStartsNs[left])); if (this.isInMarker(x, y, centerX)) { index = left; } } if (right !== -1) { - const centerX = timeScale.timeToPx(fromNs(data.tsStartsNs[right])); + const centerX = timeScale.secondsToPx(fromNs(data.tsStartsNs[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 95302b6cd..d88bc71a2 100644 --- a/ui/src/tracks/process_scheduling/index.ts +++ b/ui/src/tracks/process_scheduling/index.ts @@ -12,13 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {BigintMath} 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 { + fromNs, + TPDuration, + TPTime, + tpTimeFromSeconds, + tpTimeToNanos, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import { TrackController, @@ -91,22 +98,23 @@ class ProcessSchedulingTrackController extends TrackController<Config, Data> { this.cachedBucketNs = bucketNs; } - 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); + assertTrue( + BigintMath.popcount(resolution) === 1, + `${resolution} is not a power of 2`); - const startNs = toNs(start); - const endNs = toNs(end); + const startNs = tpTimeToNanos(start); + const endNs = tpTimeToNanos(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); + Math.max(Math.round(Number(resolution) * this.pxSize() / 2) * 2, 1); const queryRes = await this.queryData(startNs, endNs, bucketNs); const numRows = queryRes.numRows(); @@ -144,7 +152,8 @@ class ProcessSchedulingTrackController extends TrackController<Config, Data> { slices.ends[row] = fromNs(endNsQ); slices.cpus[row] = it.cpu; slices.utids[row] = it.utid; - slices.end = Math.max(slices.ends[row], slices.end); + slices.end = + BigintMath.max(tpTimeFromSeconds(slices.ends[row]), slices.end); } return slices; } @@ -208,7 +217,10 @@ class ProcessSchedulingTrack extends Track<Config, Data> { 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 +230,20 @@ class ProcessSchedulingTrack extends Track<Config, Data> { 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 startSeconds = visibleWindowTime.start.seconds; + const rawStartIdx = data.ends.findIndex((end) => end >= startSeconds); const startIdx = rawStartIdx === -1 ? data.starts.length : rawStartIdx; - const [, rawEndIdx] = searchSegment(data.starts, visibleWindowTime.end); + const [, rawEndIdx] = + searchSegment(data.starts, visibleWindowTime.end.seconds); const endIdx = rawEndIdx === -1 ? data.starts.length : rawEndIdx; const cpuTrackHeight = Math.floor(RECT_HEIGHT / data.maxCpu); @@ -241,8 +254,8 @@ class ProcessSchedulingTrack extends Track<Config, Data> { const utid = data.utids[i]; const cpu = data.cpus[i]; - const rectStart = timeScale.timeToPx(tStart); - const rectEnd = timeScale.timeToPx(tEnd); + const rectStart = visibleTimeScale.secondsToPx(tStart); + const rectEnd = visibleTimeScale.secondsToPx(tEnd); const rectWidth = rectEnd - rectStart; if (rectWidth < 0.3) continue; @@ -294,8 +307,8 @@ class ProcessSchedulingTrack extends Track<Config, Data> { 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).seconds; 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 d2d0ee8fe..8b9251164 100644 --- a/ui/src/tracks/process_summary/index.ts +++ b/ui/src/tracks/process_summary/index.ts @@ -15,7 +15,14 @@ import {colorForTid} from '../../common/colorizer'; import {PluginContext} from '../../common/plugin_api'; import {NUM} from '../../common/query_result'; -import {fromNs, toNs} from '../../common/time'; +import { + fromNs, + TPDuration, + TPTime, + tpTimeFromNanos, + tpTimeToNanos, + tpTimeToSeconds, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {LIMIT} from '../../common/track_data'; import { @@ -45,10 +52,10 @@ class ProcessSummaryTrackController extends TrackController<Config, Data> { 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); + const startNs = tpTimeToNanos(start); + const endNs = tpTimeToNanos(end); if (this.setup === false) { await this.query( @@ -85,9 +92,9 @@ class ProcessSummaryTrackController extends TrackController<Config, Data> { 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 bucketSizeNs = Math.max(1, Math.round(Number(resolution) * 10)); const windowStartNs = Math.floor(startNs / bucketSizeNs) * bucketSizeNs; const windowDurNs = Math.max(1, endNs - windowStartNs); @@ -98,14 +105,14 @@ class ProcessSummaryTrackController extends TrackController<Config, Data> { where rowid = 0;`); return this.computeSummary( - fromNs(windowStartNs), end, resolution, bucketSizeNs); + tpTimeFromNanos(windowStartNs), end, resolution, bucketSizeNs); } private async computeSummary( - start: number, end: number, resolution: number, + start: TPTime, end: TPTime, resolution: TPDuration, bucketSizeNs: number): Promise<Data> { - const startNs = toNs(start); - const endNs = toNs(end); + const startNs = Number(start); + const endNs = Number(end); const numBuckets = Math.min(Math.ceil((endNs - startNs) / bucketSizeNs), LIMIT); @@ -167,25 +174,28 @@ class ProcessSummaryTrack extends Track<Config, Data> { } 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 +212,10 @@ class ProcessSummaryTrack extends Track<Config, Data> { 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 = + i * data.bucketSizeSeconds + tpTimeToSeconds(data.start); - lastX = Math.floor(timeScale.timeToPx(startTime)); + lastX = Math.floor(visibleTimeScale.secondsToPx(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 000000000..d805e7487 --- /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 000000000..5f6d86b25 --- /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 000000000..b13fcae58 --- /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 000000000..8532a452e --- /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 {TPTime} from 'src/common/time'; +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 {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 371399988..4ad1b4cdf 100644 --- a/ui/src/tracks/thread_state/index.ts +++ b/ui/src/tracks/thread_state/index.ts @@ -17,10 +17,18 @@ import {assertFalse} from '../../base/logging'; import {Actions} from '../../common/actions'; import {cropText} from '../../common/canvas_utils'; import {colorForState} from '../../common/colorizer'; +import { + HighPrecisionTime, + 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 { + fromNs, + TPDuration, + TPTime, +} from '../../common/time'; import {TrackData} from '../../common/track_data'; import {TrackController} from '../../controller/track_controller'; import {checkerboardExcept} from '../../frontend/checkerboard'; @@ -46,7 +54,7 @@ export interface Config { 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,19 +74,15 @@ class ThreadStateTrackController extends TrackController<Config, Data> { 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); + Math.max(Math.round(Number(resolution) * this.pxSize() / 2) * 2, 1); const query = ` select @@ -92,8 +96,8 @@ class ThreadStateTrackController extends TrackController<Config, Data> { 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 `; @@ -186,7 +190,11 @@ class ThreadStateTrack extends Track<Config, Data> { } 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 +207,10 @@ class ThreadStateTrack extends Track<Config, Data> { 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 +228,19 @@ class ThreadStateTrack extends Track<Config, Data> { 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 = new HighPrecisionTimeSpan( + HighPrecisionTime.fromSeconds(tStart), + HighPrecisionTime.fromSeconds(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.secondsToPx(tStart); + const rectEnd = timeScale.secondsToPx(tEnd); const rectWidth = rectEnd - rectStart; const currentSelection = globals.state.currentSelection; @@ -255,10 +268,9 @@ class ThreadStateTrack extends Track<Config, Data> { if (isSelected) { drawRectOnSelected = () => { const rectStart = - Math.max(0 - EXCESS_WIDTH, timeScale.timeToPx(tStart)); + Math.max(0 - EXCESS_WIDTH, timeScale.secondsToPx(tStart)); const rectEnd = Math.min( - timeScale.timeToPx(visibleWindowTime.end) + EXCESS_WIDTH, - timeScale.timeToPx(tEnd)); + windowSpan.end + EXCESS_WIDTH, timeScale.secondsToPx(tEnd)); const color = colorForState(state); ctx.strokeStyle = `hsl(${color.h},${color.s}%,${color.l * 0.7}%)`; ctx.beginPath(); @@ -278,9 +290,9 @@ class ThreadStateTrack extends Track<Config, Data> { 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.seconds); if (index === -1) return false; const id = data.ids[index]; |