diff options
author | Hector Dearman <hjd@google.com> | 2023-08-22 15:03:51 +0100 |
---|---|---|
committer | Hector Dearman <hjd@google.com> | 2023-08-22 15:03:51 +0100 |
commit | c69b33b9abcc20fad9ad5f39de883216e4b43130 (patch) | |
tree | ce9f26e32accc016f1d11d4da040142498d2cab1 | |
parent | 2d045f852d287f2744492cb51a903f0297db047d (diff) | |
download | perfetto-c69b33b9abcc20fad9ad5f39de883216e4b43130.tar.gz |
Merge remote-tracking branch 'origin/main' into ui-canary
Commands:
$ git fetch origin
$ git checkout -B ui-canary -t origin/ui-canary
$ git merge --strategy=ours origin/main
$ git diff --binary origin/main | git apply --reverse --index
$ git commit --amend
End state:
$ git diff ui-canary origin/main
[no output]
$ git rev-list --count ui-canary..origin/main
0
Change-Id: Ieb3caa7b34f418a7a7e54cfe84a21f74b2f44e47
40 files changed, 1008 insertions, 177 deletions
diff --git a/Android.bp b/Android.bp index cc6473d59..5af8aec14 100644 --- a/Android.bp +++ b/Android.bp @@ -5297,6 +5297,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -5541,6 +5542,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -5613,6 +5615,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.gen.cc", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.cc", "external/perfetto/protos/perfetto/trace/ftrace/regulator.gen.cc", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.gen.cc", "external/perfetto/protos/perfetto/trace/ftrace/sched.gen.cc", "external/perfetto/protos/perfetto/trace/ftrace/scm.gen.cc", "external/perfetto/protos/perfetto/trace/ftrace/sde.gen.cc", @@ -5685,6 +5688,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -5757,6 +5761,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.gen.h", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.h", "external/perfetto/protos/perfetto/trace/ftrace/regulator.gen.h", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.gen.h", "external/perfetto/protos/perfetto/trace/ftrace/sched.gen.h", "external/perfetto/protos/perfetto/trace/ftrace/scm.gen.h", "external/perfetto/protos/perfetto/trace/ftrace/sde.gen.h", @@ -5833,6 +5838,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -5904,6 +5910,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.pb.cc", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.cc", "external/perfetto/protos/perfetto/trace/ftrace/regulator.pb.cc", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.pb.cc", "external/perfetto/protos/perfetto/trace/ftrace/sched.pb.cc", "external/perfetto/protos/perfetto/trace/ftrace/scm.pb.cc", "external/perfetto/protos/perfetto/trace/ftrace/sde.pb.cc", @@ -5976,6 +5983,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -6047,6 +6055,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.pb.h", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.h", "external/perfetto/protos/perfetto/trace/ftrace/regulator.pb.h", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.pb.h", "external/perfetto/protos/perfetto/trace/ftrace/sched.pb.h", "external/perfetto/protos/perfetto/trace/ftrace/scm.pb.h", "external/perfetto/protos/perfetto/trace/ftrace/sde.pb.h", @@ -6123,6 +6132,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -6195,6 +6205,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.cc", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.cc", "external/perfetto/protos/perfetto/trace/ftrace/regulator.pbzero.cc", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.pbzero.cc", "external/perfetto/protos/perfetto/trace/ftrace/sched.pbzero.cc", "external/perfetto/protos/perfetto/trace/ftrace/scm.pbzero.cc", "external/perfetto/protos/perfetto/trace/ftrace/sde.pbzero.cc", @@ -6267,6 +6278,7 @@ genrule { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -6339,6 +6351,7 @@ genrule { "external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.h", "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.h", "external/perfetto/protos/perfetto/trace/ftrace/regulator.pbzero.h", + "external/perfetto/protos/perfetto/trace/ftrace/samsung.pbzero.h", "external/perfetto/protos/perfetto/trace/ftrace/sched.pbzero.h", "external/perfetto/protos/perfetto/trace/ftrace/scm.pbzero.h", "external/perfetto/protos/perfetto/trace/ftrace/sde.pbzero.h", @@ -9798,6 +9811,7 @@ filegroup { "src/trace_processor/db/column_storage_overlay_unittest.cc", "src/trace_processor/db/compare_unittest.cc", "src/trace_processor/db/query_executor_unittest.cc", + "src/trace_processor/db/runtime_table_unittest.cc", "src/trace_processor/db/view_unittest.cc", ], } @@ -10773,6 +10787,7 @@ genrule { "src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql", "src/trace_processor/perfetto_sql/stdlib/experimental/slices.sql", "src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql", + "src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql", "src/trace_processor/perfetto_sql/stdlib/pkvm/hypervisor.sql", ], cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=stdlib --cpp-out=$(out) $(in)", @@ -12087,6 +12102,7 @@ java_library { "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", @@ -2308,6 +2308,7 @@ perfetto_filegroup( "src/trace_processor/perfetto_sql/stdlib/experimental/proto_path.sql", "src/trace_processor/perfetto_sql/stdlib/experimental/slices.sql", "src/trace_processor/perfetto_sql/stdlib/experimental/thread_executing_span.sql", + "src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql", ], ) @@ -4381,6 +4382,7 @@ perfetto_proto_library( "protos/perfetto/trace/ftrace/printk.proto", "protos/perfetto/trace/ftrace/raw_syscalls.proto", "protos/perfetto/trace/ftrace/regulator.proto", + "protos/perfetto/trace/ftrace/samsung.proto", "protos/perfetto/trace/ftrace/sched.proto", "protos/perfetto/trace/ftrace/scm.proto", "protos/perfetto/trace/ftrace/sde.proto", diff --git a/include/perfetto/public/abi/data_source_abi.h b/include/perfetto/public/abi/data_source_abi.h index 612c9458b..d20d351cc 100644 --- a/include/perfetto/public/abi/data_source_abi.h +++ b/include/perfetto/public/abi/data_source_abi.h @@ -50,6 +50,10 @@ typedef uint32_t PerfettoDsInstanceIndex; // PerfettoDsImplRegister(). PERFETTO_SDK_EXPORT struct PerfettoDsImpl* PerfettoDsImplCreate(void); +// Opaque handle used to perform operations from the OnSetup callback. Unused +// for now. +struct PerfettoDsOnSetupArgs; + // Called when a data source instance of a specific type is created. `ds_config` // points to a serialized perfetto.protos.DataSourceConfig message, // `ds_config_size` bytes long. `user_arg` is the value passed to @@ -60,7 +64,12 @@ typedef void* (*PerfettoDsOnSetupCb)(struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* ds_config, size_t ds_config_size, - void* user_arg); + void* user_arg, + struct PerfettoDsOnSetupArgs* args); + +// Opaque handle used to perform operations from the OnSetup callback. Unused +// for now. +struct PerfettoDsOnStartArgs; // Called when tracing starts for a data source instance. `user_arg` is the // value passed to PerfettoDsSetCbUserArg(). `inst_ctx` is the return @@ -68,12 +77,13 @@ typedef void* (*PerfettoDsOnSetupCb)(struct PerfettoDsImpl*, typedef void (*PerfettoDsOnStartCb)(struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg, - void* inst_ctx); + void* inst_ctx, + struct PerfettoDsOnStartArgs* args); -// Internal handle used to perform operations from the OnStop callback. +// Opaque handle used to perform operations from the OnStop callback. struct PerfettoDsOnStopArgs; -// Internal handle used to signal when the data source stop operation is +// Opaque handle used to signal when the data source stop operation is // complete. struct PerfettoDsAsyncStopper; @@ -106,10 +116,10 @@ typedef void (*PerfettoDsOnDestroyCb)(struct PerfettoDsImpl*, void* user_arg, void* inst_ctx); -// Internal handle used to perform operations from the OnFlush callback. +// Opaque handle used to perform operations from the OnFlush callback. struct PerfettoDsOnFlushArgs; -// Internal handle used to signal when the data source flush operation is +// Opaque handle used to signal when the data source flush operation is // complete. struct PerfettoDsAsyncFlusher; @@ -124,8 +134,9 @@ PerfettoDsOnFlushArgsPostpone(struct PerfettoDsOnFlushArgs*); // PerfettoDsOnFlushArgsPostpone). PERFETTO_SDK_EXPORT void PerfettoDsFlushDone(struct PerfettoDsAsyncFlusher*); -// Called when tracing stops for a data source instance. `user_arg` is the value -// passed to PerfettoDsSetCbUserArg(). `inst_ctx` is the return value of +// Called when the tracing service requires all the pending tracing data to be +// flushed for a data source instance. `user_arg` is the value passed to +// PerfettoDsSetCbUserArg(). `inst_ctx` is the return value of // PerfettoDsOnSetupCb. `args` can be used to postpone stopping this data source // instance. typedef void (*PerfettoDsOnFlushCb)(struct PerfettoDsImpl*, diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni index 54a226c50..bd055abdb 100644 --- a/protos/perfetto/trace/ftrace/all_protos.gni +++ b/protos/perfetto/trace/ftrace/all_protos.gni @@ -61,6 +61,7 @@ ftrace_proto_names = [ "printk.proto", "raw_syscalls.proto", "regulator.proto", + "samsung.proto", "sched.proto", "scm.proto", "sde.proto", diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto index 88d364178..f9815cdda 100644 --- a/protos/perfetto/trace/ftrace/ftrace_event.proto +++ b/protos/perfetto/trace/ftrace/ftrace_event.proto @@ -61,6 +61,7 @@ import "protos/perfetto/trace/ftrace/power.proto"; import "protos/perfetto/trace/ftrace/printk.proto"; import "protos/perfetto/trace/ftrace/raw_syscalls.proto"; import "protos/perfetto/trace/ftrace/regulator.proto"; +import "protos/perfetto/trace/ftrace/samsung.proto"; import "protos/perfetto/trace/ftrace/sched.proto"; import "protos/perfetto/trace/ftrace/scm.proto"; import "protos/perfetto/trace/ftrace/sde.proto"; @@ -597,5 +598,6 @@ message FtraceEvent { SuspendResumeMinimalFtraceEvent suspend_resume_minimal = 481; MaliMaliCSFINTERRUPTSTARTFtraceEvent mali_mali_CSF_INTERRUPT_START = 482; MaliMaliCSFINTERRUPTENDFtraceEvent mali_mali_CSF_INTERRUPT_END = 483; + SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484; } } diff --git a/protos/perfetto/trace/ftrace/samsung.proto b/protos/perfetto/trace/ftrace/samsung.proto new file mode 100644 index 000000000..5da0b4c11 --- /dev/null +++ b/protos/perfetto/trace/ftrace/samsung.proto @@ -0,0 +1,14 @@ +// Autogenerated by: +// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc +// Do not edit. + +syntax = "proto2"; +package perfetto.protos; + +message SamsungTracingMarkWriteFtraceEvent { + optional int32 pid = 1; + optional string trace_name = 2; + optional uint32 trace_begin = 3; + optional uint32 trace_type = 4; + optional int32 value = 5; +} diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto index 554413fab..20cbdd3f5 100644 --- a/protos/perfetto/trace/perfetto_trace.proto +++ b/protos/perfetto/trace/perfetto_trace.proto @@ -7991,6 +7991,18 @@ message RegulatorSetVoltageCompleteFtraceEvent { // End of protos/perfetto/trace/ftrace/regulator.proto +// Begin of protos/perfetto/trace/ftrace/samsung.proto + +message SamsungTracingMarkWriteFtraceEvent { + optional int32 pid = 1; + optional string trace_name = 2; + optional uint32 trace_begin = 3; + optional uint32 trace_type = 4; + optional int32 value = 5; +} + +// End of protos/perfetto/trace/ftrace/samsung.proto + // Begin of protos/perfetto/trace/ftrace/sched.proto message SchedSwitchFtraceEvent { @@ -9158,6 +9170,7 @@ message FtraceEvent { SuspendResumeMinimalFtraceEvent suspend_resume_minimal = 481; MaliMaliCSFINTERRUPTSTARTFtraceEvent mali_mali_CSF_INTERRUPT_START = 482; MaliMaliCSFINTERRUPTENDFtraceEvent mali_mali_CSF_INTERRUPT_END = 483; + SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484; } } diff --git a/src/shared_lib/data_source.cc b/src/shared_lib/data_source.cc index b5864ba5b..a5886e4a7 100644 --- a/src/shared_lib/data_source.cc +++ b/src/shared_lib/data_source.cc @@ -106,7 +106,7 @@ class ShlibDataSource : public perfetto::DataSourceBase { std::vector<uint8_t> serialized_config = args.config->SerializeAsArray(); inst_ctx_ = type_.on_setup_cb( &type_, args.internal_instance_index, serialized_config.data(), - serialized_config.size(), type_.cb_user_arg); + serialized_config.size(), type_.cb_user_arg, nullptr); } std::lock_guard<std::mutex> lock(type_.mu); const bool was_enabled = type_.enabled_instances.any(); @@ -119,7 +119,7 @@ class ShlibDataSource : public perfetto::DataSourceBase { void OnStart(const StartArgs& args) override { if (type_.on_start_cb) { type_.on_start_cb(&type_, args.internal_instance_index, type_.cb_user_arg, - inst_ctx_); + inst_ctx_, nullptr); } } diff --git a/src/shared_lib/test/api_integrationtest.cc b/src/shared_lib/test/api_integrationtest.cc index 6861c586b..24b2ec187 100644 --- a/src/shared_lib/test/api_integrationtest.cc +++ b/src/shared_lib/test/api_integrationtest.cc @@ -84,13 +84,15 @@ class MockDs2Callbacks : testing::Mock { PerfettoDsInstanceIndex inst_id, void* ds_config, size_t ds_config_size, - void* user_arg)); + void* user_arg, + struct PerfettoDsOnSetupArgs* args)); MOCK_METHOD(void, OnStart, (struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg, - void* inst_ctx)); + void* inst_ctx, + struct PerfettoDsOnStartArgs* args)); MOCK_METHOD(void, OnStop, (struct PerfettoDsImpl*, @@ -232,17 +234,20 @@ class SharedLibDataSourceTest : public testing::Test { struct PerfettoDsParams params = PerfettoDsParamsDefault(); params.on_setup_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, void* ds_config, - size_t ds_config_size, void* user_arg) -> void* { + size_t ds_config_size, void* user_arg, + struct PerfettoDsOnSetupArgs* args) -> void* { auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); return thiz->ds2_callbacks_.OnSetup(ds_impl, inst_id, ds_config, - ds_config_size, thiz->ds2_user_arg_); + ds_config_size, thiz->ds2_user_arg_, + args); }; params.on_start_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, void* user_arg, - void* inst_ctx) -> void { + void* inst_ctx, + struct PerfettoDsOnStartArgs* args) -> void { auto* thiz = static_cast<SharedLibDataSourceTest*>(user_arg); return thiz->ds2_callbacks_.OnStart(ds_impl, inst_id, thiz->ds2_user_arg_, - inst_ctx); + inst_ctx, args); }; params.on_stop_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, @@ -464,9 +469,10 @@ TEST_F(SharedLibDataSourceTest, LifetimeCallbacks) { void* const kInstancePtr = reinterpret_cast<void*>(0x44); testing::InSequence seq; PerfettoDsInstanceIndex setup_inst, start_inst, stop_inst; - EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg)) + EXPECT_CALL(ds2_callbacks_, OnSetup(_, _, _, _, kDataSource2UserArg, _)) .WillOnce(DoAll(SaveArg<1>(&setup_inst), Return(kInstancePtr))); - EXPECT_CALL(ds2_callbacks_, OnStart(_, _, kDataSource2UserArg, kInstancePtr)) + EXPECT_CALL(ds2_callbacks_, + OnStart(_, _, kDataSource2UserArg, kInstancePtr, _)) .WillOnce(SaveArg<1>(&start_inst)); TracingSession tracing_session = diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list index 40dd94924..af52c8add 100644 --- a/src/tools/ftrace_proto_gen/event_list +++ b/src/tools/ftrace_proto_gen/event_list @@ -478,3 +478,4 @@ hyp/host_mem_abort synthetic/suspend_resume_minimal mali/mali_CSF_INTERRUPT_START mali/mali_CSF_INTERRUPT_END +samsung/tracing_mark_write diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc index cf9654c5d..789d5bff7 100644 --- a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc +++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc @@ -59,7 +59,7 @@ std::string EventNameToProtoFieldName(const std::string& group, // These groups have events where the name alone conflicts with an existing // proto: if (group == "sde" || group == "g2d" || group == "dpu" || group == "mali" || - group == "lwis") { + group == "lwis" || group == "samsung") { event_name = group + "_" + event_name; } return event_name; diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn index 50f572c56..067d76889 100644 --- a/src/trace_processor/db/BUILD.gn +++ b/src/trace_processor/db/BUILD.gn @@ -59,6 +59,7 @@ perfetto_unittest_source_set("unittests") { "column_storage_overlay_unittest.cc", "compare_unittest.cc", "query_executor_unittest.cc", + "runtime_table_unittest.cc", "view_unittest.cc", ] deps = [ diff --git a/src/trace_processor/db/runtime_table.cc b/src/trace_processor/db/runtime_table.cc index a982b8a7c..f2559f9db 100644 --- a/src/trace_processor/db/runtime_table.cc +++ b/src/trace_processor/db/runtime_table.cc @@ -15,11 +15,29 @@ */ #include "src/trace_processor/db/runtime_table.h" +#include <cstdint> +#include <optional> +#include "perfetto/base/status.h" namespace perfetto { namespace trace_processor { +namespace { -RuntimeTable::~RuntimeTable() = default; +template <typename T, typename U> +T Fill(uint32_t leading_nulls, U value) { + T res; + for (uint32_t i = 0; i < leading_nulls; ++i) { + res.Append(value); + } + return res; +} + +bool IsPerfectlyRepresentableAsDouble(int64_t res) { + static constexpr int64_t kMaxDoubleRepresentible = 1ull << 53; + return res >= -kMaxDoubleRepresentible && res <= kMaxDoubleRepresentible; +} + +} // namespace RuntimeTable::RuntimeTable(StringPool* pool, std::vector<std::string> col_names) : Table(pool), col_names_(col_names), storage_(col_names_.size()) { @@ -27,6 +45,8 @@ RuntimeTable::RuntimeTable(StringPool* pool, std::vector<std::string> col_names) storage_[i] = std::make_unique<VariantStorage>(); } +RuntimeTable::~RuntimeTable() = default; + base::Status RuntimeTable::AddNull(uint32_t idx) { auto* col = storage_[idx].get(); if (auto* leading_nulls = std::get_if<uint32_t>(col)) { @@ -46,11 +66,21 @@ base::Status RuntimeTable::AddNull(uint32_t idx) { base::Status RuntimeTable::AddInteger(uint32_t idx, int64_t res) { auto* col = storage_[idx].get(); if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) { - RETURN_IF_ERROR(Fill<IntStorage>(col, *leading_nulls_ptr, std::nullopt)); + *col = Fill<IntStorage>(*leading_nulls_ptr, std::nullopt); + } + if (auto* doubles = std::get_if<DoubleStorage>(col)) { + if (!IsPerfectlyRepresentableAsDouble(res)) { + return base::ErrStatus("Column %s contains %" PRId64 + " which cannot be represented as a double", + col_names_[idx].c_str(), res); + } + doubles->Append(static_cast<double>(res)); + return base::OkStatus(); } auto* ints = std::get_if<IntStorage>(col); if (!ints) { - return base::ErrStatus("Column %u does not have consistent types", idx); + return base::ErrStatus("Column %s does not have consistent types", + col_names_[idx].c_str()); } ints->Append(res); return base::OkStatus(); @@ -59,11 +89,29 @@ base::Status RuntimeTable::AddInteger(uint32_t idx, int64_t res) { base::Status RuntimeTable::AddFloat(uint32_t idx, double res) { auto* col = storage_[idx].get(); if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) { - RETURN_IF_ERROR(Fill<DoubleStorage>(col, *leading_nulls_ptr, std::nullopt)); + *col = Fill<DoubleStorage>(*leading_nulls_ptr, std::nullopt); + } + if (auto* ints = std::get_if<IntStorage>(col)) { + DoubleStorage storage; + for (uint32_t i = 0; i < ints->size(); ++i) { + std::optional<int64_t> int_val = ints->Get(i); + if (!int_val) { + storage.Append(std::nullopt); + continue; + } + if (int_val && !IsPerfectlyRepresentableAsDouble(*int_val)) { + return base::ErrStatus("Column %s contains %" PRId64 + " which cannot be represented as a double", + col_names_[idx].c_str(), *int_val); + } + storage.Append(static_cast<double>(*int_val)); + } + *col = std::move(storage); } auto* doubles = std::get_if<DoubleStorage>(col); if (!doubles) { - return base::ErrStatus("Column %u does not have consistent types", idx); + return base::ErrStatus("Column %s does not have consistent types", + col_names_[idx].c_str()); } doubles->Append(res); return base::OkStatus(); @@ -72,12 +120,12 @@ base::Status RuntimeTable::AddFloat(uint32_t idx, double res) { base::Status RuntimeTable::AddText(uint32_t idx, const char* ptr) { auto* col = storage_[idx].get(); if (auto* leading_nulls_ptr = std::get_if<uint32_t>(col)) { - RETURN_IF_ERROR( - Fill<StringStorage>(col, *leading_nulls_ptr, StringPool::Id::Null())); + *col = Fill<StringStorage>(*leading_nulls_ptr, StringPool::Id::Null()); } auto* strings = std::get_if<StringStorage>(col); if (!strings) { - return base::ErrStatus("Column %u does not have consistent types", idx); + return base::ErrStatus("Column %s does not have consistent types", + col_names_[idx].c_str()); } strings->Append(string_pool_->InternString(ptr)); return base::OkStatus(); @@ -88,15 +136,19 @@ base::Status RuntimeTable::AddColumnsAndOverlays(uint32_t rows) { for (uint32_t i = 0; i < col_names_.size(); ++i) { auto* col = storage_[i].get(); if (auto* leading_nulls = std::get_if<uint32_t>(col)) { - RETURN_IF_ERROR(Fill<IntStorage>(col, *leading_nulls, std::nullopt)); + PERFETTO_CHECK(*leading_nulls == rows); + *col = Fill<IntStorage>(*leading_nulls, std::nullopt); } if (auto* ints = std::get_if<IntStorage>(col)) { + PERFETTO_CHECK(ints->size() == rows); columns_.push_back(Column(col_names_[i].c_str(), ints, Column::Flag::kNoFlag, this, i, 0)); } else if (auto* strings = std::get_if<StringStorage>(col)) { + PERFETTO_CHECK(strings->size() == rows); columns_.push_back(Column(col_names_[i].c_str(), strings, Column::Flag::kNonNull, this, i, 0)); } else if (auto* doubles = std::get_if<DoubleStorage>(col)) { + PERFETTO_CHECK(doubles->size() == rows); columns_.push_back(Column(col_names_[i].c_str(), doubles, Column::Flag::kNoFlag, this, i, 0)); } else { diff --git a/src/trace_processor/db/runtime_table.h b/src/trace_processor/db/runtime_table.h index 6e14a53a9..cef31a3ee 100644 --- a/src/trace_processor/db/runtime_table.h +++ b/src/trace_processor/db/runtime_table.h @@ -55,15 +55,6 @@ class RuntimeTable : public Table { base::Status AddColumnsAndOverlays(uint32_t rows); private: - template <typename T, typename U> - base::Status Fill(VariantStorage* col, uint32_t leading_nulls, U value) { - *col = T(); - auto* storage = std::get_if<T>(col); - for (uint32_t i = 0; i < leading_nulls; ++i) { - storage->Append(value); - } - return base::OkStatus(); - } std::vector<std::string> col_names_; std::vector<std::unique_ptr<VariantStorage>> storage_; }; diff --git a/src/trace_processor/db/runtime_table_unittest.cc b/src/trace_processor/db/runtime_table_unittest.cc new file mode 100644 index 000000000..85ca8ecd7 --- /dev/null +++ b/src/trace_processor/db/runtime_table_unittest.cc @@ -0,0 +1,60 @@ +/* + * 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/runtime_table.h" + +#include "test/gtest_and_gmock.h" + +namespace perfetto { +namespace trace_processor { +namespace { + +class RuntimeTableTest : public ::testing::Test { + protected: + StringPool pool_; + std::vector<std::string> names_{{"foo"}}; + RuntimeTable table_{&pool_, names_}; +}; + +TEST_F(RuntimeTableTest, DoubleThenIntValid) { + ASSERT_TRUE(table_.AddFloat(0, 1024.3).ok()); + ASSERT_TRUE(table_.AddInteger(0, 1ll << 53).ok()); + ASSERT_TRUE(table_.AddColumnsAndOverlays(2).ok()); + + const auto& col = table_.columns()[0]; + ASSERT_EQ(col.Get(0).AsDouble(), 1024.3); + ASSERT_EQ(col.Get(1).AsDouble(), static_cast<double>(1ll << 53)); +} + +TEST_F(RuntimeTableTest, DoubleThenIntInvalid) { + ASSERT_TRUE(table_.AddFloat(0, 1024.0).ok()); + ASSERT_FALSE(table_.AddInteger(0, (1ll << 53) + 1).ok()); + ASSERT_FALSE(table_.AddInteger(0, -(1ll << 53) - 1).ok()); +} + +TEST_F(RuntimeTableTest, IntThenDouble) { + ASSERT_TRUE(table_.AddInteger(0, 1024).ok()); + ASSERT_TRUE(table_.AddFloat(0, 1.3).ok()); + ASSERT_TRUE(table_.AddColumnsAndOverlays(2).ok()); + + const auto& col = table_.columns()[0]; + ASSERT_EQ(col.Get(0).AsDouble(), 1024.0); + ASSERT_EQ(col.Get(1).AsDouble(), 1.3); +} + +} // namespace +} // namespace trace_processor +} // namespace perfetto diff --git a/src/trace_processor/importers/json/json_utils.cc b/src/trace_processor/importers/json/json_utils.cc index be9492b61..d2e1c1850 100644 --- a/src/trace_processor/importers/json/json_utils.cc +++ b/src/trace_processor/importers/json/json_utils.cc @@ -62,17 +62,37 @@ std::optional<int64_t> CoerceToTs(const std::string& s) { PERFETTO_DCHECK(IsJsonSupported()); #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON) - size_t lhs_end = std::min<size_t>(s.find('.'), s.size()); - size_t rhs_start = std::min<size_t>(lhs_end + 1, s.size()); - std::optional<int64_t> lhs = base::StringToInt64(s.substr(0, lhs_end)); - std::optional<double> rhs = - base::StringToDouble("0." + s.substr(rhs_start, std::string::npos)); - if ((!lhs.has_value() && lhs_end > 0) || - (!rhs.has_value() && rhs_start < s.size())) { - return std::nullopt; + // 's' is formatted as a JSON Number, in microseconds + // goal: reformat 's' to be as an int, in nanoseconds + std::string s_as_ns = s; + + // detect and remove scientific notation's exponents + int32_t exp_shift = 0; + if (size_t exp_start = s.find_first_of("eE"); + exp_start != std::string::npos) { + const std::string exp_s = s.substr(exp_start + 1, s.size()); + const std::optional<int32_t> exp = base::StringToInt32(exp_s); + if (!exp.has_value()) { + return std::nullopt; + } + s_as_ns.erase(exp_start); + exp_shift = *exp; } - return lhs.value_or(0) * 1000 + - static_cast<int64_t>(rhs.value_or(0) * 1000.0); + + // detect and remove decimal separator + size_t int_size = s_as_ns.size(); + if (size_t frac_start = s.find('.'); frac_start != std::string::npos) { + s_as_ns.erase(frac_start, 1); + int_size = frac_start; + } + + // expand or shrink to the new size + constexpr int us_to_ns_shift = 3; + const size_t s_as_ns_size = size_t( + std::max<ptrdiff_t>(1, ptrdiff_t(int_size) + exp_shift + us_to_ns_shift)); + s_as_ns.resize(s_as_ns_size, '0'); // pads or truncates + + return base::StringToInt64(s_as_ns); #else perfetto::base::ignore_result(s); return std::nullopt; diff --git a/src/trace_processor/importers/json/json_utils_unittest.cc b/src/trace_processor/importers/json/json_utils_unittest.cc index 7f01ad646..5b17cc3b0 100644 --- a/src/trace_processor/importers/json/json_utils_unittest.cc +++ b/src/trace_processor/importers/json/json_utils_unittest.cc @@ -52,8 +52,29 @@ TEST(JsonTraceUtilsTest, CoerceToTs) { ASSERT_EQ(CoerceToTs(Json::Value("42.0")).value_or(-1), 42000); ASSERT_EQ(CoerceToTs(Json::Value("0.2")).value_or(-1), 200); ASSERT_EQ(CoerceToTs(Json::Value("0.2e-1")).value_or(-1), 20); + ASSERT_EQ(CoerceToTs(Json::Value("0.2e-2")).value_or(-1), 2); + ASSERT_EQ(CoerceToTs(Json::Value("0.2e-3")).value_or(-1), 0); + ASSERT_EQ(CoerceToTs(Json::Value("1.692108548132154500e+15")).value_or(-1), + 1'692'108'548'132'154'500); + ASSERT_EQ(CoerceToTs(Json::Value("1692108548132154.500")).value_or(-1), + 1'692'108'548'132'154'500); + ASSERT_EQ(CoerceToTs(Json::Value("1.692108548132154501e+15")).value_or(-1), + 1'692'108'548'132'154'501); + ASSERT_EQ(CoerceToTs(Json::Value("1692108548132154.501")).value_or(-1), + 1'692'108'548'132'154'501); + ASSERT_EQ(CoerceToTs(Json::Value("-1.692108548132154500E+15")).value_or(-1), + -1'692'108'548'132'154'500); + ASSERT_EQ(CoerceToTs(Json::Value("-1692108548132154.500")).value_or(-1), + -1'692'108'548'132'154'500); + ASSERT_EQ(CoerceToTs(Json::Value("-1.692108548132154501E+15")).value_or(-1), + -1'692'108'548'132'154'501); + ASSERT_EQ(CoerceToTs(Json::Value("-1692108548132154.501")).value_or(-1), + -1'692'108'548'132'154'501); + ASSERT_EQ(CoerceToTs(Json::Value("-0")).value_or(-1), 0); + ASSERT_EQ(CoerceToTs(Json::Value("0")).value_or(-1), 0); ASSERT_EQ(CoerceToTs(Json::Value(".")).value_or(-1), 0); ASSERT_FALSE(CoerceToTs(Json::Value("1234!")).has_value()); + ASSERT_FALSE(CoerceToTs(Json::Value("123e4!")).has_value()); } } // namespace diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/utils.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/utils.sql index 8b969bb66..167bca223 100644 --- a/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/utils.sql +++ b/src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/utils.sql @@ -89,8 +89,14 @@ SELECT -- names. For example, LongTaskTracker slices may have associated IPC -- metadata, or InterestingTask slices for input may have associated IPC to -- determine whether the task is fling/etc. +-- +-- @arg name STRING The name of slice. +-- @column interface_name Name of the interface of the IPC call. +-- @column ipc_hash Hash of the IPC call. +-- @column message_type Message type (e.g. reply). +-- @column id The slice ID. SELECT CREATE_VIEW_FUNCTION( - 'SELECT_LONG_TASK_SLICES(name STRING)', + 'CHROME_SELECT_LONG_TASK_SLICES(name STRING)', 'interface_name STRING, ipc_hash INT, message_type STRING, id INT', 'SELECT EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag") AS interface_name, diff --git a/src/trace_processor/perfetto_sql/stdlib/experimental/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/experimental/BUILD.gn index caae0a92f..d9a48c1f4 100644 --- a/src/trace_processor/perfetto_sql/stdlib/experimental/BUILD.gn +++ b/src/trace_processor/perfetto_sql/stdlib/experimental/BUILD.gn @@ -21,5 +21,6 @@ perfetto_sql_source_set("experimental") { "proto_path.sql", "slices.sql", "thread_executing_span.sql", + "thread_state_flattened.sql", ] } diff --git a/src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql b/src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql index 98063d0ff..4b1d69e54 100644 --- a/src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql +++ b/src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql @@ -88,7 +88,13 @@ WITH events.track_id FROM events ) -SELECT * FROM data WHERE depth != -1; +SELECT data.slice_id, data.ts, data.dur, data.depth, + data.name, data.track_id, thread.utid, thread.tid, thread.name as thread_name, + process.upid, process.pid, process.name as process_name + FROM data JOIN thread_track ON data.track_id = thread_track.id +JOIN thread USING(utid) +JOIN process USING(upid) +WHERE depth != -1; CREATE INDEX experimental_slice_flattened_id_idx diff --git a/src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql b/src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql new file mode 100644 index 000000000..e7f6abbc6 --- /dev/null +++ b/src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql @@ -0,0 +1,175 @@ +-- +-- 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('experimental.flat_slices'); + +-- Create a table which joins the thread state across the flattened slices. +CREATE VIRTUAL TABLE internal_experimental_span_joined_thread USING + SPAN_JOIN(experimental_slice_flattened PARTITIONED utid, thread_state PARTITIONED utid); + +-- Get the thread state breakdown of a flattened slice from it's slice id. +-- This table pivoted and summed for better visualization and aggragation. +-- The concept of a "flat slice" is to take the data in the slice table and +-- remove all notion of nesting. For more information, read the description +-- of experimental_slice_flattened. +-- +-- @arg slice_id LONG Id of the slice of interest. +-- +-- @column ts Id of a slice. +-- @column dur Name of the slice +-- @column utid Time (ns) spent in Uninterruptible Sleep (non-IO) +-- @column depth Time (ns) spent in Uninterruptible Sleep (IO) +-- @column name Time (ns) spent in Runnable +-- @column slice_id Time (ns) spent in Sleeping +-- @column track_id Time (ns) spent in Stopped +-- @column cpu Time (ns) spent in Exit (Zombie) +-- @column state Time (ns) spent in Task Dead +-- @column io_wait Time (ns) spent in Wake Kill +-- @column blocked_function Time (ns) spent in Waking +-- @column waker_utid Time (ns) spent in Parked +-- @column irq_context Time (ns) spent in No Load +CREATE PERFETTO FUNCTION experimental_get_flattened_thread_state( + slice_id LONG, utid LONG) +RETURNS + TABLE( + ts LONG, + dur LONG, + utid LONG, + depth LONG, + name STRING, + slice_id LONG, + track_id LONG, + cpu INT, + state STRING, + io_wait INT, + blocked_function STRING, + waker_utid LONG, + irq_context LONG) +AS +WITH + interesting_slice AS ( + SELECT ts, dur, slice.track_id AS track_id + FROM slice + JOIN thread_track + ON slice.track_id = thread_track.id + JOIN thread + USING (utid) + WHERE + (($slice_id IS NOT NULL AND slice.id = $slice_id) OR ($slice_id IS NULL)) + AND (($utid IS NOT NULL AND utid = $utid) OR ($utid IS NULL)) + ) +SELECT + ts, + dur, + utid, + depth, + name, + slice_id, + track_id, + cpu, + state, + io_wait, + blocked_function, + waker_utid, + irq_context +FROM internal_experimental_span_joined_thread +WHERE + track_id = (SELECT track_id FROM interesting_slice) + AND ts >= (SELECT ts FROM interesting_slice) + AND ts < (SELECT ts + dur FROM interesting_slice); + +-- Get the thread state breakdown of a flattened slice from slice id. +-- This table pivoted and summed for better visualization and aggragation. +-- The concept of a "flat slice" is to take the data in the slice table and +-- remove all notion of nesting. For more information, read the description +-- of experimental_slice_flattened. +-- +-- @arg slice_id LONG Id of the slice of interest. +-- +-- @column slice_id Id of a slice. +-- @column slice_name Name of the slice +-- @column Uninterruptible_Sleep_nonIO Time (ns) spent in Uninterruptible Sleep (non-IO) +-- @column Uninterruptible_Sleep_IO Time (ns) spent in Uninterruptible Sleep (IO) +-- @column Runnable Time (ns) spent in Runnable +-- @column Sleeping Time (ns) spent in Sleeping +-- @column Stopped Time (ns) spent in Stopped +-- @column Traced Time (ns) spent in Traced +-- @column Exit_Dead Time (ns) spent in Exit (Dead) +-- @column Exit_Zombie Time (ns) spent in Exit (Zombie) +-- @column Task_Dead Time (ns) spent in Task Dead +-- @column Wake_Kill Time (ns) spent in Wake Kill +-- @column Waking Time (ns) spent in Waking +-- @column Parked Time (ns) spent in Parked +-- @column No_Load Time (ns) spent in No Load +-- @column Runnable_Preempted Time (ns) spent in Runnable (Preempted) +-- @column Running Time (ns) spent in Running +-- @column Idle Time (ns) spent in Idle +-- @column dur Total duration of the slice +-- @column depth Depth of the slice in Perfetto +CREATE PERFETTO FUNCTION experimental_get_flattened_thread_state_aggregated( + slice_id LONG, utid LONG) +RETURNS + TABLE( + slice_id LONG, + slice_name STRING, + Uninterruptible_Sleep_nonIO LONG, + Uninterruptible_Sleep_IO LONG, + Runnable LONG, + Sleeping LONG, + Stopped LONG, + Traced LONG, + Exit_Dead LONG, + Exit_Zombie LONG, + Task_Dead LONG, + Wake_Kill LONG, + Waking LONG, + Parked LONG, + No_Load LONG, + Runnable_Preempted LONG, + Running LONG, + Idle LONG, + dur LONG, + depth LONG) +AS +WITH + final_table AS ( + SELECT * + FROM experimental_get_flattened_thread_state($slice_id, $utid) + ) +SELECT + fs.slice_id, + fs.name AS slice_name, + SUM(CASE WHEN fs.state = 'D' AND io_wait = 0 THEN fs.dur END) + Uninterruptible_Sleep_nonIO, + SUM(CASE WHEN fs.state = 'D' AND io_wait = 1 THEN fs.dur END) + Uninterruptible_Sleep_IO, + SUM(CASE WHEN fs.state = 'R' THEN fs.dur END) Runnable, + SUM(CASE WHEN fs.state = 'S' THEN fs.dur END) Sleeping, + SUM(CASE WHEN fs.state = 'T' THEN fs.dur END) Stopped, + SUM(CASE WHEN fs.state = 't' THEN fs.dur END) Traced, + SUM(CASE WHEN fs.state = 'X' THEN fs.dur END) Exit_Dead, + SUM(CASE WHEN fs.state = 'Z' THEN fs.dur END) Exit_Zombie, + SUM(CASE WHEN fs.state = 'x' THEN fs.dur END) Task_Dead, + SUM(CASE WHEN fs.state = 'K' THEN fs.dur END) Wake_Kill, + SUM(CASE WHEN fs.state = 'W' THEN fs.dur END) Waking, + SUM(CASE WHEN fs.state = 'P' THEN fs.dur END) Parked, + SUM(CASE WHEN fs.state = 'N' THEN fs.dur END) No_Load, + SUM(CASE WHEN fs.state = 'R+' THEN fs.dur END) Runnable_Preempted, + SUM(CASE WHEN fs.state = 'Running' THEN fs.dur END) Running, + SUM(CASE WHEN fs.state = 'I' THEN fs.dur END) Idle, + SUM(fs.dur) dur, + fs.depth +FROM final_table fs +GROUP BY fs.slice_id;
\ No newline at end of file diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc index a22346448..ed7231618 100644 --- a/src/traced/probes/ftrace/event_info.cc +++ b/src/traced/probes/ftrace/event_info.cc @@ -7601,6 +7601,28 @@ std::vector<Event> GetStaticEventInfo() { kUnsetFtraceId, 66, kUnsetSize}, + {"tracing_mark_write", + "samsung", + { + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "pid", 1, ProtoSchemaType::kInt32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "trace_name", 2, ProtoSchemaType::kString, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "trace_begin", 3, ProtoSchemaType::kUint32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "trace_type", 4, ProtoSchemaType::kUint32, + TranslationStrategy::kInvalidTranslationStrategy}, + {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType, + "value", 5, ProtoSchemaType::kInt32, + TranslationStrategy::kInvalidTranslationStrategy}, + }, + kUnsetFtraceId, + 484, + kUnsetSize}, {"sched_switch", "sched", { diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/samsung/tracing_mark_write/format b/src/traced/probes/ftrace/test/data/synthetic/events/samsung/tracing_mark_write/format new file mode 100644 index 000000000..553dc87a5 --- /dev/null +++ b/src/traced/probes/ftrace/test/data/synthetic/events/samsung/tracing_mark_write/format @@ -0,0 +1,14 @@ +name: tracing_mark_write +ID: 926 +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:int pid; offset:8; size:4; signed:1; + field:__data_loc char[] trace_name; offset:12; size:4; signed:0; + field:unsigned int trace_type; offset:16; size:4; signed:0; + field:int value; offset:20; size:4; signed:1; + +print fmt: "%c|%d|%s|%d", REC->trace_type, REC->pid, __get_str(trace_name), REC->value diff --git a/test/trace_processor/diff_tests/parsing/tests.py b/test/trace_processor/diff_tests/parsing/tests.py index 30aea94c6..83a986c02 100644 --- a/test/trace_processor/diff_tests/parsing/tests.py +++ b/test/trace_processor/diff_tests/parsing/tests.py @@ -833,14 +833,14 @@ class Parsing(TestSuite): "pid": 1, "tid": 1, "ph": "B", - "ts": 1597071955492308000 + "ts": 1597071955492308 }, { "name": "add_graph", "pid": 1, "tid": 1, "ph": "E", - "ts": 1597071955703771000 + "ts": 1597071955703771 } ] } @@ -850,7 +850,7 @@ class Parsing(TestSuite): """, out=Csv(""" "ts","dur","name" - -7794778920422990592,211463000000,"add_graph" + 1597071955492308000,211463000,"add_graph" """)) # Parsing sched_blocked_reason diff --git a/test/trace_processor/diff_tests/slices/tests.py b/test/trace_processor/diff_tests/slices/tests.py index d03cfff2f..63d2ebe66 100644 --- a/test/trace_processor/diff_tests/slices/tests.py +++ b/test/trace_processor/diff_tests/slices/tests.py @@ -162,4 +162,4 @@ class Slices(TestSuite): "ThreadControllerImpl::RunTask",174796099970797,186000,0 "Looper.dispatch: jy3(null)",174800056530797,1368000,0 "ThreadControllerImpl::RunTask",174800107962797,132000,0 - """)) + """))
\ No newline at end of file diff --git a/test/trace_processor/diff_tests/tables/tests.py b/test/trace_processor/diff_tests/tables/tests.py index 82fe9c370..4cf4e5536 100644 --- a/test/trace_processor/diff_tests/tables/tests.py +++ b/test/trace_processor/diff_tests/tables/tests.py @@ -263,3 +263,21 @@ class Tables(TestSuite): "cpu_track",0 "cpu_track",1 """)) + + def test_thread_state_flattened_aggregated(self): + return DiffTestBlueprint( + trace=DataPath('android_monitor_contention_trace.atr'), + query=""" + SELECT import('experimental.thread_state_flattened'); + select * from experimental_get_flattened_thread_state_aggregated(11155, NULL); + """, + out=Path('thread_state_flattened_aggregated_csv.out')) + + def test_thread_state_flattened(self): + return DiffTestBlueprint( + trace=DataPath('android_monitor_contention_trace.atr'), + query=""" + SELECT import('experimental.thread_state_flattened'); + select * from experimental_get_flattened_thread_state(11155, NULL); + """, + out=Path('thread_state_flattened_csv.out')) diff --git a/test/trace_processor/diff_tests/tables/thread_state_flattened_aggregated_csv.out b/test/trace_processor/diff_tests/tables/thread_state_flattened_aggregated_csv.out new file mode 100644 index 000000000..4982ec356 --- /dev/null +++ b/test/trace_processor/diff_tests/tables/thread_state_flattened_aggregated_csv.out @@ -0,0 +1,55 @@ +"slice_id","slice_name","Uninterruptible_Sleep_nonIO","Uninterruptible_Sleep_IO","Runnable","Sleeping","Stopped","Traced","Exit_Dead","Exit_Zombie","Task_Dead","Wake_Kill","Waking","Parked","No_Load","Runnable_Preempted","Running","Idle","dur","depth" +11155,"android.view.Choreographer$FrameHandler: android.view.Choreographer$FrameDisplayEventReceiver","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",15728,"[NULL]",15728,0 +11156,"Choreographer#doFrame 10831","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",31280,"[NULL]",31280,1 +11158,"animation","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",74845,"[NULL]",74845,2 +11159,"ShadeListBuilder.buildList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",239109,"[NULL]",239109,3 +11160,"ShadeListBuilder.filterNotifs","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",11966,"[NULL]",11966,4 +11161,"ShadeListBuilder.groupNotifs","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",3080,"[NULL]",3080,4 +11162,"ShadeListBuilder.pruneIncompleteGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",25794,"[NULL]",25794,4 +11163,"ShadeListBuilder.dispatchOnBeforeTransformGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",31921,"[NULL]",31921,4 +11164,"ShadeListBuilder.promoteNotifs","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",2000,"[NULL]",2000,4 +11165,"ShadeListBuilder.pruneIncompleteGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",2867,"[NULL]",2867,4 +11166,"ShadeListBuilder.dispatchOnBeforeSort","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",2397,"[NULL]",2397,4 +11167,"ShadeListBuilder.assignSections","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",2257,"[NULL]",2257,4 +11168,"ShadeListBuilder.notifySectionEntriesUpdated","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",201950,"[NULL]",201950,4 +11171,"Choreographer#scheduleVsyncLocked","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",11216,"[NULL]",11216,5 +11172,"AIDL::cpp::IDisplayEventConnection::requestNextVsync::cppClient","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",24872,"[NULL]",24872,6 +11175,"ShadeListBuilder.sortListAndGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",23215,"[NULL]",23215,4 +11177,"ShadeListBuilder.dispatchOnBeforeFinalizeFilter","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",102928,"[NULL]",102928,4 +11180,"ShadeListBuilder.filterNotifs","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",3361,"[NULL]",3361,4 +11181,"ShadeListBuilder.pruneIncompleteGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",4862,"[NULL]",4862,4 +11182,"ShadeListBuilder.logChanges","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",8882,"[NULL]",8882,4 +11183,"ShadeListBuilder.freeEmptyGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",17089,"[NULL]",17089,4 +11184,"ShadeListBuilder.cleanupPluggables","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",33465,"[NULL]",33465,4 +11185,"ShadeListBuilder.dispatchOnBeforeRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",214867,"[NULL]",214867,4 +11186,"ShadeListBuilder.onRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",24558,"[NULL]",24558,4 +11187,"RenderStageManager.onRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",60009,"[NULL]",60009,5 +11188,"ShadeViewManager.onRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",29508,"[NULL]",29508,6 +11189,"NodeSpecBuilder.buildNodeSpec","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",57434,"[NULL]",57434,7 +11190,"ShadeViewDiffer.applySpec","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",284569,2382651,"[NULL]",2667220,7 +11191,"Defining Lcom/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger$logDetachingChild$2;","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",108424,"[NULL]",108424,8 +11192,"binder transaction","[NULL]","[NULL]",25131,311364,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",33158,"[NULL]",369653,8 +11195,"Defining Lcom/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper$2$$ExternalSyntheticLambda0;","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",82320,"[NULL]",82320,8 +11203,"NSSLC.updateShowEmptyShadeView","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",87035,"[NULL]",87035,8 +11206,"RenderStageManager.dispatchOnAfterRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",49003,"[NULL]",49003,6 +11207,"NotifLiveDataStore.setActiveNotifList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",105577,"[NULL]",105577,7 +11208,"NotifLiveData(hasActiveNotifs).dispatchToSyncObservers","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",57076,"[NULL]",57076,8 +11209,"StackCoordinator.onAfterRenderList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",69084,"[NULL]",69084,7 +11210,"NSSLC.updateFooter","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",685660,"[NULL]",685660,8 +11211,"NSSLC.updateShowEmptyShadeView","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",248061,"[NULL]",248061,8 +11212,"NotificationIconAreaController.updateNotificationIcons","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",768503,"[NULL]",768503,8 +11214,"Defining Lcom/android/systemui/statusbar/phone/NotificationIconContainer$$ExternalSyntheticLambda0;","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",74629,"[NULL]",74629,9 +11219,"RenderStageManager.dispatchOnAfterRenderGroups","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",20355,"[NULL]",20355,6 +11220,"RenderStageManager.dispatchOnAfterRenderEntries","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",3565,"[NULL]",3565,6 +11221,"ShadeListBuilder.logEndBuildList","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",40367,81151,"[NULL]",121518,4 +11223,"traversal","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",688007,"[NULL]",688007,2 +11224,"measure","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",11410,"[NULL]",11410,3 +11225,"NotificationShadeWindowView#onMeasure","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",470142,"[NULL]",470142,4 +11226,"NotificationStackScrollLayout#onMeasure","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",141670,"[NULL]",141670,5 +11227,"NotificationStackScrollLayout#onMeasure","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",12506,"[NULL]",12506,5 +11228,"layout","[NULL]","[NULL]",6177,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",2622099,"[NULL]",2628276,3 +11241,"measure","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",402484,"[NULL]",402484,3 +11242,"layout","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",317593,"[NULL]",317593,3 +11243,"draw","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",26905,"[NULL]",26905,3 +11244,"Record View#draw()","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",172280,"[NULL]",172280,4 +11245,"postAndWait","[NULL]","[NULL]",25748,147072,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",31682,"[NULL]",204502,4 diff --git a/test/trace_processor/diff_tests/tables/thread_state_flattened_csv.out b/test/trace_processor/diff_tests/tables/thread_state_flattened_csv.out new file mode 100644 index 000000000..162c42f4a --- /dev/null +++ b/test/trace_processor/diff_tests/tables/thread_state_flattened_csv.out @@ -0,0 +1,126 @@ +"ts","dur","utid","depth","name","slice_id","track_id","cpu","state","io_wait","blocked_function","waker_utid","irq_context" +1738888555782,12607,251,0,"android.view.Choreographer$FrameHandler: android.view.Choreographer$FrameDisplayEventReceiver",11155,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888568389,17991,251,1,"Choreographer#doFrame 10831",11156,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888586380,71961,251,2,"animation",11158,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888658341,65346,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888723687,11966,251,4,"ShadeListBuilder.filterNotifs",11160,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888735653,9631,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888745284,3080,251,4,"ShadeListBuilder.groupNotifs",11161,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888748364,11493,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888759857,25794,251,4,"ShadeListBuilder.pruneIncompleteGroups",11162,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888785651,10182,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888795833,31921,251,4,"ShadeListBuilder.dispatchOnBeforeTransformGroups",11163,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888827754,8463,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888836217,2000,251,4,"ShadeListBuilder.promoteNotifs",11164,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888838217,6360,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888844577,2867,251,4,"ShadeListBuilder.pruneIncompleteGroups",11165,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888847444,10753,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888858197,2397,251,4,"ShadeListBuilder.dispatchOnBeforeSort",11166,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888860594,8103,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888868697,2257,251,4,"ShadeListBuilder.assignSections",11167,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888870954,7849,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738888878803,145337,251,4,"ShadeListBuilder.notifySectionEntriesUpdated",11168,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889024140,8745,251,5,"Choreographer#scheduleVsyncLocked",11171,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889032885,24872,251,6,"AIDL::cpp::IDisplayEventConnection::requestNextVsync::cppClient",11172,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889057757,2471,251,5,"Choreographer#scheduleVsyncLocked",11171,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889060228,56613,251,4,"ShadeListBuilder.notifySectionEntriesUpdated",11168,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889116841,9059,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889125900,23215,251,4,"ShadeListBuilder.sortListAndGroups",11175,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889149115,7998,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889157113,102928,251,4,"ShadeListBuilder.dispatchOnBeforeFinalizeFilter",11177,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889260041,8768,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889268809,3361,251,4,"ShadeListBuilder.filterNotifs",11180,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889272170,7933,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889280103,4862,251,4,"ShadeListBuilder.pruneIncompleteGroups",11181,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889284965,26046,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889311011,8882,251,4,"ShadeListBuilder.logChanges",11182,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889319893,7628,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889327521,17089,251,4,"ShadeListBuilder.freeEmptyGroups",11183,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889344610,7672,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889352282,33465,251,4,"ShadeListBuilder.cleanupPluggables",11184,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889385747,7698,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889393445,214867,251,4,"ShadeListBuilder.dispatchOnBeforeRenderList",11185,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889608312,7685,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889615997,22375,251,4,"ShadeListBuilder.onRenderList",11186,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889638372,12093,251,5,"RenderStageManager.onRenderList",11187,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889650465,16751,251,6,"ShadeViewManager.onRenderList",11188,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889667216,57434,251,7,"NodeSpecBuilder.buildNodeSpec",11189,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889724650,10641,251,6,"ShadeViewManager.onRenderList",11188,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738889735291,281631,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738890016922,108424,251,8,"Defining Lcom/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger$logDetachingChild$2;",11191,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738890125346,542783,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738890668129,30726,251,8,"binder transaction",11192,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738890698855,311364,251,8,"binder transaction",11192,1257,"[NULL]","S","[NULL]","[NULL]","[NULL]","[NULL]" +1738891010219,25131,251,8,"binder transaction",11192,1257,"[NULL]","R","[NULL]","[NULL]",495,0 +1738891035350,2432,251,8,"binder transaction",11192,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738891037782,275245,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738891313027,82320,251,8,"Defining Lcom/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper$2$$ExternalSyntheticLambda0;",11195,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738891395347,62797,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738891458144,241288,251,7,"ShadeViewDiffer.applySpec",11190,1257,"[NULL]","R+","[NULL]","[NULL]","[NULL]","[NULL]" +1738891699432,648477,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738892347909,87035,251,8,"NSSLC.updateShowEmptyShadeView",11203,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738892434944,126991,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738892561935,35098,251,7,"ShadeViewDiffer.applySpec",11190,1257,"[NULL]","R+","[NULL]","[NULL]","[NULL]","[NULL]" +1738892597033,4186,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738892601219,8183,251,7,"ShadeViewDiffer.applySpec",11190,1257,"[NULL]","R+","[NULL]","[NULL]","[NULL]","[NULL]" +1738892609402,440541,251,7,"ShadeViewDiffer.applySpec",11190,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893049943,2116,251,6,"ShadeViewManager.onRenderList",11188,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893052059,12460,251,5,"RenderStageManager.onRenderList",11187,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893064519,29926,251,6,"RenderStageManager.dispatchOnAfterRenderList",11206,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893094445,103005,251,7,"NotifLiveDataStore.setActiveNotifList",11207,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893197450,57076,251,8,"NotifLiveData(hasActiveNotifs).dispatchToSyncObservers",11208,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893254526,2572,251,7,"NotifLiveDataStore.setActiveNotifList",11207,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893257098,14001,251,6,"RenderStageManager.dispatchOnAfterRenderList",11206,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893271099,41258,251,7,"StackCoordinator.onAfterRenderList",11209,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893312357,685660,251,8,"NSSLC.updateFooter",11210,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738893998017,9168,251,7,"StackCoordinator.onAfterRenderList",11209,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738894007185,248061,251,8,"NSSLC.updateShowEmptyShadeView",11211,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738894255246,15482,251,7,"StackCoordinator.onAfterRenderList",11209,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738894270728,349348,251,8,"NotificationIconAreaController.updateNotificationIcons",11212,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738894620076,74629,251,9,"Defining Lcom/android/systemui/statusbar/phone/NotificationIconContainer$$ExternalSyntheticLambda0;",11214,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738894694705,419155,251,8,"NotificationIconAreaController.updateNotificationIcons",11212,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895113860,3176,251,7,"StackCoordinator.onAfterRenderList",11209,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895117036,5076,251,6,"RenderStageManager.dispatchOnAfterRenderList",11206,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895122112,16102,251,5,"RenderStageManager.onRenderList",11187,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895138214,20355,251,6,"RenderStageManager.dispatchOnAfterRenderGroups",11219,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895158569,9440,251,5,"RenderStageManager.onRenderList",11187,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895168009,3565,251,6,"RenderStageManager.dispatchOnAfterRenderEntries",11220,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895171574,9914,251,5,"RenderStageManager.onRenderList",11187,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895181488,2183,251,4,"ShadeListBuilder.onRenderList",11186,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895183671,7205,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895190876,72666,251,4,"ShadeListBuilder.logEndBuildList",11221,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895263542,32040,251,4,"ShadeListBuilder.logEndBuildList",11221,1257,"[NULL]","R+","[NULL]","[NULL]","[NULL]","[NULL]" +1738895295582,4470,251,4,"ShadeListBuilder.logEndBuildList",11221,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895300052,8327,251,4,"ShadeListBuilder.logEndBuildList",11221,1257,"[NULL]","R+","[NULL]","[NULL]","[NULL]","[NULL]" +1738895308379,4015,251,4,"ShadeListBuilder.logEndBuildList",11221,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895312394,3237,251,3,"ShadeListBuilder.buildList",11159,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895315631,2884,251,2,"animation",11158,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895318515,9168,251,1,"Choreographer#doFrame 10831",11156,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895327683,54243,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895381926,9329,251,3,"measure",11224,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895391255,333001,251,4,"NotificationShadeWindowView#onMeasure",11225,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895724256,141670,251,5,"NotificationStackScrollLayout#onMeasure",11226,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895865926,111203,251,4,"NotificationShadeWindowView#onMeasure",11225,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895977129,12506,251,5,"NotificationStackScrollLayout#onMeasure",11227,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738895989635,25938,251,4,"NotificationShadeWindowView#onMeasure",11225,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738896015573,2081,251,3,"measure",11224,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738896017654,7655,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738896025309,1293949,251,3,"layout",11228,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738897319258,6177,251,3,"layout",11228,1257,"[NULL]","R","[NULL]","[NULL]","[NULL]","[NULL]" +1738897325435,1328150,251,3,"layout",11228,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738898653585,597557,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738899251142,402484,251,3,"measure",11241,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738899653626,11058,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738899664684,317593,251,3,"layout",11242,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738899982277,14746,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738899997023,14681,251,3,"draw",11243,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900011704,172280,251,4,"Record View#draw()",11244,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900183984,10210,251,3,"draw",11243,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900194194,27982,251,4,"postAndWait",11245,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900222176,147072,251,4,"postAndWait",11245,1257,"[NULL]","S","[NULL]","[NULL]","[NULL]","[NULL]" +1738900369248,25748,251,4,"postAndWait",11245,1257,"[NULL]","R","[NULL]","[NULL]",703,0 +1738900394996,3700,251,4,"postAndWait",11245,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900398696,2014,251,3,"draw",11243,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900400710,2748,251,2,"traversal",11223,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900403458,4121,251,1,"Choreographer#doFrame 10831",11156,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" +1738900407579,3121,251,0,"android.view.Choreographer$FrameHandler: android.view.Choreographer$FrameDisplayEventReceiver",11155,1257,1,"Running","[NULL]","[NULL]","[NULL]","[NULL]" diff --git a/ui/src/common/colorizer.ts b/ui/src/common/colorizer.ts index 2b9006fd0..c527f02ff 100644 --- a/ui/src/common/colorizer.ts +++ b/ui/src/common/colorizer.ts @@ -190,3 +190,16 @@ export function colorToStr(color: Color) { export function colorCompare(x: Color, y: Color) { return (x.h - y.h) || (x.s - y.s) || (x.l - y.l); } + +export function getColorForSlice( + sliceName: string, hasFocus: boolean|null): Color { + const name = sliceName.replace(/( )?\d+/g, ''); + const [hue, saturation, lightness] = hslForSlice(name, hasFocus); + + return { + c: cachedHsluvToHex(hue, saturation, lightness), + h: hue, + s: saturation, + l: lightness, + }; +} diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts index 4b5c6d90d..05b449874 100644 --- a/ui/src/common/plugins.ts +++ b/ui/src/common/plugins.ts @@ -22,6 +22,7 @@ import {globals} from '../frontend/globals'; import {TrackCreator} from '../frontend/track'; import {trackRegistry} from '../frontend/track_registry'; import { + BasePlugin, Command, EngineProxy, MetricVisualisation, @@ -29,6 +30,7 @@ import { PluginClass, PluginContext, PluginInfo, + StatefulPlugin, Store, TracePluginContext, TrackInfo, @@ -41,6 +43,7 @@ import {Registry} from './registry'; // 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. +// The PluginContext exists for the whole duration a plugin is active. export class PluginContextImpl implements PluginContext, Disposable { readonly pluginId: string; readonly viewer: ViewerProxy; @@ -68,7 +71,10 @@ export class PluginContextImpl implements PluginContext, Disposable { } } -// Implementation the trace plugin context with trace-relevant properties. +// This TracePluginContext implementation provides the plugin access to trace +// related resources, such as the engine and the store. +// The TracePluginContext exists for the whole duration a plugin is active AND a +// trace is loaded. class TracePluginContextImpl<T> implements TracePluginContext<T>, Disposable { private ctx: PluginContext; readonly engine: EngineProxy; @@ -111,8 +117,8 @@ export class PluginRegistry extends Registry<PluginInfo<unknown>> { interface PluginDetails<T> { plugin: Plugin<T>; - context: PluginContextImpl; - traceContext?: TracePluginContextImpl<T>; + context: PluginContext&Disposable; + traceContext?: TracePluginContext<T>&Disposable; } function isPluginClass<T>(v: unknown): v is PluginClass<T> { @@ -151,14 +157,12 @@ export class PluginManager { return; } - // This is where the plugin context is created, and where we call - // onInit() on the plugin. const pluginInfo = this.registry.get(id); const plugin = makePlugin(pluginInfo); - const viewerProxy = viewer.getProxy(id); - // Create a proxy store for our plugin to use. + const viewerProxy = viewer.getProxy(id); const context = new PluginContextImpl(id, viewerProxy); + plugin.onActivate && plugin.onActivate(context); const pluginDetails: PluginDetails<unknown> = { @@ -166,30 +170,28 @@ export class PluginManager { context, }; - // If we already have a trace when the plugin is activated, call - // onTraceLoad() on the plugin and store the traceContext. + // If a trace is already loaded when plugin is activated, make sure to + // call onTraceLoad(). if (this.engine) { - this.initTracePlugin(pluginDetails, this.engine, id); + doPluginTraceLoad(pluginDetails, this.engine, id); } this.plugins.set(id, pluginDetails); } - deactivatePlugin(pluginId: string): void { - const pluginDetails = this.getPluginContext(pluginId); + deactivatePlugin(id: string): void { + const pluginDetails = this.getPluginContext(id); if (pluginDetails === undefined) { return; } - const {context, plugin, traceContext} = pluginDetails; + const {context, plugin} = pluginDetails; - if (traceContext) { - plugin.onTraceUnload && plugin.onTraceUnload(traceContext); - } + maybeDoPluginTraceUnload(pluginDetails); plugin.onDeactivate && plugin.onDeactivate(context); context.dispose(); - this.plugins.delete(pluginId); + this.plugins.delete(id); } isActive(pluginId: string): boolean { @@ -214,47 +216,13 @@ export class PluginManager { onTraceLoad(engine: Engine): void { this.engine = engine; for (const [id, pluginDetails] of this.plugins) { - this.initTracePlugin(pluginDetails, engine, id); + doPluginTraceLoad(pluginDetails, engine, id); } } - private initTracePlugin( - pluginDetails: PluginDetails<unknown>, engine: Engine, id: string): void { - const {plugin, context} = pluginDetails; - - const engineProxy = engine.getProxy(id); - if (plugin.migrate) { - // Extract the initial state and migrate. - const initialState = globals.store.state.plugins[id]; - const migratedState = plugin.migrate(initialState); - - // Write the the migrated state back to our root store. - globals.store.edit((draft) => { - draft.plugins[id] = migratedState; - }); - } - - const proxyStore = globals.store.createProxy<unknown>(['plugins', id]); - const traceCtx = - new TracePluginContextImpl(context, proxyStore, engineProxy); - - // TODO(stevegolton): We should probably wait for this to complete. - plugin.onTraceLoad && plugin.onTraceLoad(traceCtx); - pluginDetails.traceContext = traceCtx; - } - onTraceClose() { for (const pluginDetails of this.plugins.values()) { - const {traceContext, plugin} = pluginDetails; - - if (traceContext) { - if (plugin.onTraceUnload) { - plugin.onTraceUnload(traceContext); - } - traceContext.dispose(); - } - - pluginDetails.traceContext = undefined; + maybeDoPluginTraceUnload(pluginDetails); } this.engine = undefined; } @@ -264,7 +232,7 @@ export class PluginManager { const plugin = ctx.plugin; let commands: Command[] = []; - if (plugin && plugin.commands) { + if (plugin.commands) { commands = commands.concat(plugin.commands(ctx.context)); } @@ -288,6 +256,59 @@ export class PluginManager { } } +function isStatefulPlugin<T>(plugin: BasePlugin<T>| + StatefulPlugin<T>): plugin is StatefulPlugin<T> { + return 'migrate' in plugin; +} + +function doPluginTraceLoad<T>( + pluginDetails: PluginDetails<T>, engine: Engine, pluginId: string): void { + const {plugin, context} = pluginDetails; + + const engineProxy = engine.getProxy(pluginId); + + // Migrate state & write back to store. + if (isStatefulPlugin(plugin)) { + const initialState = globals.store.state.plugins[pluginId]; + const migratedState = plugin.migrate(initialState); + globals.store.edit((draft) => { + draft.plugins[pluginId] = migratedState; + }); + + const proxyStore = globals.store.createProxy<T>(['plugins', pluginId]); + const traceCtx = + new TracePluginContextImpl(context, proxyStore, engineProxy); + pluginDetails.traceContext = traceCtx; + + // TODO(stevegolton): Await onTraceLoad. + plugin.onTraceLoad && plugin.onTraceLoad(traceCtx); + } else { + // Stateless plugin i.e. the plugin's state type is undefined. + // Just provide a store proxy over this plugin's state, the plugin can work + // the state out for itself if it wants to, but we're not going to help it + // out by calling migrate(). + const proxyStore = globals.store.createProxy<T>(['plugins', pluginId]); + const traceCtx = + new TracePluginContextImpl(context, proxyStore, engineProxy); + pluginDetails.traceContext = traceCtx; + + // TODO(stevegolton): Await onTraceLoad. + plugin.onTraceLoad && plugin.onTraceLoad(traceCtx); + } +} + +function maybeDoPluginTraceUnload(pluginDetails: PluginDetails<unknown>): void { + const {traceContext, plugin} = pluginDetails; + + if (traceContext) { + // TODO(stevegolton): Await onTraceUnload. + plugin.onTraceUnload && plugin.onTraceUnload(traceContext); + traceContext.dispose(); + pluginDetails.traceContext = undefined; + } +} + + // TODO(hjd): Sort out the story for global singletons like these: export const pluginRegistry = new PluginRegistry(); export const pluginManager = new PluginManager(pluginRegistry); diff --git a/ui/src/common/plugins_unittest.ts b/ui/src/common/plugins_unittest.ts index 0c7e84e53..5be1f4a88 100644 --- a/ui/src/common/plugins_unittest.ts +++ b/ui/src/common/plugins_unittest.ts @@ -12,36 +12,96 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {Plugin, PluginContext} from '../public'; +import {globals} from '../frontend/globals'; +import {Plugin} from '../public'; +import {createEmptyState} from './empty_state'; +import {Engine} from './engine'; import {PluginManager, PluginRegistry} from './plugins'; import {ViewerImpl} from './viewer'; -const viewer = new ViewerImpl(); +class FakeEngine extends Engine { + id: string = 'TestEngine'; + + rpcSendRequestBytes(_data: Uint8Array) {} +} -class FooPlugin implements Plugin { - onActivate(_: PluginContext): void {} +function makeMockPlugin(): Plugin<any> { + return { + migrate: jest.fn(), + onActivate: jest.fn(), + onDeactivate: jest.fn(), + onTraceLoad: jest.fn(), + onTraceUnload: jest.fn(), + }; } -test('can activate plugin', () => { - const registry = new PluginRegistry(); - registry.register({ - pluginId: 'foo', - plugin: FooPlugin, +const viewer = new ViewerImpl(); +const engine = new FakeEngine(); +globals.initStore(createEmptyState()); + +// We use `any` here to avoid checking possibly undefined types in tests. +let mockPlugin: any; +let manager: any; + +describe('PluginManger', () => { + beforeEach(() => { + mockPlugin = makeMockPlugin(); + const registry = new PluginRegistry(); + registry.register({ + pluginId: 'foo', + plugin: mockPlugin, + }); + manager = new PluginManager(registry); }); - const manager = new PluginManager(registry); - manager.activatePlugin('foo', viewer); - expect(manager.isActive('foo')).toBe(true); -}); -test('can deactivate plugin', () => { - const registry = new PluginRegistry(); - registry.register({ - pluginId: 'foo', - plugin: FooPlugin, + it('can activate plugin', () => { + manager.activatePlugin('foo', viewer); + + expect(manager.isActive('foo')).toBe(true); + expect(mockPlugin.onActivate).toHaveBeenCalledTimes(1); + }); + + it('can deactivate plugin', () => { + manager.activatePlugin('foo', viewer); + manager.deactivatePlugin('foo'); + + expect(manager.isActive('foo')).toBe(false); + expect(mockPlugin.onDeactivate).toHaveBeenCalledTimes(1); + }); + + it('invokes onTraceLoad when trace is loaded', () => { + manager.activatePlugin('foo', viewer); + manager.onTraceLoad(engine); + + expect(mockPlugin.onTraceLoad).toHaveBeenCalledTimes(1); + }); + + it('invokes onTraceLoad when plugin activated while trace loaded', () => { + manager.onTraceLoad(engine); + manager.activatePlugin('foo', viewer); + + expect(mockPlugin.onTraceLoad).toHaveBeenCalledTimes(1); + }); + + it('invokes onTraceUnload when plugin deactivated while trace loaded', () => { + manager.activatePlugin('foo', viewer); + manager.onTraceLoad(engine); + manager.deactivatePlugin('foo'); + + expect(mockPlugin.onTraceUnload).toHaveBeenCalledTimes(1); + }); + + it('does not invoke migrate at activation time', () => { + manager.activatePlugin('foo', viewer); + + expect(mockPlugin.migrate).not.toHaveBeenCalled(); + }); + + it('invokes migrate when trace is loaded', () => { + manager.activatePlugin('foo', viewer); + manager.onTraceLoad(engine); + + expect(mockPlugin.migrate).toHaveBeenCalledTimes(1); }); - const manager = new PluginManager(registry); - manager.activatePlugin('foo', viewer); - manager.deactivatePlugin('foo'); - expect(manager.isActive('foo')).toBe(false); }); diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts index ec6961eab..881c26f65 100644 --- a/ui/src/frontend/base_slice_track.ts +++ b/ui/src/frontend/base_slice_track.ts @@ -17,7 +17,6 @@ import {Actions} from '../common/actions'; import {cropText, drawIncompleteSlice} from '../common/canvas_utils'; import { colorCompare, - colorToStr, UNEXPECTED_PINK_COLOR, } from '../common/colorizer'; import {LONG, NUM} from '../common/query_result'; @@ -32,6 +31,7 @@ import {raf} from '../core/raf_scheduler'; import {checkerboardExcept} from './checkerboard'; import {globals} from './globals'; +import {cachedHsluvToHex} from './hsluv_cache'; import {Slice} from './slice'; import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout'; import {constraintsToQuerySuffix} from './sql_utils'; @@ -401,7 +401,7 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = for (const slice of vizSlicesByColor) { if (slice.color !== lastColor) { lastColor = slice.color; - ctx.fillStyle = colorToStr(slice.color); + ctx.fillStyle = slice.color.c; } const y = padding + slice.depth * (sliceHeight + rowSpacing); if (slice.flags & SLICE_FLAGS_INSTANT) { @@ -461,7 +461,7 @@ export abstract class BaseSliceTrack<T extends BaseSliceTrackTypes = const slice = this.selectedSlice; const color = slice.color; const y = padding + slice.depth * (sliceHeight + rowSpacing); - ctx.strokeStyle = `hsl(${color.h}, ${color.s}%, 30%)`; + ctx.strokeStyle = cachedHsluvToHex(color.h, 100, 10); ctx.beginPath(); const THICKNESS = 3; ctx.lineWidth = THICKNESS; diff --git a/ui/src/frontend/named_slice_track.ts b/ui/src/frontend/named_slice_track.ts index ac1ba8e12..1601b7047 100644 --- a/ui/src/frontend/named_slice_track.ts +++ b/ui/src/frontend/named_slice_track.ts @@ -14,8 +14,7 @@ import {Actions} from '../common/actions'; import { - Color, - hslForSlice, + getColorForSlice, } from '../common/colorizer'; import {STR_NULL} from '../common/query_result'; @@ -59,10 +58,7 @@ export abstract class NamedSliceTrack< const baseSlice = super.rowToSlice(row); // Ignore PIDs or numeric arguments when hashing. const name = row.name || ''; - const nameForHashing = name.replace(/\s?\d+/g, ''); - const hsl = hslForSlice(nameForHashing, /* isSelected=*/ false); - // We cache the color so we hash only once per query. - const baseColor: Color = {c: '', h: hsl[0], s: hsl[1], l: hsl[2]}; + const baseColor = getColorForSlice(name, false); return {...baseSlice, title: name, baseColor}; } diff --git a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts b/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts index 441a28b19..5a9dec299 100644 --- a/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts +++ b/ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts @@ -16,6 +16,7 @@ import { Command, Plugin, PluginContext, + PluginInfo, } from '../../public'; // This is just an example plugin, used to prove that the plugin system works. @@ -35,7 +36,7 @@ class ExampleSimpleCommand implements Plugin { } } -export const plugin = { +export const plugin: PluginInfo = { pluginId: 'dev.perfetto.ExampleSimpleCommand', plugin: ExampleSimpleCommand, }; diff --git a/ui/src/plugins/dev.perfetto.ExampleState/index.ts b/ui/src/plugins/dev.perfetto.ExampleState/index.ts index 4303419db..ca7779ed1 100644 --- a/ui/src/plugins/dev.perfetto.ExampleState/index.ts +++ b/ui/src/plugins/dev.perfetto.ExampleState/index.ts @@ -27,12 +27,13 @@ interface State { // This example plugin shows using state that is persisted in the // permalink. class ExampleState implements Plugin<State> { - migrate(_initialState: unknown): State { - // TODO(hjd): Show validation example. - - return { - counter: 0, - }; + migrate(initialState: unknown): State { + if (initialState && typeof initialState === 'object' && + 'counter' in initialState && typeof initialState.counter === 'number') { + return {counter: initialState.counter}; + } else { + return {counter: 0}; + } } onActivate(_: PluginContext): void { @@ -56,7 +57,7 @@ class ExampleState implements Plugin<State> { } } -export const plugin: PluginInfo = { +export const plugin: PluginInfo<State> = { pluginId: 'dev.perfetto.ExampleState', plugin: ExampleState, }; diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts index 3a3c67a38..527c58239 100644 --- a/ui/src/public/index.ts +++ b/ui/src/public/index.ts @@ -163,33 +163,36 @@ export interface PluginContext { // Similar to PluginContext but with additional properties to operate on the // currently loaded trace. Passed to trace-relevant hooks instead of // PluginContext. -// TODO(stevegolton): I'm not entirely sold on this approach. For one, it means -// commands don't get to access the engine as they only get a PluginContext, -// whereas if they got a TracePluginContext they could only get evaluated -// when a trace is loaded. Thus, this needs a rethink. -export interface TracePluginContext<T = unknown> extends PluginContext { +export interface TracePluginContext<T = undefined> extends PluginContext { readonly engine: EngineProxy; readonly store: Store<T>; } -// All trace plugins must implement this interface. -export interface Plugin<T = unknown> { - // Function to migrate the persistent state. - migrate?(initialState: unknown): T; - +export interface BasePlugin<State> { // Lifecycle methods. onActivate(ctx: PluginContext): void; - onTraceLoad?(ctx: TracePluginContext<T>): Promise<void>; - onTraceUnload?(ctx: TracePluginContext<T>): Promise<void>; + onTraceLoad?(ctx: TracePluginContext<State>): Promise<void>; + onTraceUnload?(ctx: TracePluginContext<State>): Promise<void>; onDeactivate?(ctx: PluginContext): void; - // Legacy extension points. + // Extension points. commands?(ctx: PluginContext): Command[]; - traceCommands?(ctx: TracePluginContext<T>): Command[]; + traceCommands?(ctx: TracePluginContext<State>): Command[]; metricVisualisations?(ctx: PluginContext): MetricVisualisation[]; - findPotentialTracks?(ctx: TracePluginContext<T>): Promise<TrackInfo[]>; + findPotentialTracks?(ctx: TracePluginContext<State>): Promise<TrackInfo[]>; +} + +export interface StatefulPlugin<State> extends BasePlugin<State> { + // Function to migrate the persistent state. + migrate(initialState: unknown): State; } +// Generic interface all plugins must implement. +// If a state type is passed, the plugin must implement migrate(). Otherwise if +// the state type is omitted, migrate need not be defined. +export type Plugin<State = undefined> = + State extends undefined ? BasePlugin<State>: StatefulPlugin<State>; + // This interface defines what a plugin factory should look like. // This can be defined in the plugin class definition by defining a constructor // and the relevant static methods: @@ -236,7 +239,7 @@ export interface TrackTags { // implementations. export type PluginFactory<T> = PluginClass<T>|Plugin<T>|(() => Plugin<T>); -export interface PluginInfo<T = unknown> { +export interface PluginInfo<T = undefined> { // A unique string for your plugin. To ensure the name is unique you // may wish to use a URL with reversed components in the manner of // Java package names. diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts index 5f8ae0359..370c75460 100644 --- a/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/event_latency_track.ts @@ -14,11 +14,15 @@ import {v4 as uuidv4} from 'uuid'; +import { + getColorForSlice, +} from '../../common/colorizer'; import {Engine} from '../../common/engine'; import { generateSqlWithInternalLayout, } from '../../common/internal_layout_utils'; import {PrimaryTrackSortKey, SCROLLING_TRACK_GROUP} from '../../common/state'; +import {globals} from '../../frontend/globals'; import { NamedSliceTrackTypes, } from '../../frontend/named_slice_track'; @@ -30,8 +34,13 @@ import { } from '../custom_sql_table_slices'; import {EventLatencySliceDetailsPanel} from './event_latency_details_panel'; -import {ScrollJankTracks as DecideTracksResult} from './index'; -import {ScrollJankPluginState} from './index'; +import { + ScrollJankPluginState, + ScrollJankTracks as DecideTracksResult, +} from './index'; +import {DEEP_RED_COLOR, RED_COLOR} from './jank_colors'; + +const JANKY_LATENCY_NAME = 'Janky EventLatency'; export interface EventLatencyTrackTypes extends NamedSliceTrackTypes { config: {baseTable: string;} @@ -80,6 +89,29 @@ export class EventLatencyTrack extends }; } + onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) { + for (const slice of slices) { + const currentSelection = globals.state.currentSelection; + const isSelected = currentSelection && + currentSelection.kind === 'GENERIC_SLICE' && + currentSelection.id !== undefined && currentSelection.id === slice.id; + + const highlighted = globals.state.highlightedSliceId === slice.id; + const hasFocus = highlighted || isSelected; + + if (slice.title === JANKY_LATENCY_NAME) { + if (hasFocus) { + slice.baseColor = DEEP_RED_COLOR; + } else { + slice.baseColor = RED_COLOR; + } + } else { + slice.baseColor = getColorForSlice(slice.title, hasFocus); + } + } + super.onUpdatedSlices(slices); + } + // At the moment we will just display the slice details. However, on select, // this behavior should be customized to show jank-related data. } @@ -133,7 +165,7 @@ export async function addLatencyTracks(engine: Engine): CASE WHEN id IN ( SELECT id FROM chrome_janky_event_latencies_v3) - THEN 'Janky EventLatency' + THEN '${JANKY_LATENCY_NAME}' ELSE name END AS name, diff --git a/ui/src/tracks/chrome_scroll_jank/jank_colors.ts b/ui/src/tracks/chrome_scroll_jank/jank_colors.ts new file mode 100644 index 000000000..7df9ecf84 --- /dev/null +++ b/ui/src/tracks/chrome_scroll_jank/jank_colors.ts @@ -0,0 +1,29 @@ +// 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 {Color} from '../../common/colorizer'; + +export const RED_COLOR: Color = { + c: '#C41E3A', + h: 196, + s: 30, + l: 58, +}; + +export const DEEP_RED_COLOR: Color = { + c: '#880808', + h: 136, + s: 8, + l: 8, +}; diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts index 0c766b320..a065e2982 100644 --- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts +++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts @@ -14,11 +14,15 @@ import {v4 as uuidv4} from 'uuid'; +import { + getColorForSlice, +} from '../../common/colorizer'; import {Engine} from '../../common/engine'; import { PrimaryTrackSortKey, SCROLLING_TRACK_GROUP, } from '../../common/state'; +import {globals} from '../../frontend/globals'; import {NamedSliceTrackTypes} from '../../frontend/named_slice_track'; import {NewTrackArgs, Track} from '../../frontend/track'; import { @@ -27,14 +31,19 @@ import { CustomSqlTableSliceTrack, } from '../custom_sql_table_slices'; +import {EventLatencyTrackTypes} from './event_latency_track'; import { ScrollJankPluginState, ScrollJankTracks as DecideTracksResult, } from './index'; +import {DEEP_RED_COLOR, RED_COLOR} from './jank_colors'; import {ScrollJankV3DetailsPanel} from './scroll_jank_v3_details_panel'; export {Data} from '../chrome_slices'; +const UNKNOWN_SLICE_NAME = 'Unknown'; +const JANK_SLICE_NAME = ' Jank'; + export class ScrollJankV3Track extends CustomSqlTableSliceTrack<NamedSliceTrackTypes> { static readonly kind = 'org.chromium.ScrollJank.scroll_jank_v3_track'; @@ -45,7 +54,6 @@ export class ScrollJankV3Track extends constructor(args: NewTrackArgs) { super(args); - ScrollJankPluginState.getInstance().registerTrack({ kind: ScrollJankV3Track.kind, trackId: this.trackId, @@ -61,7 +69,7 @@ export class ScrollJankV3Track extends cause_of_jank IS NOT NULL, cause_of_jank || IIF( sub_cause_of_jank IS NOT NULL, "::" || sub_cause_of_jank, "" - ), "Unknown") || " Jank" AS name`, + ), "${UNKNOWN_SLICE_NAME}") || "${JANK_SLICE_NAME}" AS name`, 'id', 'ts', 'dur', @@ -84,6 +92,38 @@ export class ScrollJankV3Track extends super.onDestroy(); ScrollJankPluginState.getInstance().unregisterTrack(ScrollJankV3Track.kind); } + + onUpdatedSlices(slices: EventLatencyTrackTypes['slice'][]) { + for (const slice of slices) { + const currentSelection = globals.state.currentSelection; + const isSelected = currentSelection && + currentSelection.kind === 'GENERIC_SLICE' && + currentSelection.id !== undefined && currentSelection.id === slice.id; + + const highlighted = globals.state.highlightedSliceId === slice.id; + const hasFocus = highlighted || isSelected; + + let stage = + slice.title.substring(0, slice.title.indexOf(JANK_SLICE_NAME)); + // Stage may include substage, in which case we use the substage for + // color selection. + const separator = '::'; + if (stage.indexOf(separator) != -1) { + stage = stage.substring(stage.indexOf(separator) + separator.length); + } + + if (stage == UNKNOWN_SLICE_NAME) { + if (hasFocus) { + slice.baseColor = DEEP_RED_COLOR; + } else { + slice.baseColor = RED_COLOR; + } + } else { + slice.baseColor = getColorForSlice(stage, hasFocus); + } + } + super.onUpdatedSlices(slices); + } } export async function addScrollJankV3ScrollTrack(engine: Engine): diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts index 9c26d595b..232b9175b 100644 --- a/ui/src/tracks/chrome_slices/index.ts +++ b/ui/src/tracks/chrome_slices/index.ts @@ -15,7 +15,10 @@ import {BigintMath as BIMath} from '../../base/bigint_math'; import {Actions} from '../../common/actions'; import {cropText, drawIncompleteSlice} from '../../common/canvas_utils'; -import {colorForThreadIdleSlice, hslForSlice} from '../../common/colorizer'; +import { + colorForThreadIdleSlice, + getColorForSlice, +} from '../../common/colorizer'; import {HighPrecisionTime} from '../../common/high_precision_time'; import {LONG, LONG_NULL, NUM, STR} from '../../common/query_result'; import {duration, Span, Time, time} from '../../common/time'; @@ -236,17 +239,15 @@ export class ChromeSliceTrack extends Track<Config, Data> { currentSelection.kind === 'CHROME_SLICE' && currentSelection.id !== undefined && currentSelection.id === sliceId; - const name = title.replace(/( )?\d+/g, ''); const highlighted = titleId === this.hoveredTitleId || globals.state.highlightedSliceId === sliceId; const hasFocus = highlighted || isSelected; - - const [hue, saturation, lightness] = hslForSlice(name, hasFocus); + const colorObj = getColorForSlice(title, hasFocus); let color: string; if (colorOverride === undefined) { - color = cachedHsluvToHex(hue, saturation, lightness); + color = colorObj.c; } else { color = colorOverride; } @@ -269,7 +270,7 @@ export class ChromeSliceTrack extends Track<Config, Data> { ctx.save(); ctx.translate(0, INNER_CHEVRON_OFFSET); ctx.scale(INNER_CHEVRON_SCALE, INNER_CHEVRON_SCALE); - ctx.fillStyle = cachedHsluvToHex(hue, 100, 10); + ctx.fillStyle = cachedHsluvToHex(colorObj.h, 100, 10); this.drawChevron(ctx); ctx.restore(); @@ -299,8 +300,8 @@ export class ChromeSliceTrack extends Track<Config, Data> { const firstPartWidth = rect.width * cpuTimeRatio; const secondPartWidth = rect.width * (1 - cpuTimeRatio); ctx.fillRect(rect.left, rect.top, firstPartWidth, SLICE_HEIGHT); - ctx.fillStyle = - colorForThreadIdleSlice(hue, saturation, lightness, hasFocus); + ctx.fillStyle = colorForThreadIdleSlice( + colorObj.h, colorObj.s, colorObj.l, hasFocus); ctx.fillRect( rect.left + firstPartWidth, rect.top, @@ -313,7 +314,7 @@ export class ChromeSliceTrack extends Track<Config, Data> { // Selected case if (isSelected) { drawRectOnSelected = () => { - ctx.strokeStyle = cachedHsluvToHex(hue, 100, 10); + ctx.strokeStyle = cachedHsluvToHex(colorObj.h, 100, 10); ctx.beginPath(); ctx.lineWidth = 3; ctx.strokeRect( @@ -324,7 +325,7 @@ export class ChromeSliceTrack extends Track<Config, Data> { // Don't render text when we have less than 5px to play with. if (rect.width >= 5) { - ctx.fillStyle = lightness > 65 ? '#404040' : 'white'; + ctx.fillStyle = colorObj.l > 65 ? '#404040' : 'white'; const displayText = cropText(title, charWidth, rect.width); const rectXCenter = rect.left + rect.width / 2; ctx.textBaseline = 'middle'; |