aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHector Dearman <hjd@google.com>2023-08-22 15:03:51 +0100
committerHector Dearman <hjd@google.com>2023-08-22 15:03:51 +0100
commitc69b33b9abcc20fad9ad5f39de883216e4b43130 (patch)
treece9f26e32accc016f1d11d4da040142498d2cab1
parent2d045f852d287f2744492cb51a903f0297db047d (diff)
downloadperfetto-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
-rw-r--r--Android.bp16
-rw-r--r--BUILD2
-rw-r--r--include/perfetto/public/abi/data_source_abi.h27
-rw-r--r--protos/perfetto/trace/ftrace/all_protos.gni1
-rw-r--r--protos/perfetto/trace/ftrace/ftrace_event.proto2
-rw-r--r--protos/perfetto/trace/ftrace/samsung.proto14
-rw-r--r--protos/perfetto/trace/perfetto_trace.proto13
-rw-r--r--src/shared_lib/data_source.cc4
-rw-r--r--src/shared_lib/test/api_integrationtest.cc22
-rw-r--r--src/tools/ftrace_proto_gen/event_list1
-rw-r--r--src/tools/ftrace_proto_gen/ftrace_proto_gen.cc2
-rw-r--r--src/trace_processor/db/BUILD.gn1
-rw-r--r--src/trace_processor/db/runtime_table.cc70
-rw-r--r--src/trace_processor/db/runtime_table.h9
-rw-r--r--src/trace_processor/db/runtime_table_unittest.cc60
-rw-r--r--src/trace_processor/importers/json/json_utils.cc40
-rw-r--r--src/trace_processor/importers/json/json_utils_unittest.cc21
-rw-r--r--src/trace_processor/perfetto_sql/stdlib/chrome/scroll_jank/utils.sql8
-rw-r--r--src/trace_processor/perfetto_sql/stdlib/experimental/BUILD.gn1
-rw-r--r--src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql8
-rw-r--r--src/trace_processor/perfetto_sql/stdlib/experimental/thread_state_flattened.sql175
-rw-r--r--src/traced/probes/ftrace/event_info.cc22
-rw-r--r--src/traced/probes/ftrace/test/data/synthetic/events/samsung/tracing_mark_write/format14
-rw-r--r--test/trace_processor/diff_tests/parsing/tests.py6
-rw-r--r--test/trace_processor/diff_tests/slices/tests.py2
-rw-r--r--test/trace_processor/diff_tests/tables/tests.py18
-rw-r--r--test/trace_processor/diff_tests/tables/thread_state_flattened_aggregated_csv.out55
-rw-r--r--test/trace_processor/diff_tests/tables/thread_state_flattened_csv.out126
-rw-r--r--ui/src/common/colorizer.ts13
-rw-r--r--ui/src/common/plugins.ts129
-rw-r--r--ui/src/common/plugins_unittest.ts104
-rw-r--r--ui/src/frontend/base_slice_track.ts6
-rw-r--r--ui/src/frontend/named_slice_track.ts8
-rw-r--r--ui/src/plugins/dev.perfetto.ExampleSimpleCommand/index.ts3
-rw-r--r--ui/src/plugins/dev.perfetto.ExampleState/index.ts15
-rw-r--r--ui/src/public/index.ts35
-rw-r--r--ui/src/tracks/chrome_scroll_jank/event_latency_track.ts38
-rw-r--r--ui/src/tracks/chrome_scroll_jank/jank_colors.ts29
-rw-r--r--ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts44
-rw-r--r--ui/src/tracks/chrome_slices/index.ts21
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",
diff --git a/BUILD b/BUILD
index 078b4df15..3ae4c0d8c 100644
--- a/BUILD
+++ b/BUILD
@@ -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';