Merge "traced: preserve UUID LSB when cloning a session"
diff --git a/Android.bp b/Android.bp
index 0da0445..2586c75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4405,7 +4405,6 @@
"protos/perfetto/metrics/android/unsymbolized_frames.proto",
"protos/perfetto/metrics/chrome/all_chrome_metrics.proto",
"protos/perfetto/metrics/chrome/args_class_names.proto",
- "protos/perfetto/metrics/chrome/blink_gc_metric.proto",
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
@@ -10177,7 +10176,6 @@
"src/trace_processor/metrics/sql/chrome/touch_jank.sql",
"src/trace_processor/metrics/sql/chrome/vsync_intervals.sql",
"src/trace_processor/metrics/sql/common/parent_slice.sql",
- "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
"src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
"src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql",
"src/trace_processor/metrics/sql/experimental/frame_times.sql",
@@ -10458,6 +10456,7 @@
"src/trace_processor/stdlib/experimental/android_broadcast.sql",
"src/trace_processor/stdlib/experimental/proto_path.sql",
"src/trace_processor/stdlib/experimental/slices.sql",
+ "src/trace_processor/stdlib/experimental/thread_executing_span.sql",
"src/trace_processor/stdlib/pkvm/hypervisor.sql",
],
cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=stdlib --cpp-out=$(out) $(in)",
diff --git a/BUILD b/BUILD
index 4d4c656..9a05edc 100644
--- a/BUILD
+++ b/BUILD
@@ -354,16 +354,28 @@
":libperfetto_client_experimental",
":protos_perfetto_common_cpp",
":protos_perfetto_common_zero",
+ ":protos_perfetto_config_android_cpp",
":protos_perfetto_config_android_zero",
+ ":protos_perfetto_config_cpp",
+ ":protos_perfetto_config_ftrace_cpp",
":protos_perfetto_config_ftrace_zero",
+ ":protos_perfetto_config_gpu_cpp",
":protos_perfetto_config_gpu_zero",
+ ":protos_perfetto_config_inode_file_cpp",
":protos_perfetto_config_inode_file_zero",
+ ":protos_perfetto_config_interceptors_cpp",
":protos_perfetto_config_interceptors_zero",
+ ":protos_perfetto_config_power_cpp",
":protos_perfetto_config_power_zero",
+ ":protos_perfetto_config_process_stats_cpp",
":protos_perfetto_config_process_stats_zero",
+ ":protos_perfetto_config_profiling_cpp",
":protos_perfetto_config_profiling_zero",
+ ":protos_perfetto_config_statsd_cpp",
":protos_perfetto_config_statsd_zero",
+ ":protos_perfetto_config_sys_stats_cpp",
":protos_perfetto_config_sys_stats_zero",
+ ":protos_perfetto_config_system_info_cpp",
":protos_perfetto_config_system_info_zero",
":protos_perfetto_config_track_event_cpp",
":protos_perfetto_config_track_event_zero",
@@ -1919,7 +1931,6 @@
perfetto_filegroup(
name = "src_trace_processor_metrics_sql_experimental_experimental",
srcs = [
- "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
"src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
"src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql",
"src/trace_processor/metrics/sql/experimental/frame_times.sql",
@@ -2254,6 +2265,7 @@
"src/trace_processor/stdlib/experimental/android_broadcast.sql",
"src/trace_processor/stdlib/experimental/proto_path.sql",
"src/trace_processor/stdlib/experimental/slices.sql",
+ "src/trace_processor/stdlib/experimental/thread_executing_span.sql",
],
)
@@ -3982,7 +3994,6 @@
srcs = [
"protos/perfetto/metrics/chrome/all_chrome_metrics.proto",
"protos/perfetto/metrics/chrome/args_class_names.proto",
- "protos/perfetto/metrics/chrome/blink_gc_metric.proto",
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
diff --git a/docs/contributing/sdk-releasing.md b/docs/contributing/sdk-releasing.md
index b5b6984..07ebbd7 100644
--- a/docs/contributing/sdk-releasing.md
+++ b/docs/contributing/sdk-releasing.md
@@ -107,7 +107,7 @@
3. Upload the new release for review.
```bash
-git cl upload --no-squash
+git cl upload --no-squash --bypass-hooks -o banned-words~skip
```
If you get an error about a missing Change-Id field (`remote: ERROR: commit
diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md
index 809833b..44d9b2d 100644
--- a/docs/instrumentation/tracing-sdk.md
+++ b/docs/instrumentation/tracing-sdk.md
@@ -30,7 +30,7 @@
To start using the Client API, first check out the latest SDK release:
```bash
-git clone https://android.googlesource.com/platform/external/perfetto -b v34.0
+git clone https://android.googlesource.com/platform/external/perfetto -b v35.0
```
The SDK consists of two files, `sdk/perfetto.h` and `sdk/perfetto.cc`. These are
diff --git a/examples/sdk/README.md b/examples/sdk/README.md
index 476ebbe..3519006 100644
--- a/examples/sdk/README.md
+++ b/examples/sdk/README.md
@@ -15,7 +15,7 @@
First, check out the latest Perfetto release:
```bash
-git clone https://android.googlesource.com/platform/external/perfetto -b v34.0
+git clone https://android.googlesource.com/platform/external/perfetto -b v35.0
```
Then, build using CMake:
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index 0af6551..1b23035 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -286,6 +286,14 @@
}
}
+ if (is_wasm) {
+ # As of writing (2023-06-12) WASM 128bit SIMD is supported on
+ # stable Chrome, Safari, and Firefox. See:
+ # - https://webassembly.org/roadmap/
+ # - https://emscripten.org/docs/porting/simd.html
+ cflags += [ "-msimd128" ]
+ }
+
if (is_linux) {
# Enable LFS (large file support) for stat() and other syscalls.
cflags += [
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index d417668..159e155 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -16,6 +16,7 @@
public_deps = [
"../../../protos/perfetto/common:cpp",
"../../../protos/perfetto/common:zero",
+ "../../../protos/perfetto/config:cpp",
"../../../protos/perfetto/config/track_event:cpp",
"../../../protos/perfetto/trace:zero",
"../../../protos/perfetto/trace/interned_data:zero",
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 56229e7..32993ac 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -368,24 +368,119 @@
}
// The following methods forward all arguments to TraceForCategoryBody
- // while casting string constants to const char*.
- template <typename... Arguments>
- static void TraceForCategory(Arguments&&... args) PERFETTO_ALWAYS_INLINE {
- TraceForCategoryBody(DecayStrType(args)...);
+ // while casting string constants to const char* and integer arguments to
+ // int64_t, uint64_t or bool.
+ template <typename CategoryType,
+ typename EventNameType,
+ typename... Arguments>
+ static void TraceForCategory(uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryBody(instances, DecayStrType(category), DecayStrType(name),
+ type, DecayArgType(args)...);
}
- template <typename... Arguments>
- static void TraceForCategoryLegacy(Arguments&&... args)
- PERFETTO_ALWAYS_INLINE {
- TraceForCategoryLegacyBody(DecayStrType(args)...);
+#if PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacy(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyBody(instances, DecayStrType(category),
+ DecayStrType(event_name), type, track, phase,
+ flags, DecayArgType(args)...);
}
- template <typename... Arguments>
- static void TraceForCategoryLegacyWithId(Arguments&&... args)
- PERFETTO_ALWAYS_INLINE {
- TraceForCategoryLegacyWithIdBody(DecayStrType(args)...);
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacy(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyBody(instances, DecayStrType(category),
+ DecayStrType(event_name), type, track, phase,
+ flags, timestamp, DecayArgType(args)...);
}
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type>
+ static void TraceForCategoryLegacyWithId(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyWithIdBody(
+ instances, DecayStrType(category), DecayStrType(event_name), type,
+ track, phase, flags, thread_id, legacy_id, DecayArgType(args)...);
+ }
+
+ template <typename TrackType,
+ typename CategoryType,
+ typename EventNameType,
+ typename ThreadIdType,
+ typename LegacyIdType,
+ typename TimestampType = uint64_t,
+ typename... Arguments,
+ typename TrackTypeCheck = typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type,
+ typename TimestampTypeCheck = typename std::enable_if<
+ IsValidTimestamp<TimestampType>()>::type>
+ static void TraceForCategoryLegacyWithId(
+ uint32_t instances,
+ const CategoryType& category,
+ const EventNameType& event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType&& track,
+ char phase,
+ uint32_t flags,
+ ThreadIdType thread_id,
+ LegacyIdType legacy_id,
+ TimestampType&& timestamp,
+ Arguments&&... args) PERFETTO_ALWAYS_INLINE {
+ TraceForCategoryLegacyWithIdBody(instances, DecayStrType(category),
+ DecayStrType(event_name), type, track,
+ phase, flags, thread_id, legacy_id,
+ timestamp, DecayArgType(args)...);
+ }
+#endif
+
// Initialize the track event library. Should be called before tracing is
// enabled.
static bool Register() {
@@ -459,6 +554,30 @@
static const char* DecayStrType(const char* t) { return t; }
+ // The DecayArgType method is used to avoid unnecessary instantiations of
+ // templates on:
+ // * string constants of different sizes.
+ // * integers of different sizes or constness.
+ // * floats of different sizes.
+ // This allows to avoid extra instantiations of TraceForCategory templates.
+ template <typename T>
+ static T&& DecayArgType(T&& t) {
+ return std::forward<T>(t);
+ }
+
+ static const char* DecayArgType(const char* s) { return s; }
+ static uint64_t DecayArgType(uint64_t u) { return u; }
+ static uint64_t DecayArgType(uint32_t u) { return u; }
+ static uint64_t DecayArgType(uint16_t u) { return u; }
+ static uint64_t DecayArgType(uint8_t u) { return u; }
+ static int64_t DecayArgType(int64_t i) { return i; }
+ static int64_t DecayArgType(int32_t i) { return i; }
+ static int64_t DecayArgType(int16_t i) { return i; }
+ static int64_t DecayArgType(int8_t i) { return i; }
+ static bool DecayArgType(bool b) { return b; }
+ static double DecayArgType(float f) { return static_cast<double>(f); }
+ static double DecayArgType(double f) { return f; }
+
// Once we've determined tracing to be enabled for this category, actually
// write a trace event onto this thread's default track. Outlined to avoid
// bloating code (mostly stack depth) at the actual trace point.
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index dadd191..1a7ae98 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -23,7 +23,6 @@
sources = [
"all_chrome_metrics.proto",
"args_class_names.proto",
- "blink_gc_metric.proto",
"dropped_frames.proto",
"frame_times.proto",
"histogram_hashes.proto",
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index 9618fdd..a82d617 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -20,7 +20,6 @@
import "protos/perfetto/metrics/metrics.proto";
import "protos/perfetto/metrics/chrome/args_class_names.proto";
-import "protos/perfetto/metrics/chrome/blink_gc_metric.proto";
import "protos/perfetto/metrics/chrome/dropped_frames.proto";
import "protos/perfetto/metrics/chrome/frame_times.proto";
import "protos/perfetto/metrics/chrome/histogram_hashes.proto";
@@ -42,7 +41,7 @@
optional FrameTimes frame_times = 1002;
optional ReportedByPage reported_by_page = 1003;
optional ScrollJank scroll_jank = 1004;
- optional BlinkGcMetric blink_gc_metric = 1005;
+ // reserved 1005, was BlinkGcMetric, removed in aosp/2617418
optional MediaMetric media_metric = 1006;
optional TouchJank touch_jank = 1007;
optional ChromeDroppedFrames chrome_dropped_frames = 1008;
diff --git a/protos/perfetto/metrics/chrome/blink_gc_metric.proto b/protos/perfetto/metrics/chrome/blink_gc_metric.proto
deleted file mode 100644
index 5a367a7..0000000
--- a/protos/perfetto/metrics/chrome/blink_gc_metric.proto
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http_//www.apache.org/licenses/LICENSE_2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package perfetto.protos;
-
-import "protos/perfetto/metrics/custom_options.proto";
-
-message BlinkGcMetric {
- repeated double blink_gc_atomic_pause_mark_epilogue = 1
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_atomic_mark_epilogue = 2
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_atomic_pause_mark_prologue = 3
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_atomic_mark_prologue = 4
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_atomic_pause_mark_roots = 5
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_atomic_mark_roots = 6
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_atomic_pause_sweep_and_compact = 7
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_atomic_sweep_compact = 8
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_complete_sweep = 9 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_sweep_complete = 10
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_incremental_start = 11
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_incremental_mark_start = 12
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_incremental_step = 13
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_incremental_mark_step = 14
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_sweep_allocation = 15
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_sweep_on_allocation = 16
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_sweep_task_foreground = 17
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_sweep_idle = 18
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_unified_marking_by_v8 = 19
- [(unit) = "ms_smallerIsBetter"];
- repeated double unified_gc_main_thread_cycle_full_mark_step = 20
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_atomic_pause = 21 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_atomic = 22
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_atomic_pause_mark_transitive_closure = 23
- [(unit) = "ms_smallerIsBetter"];
- repeated double
- blink_gc_main_thread_cycle_full_atomic_mark_transitive_closure = 24
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_total = 25 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full = 26
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_mark_roots = 27 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_mark_roots = 28
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_mark_transitive_closure = 29
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_mark_transitive_closure = 30
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_mark_foreground = 31 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_mark = 32
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_mark_foreground_forced = 33
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_mark_forced = 34
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_mark_background = 35 [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_concurrent_thread_cycle_full_mark = 36
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_sweep_foreground = 37
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_main_thread_cycle_full_sweep = 38
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_sweep_background = 39
- [(unit) = "ms_smallerIsBetter"];
- repeated double blink_gc_concurrent_thread_cycle_full_sweep = 40
- [(unit) = "ms_smallerIsBetter"];
- repeated double unified_gc_total = 41 [(unit) = "ms_smallerIsBetter"];
-}
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index add6109..d4b8ccc 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -209,9 +209,9 @@
: task_runner_(task_runner),
mode_(mode),
exit_when_done_(exit_when_done),
- unwinding_workers_(MakeUnwindingWorkers(this, kUnwinderThreads)),
socket_delegate_(this),
- weak_factory_(this) {
+ weak_factory_(this),
+ unwinding_workers_(MakeUnwindingWorkers(this, kUnwinderThreads)) {
CheckDataSourceCpuTask();
CheckDataSourceMemoryTask();
}
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index 83c6cac..fa1f058 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -318,7 +318,6 @@
std::map<FlushRequestID, size_t> flushes_in_progress_;
std::map<DataSourceInstanceID, DataSource> data_sources_;
- std::vector<UnwindingWorker> unwinding_workers_;
// Specific to mode_ == kChild
Process target_process_{base::kInvalidPid, ""};
@@ -326,7 +325,11 @@
SocketDelegate socket_delegate_;
- base::WeakPtrFactory<HeapprofdProducer> weak_factory_; // Keep last.
+ base::WeakPtrFactory<HeapprofdProducer> weak_factory_;
+
+ // UnwindingWorker's destructor might attempt to post producer tasks, so this
+ // needs to outlive weak_factory_.
+ std::vector<UnwindingWorker> unwinding_workers_;
};
} // namespace profiling
diff --git a/src/trace_processor/metrics/sql/experimental/BUILD.gn b/src/trace_processor/metrics/sql/experimental/BUILD.gn
index 1431bd3..bbf3918 100644
--- a/src/trace_processor/metrics/sql/experimental/BUILD.gn
+++ b/src/trace_processor/metrics/sql/experimental/BUILD.gn
@@ -19,7 +19,6 @@
perfetto_sql_source_set("experimental") {
sources = [
- "blink_gc_metric.sql",
"chrome_dropped_frames.sql",
"chrome_long_latency.sql",
"frame_times.sql",
diff --git a/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql b/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
deleted file mode 100644
index e8307b6..0000000
--- a/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
+++ /dev/null
@@ -1,581 +0,0 @@
--- Maps non-aggregated Blink GC events in timeline to telemetry friendly
--- names.
---
--- This includes the old style or the new naming scheme one which only occur on
--- the main thread.
-DROP VIEW IF EXISTS blink_non_aggregated_gc_event_name;
-CREATE VIEW blink_non_aggregated_gc_event_name AS
-SELECT
- 'BlinkGC.AtomicPauseMarkEpilogue' AS name,
- 'blink-gc-atomic-pause-mark-epilogue' AS old_event_name,
- 'blink:gc:main_thread:cycle:full:atomic:mark:epilogue' AS new_event_name
-UNION ALL
-SELECT
- 'BlinkGC.AtomicPauseMarkPrologue',
- 'blink-gc-atomic-pause-mark-prologue',
- 'blink:gc:main_thread:cycle:full:atomic:mark:prologue'
-UNION ALL
-SELECT
- 'BlinkGC.AtomicPauseMarkRoots',
- 'blink-gc-atomic-pause-mark-roots',
- 'blink:gc:main_thread:cycle:full:atomic:mark:roots'
-UNION ALL
-SELECT
- 'BlinkGC.IncrementalMarkingStartMarking',
- 'blink-gc-incremental-start',
- 'blink:gc:main_thread:cycle:full:incremental:mark:start'
-UNION ALL
-SELECT
- 'BlinkGC.IncrementalMarkingStep',
- 'blink-gc-incremental-step',
- 'blink:gc:main_thread:cycle:full:incremental:mark:step'
-UNION ALL
-SELECT
- 'BlinkGC.UnifiedMarkingStep',
- 'blink-gc-unified-marking-by-v8',
- 'unified:gc:main_thread:cycle:full:mark:step'
-UNION ALL
-SELECT
- 'BlinkGC.CompleteSweep',
- 'blink-gc-complete-sweep',
- 'blink:gc:main_thread:cycle:full:sweep:complete'
-UNION ALL
-SELECT
- 'BlinkGC.LazySweepInIdle',
- 'blink-gc-sweep-task-foreground',
- 'blink:gc:main_thread:cycle:full:sweep:idle'
-UNION ALL
-SELECT
- 'BlinkGC.LazySweepOnAllocation',
- 'blink-gc-sweep-allocation',
- 'blink:gc:main_thread:cycle:full:sweep:on_allocation'
-UNION ALL
-SELECT
- 'BlinkGC.AtomicPauseSweepAndCompact' AS name,
- 'blink-gc-atomic-pause-sweep-and-compact' AS old_event_name,
- 'blink:gc:main_thread:cycle:full:atomic:sweep:compact' AS new_event_name;
-
--- Get all the slices we care about. These are ones that start with V8.GC or
--- BlinkGC. If you need more you need to modify the where clause for
--- blink_gc_cpu_slice.
-DROP TABLE IF EXISTS blink_gc_cpu_slice;
-CREATE TABLE blink_gc_cpu_slice AS
-SELECT
- CASE WHEN dur != 0 THEN cpuDurNs / 1e6 ELSE 0.0 END AS cpuDurMs,
- *
-FROM (
- SELECT
- COALESCE(EXTRACT_ARG(arg_set_id, 'debug.forced'), FALSE)
- -- This subquery replaces
- -- metrics.v8.utils.isForcedGarbageCollectionEvent(event)
- OR (
- SELECT
- id
- FROM ANCESTOR_SLICE(slice.id) AS ancestor
- WHERE ancestor.name = 'V8.GCLowMemoryNotification' LIMIT 1
- ) IS NOT NULL AS forced,
- -- upid replaces pid, because its more fool proof ensuring uniqueness.
- thread.upid || ':' || EXTRACT_ARG(arg_set_id, 'debug.epoch') AS epoch,
- slice.thread_dur AS cpuDurNs,
- slice.*
- FROM slice
- JOIN thread_track ON slice.track_id = thread_track.id
- JOIN thread ON thread_track.utid = thread.id
- WHERE
- slice.dur >= 0 AND (
- slice.name GLOB "V8.GC*" OR (slice.name GLOB "BlinkGC*" AND NOT forced)
- )
-);
-
--- This grabs all the single events for "BlinkGC.*", and restricts to only
--- forced events.
-DROP TABLE IF EXISTS blink_slice;
-CREATE TABLE blink_slice AS
-SELECT
- event_name.old_event_name AS blink_non_aggregated_gc_event_name,
- event_name.new_event_name AS blink_non_aggregated_gc_events_new_name,
- blink_gc_cpu_slice.*
-FROM
- blink_gc_cpu_slice LEFT JOIN
- blink_non_aggregated_gc_event_name AS event_name ON
- event_name.name = blink_gc_cpu_slice.name
-WHERE
- blink_gc_cpu_slice.name GLOB "BlinkGC*" AND NOT forced;
-
--- This groups all the events by name and epoch for from "blink_slice" for easy
--- access.
-DROP TABLE IF EXISTS blink_per_epoch_slice;
-CREATE TABLE blink_per_epoch_slice AS
-SELECT
- name,
- epoch,
- blink_non_aggregated_gc_event_name,
- blink_non_aggregated_gc_events_new_name,
- SUM(cpuDurMs) AS cpuDurPerEpochMs
-FROM blink_slice
-GROUP BY 1, 2, 3, 4;
-
--- All events that should be summed up to 'blink-gc-mark-roots'.
-DROP VIEW IF EXISTS blink_top_gc_roots_marking_event;
-CREATE VIEW blink_top_gc_roots_marking_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.VisitRoots'
-);
-
--- All events that should be summed up to
--- 'blink-gc-atomic-pause-mark-transitive-closure'.
-DROP VIEW IF EXISTS blink_gc_atomic_pause_transitive_closure_event;
-CREATE VIEW blink_gc_atomic_pause_transitive_closure_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.AtomicPauseMarkTransitiveClosure'
-);
-
--- All events that should be summed up to 'blink-gc-mark-transitive-closure'.
-DROP VIEW IF EXISTS blink_gc_foreground_marking_transitive_closure_event;
-CREATE VIEW
-blink_gc_foreground_marking_transitive_closure_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.AtomicPauseMarkTransitiveClosure',
- 'BlinkGC.IncrementalMarkingStep',
- 'BlinkGC.UnifiedMarkingStep'
-);
-
--- Names of Blink GC foreground marking events in timeline.
-DROP VIEW IF EXISTS blink_top_gc_foreground_marking_event;
-CREATE VIEW blink_top_gc_foreground_marking_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.AtomicPauseMarkEpilogue',
- 'BlinkGC.AtomicPauseMarkPrologue',
- 'BlinkGC.AtomicPauseMarkRoots',
- 'BlinkGC.IncrementalMarkingStartMarking'
-)
-UNION ALL
-SELECT * FROM blink_gc_foreground_marking_transitive_closure_event;
-
--- Names of Blink GC foreground marking events in timeline.
-DROP VIEW IF EXISTS blink_gc_forced_foreground_marking_event;
-CREATE VIEW blink_gc_forced_foreground_marking_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.AtomicPauseMarkEpilogue',
- 'BlinkGC.AtomicPauseMarkPrologue',
- 'BlinkGC.AtomicPauseMarkRoots',
- 'BlinkGC.IncrementalMarkingStartMarking',
- 'BlinkGC.MarkBailOutObjects',
- 'BlinkGC.MarkFlushV8References',
- 'BlinkGC.MarkFlushEphemeronPairs'
-);
-
--- Names of Blink GC background marking events in timeline.
-DROP VIEW IF EXISTS blink_top_gc_background_marking_event;
-CREATE VIEW blink_top_gc_background_marking_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.ConcurrentMarkingStep'
-);
-
--- Names of Blink GC foreground sweeping events in timeline.
-DROP VIEW IF EXISTS blink_top_gc_foreground_sweeping_event;
-CREATE VIEW blink_top_gc_foreground_sweeping_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.CompleteSweep',
- 'BlinkGC.LazySweepInIdle',
- 'BlinkGC.LazySweepOnAllocation'
-);
-
--- Names of Blink GC background sweeping events in timeline.
-DROP VIEW IF EXISTS blink_top_gc_background_sweeping_event;
-CREATE VIEW blink_top_gc_background_sweeping_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.ConcurrentSweepingStep'
-);
-
--- Names of all Blink Unified GC events in timeline.
-DROP VIEW IF EXISTS blink_top_gc_event;
-CREATE VIEW blink_top_gc_event AS
-SELECT * FROM blink_slice WHERE name IN (
- SELECT name FROM blink_non_aggregated_gc_event_name
-) OR name IN (
- SELECT name FROM blink_gc_atomic_pause_transitive_closure_event
-);
-
--- All events that should be summed up to 'blink-gc-atomic-pause'. Note that
--- this events need to have an epoch counter in args.epoch.
-DROP VIEW IF EXISTS atomic_pause_event;
-CREATE VIEW atomic_pause_event AS
-SELECT * FROM blink_slice WHERE name IN (
- 'BlinkGC.AtomicPauseMarkEpilogue',
- 'BlinkGC.AtomicPauseMarkPrologue',
- 'BlinkGC.AtomicPauseMarkRoots',
- 'BlinkGC.AtomicPauseMarkTransitiveClosure',
- 'BlinkGC.AtomicPauseSweepAndCompact'
-);
-
--- This is a more complex variable so benefits from additional comments so we
--- pull it out of the proto filling.
-DROP VIEW IF EXISTS unified_gc_total;
-CREATE VIEW unified_gc_total AS
-SELECT
- *
-FROM blink_gc_cpu_slice
-WHERE (
- -- This subclause replaces
- -- metrics.v8.utils.isNotForcedTopGarbageCollectionEvent()
-
- -- These names are found in isTopGarbageCollectionEvent().
- name IN (
- 'V8.GCCompactor',
- 'V8.GCFinalizeMC',
- 'V8.GCFinalizeMCReduceMemory',
- 'V8.GCIncrementalMarking',
- 'V8.GCIncrementalMarkingFinalize',
- 'V8.GCIncrementalMarkingStart',
- 'V8.GCPhantomHandleProcessingCallback',
- 'V8.GCScavenger'
- ) AND (
- -- This replaces isForcedGarbageCollectionEvent.
- SELECT name FROM ANCESTOR_SLICE(blink_gc_cpu_slice.id) AS ancestor
- WHERE ancestor.name = 'V8.GCLowMemoryNotification'
- LIMIT 1
- ) IS NULL
-) OR (
- -- This subclause replaces isNonNestedNonForcedBlinkGarbageCollectionEvent().
- name IN (
- -- This subquery replaces isNonForcedBlinkGarbageCollectionEvent().
- SELECT name FROM blink_top_gc_event
- ) AND (
- -- This subquery replaces metrics.v8.utils.isGarbageCollectionEvent().
- SELECT name FROM ANCESTOR_SLICE(blink_gc_cpu_slice.id) AS ancestor
- WHERE
- ancestor.name GLOB "V8.GC*"
- AND ancestor.name != 'V8.GCLowMemoryNotification'
- LIMIT 1
- ) IS NULL
-);
-
--- This table name is just "file_name" + "_output" used by TBMv3 to know which
--- view to extract the proto BlinkGcMetric out of.
-DROP VIEW IF EXISTS blink_gc_metric_output;
-CREATE VIEW blink_gc_metric_output AS
-SELECT BlinkGcMetric(
- 'blink_gc_atomic_pause_mark_epilogue',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE
- blink_non_aggregated_gc_event_name = 'blink-gc-atomic-pause-mark-epilogue'
- ),
- 'blink_gc_main_thread_cycle_full_atomic_mark_epilogue',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:atomic:mark:epilogue'
- ),
- 'blink_gc_atomic_pause_mark_prologue',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE
- blink_non_aggregated_gc_event_name
- = 'blink-gc-atomic-pause-mark-prologue'
- ),
- 'blink_gc_main_thread_cycle_full_atomic_mark_prologue',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:atomic:mark:prologue'
- ),
- 'blink_gc_atomic_pause_mark_roots',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE
- blink_non_aggregated_gc_event_name = 'blink-gc-atomic-pause-mark-roots'
- ),
- 'blink_gc_main_thread_cycle_full_atomic_mark_roots',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:atomic:mark:roots'
- ),
- 'blink_gc_atomic_pause_sweep_and_compact',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE
- blink_non_aggregated_gc_event_name
- = 'blink-gc-atomic-pause-sweep-and-compact'
- ),
- 'blink_gc_main_thread_cycle_full_atomic_sweep_compact',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:atomic:sweep:compact'
- ),
- 'blink_gc_complete_sweep',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-complete-sweep'
- ),
- 'blink_gc_main_thread_cycle_full_sweep_complete',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:sweep:complete'
- ),
- 'blink_gc_incremental_start',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-incremental-start'
- ),
- 'blink_gc_main_thread_cycle_full_incremental_mark_start',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:incremental:mark:start'
- ),
- 'blink_gc_incremental_step',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-incremental-step'
- ),
- 'blink_gc_main_thread_cycle_full_incremental_mark_step',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:incremental:mark:step'
- ),
- 'blink_gc_sweep_allocation',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-sweep-allocation'
- ),
- 'blink_gc_main_thread_cycle_full_sweep_on_allocation',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:sweep:on_allocation'
- ),
- 'blink_gc_sweep_task_foreground',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-sweep-task-foreground'
- ),
- 'blink_gc_main_thread_cycle_full_sweep_idle',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'blink:gc:main_thread:cycle:full:sweep:idle'
- ),
- 'blink_gc_unified_marking_by_v8',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_slice
- WHERE blink_non_aggregated_gc_event_name = 'blink-gc-unified-marking-by-v8'
- ),
- 'unified_gc_main_thread_cycle_full_mark_step',
- (
- SELECT
- RepeatedField(cpuDurPerEpochMs)
- FROM blink_per_epoch_slice
- WHERE
- blink_non_aggregated_gc_events_new_name
- = 'unified:gc:main_thread:cycle:full:mark:step'
- ),
- 'blink_gc_atomic_pause',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM atomic_pause_event
- ),
- 'blink_gc_main_thread_cycle_full_atomic',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM atomic_pause_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_atomic_pause_mark_transitive_closure',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_gc_atomic_pause_transitive_closure_event
- ),
- 'blink_gc_main_thread_cycle_full_atomic_mark_transitive_closure',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_gc_atomic_pause_transitive_closure_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_total',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_event
- ),
- 'blink_gc_main_thread_cycle_full',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_mark_roots',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_roots_marking_event
- ),
- 'blink_gc_main_thread_cycle_full_mark_roots',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_roots_marking_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_mark_transitive_closure',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_gc_foreground_marking_transitive_closure_event
- ),
- 'blink_gc_main_thread_cycle_full_mark_transitive_closure',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_gc_foreground_marking_transitive_closure_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_mark_foreground',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_foreground_marking_event
- ),
- 'blink_gc_main_thread_cycle_full_mark',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_foreground_marking_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_mark_foreground_forced',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_gc_forced_foreground_marking_event
- ),
- 'blink_gc_main_thread_cycle_full_mark_forced',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_gc_forced_foreground_marking_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_mark_background',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_background_marking_event
- ),
- 'blink_gc_concurrent_thread_cycle_full_mark',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_background_marking_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_sweep_foreground',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_foreground_sweeping_event
- ),
- 'blink_gc_main_thread_cycle_full_sweep',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_foreground_sweeping_event
- GROUP BY epoch
- )
- ),
- 'blink_gc_sweep_background',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM blink_top_gc_background_sweeping_event
- ),
- 'blink_gc_concurrent_thread_cycle_full_sweep',
- (
- SELECT RepeatedField(val) FROM (
- SELECT
- SUM(cpuDurMs) AS val
- FROM blink_top_gc_background_sweeping_event
- GROUP BY epoch
- )
- ),
- 'unified_gc_total',
- (
- SELECT
- RepeatedField(cpuDurMs)
- FROM unified_gc_total
- )
-);
diff --git a/src/trace_processor/stdlib/chrome/chrome_scroll_janks.sql b/src/trace_processor/stdlib/chrome/chrome_scroll_janks.sql
index 752b717..9f0f29e 100644
--- a/src/trace_processor/stdlib/chrome/chrome_scroll_janks.sql
+++ b/src/trace_processor/stdlib/chrome/chrome_scroll_janks.sql
@@ -14,6 +14,7 @@
-- TODO(b/286187288): Move this dependency to stdlib.
SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+SELECT IMPORT('common.slices');
-- Selects EventLatency slices that correspond with janks in a scroll. This is
-- based on the V2 version of scroll jank metrics.
@@ -38,7 +39,11 @@
e.sub_cause_of_jank
FROM slice s
JOIN event_latency_scroll_jank_cause e
- ON s.id = e.slice_id;
+ ON s.id = e.slice_id
+WHERE
+ HAS_DESCENDANT_SLICE_WITH_NAME(
+ s.id,
+ 'SubmitCompositorFrameToPresentationCompositorFrame');
-- Defines slices for all of janky scrolling intervals in a trace.
--
diff --git a/src/trace_processor/stdlib/chrome/chrome_scrolls.sql b/src/trace_processor/stdlib/chrome/chrome_scrolls.sql
index 35b13b9..8576fec 100644
--- a/src/trace_processor/stdlib/chrome/chrome_scrolls.sql
+++ b/src/trace_processor/stdlib/chrome/chrome_scrolls.sql
@@ -77,6 +77,7 @@
CREATE VIEW chrome_scrolling_intervals AS
WITH all_scrolls AS (
SELECT
+ id AS scroll_id,
s.ts AS start_ts,
s.ts + s.dur AS end_ts
FROM chrome_scrolls s),
@@ -100,6 +101,7 @@
FROM range_starts)
SELECT
range_group AS id,
+ GROUP_CONCAT(scroll_id) AS scroll_ids,
MIN(start_ts) AS ts,
MAX(end_ts) - MIN(start_ts) AS dur
FROM range_groups
diff --git a/src/trace_processor/stdlib/experimental/BUILD.gn b/src/trace_processor/stdlib/experimental/BUILD.gn
index 86fbc26..5784707 100644
--- a/src/trace_processor/stdlib/experimental/BUILD.gn
+++ b/src/trace_processor/stdlib/experimental/BUILD.gn
@@ -19,5 +19,6 @@
"android_broadcast.sql",
"proto_path.sql",
"slices.sql",
+ "thread_executing_span.sql",
]
}
diff --git a/src/trace_processor/stdlib/experimental/thread_executing_span.sql b/src/trace_processor/stdlib/experimental/thread_executing_span.sql
new file mode 100644
index 0000000..338ec66
--- /dev/null
+++ b/src/trace_processor/stdlib/experimental/thread_executing_span.sql
@@ -0,0 +1,514 @@
+--
+-- 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.
+--
+
+-- A 'thread_executing_span' is thread_state span starting with a runnable slice
+-- until the next runnable slice that's woken up by a process (as opposed
+-- to an interrupt). Note that within a 'thread_executing_span' we can have sleep
+-- spans blocked on an interrupt.
+-- We consider the id of this span to be the id of the first thread_state in the span.
+
+--
+-- Finds all runnable states that are woken up by a process.
+--
+-- We achieve this by checking that the |thread_state.irq_context|
+-- value is NOT 1. In otherwords, it is either 0 or NULL. The NULL check
+-- is important to support older Android versions.
+--
+-- On older versions of Android (<U). We don't have IRQ context information,
+-- so this table might contain wakeups from interrupt context, consequently, the
+-- wakeup graph generated might not be accurate.
+--
+CREATE VIEW internal_runnable_state
+AS
+SELECT
+ thread_state.id,
+ thread_state.ts,
+ thread_state.dur,
+ thread_state.state,
+ thread_state.utid,
+ thread_state.waker_utid
+FROM thread_state
+WHERE thread_state.dur != -1 AND thread_state.waker_utid IS NOT NULL
+ AND thread_state.irq_context = 0 OR thread_state.irq_context IS NULL;
+
+-- Similar to |internal_runnable_state| but finds the runnable states at thread fork.
+CREATE VIEW internal_fork_runnable_state
+AS
+SELECT
+ thread_state.id,
+ thread_state.ts,
+ thread_state.dur,
+ thread_state.state,
+ thread_state.utid,
+ thread_state.waker_utid,
+ thread.start_ts = thread_state.ts AS is_fork
+FROM thread_state
+JOIN thread USING(utid)
+WHERE thread_state.dur != -1 AND thread_state.waker_utid IS NOT NULL
+ AND thread.start_ts = thread_state.ts;
+
+--
+-- Finds all sleep states including interruptible (S) and uninterruptible (D).
+CREATE VIEW internal_sleep_state
+AS
+SELECT
+ thread_state.id,
+ thread_state.ts,
+ thread_state.dur,
+ thread_state.state,
+ thread_state.blocked_function,
+ thread_state.utid
+FROM thread_state
+WHERE dur != -1 AND (state = 'S' OR state = 'D');
+
+--
+-- Finds the last execution for every thread to end executing_spans without a Sleep.
+--
+CREATE VIEW internal_thread_end_ts
+AS
+SELECT
+ MAX(ts) + dur AS end_ts,
+ utid
+FROM thread_state
+WHERE dur != -1
+GROUP BY utid;
+
+-- Similar to |internal_sleep_state| but finds the first sleep state after thread fork.
+CREATE VIEW internal_fork_sleep_state
+AS
+SELECT
+ MIN(thread_state.id) AS id,
+ thread_state.ts,
+ thread_state.dur,
+ thread_state.state,
+ thread_state.blocked_function,
+ thread_state.utid
+FROM thread_state
+WHERE dur != -1 AND (state = 'S' OR state = 'D')
+GROUP BY utid;
+
+--
+-- Finds all neighbouring ('Sleeping', 'Runnable') thread_states pairs from the same thread.
+-- More succintly, pairs of S[n-1]-R[n] where R is woken by a process context and S is an
+-- interruptible or uninterruptible sleep state.
+--
+-- This is achieved by joining the |internal_runnable_state|.ts with the
+-- |internal_sleep_state|.|ts + dur|.
+--
+-- With the S-R pairs of a thread, we can re-align to [R-S) intervals with LEADS and LAGS.
+--
+-- Given the following thread_states on a thread:
+-- S0__|R0__Running0___|S1__|R1__Running1___|S2__|R2__Running2__S2|.
+--
+-- We have 3 thread_executing_spans: [R0, S0), [R1, S1), [R2, S2).
+--
+-- We define the following markers in this table:
+--
+-- prev_start_id = R0_id.
+-- prev_start_ts = R0_ts.
+-- prev_start_dur = R0_dur.
+-- prev_start_state = 'R'.
+--
+-- prev_end_id = S0_id.
+-- prev_end_ts = S0_ts.
+-- prev_end_dur = S0_dur.
+-- prev_end_state = 'S' or 'D'.
+--
+-- start_id = R1_id.
+-- start_ts = R1_ts.
+-- start_dur = R1_dur.
+-- start_state = 'R'.
+--
+-- end_id = S1_id.
+-- end_ts = S1_ts.
+-- end_dur = S1_dur.
+-- end_state = 'S' or 'D'.
+CREATE TABLE internal_wakeup
+AS
+ SELECT
+ LAG(r.id, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS prev_start_id,
+ LAG(r.ts, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS prev_start_ts,
+ LAG(r.dur, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS prev_start_dur,
+ LAG(r.state, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS prev_start_state,
+ s.id AS prev_end_id,
+ s.ts AS prev_end_ts,
+ s.dur AS prev_end_dur,
+ s.state AS prev_end_state,
+ s.blocked_function AS prev_blocked_function,
+ r.id AS start_id,
+ r.ts AS start_ts,
+ r.dur AS start_dur,
+ r.state AS start_state,
+ r.utid AS utid,
+ r.waker_utid,
+ 0 AS is_fork,
+ LEAD(s.id, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS end_id,
+ IFNULL(LEAD(s.ts, 1) OVER (PARTITION BY r.utid ORDER BY r.ts), thread_end.end_ts) AS end_ts,
+ LEAD(s.dur, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS end_dur,
+ LEAD(s.state, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS end_state,
+ LEAD(s.blocked_function, 1) OVER (PARTITION BY r.utid ORDER BY r.ts) AS blocked_function
+FROM internal_runnable_state r
+JOIN internal_sleep_state s
+ ON s.utid = r.utid AND (s.ts + s.dur = r.ts)
+LEFT JOIN internal_thread_end_ts thread_end USING(utid)
+UNION ALL
+ SELECT
+ NULL AS prev_start_id,
+ NULL AS prev_start_ts,
+ NULL AS prev_start_dur,
+ NULL AS prev_start_state,
+ NULL AS prev_end_id,
+ NULL AS prev_end_ts,
+ NULL AS prev_end_dur,
+ NULL AS prev_end_state,
+ NULL AS prev_blocked_function,
+ r.id AS start_id,
+ r.ts AS start_ts,
+ r.dur AS start_dur,
+ r.state AS start_state,
+ r.utid AS utid,
+ r.waker_utid,
+ r.is_fork,
+ s.id AS end_id,
+ s.ts AS end_ts,
+ s.dur AS end_dur,
+ s.state AS end_state,
+ s.blocked_function AS blocked_function
+FROM internal_fork_runnable_state r
+JOIN internal_fork_sleep_state s
+ ON s.utid = r.utid;
+
+-- Improves performance of |internal_wakeup_chain| computation.
+CREATE
+ INDEX internal_wakeup_idx
+ON internal_wakeup(waker_utid, start_ts);
+
+--
+-- Builds the parent-child chain from all thread_executing_spans. The parent is the waker and
+-- child is the wakee.
+--
+-- Note that this doesn't include the roots. We'll compute the roots below.
+-- This two step process improves performance because it's more efficient to scan
+-- parent and find a child between than to scan child and find the parent it lies between.
+CREATE VIEW internal_wakeup_chain
+AS
+SELECT parent.start_id AS parent_id, child.*
+FROM internal_wakeup parent
+JOIN internal_wakeup child
+ ON (
+ parent.utid = child.waker_utid
+ AND child.start_ts BETWEEN parent.start_ts AND parent.end_ts);
+
+--
+-- Finds the roots of the |internal_wakeup_chain|.
+CREATE VIEW internal_wakeup_root
+AS
+WITH
+ internal_wakeup_root_id AS (
+ SELECT DISTINCT parent_id AS id FROM internal_wakeup_chain
+ EXCEPT
+ SELECT DISTINCT start_id AS id FROM internal_wakeup_chain
+ )
+SELECT NULL AS parent_id, internal_wakeup.*
+FROM internal_wakeup
+JOIN internal_wakeup_root_id
+ ON internal_wakeup_root_id.id = internal_wakeup.start_id;
+
+CREATE TABLE internal_wakeup_leaf AS
+WITH
+ internal_wakeup_leaf_id AS (
+ SELECT DISTINCT start_id AS id FROM internal_wakeup_chain
+ EXCEPT
+ SELECT DISTINCT parent_id AS id FROM internal_wakeup_chain
+ )
+SELECT internal_wakeup_chain.*
+FROM internal_wakeup_chain
+JOIN internal_wakeup_leaf_id
+ ON internal_wakeup_leaf_id.id = internal_wakeup_chain.start_id;
+
+--
+-- Merges the roots and the rest of the chain.
+CREATE TABLE internal_wakeup_graph
+AS
+SELECT internal_wakeup_chain.*, 0 AS is_root, (internal_wakeup_leaf.start_id IS NOT NULL) AS is_leaf
+FROM internal_wakeup_chain
+LEFT JOIN internal_wakeup_leaf
+ USING (start_id)
+UNION ALL
+SELECT *, 1 AS is_root, 0 AS is_leaf FROM internal_wakeup_root;
+
+-- thread_executing_span graph of all wakeups across all processes.
+--
+-- @column parent_id Id of thread_executing_span that directly woke |id|
+-- @column id Id of the first (runnable) thread state in thread_executing_span.
+-- @column ts Timestamp of first thread_state in thread_executing_span.
+-- @column dur Duration of thread_executing_span.
+-- @column tid Tid of thread with thread_state.
+-- @column pid Pid of process with thread_state.
+-- @column utid Utid of thread with thread_state.
+-- @column upid Upid of process with thread_state.
+-- @column thread_name Name of thread with thread_state.
+-- @column process_name Name of process with thread_state.
+-- @column waker_tid Tid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_pid Pid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_utid Utid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_upid Upid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_thread_name Name of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_process_name Name of process that woke the first thread_state in thread_executing_span.
+-- @column blocked_dur Duration of blocking thread state before waking up.
+-- @column blocked_state Thread state ('D' or 'S') of blocked thread_state before waking up.
+-- @column blocked_function Kernel blocking function of thread state before waking up.
+CREATE TABLE experimental_thread_executing_span_graph
+AS
+SELECT
+ graph.parent_id,
+ graph.start_id AS id,
+ graph.start_ts AS ts,
+ graph.end_ts - graph.start_ts AS dur,
+ thread.tid,
+ process.pid,
+ graph.utid,
+ process.upid,
+ thread.name AS thread_name,
+ process.name AS process_name,
+ waker_thread.tid AS waker_tid,
+ waker_process.pid AS waker_pid,
+ graph.waker_utid,
+ waker_process.upid AS waker_upid,
+ waker_thread.name AS waker_thread_name,
+ waker_process.name AS waker_process_name,
+ graph.prev_end_dur AS blocked_dur,
+ graph.prev_end_state AS blocked_state,
+ graph.prev_blocked_function AS blocked_function,
+ graph.is_root,
+ graph.is_leaf
+FROM internal_wakeup_graph graph
+JOIN thread
+ ON thread.utid = graph.utid
+LEFT JOIN process
+ ON process.upid = thread.upid
+LEFT JOIN thread waker_thread
+ ON waker_thread.utid = graph.waker_utid
+LEFT JOIN process waker_process
+ ON waker_process.upid = waker_thread.upid;
+
+CREATE
+ INDEX experimental_thread_executing_span_graph_id_idx
+ ON experimental_thread_executing_span_graph(id);
+
+CREATE
+ INDEX experimental_thread_executing_span_graph_parent_id_idx
+ON experimental_thread_executing_span_graph(parent_id);
+
+
+-- All thread_executing_spans that were recursively woken by |root_id|, all thread_executing_spans
+-- in trace.
+-- if root_id IS NULL, empty results if no matching thread_state id found.
+--
+-- @arg root_id INT Thread state id to start recursion
+--
+-- @column parent_id Id of thread_executing_span that directly woke |start_id|
+-- @column id Id of the first (runnable) thread state in thread_executing_span.
+-- @column ts Timestamp of first thread_state in thread_executing_span.
+-- @column dur Duration of thread_executing_span.
+-- @column tid Tid of thread with thread_state.
+-- @column pid Pid of process with thread_state.
+-- @column utid Utid of thread with thread_state.
+-- @column upid Upid of process with thread_state.
+-- @column thread_name Name of thread with thread_state.
+-- @column process_name Name of process with thread_state.
+-- @column waker_tid Tid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_pid Pid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_utid Utid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_upid Upid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_thread_name Name of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_process_name Name of process that woke the first thread_state in thread_executing_span.
+-- @column blocked_dur Duration of blocking thread state before waking up.
+-- @column blocked_state Thread state ('D' or 'S') of blocked thread_state before waking up.
+-- @column blocked_function Kernel blocking function of thread state before waking up.
+-- @column depth Tree depth from |root_id|
+-- @column root_id Thread state id used to start the recursion. Helpful for SQL JOINs
+SELECT CREATE_VIEW_FUNCTION(
+'EXPERIMENTAL_THREAD_EXECUTING_SPAN_DESCENDANTS(root_id INT)',
+'
+ parent_id LONG,
+ id LONG,
+ ts LONG,
+ dur LONG,
+ tid INT,
+ pid INT,
+ utid INT,
+ upid INT,
+ thread_name STRING,
+ process_name STRING,
+ waker_tid INT,
+ waker_pid INT,
+ waker_utid INT,
+ waker_upid INT,
+ waker_thread_name STRING,
+ waker_process_name STRING,
+ blocked_dur LONG,
+ blocked_state STRING,
+ blocked_function STRING,
+ is_root INT,
+ is_leaf INT,
+ depth INT,
+ root_id INT
+',
+'
+WITH chain AS (
+ SELECT
+ *,
+ 0 AS depth,
+ id AS root_id
+ FROM experimental_thread_executing_span_graph
+ WHERE ($root_id IS NOT NULL AND id = $root_id) OR ($root_id IS NULL AND is_root)
+ UNION ALL
+ SELECT
+ graph.*,
+ chain.depth + 1 AS depth,
+ chain.root_id
+ FROM experimental_thread_executing_span_graph graph
+ JOIN chain ON chain.id = graph.parent_id
+)
+SELECT * FROM chain
+');
+
+-- All thread_executing_spans that are ancestors of |leaf_id|
+--
+-- @arg leaf_id INT Thread state id to start recursion
+--
+-- @column parent_id Id of thread_executing_span that directly woke |id|
+-- @column id Id of the first (runnable) thread state in thread_executing_span.
+-- @column ts Timestamp of first thread_state in thread_executing_span.
+-- @column dur Duration of thread_executing_span.
+-- @column tid Tid of thread with thread_state.
+-- @column pid Pid of process with thread_state.
+-- @column utid Utid of thread with thread_state.
+-- @column upid Upid of process with thread_state.
+-- @column thread_name Name of thread with thread_state.
+-- @column process_name Name of process with thread_state.
+-- @column waker_tid Tid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_pid Pid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_utid Utid of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_upid Upid of process that woke the first thread_state in thread_executing_span.
+-- @column waker_thread_name Name of thread that woke the first thread_state in thread_executing_span.
+-- @column waker_process_name Name of process that woke the first thread_state in thread_executing_span.
+-- @column blocked_dur Duration of blocking thread state before waking up.
+-- @column blocked_state Thread state ('D' or 'S') of blocked thread_state before waking up.
+-- @column blocked_function Kernel blocking function of thread state before waking up.
+-- @column height Tree height from |leaf_id|
+-- @column leaf_id Thread state id used to start the recursion. Helpful for SQL JOINs
+SELECT CREATE_VIEW_FUNCTION(
+'EXPERIMENTAL_THREAD_EXECUTING_SPAN_ANCESTORS(leaf_id INT)',
+'
+ parent_id LONG,
+ id LONG,
+ ts LONG,
+ dur LONG,
+ tid INT,
+ pid INT,
+ utid INT,
+ upid INT,
+ thread_name STRING,
+ process_name STRING,
+ waker_tid INT,
+ waker_pid INT,
+ waker_utid INT,
+ waker_upid INT,
+ waker_thread_name STRING,
+ waker_process_name STRING,
+ blocked_dur LONG,
+ blocked_state STRING,
+ blocked_function STRING,
+ is_root INT,
+ is_leaf INT,
+ height INT,
+ leaf_id INT
+',
+'
+WITH
+chain AS (
+ SELECT
+ *,
+ 0 AS height,
+ id AS leaf_id,
+ ts AS leaf_ts,
+ blocked_dur AS leaf_blocked_dur,
+ blocked_function AS leaf_blocked_function
+ FROM experimental_thread_executing_span_graph
+ WHERE ($leaf_id IS NOT NULL AND id = $leaf_id) OR ($leaf_id IS NULL AND is_leaf)
+ UNION ALL
+ SELECT
+ graph.*,
+ chain.height + 1 AS height,
+ chain.leaf_id,
+ chain.leaf_ts,
+ chain.leaf_blocked_dur,
+ chain.leaf_blocked_function
+ FROM experimental_thread_executing_span_graph graph
+ JOIN chain ON chain.parent_id = graph.id AND chain.ts >= (leaf_ts - leaf_blocked_dur)
+) SELECT
+ parent_id,
+ id,
+ ts,
+ dur,
+ tid,
+ pid,
+ utid,
+ upid,
+ thread_name,
+ process_name,
+ waker_tid,
+ waker_pid,
+ waker_utid,
+ waker_upid,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function,
+ is_root,
+ is_leaf,
+ height,
+ leaf_id
+ FROM chain;
+');
+
+-- Gets the thread_executing_span id a thread_state belongs to. Returns NULL if thread state is
+-- sleeping and not blocked on an interrupt.
+--
+-- @arg thread_state_id INT Id of the thread_state to get the thread_executing_span id for
+-- @ret INT thread_executing_span id
+SELECT
+ CREATE_FUNCTION(
+'EXPERIMENTAL_THREAD_EXECUTING_SPAN_ID_FROM_THREAD_STATE_ID(thread_state_id INT)',
+'INT',
+'
+WITH
+ t AS (
+ SELECT
+ ts,
+ utid
+ FROM thread_state
+ WHERE
+ id = $thread_state_id
+ )
+ SELECT
+ MAX(start_id) AS thread_executing_span_id
+ FROM internal_wakeup w, t
+ WHERE t.utid = w.utid AND t.ts >= w.start_ts AND t.ts < w.end_ts;
+');
diff --git a/test/trace_processor/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
index 3ef8d14..3314008 100644
--- a/test/trace_processor/diff_tests/tables/tests_sched.py
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -120,3 +120,233 @@
19,"ftrace_event",1735490039439,"sched_waking",1,298,18,1
20,"ftrace_event",1735490042084,"sched_waking",1,298,19,1
"""))
+
+ def test_thread_executing_span_graph(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ ts,
+ dur,
+ tid,
+ pid,
+ thread_name,
+ process_name,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function
+ FROM experimental_thread_executing_span_graph
+ WHERE blocked_function IS NOT NULL
+ ORDER BY ts, tid
+ LIMIT 10
+ """,
+ out=Csv("""
+ "ts","dur","tid","pid","thread_name","process_name","waker_thread_name","waker_process_name","blocked_dur","blocked_state","blocked_function"
+ 1736413946850,576475,527,527,"adbd","/apex/com.android.adbd/bin/adbd","shell svc 3476","/apex/com.android.adbd/bin/adbd",507,"D","__down_read_common"
+ 1737047193524,1070032,3482,3482,"cmd","cmd","binder:3482_2","cmd",3892,"D","rwsem_down_write_slowpath"
+ 1737107227334,13924,15,15,"rcub/0","rcub/0","ActivityManager","system_server",10790,"D","rcu_boost_kthread"
+ 1737107244629,14884,17,17,"rcu_exp_gp_kthr","rcu_exp_gp_kthr","system_server","system_server",41867,"D","rcu_exp_sel_wait_wake"
+ 1737107251086,7335,15,15,"rcub/0","rcub/0","system_server","system_server",9828,"D","rcu_boost_kthread"
+ 1737107254718,3140060,1821,1800,"binder:1800_1","com.android.music","rcu_exp_gp_kthr","rcu_exp_gp_kthr",75180,"D","synchronize_rcu_expedited"
+ 1737114706120,6053987,1801,1789,"binder:1789_1","com.android.provision","Jit thread pool","com.android.providers.media.module",5495734,"D","rwsem_down_write_slowpath"
+ 1737116846911,95462739,2125,2110,"Profile Saver","com.android.externalstorage","binder:1789_1","com.android.provision",2664091,"D","rwsem_down_write_slowpath"
+ 1737120785844,15257,15,15,"rcub/0","rcub/0","ActivityManager","system_server",9143,"D","rcu_boost_kthread"
+ 1737120805447,16572,17,17,"rcu_exp_gp_kthr","rcu_exp_gp_kthr","android.bg","system_server",47725,"D","rcu_exp_sel_wait_wake"
+ """))
+
+ def test_thread_executing_span_graph_contains_forked_states(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ ts,
+ dur,
+ tid,
+ pid,
+ thread_name,
+ process_name,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function
+ FROM experimental_thread_executing_span_graph
+ WHERE id = 348
+ """,
+ out=Csv("""
+ "ts","dur","tid","pid","thread_name","process_name","waker_thread_name","waker_process_name","blocked_dur","blocked_state","blocked_function"
+ 1735842081507,293868,3475,527,"shell svc 3474","/apex/com.android.adbd/bin/adbd","adbd","/apex/com.android.adbd/bin/adbd","[NULL]","[NULL]","[NULL]"
+ """))
+
+ def test_thread_executing_span_graph_has_no_null_dur(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT ts,dur FROM experimental_thread_executing_span_graph
+ WHERE dur IS NULL OR ts IS NULL
+ """,
+ out=Csv("""
+ "ts","dur"
+ """))
+
+ def test_thread_executing_span_graph_accepts_null_irq_context(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_switch_original.pb'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT COUNT(*) AS count FROM experimental_thread_executing_span_graph
+ """,
+ out=Csv("""
+ "count"
+ 9
+ """))
+
+ def test_thread_executing_span_descendants_null(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ ts,
+ dur,
+ tid,
+ pid,
+ thread_name,
+ process_name,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function,
+ depth,
+ is_root
+ FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_DESCENDANTS(NULL)
+ ORDER BY depth DESC, ts, tid
+ LIMIT 10
+ """,
+ out=Csv("""
+ "ts","dur","tid","pid","thread_name","process_name","waker_thread_name","waker_process_name","blocked_dur","blocked_state","blocked_function","depth","is_root"
+ 1740321632480,20897,404,398,"binder:398_2","/apex/com.android.os.statsd/bin/statsd","statsd.writer","/apex/com.android.os.statsd/bin/statsd",64173354,"S","[NULL]",324,0
+ 1740470009095,113509,3494,3487,"HeapTaskDaemon","com.android.providers.media.module","AsyncTask #1","com.android.providers.media.module",1204928,"S","[NULL]",324,0
+ 1740470126280,60885652,3494,3487,"HeapTaskDaemon","com.android.providers.media.module","AsyncTask #1","com.android.providers.media.module",3676,"S","[NULL]",324,0
+ 1740321596028,46679,633,398,"statsd.writer","/apex/com.android.os.statsd/bin/statsd","mediametrics","media.metrics",64143546,"S","[NULL]",323,0
+ 1740468702535,1449612,3548,3487,"AsyncTask #1","com.android.providers.media.module","HeapTaskDaemon","com.android.providers.media.module",1003391,"S","[NULL]",323,0
+ 1740321315576,62532,2161,553,"binder:553_7","/system/bin/mediaserver","binder:551_4","media.extractor",63953635,"S","[NULL]",322,0
+ 1740321344727,346525,552,552,"mediametrics","media.metrics","binder:551_4","media.extractor",63860347,"S","[NULL]",322,0
+ 1740419776108,13020460,3494,3487,"HeapTaskDaemon","com.android.providers.media.module","AsyncTask #1","com.android.providers.media.module",597159,"S","[NULL]",322,0
+ 1740428968606,362233,3515,3487,"ackgroundThread","com.android.providers.media.module","AsyncTask #1","com.android.providers.media.module",9601023,"S","[NULL]",322,0
+ 1740432834772,3770512,3494,3487,"HeapTaskDaemon","com.android.providers.media.module","AsyncTask #1","com.android.providers.media.module",38204,"S","[NULL]",322,0
+ """))
+
+ def test_thread_executing_span_ancestors_null(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ ts,
+ dur,
+ tid,
+ pid,
+ thread_name,
+ process_name,
+ waker_thread_name,
+ waker_process_name,
+ blocked_dur,
+ blocked_state,
+ blocked_function,
+ height,
+ is_leaf
+ FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_ANCESTORS(NULL)
+ ORDER BY height DESC, ts, tid
+ LIMIT 10
+ """,
+ out=Csv("""
+ "ts","dur","tid","pid","thread_name","process_name","waker_thread_name","waker_process_name","blocked_dur","blocked_state","blocked_function","height","is_leaf"
+ 1740252621947,35807217,3548,3487,"AsyncTask #1","com.android.providers.media.module","binder:553_7","/system/bin/mediaserver",16688,"S","[NULL]",153,0
+ 1740287823247,9522163,282,282,"f2fs_ckpt-254:5","f2fs_ckpt-254:5","AsyncTask #1","com.android.providers.media.module",332115250,"S","[NULL]",152,0
+ 1740252621947,35807217,3548,3487,"AsyncTask #1","com.android.providers.media.module","binder:553_7","/system/bin/mediaserver",16688,"S","[NULL]",151,0
+ 1740297282282,13298848,3548,3487,"AsyncTask #1","com.android.providers.media.module","f2fs_ckpt-254:5","f2fs_ckpt-254:5",8853118,"D","f2fs_issue_checkpoint",151,0
+ 1740287823247,9522163,282,282,"f2fs_ckpt-254:5","f2fs_ckpt-254:5","AsyncTask #1","com.android.providers.media.module",332115250,"S","[NULL]",150,0
+ 1740310563850,141705,2134,553,"binder:553_3","/system/bin/mediaserver","AsyncTask #1","com.android.providers.media.module",53196161,"S","[NULL]",150,0
+ 1740297282282,13298848,3548,3487,"AsyncTask #1","com.android.providers.media.module","f2fs_ckpt-254:5","f2fs_ckpt-254:5",8853118,"D","f2fs_issue_checkpoint",149,0
+ 1740310673843,167265,3548,3487,"AsyncTask #1","com.android.providers.media.module","binder:553_3","/system/bin/mediaserver",92713,"S","[NULL]",149,0
+ 1740310563850,141705,2134,553,"binder:553_3","/system/bin/mediaserver","AsyncTask #1","com.android.providers.media.module",53196161,"S","[NULL]",148,0
+ 1740310830675,146216,2134,553,"binder:553_3","/system/bin/mediaserver","AsyncTask #1","com.android.providers.media.module",125120,"S","[NULL]",148,0
+ """))
+
+ def test_thread_executing_span_descendants_id(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ thread_name,
+ waker_thread_name,
+ depth,
+ is_root,
+ COUNT(thread_name) AS count
+ FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_DESCENDANTS(10834)
+ GROUP BY 1,2,3,4
+ ORDER BY depth
+ """,
+ out=Csv("""
+ "thread_name","waker_thread_name","depth","is_root","count"
+ "android.hardwar","android.bg",0,0,1
+ "android.bg","android.hardwar",1,0,1
+ "android.hardwar","android.bg",2,0,1
+ "android.bg","android.hardwar",3,0,1
+ "logd.writer","android.bg",4,0,1
+ "statsd.writer","android.bg",4,0,4
+ "system_server","android.bg",4,0,32
+ "binder:398_2","statsd.writer",5,0,3
+ "logd.reader.per","logd.writer",5,0,1
+ "logcat","logd.reader.per",6,0,1
+ """))
+
+ def test_thread_executing_span_ancestors_id(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT
+ thread_name,
+ waker_thread_name,
+ height,
+ is_leaf
+ FROM EXPERIMENTAL_THREAD_EXECUTING_SPAN_ANCESTORS(10840) ORDER BY height
+ """,
+ out=Csv("""
+ "thread_name","waker_thread_name","height","is_leaf"
+ "android.hardwar","android.bg",0,0
+ "android.bg","android.hardwar",1,0
+ """))
+
+ def test_thread_executing_span_from_non_sleep_thread_state(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_ID_FROM_THREAD_STATE_ID(11933) AS thread_executing_span_id
+ """,
+ out=Csv("""
+ "thread_executing_span_id"
+ 11888
+ """))
+
+ def test_thread_executing_span_from_sleep_thread_state(self):
+ return DiffTestBlueprint(
+ trace=DataPath('sched_wakeup_trace.atr'),
+ query="""
+ SELECT IMPORT('experimental.thread_executing_span');
+ SELECT EXPERIMENTAL_THREAD_EXECUTING_SPAN_ID_FROM_THREAD_STATE_ID(11845) AS thread_executing_span_id
+ """,
+ out=Csv("""
+ "thread_executing_span_id"
+ "[NULL]"
+ """))
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index c26ee4f..a83a9f6 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -457,7 +457,7 @@
.scrolling-panel-container {
position: relative;
overflow-x: hidden;
- overflow-y: scroll; // Always show vertical scrollbar
+ overflow-y: auto;
flex: 1 1 auto;
will-change: transform; // Force layer creation.
display: grid;
diff --git a/ui/src/base/math_utils.ts b/ui/src/base/math_utils.ts
new file mode 100644
index 0000000..b86f231
--- /dev/null
+++ b/ui/src/base/math_utils.ts
@@ -0,0 +1,18 @@
+// 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.
+
+// Restrict the value of a number between two values (inclusive)
+export function clamp(val: number, lower: number, upper: number): number {
+ return Math.max(lower, Math.min(upper, val));
+}
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index d92090d..efbae6c 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -41,7 +41,6 @@
defaultValue: false,
});
-
export class FlowEventsController extends Controller<'main'> {
private lastSelectedSliceId?: number;
private lastSelectedArea?: Area;
@@ -76,109 +75,234 @@
);`);
}
- queryFlowEvents(query: string, callback: (flows: Flow[]) => void) {
- this.args.engine.query(query).then((result) => {
- const flows: Flow[] = [];
- const it = result.iter({
- beginSliceId: NUM,
- beginTrackId: NUM,
- beginSliceName: STR_NULL,
- beginSliceChromeCustomName: STR_NULL,
- beginSliceCategory: STR_NULL,
- beginSliceStartTs: LONG,
- beginSliceEndTs: LONG,
- beginDepth: NUM,
- beginThreadName: STR_NULL,
- beginProcessName: STR_NULL,
- endSliceId: NUM,
- endTrackId: NUM,
- endSliceName: STR_NULL,
- endSliceChromeCustomName: STR_NULL,
- endSliceCategory: STR_NULL,
- endSliceStartTs: LONG,
- endSliceEndTs: LONG,
- endDepth: NUM,
- endThreadName: STR_NULL,
- endProcessName: STR_NULL,
- name: STR_NULL,
- category: STR_NULL,
- id: NUM,
- });
- for (; it.valid(); it.next()) {
- const beginSliceId = it.beginSliceId;
- const beginTrackId = it.beginTrackId;
- const beginSliceName =
- it.beginSliceName === null ? 'NULL' : it.beginSliceName;
- const beginSliceChromeCustomName =
- it.beginSliceChromeCustomName === null ?
- undefined :
- it.beginSliceChromeCustomName;
- const beginSliceCategory =
- it.beginSliceCategory === null ? 'NULL' : it.beginSliceCategory;
- const beginSliceStartTs = it.beginSliceStartTs;
- const beginSliceEndTs = it.beginSliceEndTs;
- const beginDepth = it.beginDepth;
- const beginThreadName =
- it.beginThreadName === null ? 'NULL' : it.beginThreadName;
- const beginProcessName =
- it.beginProcessName === null ? 'NULL' : it.beginProcessName;
+ async queryFlowEvents(query: string, callback: (flows: Flow[]) => void) {
+ const result = await this.args.engine.query(query);
+ const flows: Flow[] = [];
- const endSliceId = it.endSliceId;
- const endTrackId = it.endTrackId;
- const endSliceName =
- it.endSliceName === null ? 'NULL' : it.endSliceName;
- const endSliceChromeCustomName = it.endSliceChromeCustomName === null ?
- undefined :
- it.endSliceChromeCustomName;
- const endSliceCategory =
- it.endSliceCategory === null ? 'NULL' : it.endSliceCategory;
- const endSliceStartTs = it.endSliceStartTs;
- const endSliceEndTs = it.endSliceEndTs;
- const endDepth = it.endDepth;
- const endThreadName =
- it.endThreadName === null ? 'NULL' : it.endThreadName;
- const endProcessName =
- it.endProcessName === null ? 'NULL' : it.endProcessName;
-
- // Category and name present only in version 1 flow events
- // It is most likelly NULL for all other versions
- const category = it.category === null ? undefined : it.category;
- const name = it.name === null ? undefined : it.name;
- const id = it.id;
-
- flows.push({
- id,
- begin: {
- trackId: beginTrackId,
- sliceId: beginSliceId,
- sliceName: beginSliceName,
- sliceChromeCustomName: beginSliceChromeCustomName,
- sliceCategory: beginSliceCategory,
- sliceStartTs: beginSliceStartTs,
- sliceEndTs: beginSliceEndTs,
- depth: beginDepth,
- threadName: beginThreadName,
- processName: beginProcessName,
- },
- end: {
- trackId: endTrackId,
- sliceId: endSliceId,
- sliceName: endSliceName,
- sliceChromeCustomName: endSliceChromeCustomName,
- sliceCategory: endSliceCategory,
- sliceStartTs: endSliceStartTs,
- sliceEndTs: endSliceEndTs,
- depth: endDepth,
- threadName: endThreadName,
- processName: endProcessName,
- },
- dur: endSliceStartTs - beginSliceEndTs,
- category,
- name,
- });
- }
- callback(flows);
+ const it = result.iter({
+ beginSliceId: NUM,
+ beginTrackId: NUM,
+ beginSliceName: STR_NULL,
+ beginSliceChromeCustomName: STR_NULL,
+ beginSliceCategory: STR_NULL,
+ beginSliceStartTs: LONG,
+ beginSliceEndTs: LONG,
+ beginDepth: NUM,
+ beginThreadName: STR_NULL,
+ beginProcessName: STR_NULL,
+ endSliceId: NUM,
+ endTrackId: NUM,
+ endSliceName: STR_NULL,
+ endSliceChromeCustomName: STR_NULL,
+ endSliceCategory: STR_NULL,
+ endSliceStartTs: LONG,
+ endSliceEndTs: LONG,
+ endDepth: NUM,
+ endThreadName: STR_NULL,
+ endProcessName: STR_NULL,
+ name: STR_NULL,
+ category: STR_NULL,
+ id: NUM,
});
+
+ const nullToStr = (s: null|string): string => {
+ return s === null ? 'NULL' : s;
+ };
+
+ const nullToUndefined = (s: null|string): undefined|string => {
+ return s === null ? undefined : s;
+ };
+
+ const nodes = [];
+
+ for (; it.valid(); it.next()) {
+ // Category and name present only in version 1 flow events
+ // It is most likelly NULL for all other versions
+ const category = nullToUndefined(it.category);
+ const name = nullToUndefined(it.name);
+ const id = it.id;
+
+ const begin = {
+ trackId: it.beginTrackId,
+ sliceId: it.beginSliceId,
+ sliceName: nullToStr(it.beginSliceName),
+ sliceChromeCustomName: nullToUndefined(it.beginSliceChromeCustomName),
+ sliceCategory: nullToStr(it.beginSliceCategory),
+ sliceStartTs: it.beginSliceStartTs,
+ sliceEndTs: it.beginSliceEndTs,
+ depth: it.beginDepth,
+ threadName: nullToStr(it.beginThreadName),
+ processName: nullToStr(it.beginProcessName),
+ };
+
+ const end = {
+ trackId: it.endTrackId,
+ sliceId: it.endSliceId,
+ sliceName: nullToStr(it.endSliceName),
+ sliceChromeCustomName: nullToUndefined(it.endSliceChromeCustomName),
+ sliceCategory: nullToStr(it.endSliceCategory),
+ sliceStartTs: it.endSliceStartTs,
+ sliceEndTs: it.endSliceEndTs,
+ depth: it.endDepth,
+ threadName: nullToStr(it.endThreadName),
+ processName: nullToStr(it.endProcessName),
+ };
+
+ nodes.push(begin);
+ nodes.push(end);
+
+ flows.push({
+ id,
+ begin,
+ end,
+ dur: it.endSliceStartTs - it.beginSliceEndTs,
+ category,
+ name,
+ });
+ }
+
+
+ // Everything below here is a horrible hack to support flows for
+ // async slice tracks.
+ // In short the issue is this:
+ // - For most slice tracks there is a one-to-one mapping between
+ // the track in the UI and the track in the TP. n.b. Even in this
+ // case the UI 'trackId' and the TP 'track.id' may not be the
+ // same. In this case 'depth' in the TP is the exact depth in the
+ // UI.
+ // - In the case of aysnc tracks however the mapping is
+ // one-to-many. Each async slice track in the UI is 'backed' but
+ // multiple TP tracks. In order to render this track we need
+ // to adjust depth to avoid overlapping slices. In the render
+ // path we use experimental_slice_layout for this purpose. This
+ // is a virtual table in the TP which, for an arbitrary collection
+ // of TP trackIds, computes for each slice a 'layout_depth'.
+ // - Everything above in this function and its callers doesn't
+ // know anything about layout_depth.
+ //
+ // So if we stopped here we would have incorrect rendering for
+ // async slice tracks. Instead we want to 'fix' depth for these
+ // cases. We do this in two passes.
+ // - First we collect all the information we need in 'Info' POJOs
+ // - Secondly we loop over those Infos querying
+ // the database to find the layout_depth for each sliceId
+ // TODO(hjd): This should not be needed after TracksV2 lands.
+
+ // We end up with one Info POJOs for each UI async slice track
+ // which has at least one flow {begin,end}ing in one of its slices.
+ interface Info {
+ uiTrackId: string;
+ siblingTrackIds: number[];
+ sliceIds: number[];
+ nodes: Array<{
+ sliceId: number,
+ depth: number,
+ }>;
+ }
+
+ const uiTrackIdToInfo = new Map<string, null|Info>();
+ const trackIdToInfo = new Map<number, null|Info>();
+
+ const trackIdToUiTrackId = globals.state.uiTrackIdByTraceTrackId;
+ const tracks = globals.state.tracks;
+
+ const getInfo = (trackId: number): null|Info => {
+ let info = trackIdToInfo.get(trackId);
+ if (info !== undefined) {
+ return info;
+ }
+
+ const uiTrackId = trackIdToUiTrackId[trackId];
+ if (uiTrackId === undefined) {
+ trackIdToInfo.set(trackId, null);
+ return null;
+ }
+
+ const track = tracks[uiTrackId];
+ if (track === undefined) {
+ trackIdToInfo.set(trackId, null);
+ return null;
+ }
+
+ info = uiTrackIdToInfo.get(uiTrackId);
+ if (info !== undefined) {
+ return info;
+ }
+
+ // If 'trackIds' is undefined this is not an async slice track so
+ // we don't need to do anything. We also don't need to do
+ // anything if there is only one TP track in this async track. In
+ // that case experimental_slice_layout is just an expensive way
+ // to find out depth === layout_depth.
+ const trackIds = track.config.trackIds;
+ if (trackIds === undefined || trackIds.length <= 1) {
+ uiTrackIdToInfo.set(uiTrackId, null);
+ trackIdToInfo.set(trackId, null);
+ return null;
+ }
+
+ const newInfo = {
+ uiTrackId,
+ siblingTrackIds: trackIds,
+ sliceIds: [],
+ nodes: [],
+ };
+
+ uiTrackIdToInfo.set(uiTrackId, newInfo);
+ trackIdToInfo.set(trackId, newInfo);
+
+ return newInfo;
+ };
+
+ // First pass, collect:
+ // - all slices that belong to async slice track
+ // - grouped by the async slice track in question
+ for (const node of nodes) {
+ const info = getInfo(node.trackId);
+ if (info !== null) {
+ info.sliceIds.push(node.sliceId);
+ info.nodes.push(node);
+ }
+ }
+
+ // Second pass, for each async track:
+ // - Query to find the layout_depth for each relevant sliceId
+ // - Iterate through the nodes updating the depth in place
+ for (const info of uiTrackIdToInfo.values()) {
+ if (info === null) {
+ continue;
+ }
+ const r = await this.args.engine.query(`
+ SELECT
+ id,
+ layout_depth as depth
+ FROM
+ experimental_slice_layout
+ WHERE
+ filter_track_ids = '${info.siblingTrackIds.join(',')}'
+ AND id in (${info.sliceIds.join(', ')})
+ `);
+
+ // Create the sliceId -> new depth map:
+ const it = r.iter({
+ id: NUM,
+ depth: NUM,
+ });
+ const sliceIdToDepth = new Map<number, number>();
+ for (; it.valid(); it.next()) {
+ sliceIdToDepth.set(it.id, it.depth);
+ }
+
+ // For each begin/end from an async track update the depth:
+ for (const node of info.nodes) {
+ const newDepth = sliceIdToDepth.get(node.sliceId);
+ if (newDepth !== undefined) {
+ node.depth = newDepth;
+ }
+ }
+ }
+
+ callback(flows);
}
sliceSelected(sliceId: number) {
@@ -262,6 +386,10 @@
for (const trackId of actualConfig.trackIds) {
trackIds.push(trackId);
}
+ } else if (track.config.trackIds !== undefined) {
+ for (const trackId of track.config.trackIds) {
+ trackIds.push(trackId);
+ }
}
}
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 0f8414c..b61c1c9 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -13,8 +13,9 @@
// limitations under the License.
import m from 'mithril';
-import {BigintMath} from '../base/bigint_math';
+import {BigintMath} from '../base/bigint_math';
+import {clamp} from '../base/math_utils';
import {Actions} from '../common/actions';
import {featureFlags} from '../common/feature_flags';
@@ -187,8 +188,9 @@
let startPx = Math.min(dragStartX, currentX) - TRACK_SHELL_WIDTH;
let endPx = Math.max(dragStartX, currentX) - TRACK_SHELL_WIDTH;
if (startPx < 0 && endPx < 0) return;
- startPx = Math.max(startPx, visibleTimeScale.pxSpan.start);
- endPx = Math.min(endPx, visibleTimeScale.pxSpan.end);
+ const {pxSpan} = visibleTimeScale;
+ startPx = clamp(startPx, pxSpan.start, pxSpan.end);
+ endPx = clamp(endPx, pxSpan.start, pxSpan.end);
frontendLocalState.selectArea(
visibleTimeScale.pxToHpTime(startPx).toTPTime('floor'),
visibleTimeScale.pxToHpTime(endPx).toTPTime('ceil'),
diff --git a/ui/src/tracks/scroll_jank/event_latency_track.ts b/ui/src/tracks/scroll_jank/event_latency_track.ts
index 4b695c2..e1a3633 100644
--- a/ui/src/tracks/scroll_jank/event_latency_track.ts
+++ b/ui/src/tracks/scroll_jank/event_latency_track.ts
@@ -19,11 +19,18 @@
generateSqlWithInternalLayout,
} from '../../common/internal_layout_utils';
import {PrimaryTrackSortKey, SCROLLING_TRACK_GROUP} from '../../common/state';
-import {NamedSliceTrack} from '../../frontend/named_slice_track';
+import {
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
import {NewTrackArgs, Track} from '../../frontend/track';
import {DecideTracksResult} from '../chrome_scroll_jank';
-export class EventLatencyTrack extends NamedSliceTrack {
+export interface EventLatencyTrackTypes extends NamedSliceTrackTypes {
+ config: {baseTable: string;}
+}
+
+export class EventLatencyTrack extends NamedSliceTrack<EventLatencyTrackTypes> {
static readonly kind = 'org.chromium.ScrollJank.event_latencies';
static create(args: NewTrackArgs): Track {
@@ -36,12 +43,7 @@
async initSqlTable(tableName: string) {
const sql =
- `CREATE VIEW ${tableName} AS ` +
- generateSqlWithInternalLayout({
- columns: ['id', 'ts', 'dur', 'track_id', 'cause_of_jank AS name'],
- layoutParams: {ts: 'ts', dur: 'dur'},
- sourceTable: 'chrome_janky_event_latencies_v2',
- }) + `;`;
+ `CREATE VIEW ${tableName} AS SELECT * FROM ${this.config.baseTable}`;
await this.engine.query(sql);
}
@@ -50,21 +52,81 @@
// this behavior should be customized to show jank-related data.
}
-export async function addLatenciesTrack(engine: Engine):
+export async function addLatencyTracks(engine: Engine):
Promise<DecideTracksResult> {
const result: DecideTracksResult = {
tracksToAdd: [],
};
- await engine.query(`SELECT IMPORT('chrome.chrome_scroll_janks');`);
+ const subTableSql = generateSqlWithInternalLayout({
+ columns: ['id', 'ts', 'dur', 'track_id', 'name'],
+ layoutParams: {ts: 'ts', dur: 'dur'},
+ sourceTable: 'slice',
+ whereClause: `
+ EXTRACT_ARG(arg_set_id, 'event_latency.event_type') IN (
+ 'FIRST_GESTURE_SCROLL_UPDATE',
+ 'GESTURE_SCROLL_UPDATE',
+ 'INERTIAL_GESTURE_SCROLL_UPDATE')
+ AND HAS_DESCENDANT_SLICE_WITH_NAME(
+ id,
+ 'SubmitCompositorFrameToPresentationCompositorFrame')`,
+ });
+
+ // Table name must be unique - it cannot include '-' characters or begin with
+ // a numeric value.
+ const baseTable =
+ `table_${uuidv4().split('-').join('_')}_janky_event_latencies_v2`;
+ const tableDefSql = `CREATE TABLE ${baseTable} AS
+ WITH event_latencies AS (
+ ${subTableSql}
+ ), latency_stages AS (
+ SELECT
+ d.id,
+ d.ts,
+ d.dur,
+ d.track_id,
+ d.name,
+ d.depth,
+ min(a.id) AS parent_id
+ FROM slice s
+ JOIN descendant_slice(s.id) d
+ JOIN ancestor_slice(d.id) a
+ WHERE s.id IN (SELECT id FROM event_latencies)
+ GROUP BY d.id, d.ts, d.dur, d.track_id, d.name, d.parent_id, d.depth)
+ SELECT
+ id,
+ ts,
+ dur,
+ CASE
+ WHEN id IN (
+ SELECT id FROM chrome_janky_event_latencies_v2)
+ THEN 'Janky EventLatency'
+ ELSE name
+ END
+ AS name,
+ depth * 3 AS depth
+ FROM event_latencies
+ UNION ALL
+ SELECT
+ ls.id,
+ ls.ts,
+ ls.dur,
+ ls.name,
+ depth + (
+ (SELECT depth FROM event_latencies
+ WHERE id = ls.parent_id LIMIT 1) * 3) AS depth
+ FROM latency_stages ls;`;
+
+ await engine.query(`SELECT IMPORT('chrome.chrome_scroll_janks')`);
+ await engine.query(tableDefSql);
result.tracksToAdd.push({
id: uuidv4(),
engineId: engine.id,
kind: EventLatencyTrack.kind,
trackSortKey: PrimaryTrackSortKey.NULL_TRACK,
- name: 'Scroll Janks',
- config: {},
+ name: 'Input Event Latencies',
+ config: {baseTable: baseTable},
trackGroup: SCROLLING_TRACK_GROUP,
});
diff --git a/ui/src/tracks/scroll_jank/index.ts b/ui/src/tracks/scroll_jank/index.ts
index c6d9ef3..c7452c2 100644
--- a/ui/src/tracks/scroll_jank/index.ts
+++ b/ui/src/tracks/scroll_jank/index.ts
@@ -12,17 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {AddTrackArgs} from '../../common/actions';
+import {Engine} from '../../common/engine';
import {featureFlags} from '../../common/feature_flags';
import {PluginContext} from '../../common/plugin_api';
-import {addLatenciesTrack, EventLatencyTrack} from './event_latency_track';
+import {addLatencyTracks, EventLatencyTrack} from './event_latency_track';
import {
- addTopLevelScrollTrack,
TopLevelScrollTrack,
} from './scroll_track';
-import {Engine} from '../../common/engine';
import {addTopLevelJankTrack, TopLevelJankTrack} from './top_level_jank_track';
-import {AddTrackArgs} from '../../common/actions';
+import {
+ addJankyLatenciesTrack,
+ TopLevelEventLatencyTrack,
+} from './top_level_janky_event_latencies';
// Constants for rendering plugin tracks.
export const INPUT_LATENCY_TRACK = 'InputLatency::';
@@ -55,20 +58,20 @@
topLevelJanksResult.tracksToAdd[i];
}
- const topLevelScrolls = addTopLevelScrollTrack(engine);
- const topLevelScrollsResult = await topLevelScrolls;
- originalLength = result.tracksToAdd.length;
- result.tracksToAdd.length += topLevelScrollsResult.tracksToAdd.length;
- for (let i = 0; i < topLevelScrollsResult.tracksToAdd.length; ++i) {
- result.tracksToAdd[i + originalLength] =
- topLevelScrollsResult.tracksToAdd[i];
- }
-
// TODO(b/278844325): Top Level event latency summary is already rendered in
// the TopLevelJankTrack; this track should be rendered at a more
// intuitive location when the descendant slices are rendered.
originalLength = result.tracksToAdd.length;
- const eventLatencies = addLatenciesTrack(engine);
+ const jankyEventLatencies = addJankyLatenciesTrack(engine);
+ const jankyEventLatencyResult = await jankyEventLatencies;
+ result.tracksToAdd.length += jankyEventLatencyResult.tracksToAdd.length;
+ for (let i = 0; i < jankyEventLatencyResult.tracksToAdd.length; ++i) {
+ result.tracksToAdd[i + originalLength] =
+ jankyEventLatencyResult.tracksToAdd[i];
+ }
+
+ originalLength = result.tracksToAdd.length;
+ const eventLatencies = addLatencyTracks(engine);
const eventLatencyResult = await eventLatencies;
result.tracksToAdd.length += eventLatencyResult.tracksToAdd.length;
for (let i = 0; i < eventLatencyResult.tracksToAdd.length; ++i) {
@@ -81,6 +84,7 @@
function activate(ctx: PluginContext) {
ctx.registerTrack(TopLevelJankTrack);
ctx.registerTrack(TopLevelScrollTrack);
+ ctx.registerTrack(TopLevelEventLatencyTrack);
ctx.registerTrack(EventLatencyTrack);
}
diff --git a/ui/src/tracks/scroll_jank/top_level_jank_track.ts b/ui/src/tracks/scroll_jank/top_level_jank_track.ts
index 54074e5..6c2c467 100644
--- a/ui/src/tracks/scroll_jank/top_level_jank_track.ts
+++ b/ui/src/tracks/scroll_jank/top_level_jank_track.ts
@@ -38,7 +38,7 @@
const sql = `CREATE VIEW ${tableName} AS
WITH unioned_data AS (
SELECT
- "Scrolling" AS name,
+ "Scrolling: " || scroll_ids AS name,
ts,
dur,
0 AS depth
@@ -100,6 +100,7 @@
tracksToAdd: [],
};
+ await engine.query(`SELECT IMPORT('chrome.chrome_scrolls');`);
await engine.query(`SELECT IMPORT('chrome.chrome_scroll_janks');`);
result.tracksToAdd.push({
diff --git a/ui/src/tracks/scroll_jank/top_level_janky_event_latencies.ts b/ui/src/tracks/scroll_jank/top_level_janky_event_latencies.ts
new file mode 100644
index 0000000..757691a
--- /dev/null
+++ b/ui/src/tracks/scroll_jank/top_level_janky_event_latencies.ts
@@ -0,0 +1,128 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {v4 as uuidv4} from 'uuid';
+
+import {Actions} from '../../common/actions';
+import {Engine} from '../../common/engine';
+import {
+ generateSqlWithInternalLayout,
+} from '../../common/internal_layout_utils';
+import {
+ PrimaryTrackSortKey,
+ SCROLLING_TRACK_GROUP,
+ Selection,
+} from '../../common/state';
+import {OnSliceClickArgs} from '../../frontend/base_slice_track';
+import {
+ Columns,
+ GenericSliceDetailsTab,
+} from '../../frontend/generic_slice_details_tab';
+import {globals} from '../../frontend/globals';
+import {
+ NamedSliceTrack,
+ NamedSliceTrackTypes,
+} from '../../frontend/named_slice_track';
+import {NewTrackArgs, Track} from '../../frontend/track';
+import {DecideTracksResult} from '../chrome_scroll_jank';
+
+export class TopLevelEventLatencyTrack extends NamedSliceTrack {
+ static readonly kind = 'org.chromium.ScrollJank.top_level_event_latencies';
+
+ static create(args: NewTrackArgs): Track {
+ return new TopLevelEventLatencyTrack(args);
+ }
+
+ constructor(args: NewTrackArgs) {
+ super(args);
+ }
+
+ async initSqlTable(tableName: string) {
+ const sql =
+ `CREATE VIEW ${tableName} AS ` + generateSqlWithInternalLayout({
+ columns: [
+ 'id',
+ 'ts',
+ 'dur',
+ 'track_id',
+ 'cause_of_jank || IIF(sub_cause_of_jank IS NOT NULL, "::" || sub_cause_of_jank, "") AS name',
+ 'name AS type',
+ 'sub_cause_of_jank',
+ ],
+ layoutParams: {ts: 'ts', dur: 'dur'},
+ sourceTable: 'chrome_janky_event_latencies_v2',
+ }) +
+ `;`;
+
+ await this.engine.query(sql);
+ }
+
+ isSelectionHandled(selection: Selection) {
+ if (selection.kind !== 'BASIC_SQL_OBJECT') {
+ return false;
+ }
+ return selection.trackId === this.trackId;
+ }
+
+ onSliceClick(args: OnSliceClickArgs<NamedSliceTrackTypes['slice']>) {
+ const columns: Columns = {};
+ columns['name'] = {displayName: 'Cause of Jank'};
+ columns['sub_cause_of_jank'] = {displayName: 'Sub-cause of Jank'};
+ columns['id'] = {displayName: 'Slice ID'};
+ columns['ts'] = {displayName: 'Start time'};
+ columns['dur'] = {displayName: 'Duration'};
+ columns['type'] = {displayName: 'Slice Type'};
+
+
+ const title = 'Scroll Jank Summary';
+
+ globals.dispatch(Actions.selectBasicSqlSlice({
+ id: args.slice.id,
+ sqlTableName: this.tableName,
+ start: args.slice.start,
+ duration: args.slice.duration,
+ trackId: this.trackId,
+ detailsPanelConfig: {
+ kind: GenericSliceDetailsTab.kind,
+ config: {
+ id: args.slice.id,
+ sqlTableName: this.tableName,
+ title: title,
+ columns: columns,
+ },
+ },
+ }));
+ }
+}
+
+export async function addJankyLatenciesTrack(engine: Engine):
+ Promise<DecideTracksResult> {
+ const result: DecideTracksResult = {
+ tracksToAdd: [],
+ };
+
+ await engine.query(`SELECT IMPORT('chrome.chrome_scroll_janks');`);
+
+ result.tracksToAdd.push({
+ id: uuidv4(),
+ engineId: engine.id,
+ kind: TopLevelEventLatencyTrack.kind,
+ trackSortKey: PrimaryTrackSortKey.NULL_TRACK,
+ name: 'Scroll Janks',
+ config: {},
+ trackGroup: SCROLLING_TRACK_GROUP,
+ });
+
+ return result;
+}