Merge "Add command to pin blocking calls in Android CUJs" 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/test/data/chrome_input_with_frame_view.pftrace.sha256 b/test/data/chrome_input_with_frame_view.pftrace.sha256
index ea5a606..d0943a8 100644
--- a/test/data/chrome_input_with_frame_view.pftrace.sha256
+++ b/test/data/chrome_input_with_frame_view.pftrace.sha256
@@ -1 +1 @@
-1e4e1b7098c3c1b900d31fa6d6791e7b022e85ecebbb560123ce7139b3f82231
\ No newline at end of file
+a93548822e481508c728ccc5da3ad34afcd0aec02ca7a7a4dad84ff340ee5975
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
index 70dec5c..fa52426 100644
--- a/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/metrics/chrome/tests_scroll_jank.py
@@ -429,32 +429,18 @@
query=Metric('chrome_scroll_jank_v3'),
out=TextProto(r"""
[perfetto.protos.chrome_scroll_jank_v3] {
- trace_num_frames: 291
- trace_num_janky_frames: 3
- trace_scroll_jank_percentage: 1.0309278350515463
- vsync_interval_ms: 16.368
+ trace_num_frames: 354
+ trace_num_janky_frames: 1
+ trace_scroll_jank_percentage: 0.2824858757062147
+ vsync_interval_ms: 10.483
scrolls {
- num_frames: 105
- num_janky_frames: 2
- scroll_jank_percentage: 1.9047619047619047
- max_delay_since_last_frame: 6.126221896383187
- scroll_jank_causes {
- cause: "RendererCompositorQueueingDelay"
- delay_since_last_frame: 2.044354838709678
- }
- scroll_jank_causes {
- cause: "RendererCompositorFinishedToBeginImplFrame"
- delay_since_last_frame: 6.126221896383187
- }
- }
- scrolls {
- num_frames: 84
+ num_frames: 122
num_janky_frames: 1
- scroll_jank_percentage: 1.1904761904761905
- max_delay_since_last_frame: 2.040811339198436
+ scroll_jank_percentage: 0.819672131147541
+ max_delay_since_last_frame: 2.13021081751407
scroll_jank_causes {
cause: "RendererCompositorQueueingDelay"
- delay_since_last_frame: 2.040811339198436
+ delay_since_last_frame: 2.13021081751407
}
}
}
@@ -469,8 +455,8 @@
INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3;
SELECT
- _HAS_DESCENDANT_SLICE_WITH_NAME(
- (SELECT id from slice where dur = 46046000),
+ HAS_DESCENDANT_SLICE_WITH_NAME(
+ (SELECT id from slice where dur = 60156000),
'SwapEndToPresentationCompositorFrame') AS has_descendant;
""",
out=Csv("""
@@ -487,8 +473,8 @@
INCLUDE PERFETTO MODULE chrome.scroll_jank.scroll_jank_v3;
SELECT
- _HAS_DESCENDANT_SLICE_WITH_NAME(
- (SELECT id from slice where dur = 11666000),
+ HAS_DESCENDANT_SLICE_WITH_NAME(
+ (SELECT id from slice where dur = 77247000),
'SwapEndToPresentationCompositorFrame') AS has_descendant;
""",
out=Csv("""
@@ -506,7 +492,7 @@
SELECT
_DESCENDANT_SLICE_END(
- (SELECT id from slice where dur = 11666000),
+ (SELECT id from slice where dur = 77247000),
'SwapEndToPresentationCompositorFrame') AS end_ts;
""",
out=Csv("""
@@ -524,10 +510,10 @@
SELECT
_DESCENDANT_SLICE_END(
- (SELECT id from slice where dur = 46046000),
+ (SELECT id from slice where dur = 60156000),
'SwapEndToPresentationCompositorFrame') AS end_ts;
""",
out=Csv("""
"end_ts"
- 174797566610797
+ 1035869424631926
"""))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/stdlib/slices/tests.py b/test/trace_processor/diff_tests/stdlib/slices/tests.py
index 053dabf..747ffb2 100644
--- a/test/trace_processor/diff_tests/stdlib/slices/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/slices/tests.py
@@ -76,21 +76,23 @@
SELECT e.name, e.ts, e.dur, e.depth
FROM _slice_flattened e
- JOIN thread_track ON e.track_id = thread_track.id
- JOIN thread USING(utid)
- WHERE thread.tid = 30944;
+ JOIN thread_track ON e.track_id = thread_track.id
+ JOIN thread USING(utid)
+ WHERE thread.tid = 30196
+ LIMIT 10;
""",
out=Csv("""
"name","ts","dur","depth"
- "ThreadControllerImpl::RunTask",174793737042797,3937000,0
- "ThreadControllerImpl::RunTask",174793741016797,5930000,0
- "ThreadControllerImpl::RunTask",174793747000797,47000,0
- "Receive mojo message",174793747047797,136000,1
- "ThreadControllerImpl::RunTask",174793747183797,17000,0
- "Looper.dispatch: android.os.Handler(Kx3@57873a8)",174793747546797,119000,0
- "ThreadControllerImpl::RunTask",174796099970797,186000,0
- "Looper.dispatch: jy3(null)",174800056530797,1368000,0
- "ThreadControllerImpl::RunTask",174800107962797,132000,0
+ "EventForwarder::OnTouchEvent",1035865509936036,211000,0
+ "EventForwarder::OnTouchEvent",1035865510234036,48000,0
+ "EventForwarder::OnTouchEvent",1035865510673036,10000,0
+ "GestureProvider::OnTouchEvent",1035865510147036,87000,1
+ "RenderWidgetHostImpl::ForwardTouchEvent",1035865510282036,41000,1
+ "RenderWidgetHostImpl::ForwardTouchEvent",1035865510331036,16000,1
+ "RenderWidgetHostImpl::ForwardTouchEvent",1035865510670036,3000,1
+ "LatencyInfo.Flow",1035865510323036,8000,2
+ "PassthroughTouchEventQueue::QueueEvent",1035865510347036,30000,2
+ "PassthroughTouchEventQueue::QueueEvent",1035865510666036,4000,2
"""))
def test_thread_slice_cpu_time(self):
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/common/plugins.ts b/ui/src/common/plugins.ts
index ba60270..0750e2e 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -299,11 +299,20 @@
},
get tracks(): TrackRef[] {
- return Object.values(globals.state.tracks).map((trackState) => {
+ const tracks = Object.values(globals.state.tracks);
+ const pinnedTracks = globals.state.pinnedTracks;
+ const groups = globals.state.trackGroups;
+ return tracks.map((trackState) => {
+ const group = trackState.trackGroup
+ ? groups[trackState.trackGroup]
+ : undefined;
return {
displayName: trackState.name,
uri: trackState.uri,
params: trackState.params,
+ key: trackState.key,
+ groupName: group?.name,
+ isPinned: pinnedTracks.includes(trackState.key),
};
});
},
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index 583e6bc..a9f8395 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -32,6 +32,7 @@
'dev.perfetto.BookmarkletApi',
'dev.perfetto.CoreCommands',
'dev.perfetto.LargeScreensPerf',
+ 'dev.perfetto.RestorePinnedTrack',
'perfetto.AndroidLog',
'perfetto.Annotation',
'perfetto.AsyncSlices',
@@ -48,7 +49,6 @@
'perfetto.Frames',
'perfetto.FtraceRaw',
'perfetto.HeapProfile',
- 'perfetto.NullTrack',
'perfetto.PerfSamplesProfile',
'perfetto.PivotTable',
'perfetto.ProcessSummary',
diff --git a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
index 7942e0a..aaef484 100644
--- a/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidCujs/index.ts
@@ -65,15 +65,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<*>'
@@ -177,7 +179,7 @@
},
'Jank CUJs',
{ts: 'ts', dur: 'dur', name: 'name'},
- [],
+ JANK_COLUMNS,
);
});
},
diff --git a/ui/src/plugins/dev.perfetto.RestorePinnedTracks/OWNERS b/ui/src/plugins/dev.perfetto.RestorePinnedTracks/OWNERS
new file mode 100644
index 0000000..987684d
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RestorePinnedTracks/OWNERS
@@ -0,0 +1,2 @@
+nicomazz@google.com
+nickchameyev@google.com
diff --git a/ui/src/plugins/dev.perfetto.RestorePinnedTracks/index.ts b/ui/src/plugins/dev.perfetto.RestorePinnedTracks/index.ts
new file mode 100644
index 0000000..81036db
--- /dev/null
+++ b/ui/src/plugins/dev.perfetto.RestorePinnedTracks/index.ts
@@ -0,0 +1,135 @@
+// 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.
+
+import {
+ Plugin,
+ PluginContext,
+ PluginContextTrace,
+ PluginDescriptor,
+ TrackRef,
+} from '../../public';
+
+const PLUGIN_ID = 'dev.perfetto.RestorePinnedTrack';
+const SAVED_TRACKS_KEY = `${PLUGIN_ID}#savedPerfettoTracks`;
+
+/**
+ * Fuzzy save and restore of pinned tracks.
+ *
+ * Tries to persist pinned tracks. Uses full string matching between track name
+ * and group name. When no match is found for a saved track, it tries again
+ * without numbers.
+ */
+class RestorePinnedTrack implements Plugin {
+ onActivate(_ctx: PluginContext): void {}
+
+ private ctx!: PluginContextTrace;
+
+ async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+ this.ctx = ctx;
+ ctx.registerCommand({
+ id: `${PLUGIN_ID}#save`,
+ name: 'Save: Pinned tracks',
+ callback: () => {
+ this.saveTracks();
+ },
+ });
+ ctx.registerCommand({
+ id: `${PLUGIN_ID}#restore`,
+ name: 'Restore: Pinned tracks',
+ callback: () => {
+ this.restoreTracks();
+ },
+ });
+ }
+
+ private saveTracks() {
+ const pinnedTracks = this.ctx.timeline.tracks.filter(
+ (trackRef) => trackRef.isPinned,
+ );
+ const tracksToSave: SavedPinnedTrack[] = pinnedTracks.map((trackRef) => ({
+ groupName: trackRef.groupName,
+ trackName: trackRef.displayName,
+ }));
+ window.localStorage.setItem(SAVED_TRACKS_KEY, JSON.stringify(tracksToSave));
+ }
+
+ private restoreTracks() {
+ const savedTracks = window.localStorage.getItem(SAVED_TRACKS_KEY);
+ if (!savedTracks) {
+ alert('No saved tracks. Use the Save command first');
+ return;
+ }
+ const tracksToRestore: SavedPinnedTrack[] = JSON.parse(savedTracks);
+ const tracks: TrackRef[] = this.ctx.timeline.tracks;
+ tracksToRestore.forEach((trackToRestore) => {
+ // Check for an exact match
+ const exactMatch = tracks.find((track) => {
+ return (
+ track.key &&
+ trackToRestore.trackName === track.displayName &&
+ trackToRestore.groupName === track.groupName
+ );
+ });
+
+ if (exactMatch) {
+ this.ctx.timeline.pinTrack(exactMatch.key!);
+ } else {
+ // We attempt a match after removing numbers to potentially pin a
+ // "similar" track from a different trace. Removing numbers allows
+ // flexibility; for instance, with multiple 'sysui' processes (e.g.
+ // track group name: "com.android.systemui 123") without this approach,
+ // any could be mistakenly pinned. The goal is to restore specific
+ // tracks within the same trace, ensuring that a previously pinned track
+ // is pinned again.
+ // If the specific process with that PID is unavailable, pinning any
+ // other process matching the package name is attempted.
+ const fuzzyMatch = tracks.find((track) => {
+ return (
+ track.key &&
+ this.removeNumbers(trackToRestore.trackName) ===
+ this.removeNumbers(track.displayName) &&
+ this.removeNumbers(trackToRestore.groupName) ===
+ this.removeNumbers(track.groupName)
+ );
+ });
+
+ if (fuzzyMatch) {
+ this.ctx.timeline.pinTrack(fuzzyMatch.key!);
+ } else {
+ console.warn(
+ '[RestorePinnedTracks] No track found that matches',
+ trackToRestore,
+ );
+ }
+ }
+ });
+ }
+
+ private removeNumbers(inputString?: string): string | undefined {
+ return inputString?.replace(/\d+/g, '');
+ }
+}
+
+interface SavedPinnedTrack {
+ // Optional: group name for the track. Usually matches with process name.
+ groupName?: string;
+
+ // Track name to restore.
+ trackName: string;
+}
+
+export const plugin: PluginDescriptor = {
+ pluginId: PLUGIN_ID,
+ plugin: RestorePinnedTrack,
+};
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index ece6ea0..f3e4338 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -482,6 +482,12 @@
// Optional: Add tracks to a group with this name.
groupName?: string;
+
+ // Optional: Track key
+ key?: string;
+
+ // Optional: Whether the track is pinned
+ isPinned?: boolean;
}
// A predicate for selecting a subset of tracks.