Merge "Add plugin to save and restore pinned track on a best effort" into main
diff --git a/Android.bp b/Android.bp
index e435f99..94b84ff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5323,6 +5323,7 @@
genrule {
name: "perfetto_protos_perfetto_metrics_webview_descriptor",
srcs: [
+ ":libprotobuf-internal-descriptor-proto",
"protos/perfetto/metrics/android/ad_services_metric.proto",
"protos/perfetto/metrics/android/android_blocking_call.proto",
"protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
@@ -5388,7 +5389,7 @@
tools: [
"aprotoc",
],
- cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
+ cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --proto_path=external/protobuf/src --descriptor_set_out=$(out) $(in)",
out: [
"perfetto_protos_perfetto_metrics_webview_descriptor.bin",
],
@@ -11347,6 +11348,7 @@
srcs: [
"src/trace_processor/db/column/arrangement_overlay_unittest.cc",
"src/trace_processor/db/column/dense_null_overlay_unittest.cc",
+ "src/trace_processor/db/column/fake_storage_unittest.cc",
"src/trace_processor/db/column/id_storage_unittest.cc",
"src/trace_processor/db/column/null_overlay_unittest.cc",
"src/trace_processor/db/column/numeric_storage_unittest.cc",
diff --git a/CHANGELOG b/CHANGELOG
index b600600..99ff035 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,7 +4,18 @@
Trace Processor:
*
UI:
- *
+ * Add tracks to the list of searchable items.
+ * Use mipmaps to improve track query performance on large traces.
+ * Fix slow scrolling bug in ftrace explorer tab on low DPI machines.
+ * Overhaul track decider queries to improve trace load times.
+ * Add track
+ * Tidy up command names & remove some example ones.
+ * Remove arg auto-completion in pivot table.
+ * Show dominator tree views by default.
+ * Fix counter event selection off-by-one error.
+ * Add viewport control to the plugin API.
+ * Sticky track titles to improve track button accessibility in tall tracks.
+ * A handful of small bugfixes.
SDK:
* The TRACE_EVENT macro used to reject `const char *` event names: either
`StaticString` or `DynamicString` needed to be specified. In the last year
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index f69d3ce..a7a77c6 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -371,7 +371,7 @@
metadata = {
proto_library_sources = invoker.sources
- import_dirs = import_dirs_
+ proto_import_dirs = import_dirs_
exports = get_path_info(public_deps_, "abspath")
}
forward_variables_from(invoker, vars_to_forward)
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 07b8140..1a23a97 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -170,6 +170,10 @@
]
}
+ metadata = {
+ proto_import_dirs = import_dirs
+ }
+
if (generate_cc) {
cc_generator_options_ = ""
if (defined(invoker.cc_generator_options)) {
diff --git a/src/trace_processor/db/column/BUILD.gn b/src/trace_processor/db/column/BUILD.gn
index 74d1611..d53e7ae 100644
--- a/src/trace_processor/db/column/BUILD.gn
+++ b/src/trace_processor/db/column/BUILD.gn
@@ -75,6 +75,7 @@
sources = [
"arrangement_overlay_unittest.cc",
"dense_null_overlay_unittest.cc",
+ "fake_storage_unittest.cc",
"id_storage_unittest.cc",
"null_overlay_unittest.cc",
"numeric_storage_unittest.cc",
diff --git a/src/trace_processor/db/column/fake_storage.cc b/src/trace_processor/db/column/fake_storage.cc
index f587c77..babfb7c 100644
--- a/src/trace_processor/db/column/fake_storage.cc
+++ b/src/trace_processor/db/column/fake_storage.cc
@@ -41,6 +41,7 @@
SingleSearchResult FakeStorageChain::SingleSearch(FilterOp,
SqlValue,
uint32_t i) const {
+ PERFETTO_CHECK(i < size_);
switch (strategy_) {
case kAll:
return SingleSearchResult::kMatch;
@@ -115,37 +116,37 @@
FilterOp,
SqlValue,
const OrderedIndices& indices) const {
- if (strategy_ == kAll) {
- return {0, indices.size};
- }
-
- if (strategy_ == kNone) {
- return {};
- }
-
- if (strategy_ == kRange) {
- // We are looking at intersection of |range_| and |indices_|.
- const uint32_t* first_in_range = std::partition_point(
- indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !range_.Contains(i); });
- const uint32_t* first_outside_range =
- std::partition_point(first_in_range, indices.data + indices.size,
- [this](uint32_t i) { return range_.Contains(i); });
- return {static_cast<uint32_t>(std::distance(indices.data, first_in_range)),
- static_cast<uint32_t>(
- std::distance(indices.data, first_outside_range))};
- }
-
- PERFETTO_DCHECK(strategy_ == kBitVector);
- // We are looking at intersection of |range_| and |bit_vector_|.
- const uint32_t* first_set = std::partition_point(
- indices.data, indices.data + indices.size,
- [this](uint32_t i) { return !bit_vector_.IsSet(i); });
- const uint32_t* first_non_set =
- std::partition_point(first_set, indices.data + indices.size,
- [this](uint32_t i) { return bit_vector_.IsSet(i); });
- return {static_cast<uint32_t>(std::distance(indices.data, first_set)),
+ switch (strategy_) {
+ case kAll:
+ return {0, indices.size};
+ case kNone:
+ return {};
+ case kRange: {
+ // We are looking at intersection of |range_| and |indices_|.
+ const uint32_t* first_in_range = std::partition_point(
+ indices.data, indices.data + indices.size,
+ [this](uint32_t i) { return !range_.Contains(i); });
+ const uint32_t* first_outside_range = std::partition_point(
+ first_in_range, indices.data + indices.size,
+ [this](uint32_t i) { return range_.Contains(i); });
+ return {
+ static_cast<uint32_t>(std::distance(indices.data, first_in_range)),
+ static_cast<uint32_t>(
+ std::distance(indices.data, first_outside_range))};
+ }
+ case kBitVector:
+ // We are looking at intersection of |range_| and |bit_vector_|.
+ const uint32_t* first_set = std::partition_point(
+ indices.data, indices.data + indices.size,
+ [this](uint32_t i) { return !bit_vector_.IsSet(i); });
+ const uint32_t* first_non_set = std::partition_point(
+ first_set, indices.data + indices.size,
+ [this](uint32_t i) { return bit_vector_.IsSet(i); });
+ return {
+ static_cast<uint32_t>(std::distance(indices.data, first_set)),
static_cast<uint32_t>(std::distance(indices.data, first_non_set))};
+ }
+ PERFETTO_FATAL("For GCC");
}
void FakeStorageChain::StableSort(SortToken*, SortToken*, SortDirection) const {
diff --git a/src/trace_processor/db/column/fake_storage_unittest.cc b/src/trace_processor/db/column/fake_storage_unittest.cc
new file mode 100644
index 0000000..0bc0211
--- /dev/null
+++ b/src/trace_processor/db/column/fake_storage_unittest.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "src/trace_processor/db/column/fake_storage.h"
+
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/column/data_layer.h"
+#include "src/trace_processor/db/column/types.h"
+#include "src/trace_processor/db/column/utils.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto::trace_processor {
+
+inline bool operator==(const Range& a, const Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
+inline bool operator==(const BitVector& a, const BitVector& b) {
+ return a.size() == b.size() && a.CountSetBits() == b.CountSetBits();
+}
+
+namespace column {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+using Indices = DataLayerChain::Indices;
+using OrderedIndices = DataLayerChain::OrderedIndices;
+
+TEST(FakeStorage, ValidateSearchConstraints) {
+ {
+ // All passes
+ auto fake = FakeStorageChain::SearchAll(10);
+ EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+ SearchValidationResult::kOk);
+ }
+ {
+ // None passes
+ auto fake = FakeStorageChain::SearchNone(10);
+ EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+ SearchValidationResult::kOk);
+ }
+ {
+ // Index vector
+ auto fake =
+ FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3, 4, 5});
+ EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+ SearchValidationResult::kOk);
+ }
+ {
+ // BitVector
+ auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+ EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+ SearchValidationResult::kOk);
+ }
+ {
+ // Range
+ auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+ EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+ SearchValidationResult::kOk);
+ }
+}
+
+TEST(FakeStorage, SingleSearch) {
+ {
+ // All passes
+ auto fake = FakeStorageChain::SearchAll(10);
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 5u),
+ SingleSearchResult::kMatch);
+ }
+ {
+ // None passes
+ auto fake = FakeStorageChain::SearchNone(10);
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 5u),
+ SingleSearchResult::kNoMatch);
+ }
+ {
+ // Index vector
+ auto fake =
+ FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3, 4, 5});
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0u),
+ SingleSearchResult::kNoMatch);
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+ SingleSearchResult::kMatch);
+ }
+ {
+ // BitVector
+ auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0),
+ SingleSearchResult::kNoMatch);
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+ SingleSearchResult::kMatch);
+ }
+ {
+ // Range
+ auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0),
+ SingleSearchResult::kNoMatch);
+ EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+ SingleSearchResult::kMatch);
+ }
+}
+
+TEST(FakeStorage, IndexSearchValidated) {
+ {
+ // All passes
+ Indices indices = Indices::CreateWithIndexPayloadForTesting(
+ {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ auto fake = FakeStorageChain::SearchAll(5);
+ fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 1, 2));
+ }
+ {
+ // None passes
+ Indices indices = Indices::CreateWithIndexPayloadForTesting(
+ {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ auto fake = FakeStorageChain::SearchNone(5);
+ fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_TRUE(utils::ExtractPayloadForTesting(indices).empty());
+ }
+ {
+ // BitVector
+ Indices indices = Indices::CreateWithIndexPayloadForTesting(
+ {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+ fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+ }
+ {
+ // Index vector
+ Indices indices = Indices::CreateWithIndexPayloadForTesting(
+ {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ auto fake =
+ FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3});
+ fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+ }
+ {
+ // Range
+ Indices indices = Indices::CreateWithIndexPayloadForTesting(
+ {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+ fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+ }
+}
+
+TEST(FakeStorage, OrderedIndexSearchValidated) {
+ std::vector<uint32_t> table_idx{4, 3, 2, 1};
+ OrderedIndices indices{table_idx.data(), uint32_t(table_idx.size()),
+ Indices::State::kNonmonotonic};
+ {
+ // All passes
+ auto fake = FakeStorageChain::SearchAll(5);
+ Range ret =
+ fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_EQ(ret, Range(0, 4));
+ }
+ {
+ // None passes
+ auto fake = FakeStorageChain::SearchNone(5);
+ Range ret =
+ fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_EQ(ret, Range(0, 0));
+ }
+ {
+ // BitVector
+ auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 0, 1, 1, 1});
+ Range ret =
+ fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_EQ(ret, Range(0, 3));
+ }
+ {
+ // Index vector
+ auto fake =
+ FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3});
+ Range ret =
+ fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_EQ(ret, Range(1, 4));
+ }
+ {
+ // Range
+ auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+ Range ret =
+ fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+ EXPECT_EQ(ret, Range(1, 4));
+ }
+}
+
+} // namespace
+} // namespace column
+} // namespace perfetto::trace_processor
diff --git a/src/trace_processor/db/column/range_overlay_unittest.cc b/src/trace_processor/db/column/range_overlay_unittest.cc
index a965be3..240e998 100644
--- a/src/trace_processor/db/column/range_overlay_unittest.cc
+++ b/src/trace_processor/db/column/range_overlay_unittest.cc
@@ -95,14 +95,17 @@
TEST(RangeOverlay, IndexSearch) {
auto fake =
FakeStorageChain::SearchSubset(8, BitVector({0, 1, 0, 1, 0, 1, 0, 0}));
+
+ // {true, false}
Range range(3, 5);
RangeOverlay storage(&range);
auto chain = storage.MakeChain(std::move(fake));
+ // {true, false, true}
Indices indices = Indices::CreateWithIndexPayloadForTesting(
- {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+ {0, 1, 0}, Indices::State::kNonmonotonic);
chain->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
- ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(1u));
+ ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
}
TEST(RangeOverlay, StableSort) {
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index d71b0dc..c8a2b1a 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -768,7 +768,6 @@
# The .proto filegroup will be added to `tool_files` of rdeps so that the
# genrules can be sandboxed.
- tool_files = set()
for proto_dep in target.proto_deps().union(target.transitive_proto_deps()):
tool_files.add(":" + label_to_module_name(proto_dep.name))
diff --git a/tools/gen_tp_table_headers.py b/tools/gen_tp_table_headers.py
index f15245e..91e4cdb 100755
--- a/tools/gen_tp_table_headers.py
+++ b/tools/gen_tp_table_headers.py
@@ -72,7 +72,8 @@
]
headers: Dict[str, Header] = {}
for table in parse_tables_from_modules(modules):
- input_path = os.path.relpath(table.table.python_module, ROOT_DIR)
+ raw_path = table.table.python_module
+ input_path = raw_path[raw_path.rfind('/src') + 1:]
header = headers.get(input_path, Header([]))
header.tables.append(table)
headers[input_path] = header
diff --git a/tools/gn_utils.py b/tools/gn_utils.py
index d0417d7..904760e 100644
--- a/tools/gn_utils.py
+++ b/tools/gn_utils.py
@@ -529,9 +529,8 @@
return metadata.get('exports', [])
def get_proto_paths(self, proto_desc):
- # import_dirs in metadata will be available for source_set targets.
metadata = proto_desc.get('metadata', {})
- return metadata.get('import_dirs', [])
+ return metadata.get('proto_import_dirs', [])
def get_proto_target_type(self, target: Target
) -> Tuple[Optional[str], Optional[Dict]]:
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index 0817ebf..acf2ee8 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -24,7 +24,7 @@
id: 'showHeapGraphDominatorTree',
name: 'Show heap graph dominator tree',
description: 'Show dominated size and objects tabs in Java heap graph view.',
- defaultValue: false,
+ defaultValue: true,
});
export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index 564b006..a9f8395 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -49,7 +49,6 @@
'perfetto.Frames',
'perfetto.FtraceRaw',
'perfetto.HeapProfile',
- 'perfetto.NullTrack',
'perfetto.PerfSamplesProfile',
'perfetto.PivotTable',
'perfetto.ProcessSummary',
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index caf2ef6..91b0939 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -30,6 +30,7 @@
import {NewTrackArgs} from './track';
import {CacheKey} from '../core/timeline_cache';
import {featureFlags} from '../core/feature_flags';
+import {uuidv4Sql} from '../base/uuid';
export const COUNTER_DEBUG_MENU_ITEMS = featureFlags.register({
id: 'counterDebugMenuItems',
@@ -195,6 +196,7 @@
export abstract class BaseCounterTrack implements Track {
protected engine: EngineProxy;
protected trackKey: string;
+ protected trackUuid = uuidv4Sql();
// This is the over-skirted cached bounds:
private countersKey: CacheKey = CacheKey.zero();
@@ -256,7 +258,7 @@
constructor(args: BaseCounterTrackArgs) {
this.engine = args.engine;
- this.trackKey = args.trackKey.replaceAll('-', '_');
+ this.trackKey = args.trackKey;
this.defaultOptions = args.options ?? {};
}
@@ -449,6 +451,33 @@
async onCreate(): Promise<void> {
this.initState = await this.onInit();
+
+ const displayValueQuery = await this.engine.query(`
+ create virtual table ${this.getTableName()}
+ using __intrinsic_counter_mipmap((
+ SELECT
+ ts,
+ ${this.getValueExpression()} as value
+ FROM (${this.getSqlSource()})
+ ));
+
+ select
+ min_value as minDisplayValue,
+ max_value as maxDisplayValue
+ from ${this.getTableName()}(
+ trace_start(), trace_end(), trace_dur()
+ );
+ `);
+
+ const {minDisplayValue, maxDisplayValue} = displayValueQuery.firstRow({
+ minDisplayValue: NUM,
+ maxDisplayValue: NUM,
+ });
+
+ this.limits = {
+ minDisplayValue,
+ maxDisplayValue,
+ };
}
async onUpdate(): Promise<void> {
@@ -691,11 +720,14 @@
this.hover = undefined;
}
- onDestroy(): void {
+ async onDestroy(): Promise<void> {
if (this.initState) {
this.initState.dispose();
this.initState = undefined;
}
+ if (this.engine.isAlive) {
+ await this.engine.query(`drop table if exists ${this.getTableName()}`);
+ }
}
// Compute the range of values to display and range label.
@@ -811,37 +843,11 @@
}
}
+ private getTableName(): string {
+ return `counter_${this.trackUuid}`;
+ }
+
private async maybeRequestData(rawCountersKey: CacheKey) {
- let limits = this.limits;
- if (limits === undefined) {
- const displayValueQuery = await this.engine.query(`
- drop table if exists counter_${this.trackKey};
-
- create virtual table counter_${this.trackKey}
- using __intrinsic_counter_mipmap((
- SELECT
- ts,
- ${this.getValueExpression()} as value
- FROM (${this.getSqlSource()})
- ));
-
- select
- min_value as minDisplayValue,
- max_value as maxDisplayValue
- from counter_${this.trackKey}(
- trace_start(), trace_end(), trace_dur()
- );
- `);
- const {minDisplayValue, maxDisplayValue} = displayValueQuery.firstRow({
- minDisplayValue: NUM,
- maxDisplayValue: NUM,
- });
- limits = this.limits = {
- minDisplayValue,
- maxDisplayValue,
- };
- }
-
if (rawCountersKey.isCoveredBy(this.countersKey)) {
return; // We have the data already, no need to re-query.
}
@@ -859,7 +865,7 @@
max_value as maxDisplayValue,
last_ts as ts,
last_value as lastDisplayValue
- FROM counter_${this.trackKey}(
+ FROM ${this.getTableName()}(
${countersKey.start},
${countersKey.end},
${countersKey.bucketSize}
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index ca4fc41..e4b03a2 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -41,6 +41,7 @@
import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
import {NewTrackArgs} from './track';
import {BUCKETS_PER_PIXEL, CacheKey} from '../core/timeline_cache';
+import {uuidv4Sql} from '../base/uuid';
// The common class that underpins all tracks drawing slices.
@@ -174,6 +175,7 @@
protected sliceLayout: SliceLayout = {...DEFAULT_SLICE_LAYOUT};
protected engine: EngineProxy;
protected trackKey: string;
+ protected trackUuid = uuidv4Sql();
// This is the over-skirted cached bounds:
private slicesKey: CacheKey = CacheKey.zero();
@@ -307,6 +309,10 @@
return `${size}px Roboto Condensed`;
}
+ private getTableName(): string {
+ return `slice_${this.trackUuid}`;
+ }
+
async onCreate(): Promise<void> {
this.initState = await this.onInit();
@@ -357,7 +363,7 @@
this.incomplete = incomplete;
await this.engine.query(`
- create virtual table slice_${this.trackKey}
+ create virtual table ${this.getTableName()}
using __intrinsic_slice_mipmap((
select id, ts, dur, ${this.depthColumn()}
from (${this.getSqlSource()})
@@ -654,7 +660,9 @@
this.initState.dispose();
this.initState = undefined;
}
- await this.engine.execute(`drop table slice_${this.trackKey}`);
+ if (this.engine.isAlive) {
+ await this.engine.execute(`drop table ${this.getTableName()}`);
+ }
}
// This method figures out if the visible window is outside the bounds of
@@ -681,7 +689,7 @@
s.id,
z.depth
${extraCols ? ',' + extraCols : ''}
- FROM slice_${this.trackKey}(
+ FROM ${this.getTableName()}(
${slicesKey.start},
${slicesKey.end},
${slicesKey.bucketSize}
diff --git a/ui/src/frontend/simple_counter_track.ts b/ui/src/frontend/simple_counter_track.ts
index 084c14f..361480b 100644
--- a/ui/src/frontend/simple_counter_track.ts
+++ b/ui/src/frontend/simple_counter_track.ts
@@ -17,6 +17,7 @@
import {BaseCounterTrack, CounterOptions} from './base_counter_track';
import {CounterColumns, SqlDataSource} from './debug_tracks';
import {Disposable, DisposableCallback} from '../base/disposable';
+import {uuidv4Sql} from '../base/uuid';
export type SimpleCounterTrackConfig = {
data: SqlDataSource;
@@ -39,7 +40,7 @@
options: config.options,
});
this.config = config;
- this.sqlTableName = `__simple_counter_${this.trackKey}`;
+ this.sqlTableName = `__simple_counter_${uuidv4Sql()}`;
}
async onInit(): Promise<Disposable> {
@@ -74,7 +75,7 @@
private async dropTrackTable(): Promise<void> {
if (this.engine.isAlive) {
- this.engine.query(`drop table if exists ${this.sqlTableName}`);
+ await this.engine.query(`drop table if exists ${this.sqlTableName}`);
}
}
}
diff --git a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
index 2c13c76..166a007 100644
--- a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
@@ -64,15 +64,17 @@
sf_callback_missed_frames,
hwui_callback_missed_frames,
cuj_layer.layer_name,
- cuj.ts,
- cuj.dur,
+ /* Boundaries table doesn't contain ts and dur when a CUJ didn't complete successfully.
+ In that case we still want to show that it was canceled, so let's take the slice timestamps. */
+ CASE WHEN boundaries.ts IS NOT NULL THEN boundaries.ts ELSE cuj.ts END AS ts,
+ CASE WHEN boundaries.dur IS NOT NULL THEN boundaries.dur ELSE cuj.dur END AS dur,
cuj.track_id,
cuj.slice_id
FROM slice AS cuj
- JOIN process_track AS pt
- ON cuj.track_id = pt.id
+ JOIN process_track AS pt ON cuj.track_id = pt.id
LEFT JOIN android_jank_cuj jc
ON pt.upid = jc.upid AND cuj.name = jc.cuj_slice_name AND cuj.ts = jc.ts
+ LEFT JOIN android_jank_cuj_main_thread_cuj_boundary boundaries using (cuj_id)
LEFT JOIN android_jank_cuj_layer_name cuj_layer USING (cuj_id)
LEFT JOIN android_jank_cuj_counter_metrics USING (cuj_id)
WHERE cuj.name GLOB 'J<*>'
@@ -140,7 +142,7 @@
},
'Jank CUJs',
{ts: 'ts', dur: 'dur', name: 'name'},
- [],
+ JANK_COLUMNS,
);
});
},
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index 92e30f9..b5fa5fa 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -31,6 +31,7 @@
Track,
} from '../../public';
import {LONG, NUM, NUM_NULL} from '../../trace_processor/query_result';
+import {uuidv4Sql} from '../../base/uuid';
export const CPU_FREQ_TRACK_KIND = 'CpuFreqTrack';
@@ -63,30 +64,29 @@
private engine: EngineProxy;
private config: Config;
- private trackKey: string;
+ private trackUuid = uuidv4Sql();
- constructor(config: Config, engine: EngineProxy, trackKey: string) {
+ constructor(config: Config, engine: EngineProxy) {
this.config = config;
this.engine = engine;
- this.trackKey = trackKey.split('-').join('_');
}
async onCreate() {
if (this.config.idleTrackId === undefined) {
await this.engine.execute(`
- create view raw_freq_idle_${this.trackKey} as
+ create view raw_freq_idle_${this.trackUuid} as
select ts, dur, value as freqValue, -1 as idleValue
from experimental_counter_dur c
where track_id = ${this.config.freqTrackId}
`);
} else {
await this.engine.execute(`
- create view raw_freq_${this.trackKey} as
+ create view raw_freq_${this.trackUuid} as
select ts, dur, value as freqValue
from experimental_counter_dur c
where track_id = ${this.config.freqTrackId};
- create view raw_idle_${this.trackKey} as
+ create view raw_idle_${this.trackUuid} as
select
ts,
dur,
@@ -94,22 +94,22 @@
from experimental_counter_dur c
where track_id = ${this.config.idleTrackId};
- create virtual table raw_freq_idle_${this.trackKey}
- using span_join(raw_freq_${this.trackKey}, raw_idle_${this.trackKey});
+ create virtual table raw_freq_idle_${this.trackUuid}
+ using span_join(raw_freq_${this.trackUuid}, raw_idle_${this.trackUuid});
`);
}
await this.engine.execute(`
- create virtual table cpu_freq_${this.trackKey}
+ create virtual table cpu_freq_${this.trackUuid}
using __intrinsic_counter_mipmap((
select ts, freqValue as value
- from raw_freq_idle_${this.trackKey}
+ from raw_freq_idle_${this.trackUuid}
));
- create virtual table cpu_idle_${this.trackKey}
+ create virtual table cpu_idle_${this.trackUuid}
using __intrinsic_counter_mipmap((
select ts, idleValue as value
- from raw_freq_idle_${this.trackKey}
+ from raw_freq_idle_${this.trackUuid}
));
`);
}
@@ -120,11 +120,11 @@
async onDestroy(): Promise<void> {
if (this.engine.isAlive) {
- await this.engine.query(`drop table cpu_freq_${this.trackKey}`);
- await this.engine.query(`drop table cpu_idle_${this.trackKey}`);
- await this.engine.query(`drop table raw_freq_idle_${this.trackKey}`);
- await this.engine.query(`drop view if exists raw_freq_${this.trackKey}`);
- await this.engine.query(`drop view if exists raw_idle_${this.trackKey}`);
+ await this.engine.query(`drop table cpu_freq_${this.trackUuid}`);
+ await this.engine.query(`drop table cpu_idle_${this.trackUuid}`);
+ await this.engine.query(`drop table raw_freq_idle_${this.trackUuid}`);
+ await this.engine.query(`drop view if exists raw_freq_${this.trackUuid}`);
+ await this.engine.query(`drop view if exists raw_idle_${this.trackUuid}`);
}
}
@@ -143,7 +143,7 @@
max_value as maxFreq,
last_ts as ts,
last_value as lastFreq
- FROM cpu_freq_${this.trackKey}(
+ FROM cpu_freq_${this.trackUuid}(
${start},
${end},
${resolution}
@@ -151,7 +151,7 @@
`);
const idleResult = await this.engine.query(`
SELECT last_value as lastIdle
- FROM cpu_idle_${this.trackKey}(
+ FROM cpu_idle_${this.trackUuid}(
${start},
${end},
${resolution}
@@ -450,7 +450,7 @@
displayName: `Cpu ${cpu} Frequency`,
kind: CPU_FREQ_TRACK_KIND,
cpu,
- trackFactory: (c) => new CpuFreqTrack(config, ctx.engine, c.trackKey),
+ trackFactory: () => new CpuFreqTrack(config, ctx.engine),
});
}
}
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 552092c..5c042dc 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -40,6 +40,7 @@
Track,
} from '../../public';
import {LONG, NUM, STR_NULL} from '../../trace_processor/query_result';
+import {uuidv4Sql} from '../../base/uuid';
export const CPU_SLICE_TRACK_KIND = 'CpuSliceTrack';
@@ -69,6 +70,7 @@
private engine: EngineProxy;
private cpu: number;
private trackKey: string;
+ private trackUuid = uuidv4Sql();
constructor(engine: EngineProxy, trackKey: string, cpu: number) {
this.engine = engine;
@@ -78,7 +80,7 @@
async onCreate() {
await this.engine.query(`
- create virtual table cpu_slice_${this.trackKey}
+ create virtual table cpu_slice_${this.trackUuid}
using __intrinsic_slice_mipmap((
select
id,
@@ -116,7 +118,7 @@
s.id,
s.dur = -1 as isIncomplete,
ifnull(s.priority < 100, 0) as isRealtime
- from cpu_slice_${this.trackKey}(${start}, ${end}, ${resolution}) z
+ from cpu_slice_${this.trackUuid}(${start}, ${end}, ${resolution}) z
cross join sched s using (id)
`);
@@ -165,7 +167,7 @@
async onDestroy() {
if (this.engine.isAlive) {
await this.engine.query(
- `drop table if exists cpu_slice_${this.trackKey}`,
+ `drop table if exists cpu_slice_${this.trackUuid}`,
);
}
this.fetcher.dispose();
diff --git a/ui/src/tracks/process_summary/index.ts b/ui/src/tracks/process_summary/index.ts
index 37ac68f..ea6845f 100644
--- a/ui/src/tracks/process_summary/index.ts
+++ b/ui/src/tracks/process_summary/index.ts
@@ -103,8 +103,8 @@
tags: {
isDebuggable,
},
- trackFactory: ({trackKey}) => {
- return new ProcessSchedulingTrack(ctx.engine, trackKey, config);
+ trackFactory: () => {
+ return new ProcessSchedulingTrack(ctx.engine, config);
},
});
} else {
diff --git a/ui/src/tracks/process_summary/process_scheduling_track.ts b/ui/src/tracks/process_summary/process_scheduling_track.ts
index 5905de2..9725c29 100644
--- a/ui/src/tracks/process_summary/process_scheduling_track.ts
+++ b/ui/src/tracks/process_summary/process_scheduling_track.ts
@@ -27,6 +27,7 @@
import {PanelSize} from '../../frontend/panel';
import {EngineProxy, Track} from '../../public';
import {LONG, NUM, QueryResult} from '../../trace_processor/query_result';
+import {uuidv4Sql} from '../../base/uuid';
export const PROCESS_SCHEDULING_TRACK_KIND = 'ProcessSchedulingTrack';
@@ -57,13 +58,12 @@
private fetcher = new TimelineFetcher(this.onBoundsChange.bind(this));
private maxCpu = 0;
private engine: EngineProxy;
- private trackKey: string;
+ private trackUuid = uuidv4Sql();
private config: Config;
- constructor(engine: EngineProxy, trackKey: string, config: Config) {
+ constructor(engine: EngineProxy, config: Config) {
this.engine = engine;
this.config = config;
- this.trackKey = trackKey.split('-').join('_');
}
async onCreate(): Promise<void> {
@@ -75,7 +75,7 @@
if (this.config.upid !== null) {
await this.engine.query(`
- create virtual table process_scheduling_${this.trackKey}
+ create virtual table process_scheduling_${this.trackUuid}
using __intrinsic_slice_mipmap((
select
id,
@@ -95,7 +95,7 @@
} else {
assertExists(this.config.utid);
await this.engine.query(`
- create virtual table process_scheduling_${this.trackKey}
+ create virtual table process_scheduling_${this.trackUuid}
using __intrinsic_slice_mipmap((
select
id,
@@ -121,7 +121,7 @@
this.fetcher.dispose();
if (this.engine.isAlive) {
await this.engine.query(`
- drop table process_scheduling_${this.trackKey}
+ drop table process_scheduling_${this.trackUuid}
`);
}
}
@@ -182,7 +182,7 @@
s.id,
z.depth as cpu,
utid
- from process_scheduling_${this.trackKey}(
+ from process_scheduling_${this.trackUuid}(
${start}, ${end}, ${bucketSize}
) z
cross join sched s using (id)