Merge "Output all_data_source_flushed_ns to metadata as an array" into main
diff --git a/Android.bp b/Android.bp
index bfa3740..6dff4d9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11879,6 +11879,7 @@
"src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
"src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
"src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
+ "src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql",
"src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql",
"src/trace_processor/perfetto_sql/stdlib/experimental/android_broadcast.sql",
"src/trace_processor/perfetto_sql/stdlib/experimental/flat_slices.sql",
diff --git a/BUILD b/BUILD
index 1ba49a5..edc1eca 100644
--- a/BUILD
+++ b/BUILD
@@ -2259,6 +2259,7 @@
"src/trace_processor/perfetto_sql/stdlib/common/metadata.sql",
"src/trace_processor/perfetto_sql/stdlib/common/percentiles.sql",
"src/trace_processor/perfetto_sql/stdlib/common/slices.sql",
+ "src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql",
"src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql",
],
)
diff --git a/protos/perfetto/metrics/android/android_boot.proto b/protos/perfetto/metrics/android/android_boot.proto
index 820b4e0..53de221 100644
--- a/protos/perfetto/metrics/android/android_boot.proto
+++ b/protos/perfetto/metrics/android/android_boot.proto
@@ -29,4 +29,10 @@
optional ProcessStateDurations systemui_durations = 2;
optional ProcessStateDurations launcher_durations = 3;
optional ProcessStateDurations gms_durations = 4;
+ // Launcher related boot metrics
+ message LauncherBreakdown {
+ // reports cold start time of NexusLauncher
+ optional int64 cold_start_dur = 1;
+ }
+ optional LauncherBreakdown launcher_breakdown = 5;
}
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 3f270f6..d072a39 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -143,6 +143,12 @@
optional ProcessStateDurations systemui_durations = 2;
optional ProcessStateDurations launcher_durations = 3;
optional ProcessStateDurations gms_durations = 4;
+ // Launcher related boot metrics
+ message LauncherBreakdown {
+ // reports cold start time of NexusLauncher
+ optional int64 cold_start_dur = 1;
+ }
+ optional LauncherBreakdown launcher_breakdown = 5;
}
// End of protos/perfetto/metrics/android/android_boot.proto
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index fbf1987..8a6f511 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -45,7 +45,8 @@
// Changes:
// 7. Introduce GUESS_CPU_SIZE
// 8. Add 'json' option to ComputeMetricArgs
- TRACE_PROCESSOR_CURRENT_API_VERSION = 8;
+ // 9. Add get_thread_state_summary_for_interval.
+ TRACE_PROCESSOR_CURRENT_API_VERSION = 9;
}
// At lowest level, the wire-format of the RPC procol is a linear sequence of
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 7af1fde..d62f1fd 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor b/python/perfetto/trace_processor/trace_processor.descriptor
index 33ca764..8b8383f 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor
+++ b/python/perfetto/trace_processor/trace_processor.descriptor
Binary files differ
diff --git a/src/trace_processor/metrics/sql/android/android_boot.sql b/src/trace_processor/metrics/sql/android/android_boot.sql
index 46caea8..4dc8936 100644
--- a/src/trace_processor/metrics/sql/android/android_boot.sql
+++ b/src/trace_processor/metrics/sql/android/android_boot.sql
@@ -47,5 +47,9 @@
SELECT NULL_IF_EMPTY(ProcessStateDurations(
'total_dur', total_dur,
'uninterruptible_sleep_dur', uint_sleep_dur))
- FROM get_durations('com.google.android.gms.persistent'))
+ FROM get_durations('com.google.android.gms.persistent')),
+ 'launcher_breakdown', (
+ SELECT NULL_IF_EMPTY(AndroidBootMetric_LauncherBreakdown(
+ 'cold_start_dur', dur))
+ FROM slice where name="LauncherColdStartup")
);
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
index 93ffc3d..bb4d758 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/common/BUILD.gn
@@ -22,6 +22,7 @@
"metadata.sql",
"percentiles.sql",
"slices.sql",
+ "thread_states.sql",
"timestamps.sql",
]
}
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql b/src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql
new file mode 100644
index 0000000..2e9dce1
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql
@@ -0,0 +1,111 @@
+--
+-- Copyright 2022 The Android Open Source Project
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- 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.
+
+INCLUDE PERFETTO MODULE common.timestamps;
+INCLUDE PERFETTO MODULE common.cpus;
+
+-- TODO(altimin): this doesn't handle some corner cases which thread_state.ts
+-- handles (as complex strings manipulations in SQL are pretty painful),
+-- but they are pretty niche.
+-- Translates the thread state name from a single-letter shorthard to
+-- a human-readable name.
+CREATE PERFETTO FUNCTION internal_translate_thread_state_name(name STRING)
+RETURNS STRING AS
+SELECT CASE $name
+WHEN 'Running' THEN 'Running'
+WHEN 'R' THEN 'Runnable'
+WHEN 'R+' THEN 'Runnable (Preempted)'
+WHEN 'S' THEN 'Sleeping'
+WHEN 'D' THEN 'Uninterruptible Sleep'
+WHEN 'T' THEN 'Stopped'
+WHEN 't' THEN 'Traced'
+WHEN 'X' THEN 'Exit (Dead)'
+WHEN 'Z' THEN 'Exit (Zombie)'
+WHEN 'x' THEN 'Task Dead'
+WHEN 'I' THEN 'Idle'
+WHEN 'K' THEN 'Wakekill'
+WHEN 'W' THEN 'Waking'
+WHEN 'P' THEN 'Parked'
+WHEN 'N' THEN 'No Load'
+ELSE $name
+END;
+
+-- Returns a human-readable name for a thread state.
+-- @arg id INT Thread state id.
+-- @ret STRING Human-readable name for the thread state.
+CREATE PERFETTO FUNCTION human_readable_thread_state_name(id INT)
+RETURNS STRING AS
+WITH data AS (
+ SELECT
+ internal_translate_thread_state_name(state) AS state,
+ (CASE io_wait
+ WHEN 1 THEN ' (IO)'
+ WHEN 0 THEN ' (non-IO)'
+ ELSE ''
+ END) AS io_wait
+ FROM thread_state
+ WHERE id = $id
+)
+SELECT
+ printf('%s%s', state, io_wait)
+FROM data;
+
+-- Returns an aggregation of thread states (by state and cpu) for a given
+-- interval of time for a given thread.
+-- @arg ts INT The start of the interval.
+-- @arg dur INT The duration of the interval.
+-- @arg utid INT The utid of the thread.
+-- @column state Human-readable thread state name.
+-- @column raw_state Raw thread state name, alias of `thread_state.state`.
+-- @column cpu_type The type of CPU if available (e.g. "big" / "mid" / "little").
+-- @column cpu The CPU index.
+-- @column blocked_function The name of the kernel function execution is blocked in.
+-- @column dur The total duration.
+CREATE PERFETTO FUNCTION thread_state_summary_for_interval(
+ ts INT, dur INT, utid INT)
+RETURNS TABLE(
+ state STRING, raw_state STRING, cpu_type STRING, cpu INT, blocked_function STRING, dur INT)
+AS
+WITH
+states_starting_inside AS (
+ SELECT id
+ FROM thread_state
+ WHERE $ts <= ts
+ AND ts <= $ts + $dur
+ AND utid = $utid
+),
+first_state_starting_before AS (
+ SELECT id
+ FROM thread_state
+ WHERE ts < $ts AND utid = $utid
+ ORDER BY ts DESC
+ LIMIT 1
+),
+relevant_states AS (
+ SELECT * FROM states_starting_inside
+ UNION ALL
+ SELECT * FROM first_state_starting_before
+)
+SELECT
+ human_readable_thread_state_name(id) as state,
+ state as raw_state,
+ guess_cpu_size(cpu) as cpu_type,
+ cpu,
+ blocked_function,
+ sum(spans_overlapping_dur($ts, $dur, ts, dur)) as dur
+FROM thread_state
+JOIN relevant_states USING (id)
+GROUP BY state, raw_state, cpu_type, cpu, blocked_function
+ORDER BY dur desc;
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
index e5e8fa3..e908009 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
@@ -1 +1 @@
-041ebd18b8b3f62f76b1613f548c8cf8ea5660f818b4ecfab787c4b08fd55b50
\ No newline at end of file
+3a07a7ddf3093eb5c6672262585ffa510a61a6697e1bd0abc2cdea5a4f7d43e8
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
index 6cee1b2..34a1640 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
@@ -1 +1 @@
-ec0a00856b147b2e13d0fe18666a307eb085ac437d67f78787131d4ea4190581
\ No newline at end of file
+988f934497578397dd5296c2b13b102b0f20d0c0be7a3a456edbb0af12d86af3
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
index 6e1e7b7..b54ff87 100644
--- a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
@@ -1 +1 @@
-875e17941e1dd5c362eef4bc679af41db5a9b2e37bbde5e5b2d1e90fd54b9e28
\ No newline at end of file
+5da7d4256544d4d354adb709731c6f0f190fa02545c8d0ae5ca3bf5cd92c4737
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
index 9203ad1..6920dd4 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
@@ -1 +1 @@
-1d185424a99b85372cecac21728fbf782fe33abf6a0664791ad08a3902bcdc3e
\ No newline at end of file
+3cef07cfaa35974d947d078764da91201dad7d8dd524fcfe72640de7adea6806
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
index 64e5c99..022b7e0 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
@@ -1 +1 @@
-1ae776d7033331f560685bfd61aa83a8a3f9639a400150bfe7be19642be3855a
\ No newline at end of file
+3095f3a5dd9b778eea5b3dff0b2e16b0eb1f7465be30ae57ac6403d79059c0d4
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
index 60017fd..d06f2da 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
@@ -1 +1 @@
-d4047dbc06457be945fc612e629fca6a8ffaf15332fe76c653b1dd7a9a58d06b
\ No newline at end of file
+22ac238188e556571d92ef85001b632f880e00833de3902a670c70e887f2a2b1
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
index 9b5f546..27e856c 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_dismiss_1.png.sha256
@@ -1 +1 @@
-846bdc1f6e20082da46d483049322300d91cb38ef72f922c934d46cc5df3507d
\ No newline at end of file
+20b4490ecc9f130cd3a4a4fb32b8474cf85a5217e4d2513bcd5a72aa42a868ae
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
index 6710525..f43095d 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_dismiss_2.png.sha256
@@ -1 +1 @@
-5f6f302958f0b26df40b90740bd8786480d7e8aa89232baa4fe2d881c070ba3c
\ No newline at end of file
+efc88907b3637cb94687b2dea5f84c0545c3e2d893e7c704d2891b9e9d0c6804
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
index 655b0f9..73fda6b 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
@@ -1 +1 @@
-fff7ed44c6100f75f93e21bdb9dbd093a8e67274d5eee255bf96e7d26a5614ca
\ No newline at end of file
+8795bbc1fe7e8394bdfebab94fb52d523db94645d8a0e9e0936ff7aebd26005c
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
index 8febff4..b7d6ed3 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
@@ -1 +1 @@
-31029a524c31322ce5c72f246de163b28047b839275a30b07061153a963a386b
\ No newline at end of file
+9d49ae65dbefffb6ecff8c5fa88c40fda7010597c30f5f5352bb60bd4c7fa2f8
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
index 38f1e1d..2b82951 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_switch_page_no_dialog.png.sha256
@@ -1 +1 @@
-cf12d662d4137c081875afbb1c508827c9430e2837013bd917acd6eaadfac37c
\ No newline at end of file
+919fb0fb7dee21d9863631dfefe1dc361b10bcfbc538d6304bdf6bf08956b9ea
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
index 895dc82..dcf9e23 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
@@ -1 +1 @@
-177e27d4d86bee1a17fce48d651b160f1541434aeb0f9e8fc1bac2b8fb07ac6d
\ No newline at end of file
+2bb21af6fe2d7485395456fc3645a966ac8127848b08b43da1a6acbb94fffedf
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
index 5294534..3bf47c4 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+c67a816aa2f557db82b0ba7c5f75b9bb055b1a5e4bebee7621e86c3ba39f4598
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
index 7a976a8..5dad293 100644
--- a/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_invalid_trace_from_blank_page.png.sha256
@@ -1 +1 @@
-4486b3cba3f423541c1efd3f8dba5ee157e8215263c3250e8cefac6382905691
\ No newline at end of file
+90fbaef03da9a8b0af6a3d20ae58fd9c77bfd9dcb878eebe4b7b0fd6863faa1a
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
index b0f92e9..f19511f 100644
--- a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
@@ -1 +1 @@
-3c80ba72b9bd0454af4aac3352c4e8f855f48feeba53d2a5ac7566333b4cf763
\ No newline at end of file
+3f15c9a2d649e73575a524ee64832edcd994d2f46b0452a506c9b7a88b6439c4
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
index 33f95ef..d0c2640 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+361878a37a45869f351268cebb3da33476b8dbe41a15f498ee0c4a6c7794ce36
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
index 5294534..3bf47c4 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+c67a816aa2f557db82b0ba7c5f75b9bb055b1a5e4bebee7621e86c3ba39f4598
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
index 33f95ef..d0c2640 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+361878a37a45869f351268cebb3da33476b8dbe41a15f498ee0c4a6c7794ce36
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
index 33f95ef..d0c2640 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+361878a37a45869f351268cebb3da33476b8dbe41a15f498ee0c4a6c7794ce36
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
index 22444d8..09f7455 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_to_page_with_no_trace.png.sha256
@@ -1 +1 @@
-52be49df180c482cad1a48979ce0bb2d20a7cdd27ad10cb972b5bad61b9865ca
\ No newline at end of file
+90d36af3ef3497134692d91b177f598a7b8998db7f74fb24d69440eaff3c24fa
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
index 99e5f07..b204fca 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_invalid_trace.png.sha256
@@ -1 +1 @@
-4b96c2f13ce655e31532c21eac0652244803043ae6c56b5dcaec0e3158552256
\ No newline at end of file
+44562cc042fd22093ceb0f7e857f1fb7906324fe03106ee5bec356764bd534b0
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
index 5294534..3bf47c4 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
@@ -1 +1 @@
-c099f4ab43ee73de87c83ca2bb8cd2c087abdb12512ca3855e5cb6e5203e378b
\ No newline at end of file
+c67a816aa2f557db82b0ba7c5f75b9bb055b1a5e4bebee7621e86c3ba39f4598
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
index 33f95ef..d0c2640 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+361878a37a45869f351268cebb3da33476b8dbe41a15f498ee0c4a6c7794ce36
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
index 33f95ef..d0c2640 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
@@ -1 +1 @@
-7506ad1268c6d92743d19f52d37a1e8b7cf00fc7907bf9e3e06966dbbb1b40c1
\ No newline at end of file
+361878a37a45869f351268cebb3da33476b8dbe41a15f498ee0c4a6c7794ce36
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 0918d10..1c05fad 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -87,6 +87,7 @@
from diff_tests.parser.translated_args.tests import TranslatedArgs
from diff_tests.parser.ufs.tests import Ufs
from diff_tests.stdlib.android.tests import AndroidStdlib
+from diff_tests.stdlib.common.tests import StdlibCommon
from diff_tests.stdlib.chrome.tests import ChromeStdlib
from diff_tests.stdlib.chrome.tests_scroll_jank import ChromeScrollJankStdlib
from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
@@ -160,7 +161,8 @@
*TranslatedArgs(index_path, 'parser/translated_args',
'TranslatedArgs').fetch(),
*Ufs(index_path, 'parser/ufs', 'Ufs').fetch(),
- # TODO(altimin, lalitm): "parsing" should be split into more specific directories.
+ # TODO(altimin, lalitm): "parsing" should be split into more specific
+ # directories.
*Parsing(index_path, 'parser/parsing', 'Parsing').fetch(),
*ParsingDebugAnnotation(index_path, 'parser/parsing',
'ParsingDebugAnnotation').fetch(),
@@ -208,6 +210,7 @@
*DynamicTables(index_path, 'stdlib/dynamic_tables',
'DynamicTables').fetch(),
*Pkvm(index_path, 'stdlib/pkvm', 'Pkvm').fetch(),
+ *StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*Slices(index_path, 'stdlib/slices', 'Slices').fetch(),
*SpanJoinLeftJoin(index_path, 'stdlib/span_join',
'SpanJoinLeftJoin').fetch(),
diff --git a/test/trace_processor/diff_tests/stdlib/common/tests.py b/test/trace_processor/diff_tests/stdlib/common/tests.py
new file mode 100644
index 0000000..a902b5b
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/common/tests.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License a
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class StdlibCommon(TestSuite):
+
+ def test_thread_state_summary(self):
+ return DiffTestBlueprint(
+ trace=Path('../../common/synth_1.py'),
+ query="""
+ INCLUDE PERFETTO MODULE common.thread_states;
+
+ SELECT
+ state,
+ cpu,
+ dur
+ FROM thread_state_summary_for_interval(
+ 25,
+ 75,
+ (
+ SELECT utid
+ FROM thread
+ WHERE name = 'init'
+ )
+ )
+ """,
+ out=Csv("""
+ "state","cpu","dur"
+ "Running",1,50
+ "Runnable","[NULL]",25
+ """))
\ No newline at end of file
diff --git a/ui/src/base/mithril_utils.ts b/ui/src/base/mithril_utils.ts
index 854221f..1f07578 100644
--- a/ui/src/base/mithril_utils.ts
+++ b/ui/src/base/mithril_utils.ts
@@ -14,10 +14,8 @@
import m from 'mithril';
-import {exists} from './utils';
-
// Check if a mithril component vnode has children
export function hasChildren({children}: m.Vnode<any>): boolean {
return Array.isArray(children) && children.length > 0 &&
- children.some(exists);
+ children.some((value) => value);
}
diff --git a/ui/src/base/time.ts b/ui/src/base/time.ts
index 528fdbf..c8e5837 100644
--- a/ui/src/base/time.ts
+++ b/ui/src/base/time.ts
@@ -319,6 +319,10 @@
this.end = end;
}
+ static fromTimeAndDuration(start: time, duration: duration): TimeSpan {
+ return new TimeSpan(start, Time.add(start, duration));
+ }
+
get duration(): duration {
return this.end - this.start;
}
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index a298b36..881cb37 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -15,11 +15,12 @@
import m from 'mithril';
import {Icons} from '../base/semantic_icons';
-import {duration, Time} from '../base/time';
+import {duration, Time, TimeSpan} from '../base/time';
import {exists} from '../base/utils';
import {EngineProxy} from '../common/engine';
import {runQuery} from '../common/queries';
import {LONG, LONG_NULL, NUM, STR_NULL} from '../common/query_result';
+import {raf} from '../core/raf_scheduler';
import {addDebugSliceTrack} from '../tracks/debug/slice_track';
import {Button} from '../widgets/button';
import {DetailsShell} from '../widgets/details_shell';
@@ -39,6 +40,10 @@
import {renderArguments} from './slice_args';
import {renderDetails} from './slice_details';
import {getSlice, SliceDetails, SliceRef} from './sql/slice';
+import {
+ BreakdownByThreadState,
+ breakDownIntervalByThreadState,
+} from './sql/thread_state';
import {asSliceSqlId} from './sql_types';
interface ContextMenuItem {
@@ -226,6 +231,7 @@
static readonly kind = 'dev.perfetto.ChromeSliceDetailsTab';
private sliceDetails?: SliceDetails;
+ private breakdownByThreadState?: BreakdownByThreadState;
static create(args: NewBottomTabArgs): ChromeSliceDetailsTab {
return new ChromeSliceDetailsTab(args);
@@ -233,11 +239,23 @@
constructor(args: NewBottomTabArgs) {
super(args);
+ this.load();
+ }
+ async load() {
// Start loading the slice details
const {id, table} = this.config;
- getSliceDetails(this.engine, id, table)
- .then((sliceDetails) => this.sliceDetails = sliceDetails);
+ const details = await getSliceDetails(this.engine, id, table);
+
+ if (details !== undefined && details.thread !== undefined) {
+ this.breakdownByThreadState = await breakDownIntervalByThreadState(
+ this.engine,
+ TimeSpan.fromTimeAndDuration(details.ts, details.dur),
+ details.thread.utid);
+ }
+
+ this.sliceDetails = details;
+ raf.scheduleFullRedraw();
}
getTitle(): string {
@@ -245,24 +263,23 @@
}
viewTab() {
- if (exists(this.sliceDetails)) {
- const slice = this.sliceDetails;
- return m(
- DetailsShell,
- {
- title: 'Slice',
- description: slice.name,
- buttons: this.renderContextButton(slice),
- },
- m(
- GridLayout,
- renderDetails(slice),
- this.renderRhs(this.engine, slice),
- ),
- );
- } else {
+ if (!exists(this.sliceDetails)) {
return m(DetailsShell, {title: 'Slice', description: 'Loading...'});
}
+ const slice = this.sliceDetails;
+ return m(
+ DetailsShell,
+ {
+ title: 'Slice',
+ description: slice.name,
+ buttons: this.renderContextButton(slice),
+ },
+ m(
+ GridLayout,
+ renderDetails(slice, this.breakdownByThreadState),
+ this.renderRhs(this.engine, slice),
+ ),
+ );
}
isLoading() {
diff --git a/ui/src/frontend/slice_details.ts b/ui/src/frontend/slice_details.ts
index 1f782a7..6056bbc 100644
--- a/ui/src/frontend/slice_details.ts
+++ b/ui/src/frontend/slice_details.ts
@@ -28,6 +28,10 @@
import {addTab} from './bottom_tab';
import {globals} from './globals';
import {SliceDetails} from './sql/slice';
+import {
+ BreakdownByThreadState,
+ BreakdownByThreadStateTreeNode,
+} from './sql/thread_state';
import {SqlTableTab} from './sql_table/tab';
import {SqlTables} from './sql_table/well_known_tables';
import {getProcessName, getThreadName} from './thread_and_process_info';
@@ -44,7 +48,8 @@
// Renders a widget storing all of the generic details for a slice from the
// slice table.
-export function renderDetails(slice: SliceDetails) {
+export function renderDetails(
+ slice: SliceDetails, durationBreakdown?: BreakdownByThreadState) {
return m(
Section,
{title: 'Details'},
@@ -84,10 +89,18 @@
}),
exists(slice.absTime) &&
m(TreeNode, {left: 'Absolute Time', right: slice.absTime}),
- m(TreeNode, {
- left: 'Duration',
- right: computeDuration(slice.ts, slice.dur),
- }),
+ m(
+ TreeNode,
+ {
+ left: 'Duration',
+ right: computeDuration(slice.ts, slice.dur),
+ },
+ exists(durationBreakdown) && slice.dur > 0 &&
+ m(BreakdownByThreadStateTreeNode, {
+ data: durationBreakdown,
+ dur: slice.dur,
+ }),
+ ),
renderThreadDuration(slice),
slice.thread && m(TreeNode, {
left: 'Thread',
diff --git a/ui/src/frontend/sql/thread_state.ts b/ui/src/frontend/sql/thread_state.ts
new file mode 100644
index 0000000..2de948b
--- /dev/null
+++ b/ui/src/frontend/sql/thread_state.ts
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import m from 'mithril';
+
+import {Duration, duration, TimeSpan} from '../../base/time';
+import {LONG, NUM_NULL, STR, STR_NULL} from '../../common/query_result';
+import {EngineProxy} from '../../public';
+import {TreeNode} from '../../widgets/tree';
+import {Utid} from '../sql_types';
+
+// An individual node of the thread state breakdown tree.
+class Node {
+ parent?: Node;
+ children: Map<string, Node>;
+ dur: duration;
+ startsCollapsed: boolean = true;
+
+ constructor(parent?: Node) {
+ this.parent = parent;
+ this.children = new Map();
+ this.dur = 0n;
+ }
+
+ getOrCreateChild(name: string) {
+ let child = this.children.get(name);
+ if (!child) {
+ child = new Node(this);
+ this.children.set(name, child);
+ }
+ return child;
+ }
+
+ addDuration(dur: duration) {
+ let node: Node|undefined = this;
+ while (node !== undefined) {
+ node.dur += dur;
+ node = node.parent;
+ }
+ }
+}
+
+// Thread state breakdown data (tree).
+// Can be passed to ThreadStateBreakdownTreeNode to be rendered as a part of a
+// tree.
+export interface BreakdownByThreadState {
+ root: Node;
+}
+
+// Compute a breakdown of thread states for a given thread for a given time
+// interval.
+export async function breakDownIntervalByThreadState(
+ engine: EngineProxy, range: TimeSpan, utid: Utid):
+ Promise<BreakdownByThreadState> {
+ // TODO(altimin): this probably should share some code with pivot tables when
+ // we actually get some pivot tables we like.
+ const query = await engine.query(`
+ INCLUDE PERFETTO MODULE common.thread_states;
+
+ SELECT
+ state,
+ raw_state as rawState,
+ cpu_type as cpuType,
+ cpu,
+ blocked_function as blockedFunction,
+ dur
+ FROM thread_state_summary_for_interval(${range.start}, ${range.duration}, ${
+ utid});
+ `);
+ const it = query.iter({
+ state: STR,
+ rawState: STR,
+ cpuType: STR_NULL,
+ cpu: NUM_NULL,
+ blockedFunction: STR_NULL,
+ dur: LONG,
+ });
+ const root = new Node();
+ for (; it.valid(); it.next()) {
+ let currentNode = root;
+ currentNode = currentNode.getOrCreateChild(it.state);
+ // If the CPU time is not null, add it to the breakdown.
+ if (it.cpuType !== null) {
+ currentNode = currentNode.getOrCreateChild(it.cpuType);
+ }
+ if (it.cpu !== null) {
+ currentNode = currentNode.getOrCreateChild(`CPU ${it.cpu}`);
+ }
+ if (it.blockedFunction !== null) {
+ currentNode = currentNode.getOrCreateChild(`${it.blockedFunction}`);
+ }
+ currentNode.addDuration(it.dur);
+ }
+ return {
+ root,
+ };
+}
+
+function renderChildren(node: Node, totalDur: duration): m.Child[] {
+ const res = Array.from(node.children.entries())
+ .map(([name, child]) => renderNode(child, name, totalDur));
+ return res;
+}
+
+function renderNode(node: Node, name: string, totalDur: duration): m.Child {
+ const durPercent = 100. * Number(node.dur) / Number(totalDur);
+ return m(
+ TreeNode,
+ {
+ left: name,
+ right: `${Duration.humanise(node.dur)} (${durPercent.toFixed(2)}%)`,
+ startsCollapsed: node.startsCollapsed,
+ },
+ renderChildren(node, totalDur));
+}
+
+interface BreakdownByThreadStateTreeNodeAttrs {
+ dur: duration;
+ data: BreakdownByThreadState;
+}
+
+// A tree node that displays a nested breakdown a time interval by thread state.
+export class BreakdownByThreadStateTreeNode implements
+ m.ClassComponent<BreakdownByThreadStateTreeNodeAttrs> {
+ view({attrs}: m.Vnode<BreakdownByThreadStateTreeNodeAttrs>): m.Child[] {
+ return renderChildren(attrs.data.root, attrs.dur);
+ }
+}
diff --git a/ui/src/tracks/debug/counter_track.ts b/ui/src/tracks/debug/counter_track.ts
index adcc92d..56e3d42 100644
--- a/ui/src/tracks/debug/counter_track.ts
+++ b/ui/src/tracks/debug/counter_track.ts
@@ -13,6 +13,7 @@
// limitations under the License.
import m from 'mithril';
+import {v4 as uuidv4} from 'uuid';
import {Actions, DEBUG_COUNTER_TRACK_KIND} from '../../common/actions';
import {EngineProxy} from '../../common/engine';
@@ -103,14 +104,19 @@
from data
order by ts;`);
- globals.dispatch(Actions.addTrack({
- uri: DEBUG_COUNTER_TRACK_URI,
- name: trackName.trim() || `Debug Track ${debugTrackId}`,
- trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
- trackGroup: SCROLLING_TRACK_GROUP,
- initialState: {
- sqlTableName,
- columns,
- },
- }));
+ const trackInstanceId = uuidv4();
+ globals.dispatchMultiple([
+ Actions.addTrack({
+ id: trackInstanceId,
+ uri: DEBUG_COUNTER_TRACK_URI,
+ name: trackName.trim() || `Debug Track ${debugTrackId}`,
+ trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
+ trackGroup: SCROLLING_TRACK_GROUP,
+ initialState: {
+ sqlTableName,
+ columns,
+ },
+ }),
+ Actions.toggleTrackPinned({trackId: trackInstanceId}),
+ ]);
}
diff --git a/ui/src/tracks/debug/slice_track.ts b/ui/src/tracks/debug/slice_track.ts
index e05fdaf..6a1accb 100644
--- a/ui/src/tracks/debug/slice_track.ts
+++ b/ui/src/tracks/debug/slice_track.ts
@@ -13,6 +13,7 @@
// limitations under the License.
import m from 'mithril';
+import {v4 as uuidv4} from 'uuid';
import {Disposable} from '../../base/disposable';
import {Actions, DEBUG_SLICE_TRACK_KIND} from '../../common/actions';
@@ -145,14 +146,19 @@
from prepared_data
order by ts;`);
- globals.dispatch(Actions.addTrack({
- uri: DEBUG_SLICE_TRACK_URI,
- name: trackName.trim() || `Debug Track ${debugTrackId}`,
- trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
- trackGroup: SCROLLING_TRACK_GROUP,
- initialState: {
- sqlTableName,
- columns: sliceColumns,
- },
- }));
+ const trackInstanceId = uuidv4();
+ globals.dispatchMultiple([
+ Actions.addTrack({
+ id: trackInstanceId,
+ uri: DEBUG_SLICE_TRACK_URI,
+ name: trackName.trim() || `Debug Track ${debugTrackId}`,
+ trackSortKey: PrimaryTrackSortKey.DEBUG_TRACK,
+ trackGroup: SCROLLING_TRACK_GROUP,
+ initialState: {
+ sqlTableName,
+ columns: sliceColumns,
+ },
+ }),
+ Actions.toggleTrackPinned({trackId: trackInstanceId}),
+ ]);
}
diff --git a/ui/src/widgets/tree.ts b/ui/src/widgets/tree.ts
index e046a35..034bf7c 100644
--- a/ui/src/widgets/tree.ts
+++ b/ui/src/widgets/tree.ts
@@ -59,6 +59,8 @@
// Whether this node is collapsed or not.
// If omitted, collapsed state 'uncontrolled' - i.e. controlled internally.
collapsed?: boolean;
+ // Whether the node should start collapsed or not, default: false.
+ startsCollapsed?: boolean;
loading?: boolean;
showCaret?: boolean;
// Optional icon to show to the left of the text.
@@ -69,7 +71,12 @@
}
export class TreeNode implements m.ClassComponent<TreeNodeAttrs> {
- private collapsed = false;
+ private collapsed;
+
+ constructor({attrs}: m.CVnode<TreeNodeAttrs>) {
+ this.collapsed = attrs.startsCollapsed ?? false;
+ }
+
view(vnode: m.CVnode<TreeNodeAttrs>): m.Children {
const {children, attrs, attrs: {left, onCollapseChanged = () => {}}} =
vnode;