From 92b7f3f850ce63f9edbd0118901b4eb97de23bfe Mon Sep 17 00:00:00 2001 From: Lalit Maganti Date: Mon, 23 Mar 2026 22:44:08 +0000 Subject: [PATCH 1/3] tp: fix descendant_slice missing boundary instant children descendant_slice used strict Lt for the upper timestamp bound, which excluded instant children at exactly ts + dur. These children have a valid parent_id link (set during ingestion while the parent was still open) but were missed by the geometric filter. Switch to Le and verify candidates at the end boundary with a parent chain walk, matching the existing start-boundary logic. Add a regression test reproducing the real-world sequence: Begin(parent) -> Scoped(instant@boundary) -> End(parent). --- .../intrinsics/table_functions/descendant.cc | 35 ++++--- .../diff_tests/stdlib/dynamic_tables/tests.py | 99 +++++++++++++++++++ 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.cc b/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.cc index 581cbb79553..ffe32c99e6b 100644 --- a/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.cc +++ b/src/trace_processor/perfetto_sql/intrinsics/table_functions/descendant.cc @@ -71,30 +71,29 @@ bool GetDescendantsInternal( cursor.SetFilterValueUnchecked(0, start_ref->ts()); cursor.SetFilterValueUnchecked(1, start_ref->track_id().value); cursor.SetFilterValueUnchecked(2, start_ref->depth()); - // Intervals are closed on the left and open on the right, so we use Lt for - // the upper bound. However, instants (dur=0) stack on top of each other, so - // for an instant at ts=T we need child_ts <= T, achieved by using T+1 as - // the Lt bound. See SliceTracker::TryCloseStack for the matching logic. - int64_t ts_upper_bound; - if (start_ref->dur() > 0) { - ts_upper_bound = start_ref->ts() + start_ref->dur(); - } else if (start_ref->dur() == 0) { - ts_upper_bound = start_ref->ts() + 1; + // Use Le for the upper bound so that slices starting exactly at the end + // boundary are included as candidates; we verify those with a parent chain + // walk below. This is necessary because an instant child emitted while its + // parent is still open gets parent_id set to that parent, even though its + // ts equals parent.ts + parent.dur (the open-right boundary). + int64_t ts_end; + if (start_ref->dur() >= 0) { + ts_end = start_ref->ts() + start_ref->dur(); } else { - ts_upper_bound = std::numeric_limits::max(); + ts_end = std::numeric_limits::max(); } - cursor.SetFilterValueUnchecked(3, ts_upper_bound); + cursor.SetFilterValueUnchecked(3, ts_end); - // The timestamp filter can produce false positives at the start boundary - // (candidate.ts == start.ts) where a child of a slice ending at start.ts - // shares the same timestamp. For such candidates, walk the parent chain to - // verify ancestry. For candidates strictly inside the interval (ts > - // start.ts), same-depth non-overlapping guarantees they are true descendants. + // The timestamp filter can produce false positives at the start and end + // boundaries where a candidate shares the same timestamp but belongs to a + // different subtree. For such candidates, walk the parent chain to verify + // ancestry. For candidates strictly inside the interval, same-depth + // non-overlapping guarantees they are true descendants. int64_t start_ts = start_ref->ts(); for (cursor.Execute(); !cursor.Eof(); cursor.Next()) { auto row_num = cursor.ToRowNumber(); auto ref = row_num.ToRowReference(slices); - if (ref.ts() == start_ts && + if ((ref.ts() == start_ts || ref.ts() == ts_end) && !IsAncestor(slices, ref, starting_id, start_ref->depth())) { continue; } @@ -206,7 +205,7 @@ tables::SliceTable::ConstCursor Descendant::MakeCursor( dataframe::FilterSpec{ tables::SliceTable::ColumnIndex::ts, 3, - dataframe::Lt{}, + dataframe::Le{}, std::nullopt, }, }); diff --git a/test/trace_processor/diff_tests/stdlib/dynamic_tables/tests.py b/test/trace_processor/diff_tests/stdlib/dynamic_tables/tests.py index efc6e87a985..591cdefac4a 100644 --- a/test/trace_processor/diff_tests/stdlib/dynamic_tables/tests.py +++ b/test/trace_processor/diff_tests/stdlib/dynamic_tables/tests.py @@ -262,6 +262,105 @@ def test_descendant_slice_boundary_instant(self): "descendant_name" """)) + # Regression test: descendant_slice should include an instant child at the + # exact end boundary of its parent when the parent_id chain confirms it. + # This mirrors the real-world sequence: Begin(parent) -> Scoped(instant at + # parent_end) -> End(parent) -> Begin(uncle). + def test_descendant_slice_boundary_instant_parent(self): + return DiffTestBlueprint( + trace=TextProto(r""" + packet { + trusted_packet_sequence_id: 1 + timestamp: 0 + incremental_state_cleared: true + track_descriptor { + uuid: 1 + parent_uuid: 10 + thread { + pid: 5 + tid: 1 + thread_name: "t1" + } + } + trace_packet_defaults { + track_event_defaults { + track_uuid: 1 + } + } + } + packet { + trusted_packet_sequence_id: 1 + timestamp: 0 + track_descriptor { + uuid: 10 + process { + pid: 5 + process_name: "p1" + } + } + } + # Parent slice [1000, 3000) + packet { + trusted_packet_sequence_id: 1 + timestamp: 1000 + track_event { + categories: "cat" + name: "parent" + type: 1 + } + } + # Child instant at ts=3000, emitted while parent is still open + packet { + trusted_packet_sequence_id: 1 + timestamp: 3000 + track_event { + categories: "cat" + name: "child_instant" + type: 3 + } + } + # Parent ends at 3000 + packet { + trusted_packet_sequence_id: 1 + timestamp: 3000 + track_event { + categories: "cat" + name: "parent" + type: 2 + } + } + # Uncle slice [3000, 5000) adjacent to parent + packet { + trusted_packet_sequence_id: 1 + timestamp: 3000 + track_event { + categories: "cat" + name: "uncle" + type: 1 + } + } + packet { + trusted_packet_sequence_id: 1 + timestamp: 5000 + track_event { + categories: "cat" + name: "uncle" + type: 2 + } + } + """), + query=""" + SELECT d.name AS descendant_name + FROM slice AS s + JOIN descendant_slice(s.id) AS d + WHERE s.name = 'parent' + ORDER BY d.ts, d.name; + """, + out=Csv(""" + "descendant_name" + "child_instant" + """)) + # Ancestor slice by stack table. def testancestor_slice_by_stack(self): return DiffTestBlueprint( From f9502f0c8ed53731a157a30168083fbaabc43a9e Mon Sep 17 00:00:00 2001 From: Lalit Maganti Date: Mon, 23 Mar 2026 23:34:11 +0000 Subject: [PATCH 2/3] FIx --- .../details-panel-link-to-scroll-timeline-v4.png.sha256 | 2 +- .../details-panel-link-to-scroll-timeline-v4.png.sha256 | 2 +- .../scroll-timeline-v4-details-panel-frame.png.sha256 | 2 +- .../scroll-timeline-v4-details-panel-stage.png.sha256 | 2 +- .../scroll-timeline-v4-track.png.sha256 | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 index 9395201bfc9..bfe6b7b9fb9 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 @@ -1 +1 @@ -b1a2941b2d324a2d30e10d724159faf619710be5e79594f98a3f119be064f645 \ No newline at end of file +5fcde60486cf84ce1430abbfb2229d456640a8fadeeb277fc21b0b51e5094b2a \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 index 1e800fc2852..cab6cf4ba42 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 @@ -1 +1 @@ -bf6d7f44e7ae8b346b85a49ac55f113b241279680c4be29c1529532fbe296d34 \ No newline at end of file +8307b9c488adf2bf9dd0ee6ff97dbf1ed330890724a2cf84a608d16aa102ca62 \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 index 6525ae6ba0a..9db880fe87d 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 @@ -1 +1 @@ -c7474e3f6ddf13fc9e77949050092216304a45340da2f39681cc8cfd5fabadd9 \ No newline at end of file +8cef83398f7c9cc7150db5f5fbb314fe89e9b21f4703bc2f8da1e68688d48cd1 \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 index 18a650c1e3e..7e06c9905ec 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 @@ -1 +1 @@ -7cecffd8b72e5734e793a063763e88f7d46f07143e72a953bb126913fbcc861f \ No newline at end of file +e254e8fc5c828a5015c855f79150350fa651e6e88ae0aea9a0e06b6419a2ef7b \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 index 8a4c179410c..6687323488e 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 @@ -1 +1 @@ -7bf9c9b29b41b9f60a4854b93eaafb072d328472a47a5612eb7b3a2864fdc03c \ No newline at end of file +e2218fe79abf6e846f78ef7cf0586b40921099821d3dd9762f4fd0225e133e60 \ No newline at end of file From ace52334e174e9debd9e5bae23a4a07f4ff5d578 Mon Sep 17 00:00:00 2001 From: Lalit Maganti Date: Mon, 30 Mar 2026 12:50:15 +0000 Subject: [PATCH 3/3] Fix --- .../details-panel-link-to-scroll-timeline-v4.png.sha256 | 6 +----- .../details-panel-link-to-scroll-timeline-v4.png.sha256 | 6 +----- .../scroll-timeline-v4-details-panel-frame.png.sha256 | 6 +----- .../scroll-timeline-v4-details-panel-stage.png.sha256 | 6 +----- .../scroll-timeline-v4-track.png.sha256 | 6 +----- 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 index 6a9298c6bdc..4efc38c1829 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/event-latency-track/details-panel-link-to-scroll-timeline-v4.png.sha256 @@ -1,5 +1 @@ -<<<<<<< HEAD -5fcde60486cf84ce1430abbfb2229d456640a8fadeeb277fc21b0b51e5094b2a -======= -959b666033d5c4a099ea1a9a2c6ad4af53be0d4b826b173c6d8856564bb84c04 ->>>>>>> origin/main +9981e347444c732db35b3b7e81846dc2c2da265df14078ad89e03827c2ee9e58 \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 index 5344126dc25..6a0a51a2427 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-track/details-panel-link-to-scroll-timeline-v4.png.sha256 @@ -1,5 +1 @@ -<<<<<<< HEAD -8307b9c488adf2bf9dd0ee6ff97dbf1ed330890724a2cf84a608d16aa102ca62 -======= -8ad324edbe196b5cfcfc5e090d44c3467022a9b24acf953c65552d966b7ec549 ->>>>>>> origin/main +2be0f5f7c6e163cccd36259c05cfa9f62a0b68eab9b2c3b8c0e8c2527151503b \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 index 3f10c2a0373..174e974eb41 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-frame.png.sha256 @@ -1,5 +1 @@ -<<<<<<< HEAD -8cef83398f7c9cc7150db5f5fbb314fe89e9b21f4703bc2f8da1e68688d48cd1 -======= -1b19c6136b36debfc8786fd791ac187bdc98408af108dbf6606150a05f32311e ->>>>>>> origin/main +c909bfc5dc1e91d0c22a44288918ed146a9b656ed1002e11ff9bd18219fbd241 \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 index 51883825148..7eab8342f65 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-details-panel-stage.png.sha256 @@ -1,5 +1 @@ -<<<<<<< HEAD -e254e8fc5c828a5015c855f79150350fa651e6e88ae0aea9a0e06b6419a2ef7b -======= -5b043f25cc330fc78e51e1e6fd17cdead37e73f8e40d6d1ef75df6ba2faa109f ->>>>>>> origin/main +cbe52708cc4865c41137c0bdfb1e0761db520d62aa7cf415b1fd4c978875f49e \ No newline at end of file diff --git a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 index a68388b82ae..8cccb7c75a9 100644 --- a/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 +++ b/test/data/ui-screenshots/chrome_scroll_jank_plugin.test.ts/scroll-timeline-v4-track/scroll-timeline-v4-track.png.sha256 @@ -1,5 +1 @@ -<<<<<<< HEAD -e2218fe79abf6e846f78ef7cf0586b40921099821d3dd9762f4fd0225e133e60 -======= -9316d3df707691eca92dbd0306ae2fa459f2f7dc7286ef32b9824cab841eb598 ->>>>>>> origin/main +52e8d68785d62d54799d903ea5e1d5322ae37bac32f4dcda14acb807da55b213 \ No newline at end of file