diff --git a/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts b/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts index fe7d4d64a82..baa9ef57ba4 100644 --- a/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts +++ b/ui/src/plugins/dev.perfetto.GpuByProcess/index.ts @@ -418,6 +418,10 @@ export default class implements PerfettoPlugin { trace: ctx, uri, dataset: t.dataset, + // Root table for SQL-event resolution: without it these tracks + // are invisible to selectSqlEvent(), so ts/dur deep links into + // GPU streams zoom but never select the slice. + rootTableName: 'slice', detailsPanel: () => new ThreadSliceDetailsPanel(ctx), }), }); diff --git a/ui/src/trace_processor/dataset.ts b/ui/src/trace_processor/dataset.ts index 9cc44715e01..8984838615c 100644 --- a/ui/src/trace_processor/dataset.ts +++ b/ui/src/trace_processor/dataset.ts @@ -491,7 +491,14 @@ function getJoinSignature(dataset: SourceDataset): string { if (!dataset.joins) { return ''; } - return Object.keys(dataset.joins).sort().join(','); + // The signature must identify the join definition, not just its alias: + // two datasets whose 'depth' joins point at different tables must not be + // merged, or the merged query joins every row through one table and + // silently drops the rows only present in the others. + return Object.entries(dataset.joins) + .map(([id, join]) => `${id}:${join.from}:${join.unique ?? false}`) + .sort() + .join(','); } function mergeFilters(filters: InFilter[]): InFilter | undefined { diff --git a/ui/src/trace_processor/dataset_unittest.ts b/ui/src/trace_processor/dataset_unittest.ts index 9d034278a49..9293f3cd172 100644 --- a/ui/src/trace_processor/dataset_unittest.ts +++ b/ui/src/trace_processor/dataset_unittest.ts @@ -396,6 +396,39 @@ FROM (slice) WHERE id IN (123, 456)`); }); +test('union dataset does not merge datasets whose joins differ', () => { + // Mirrors the multi-track slice tracks created for grouped/split tracks: + // each track's dataset joins its own layout-depth table under the same + // 'depth' alias. Merging them would join every row through one table and + // silently drop the rows only present in the others. + const makeDepthDataset = (table: string, trackIds: number[]) => + new SourceDataset({ + src: 'slice', + schema: {id: NUM, depth: NUM}, + select: { + id: 'id', + depth: {join: 'depth', expr: 'depth.depth'}, + }, + joins: { + depth: {from: `${table} USING (id)`, unique: true}, + }, + filter: { + col: 'track_id', + in: trackIds, + }, + }); + + const dataset = UnionDataset.create([ + makeDepthDataset('__depth_1', [1, 2]), + makeDepthDataset('__depth_2', [3, 4]), + ]); + + const query = dataset.query(); + expect(query).toContain('__depth_1'); + expect(query).toContain('__depth_2'); + expect(query).toContain('UNION ALL'); +}); + test('union dataset batches large numbers of unions', () => { const datasets = []; for (let i = 0; i < 800; i++) {