1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {Trace} from '../../public/trace'; 16import {PerfettoPlugin} from '../../public/plugin'; 17import {TrackNode} from '../../public/workspace'; 18import { 19 DatasetSliceTrack, 20 DatasetSliceTrackAttrs, 21 ROW_SCHEMA, 22} from '../../components/tracks/dataset_slice_track'; 23import {LONG, NUM, STR} from '../../trace_processor/query_result'; 24import {SourceDataset} from '../../trace_processor/dataset'; 25import {getColorForSlice, makeColorScheme} from '../../components/colorizer'; 26import {HSLColor} from '../../base/color'; 27 28export default class implements PerfettoPlugin { 29 // TODO(stevegolton): Call this plugins ExampleTracks or something, as it has 30 // turned into more of a generic plugin showcasing what you can do with 31 // tracks. 32 static readonly id = 'com.example.ExampleNestedTracks'; 33 async onTraceLoad(ctx: Trace): Promise<void> { 34 const traceStartTime = ctx.traceInfo.start; 35 const traceDur = ctx.traceInfo.end - ctx.traceInfo.start; 36 await ctx.engine.query(` 37 create table example_events ( 38 id INTEGER PRIMARY KEY AUTOINCREMENT, 39 name TEXT, 40 ts INTEGER, 41 dur INTEGER, 42 arg INTEGER 43 ); 44 45 insert into example_events (name, ts, dur, arg) 46 values 47 ('Foo', ${traceStartTime}, ${traceDur}, 'aaa'), 48 ('Bar', ${traceStartTime}, ${traceDur / 2n}, 'bbb'), 49 ('Baz', ${traceStartTime}, ${traceDur / 3n}, 'aaa'), 50 ('Qux', ${traceStartTime + traceDur / 2n}, ${traceDur / 2n}, 'bbb') 51 ; 52 `); 53 54 const title = 'Test Track'; 55 const uri = `com.example.ExampleNestedTracks#TestTrack`; 56 const track = new DatasetSliceTrack({ 57 trace: ctx, 58 uri, 59 dataset: new SourceDataset({ 60 src: 'select *, id as depth from example_events', 61 schema: { 62 ts: LONG, 63 name: STR, 64 dur: LONG, 65 id: NUM, 66 arg: STR, 67 }, 68 }), 69 colorizer: (row) => { 70 // Example usage of colorizer 71 return getColorForSlice(`${row.arg}`); 72 }, 73 }); 74 ctx.tracks.registerTrack({ 75 uri, 76 title, 77 track, 78 }); 79 80 this.addNestedTracks(ctx, uri); 81 82 // The following are some examples of dataset tracks with different configurations. 83 this.addTrack(ctx, { 84 trace: ctx, 85 uri: 'Red track', 86 dataset: new SourceDataset({ 87 src: 'example_events', 88 schema: { 89 id: NUM, 90 ts: LONG, 91 dur: LONG, 92 name: STR, 93 }, 94 }), 95 colorizer: () => makeColorScheme(new HSLColor({h: 0, s: 50, l: 50})), 96 }); 97 98 this.addTrack(ctx, { 99 trace: ctx, 100 uri: 'Instants', 101 dataset: new SourceDataset({ 102 src: 'example_events', 103 schema: { 104 id: NUM, 105 ts: LONG, 106 }, 107 }), 108 colorizer: () => makeColorScheme(new HSLColor({h: 90, s: 50, l: 50})), 109 }); 110 111 this.addTrack(ctx, { 112 trace: ctx, 113 uri: 'Flat', 114 dataset: new SourceDataset({ 115 src: 'select 0 as depth, * from example_events', 116 schema: { 117 id: NUM, 118 ts: LONG, 119 dur: LONG, 120 name: STR, 121 depth: NUM, 122 }, 123 }), 124 colorizer: () => makeColorScheme(new HSLColor({h: 180, s: 50, l: 50})), 125 }); 126 } 127 128 private addTrack<T extends ROW_SCHEMA>( 129 ctx: Trace, 130 attrs: DatasetSliceTrackAttrs<T>, 131 ) { 132 const title = attrs.uri; 133 const uri = attrs.uri; 134 const track = new DatasetSliceTrack(attrs); 135 ctx.tracks.registerTrack({ 136 uri, 137 title, 138 track, 139 }); 140 ctx.workspace.addChildInOrder(new TrackNode({title, uri, sortOrder: -100})); 141 } 142 143 private addNestedTracks(ctx: Trace, uri: string): void { 144 const trackRoot = new TrackNode({uri, title: 'Root'}); 145 const track1 = new TrackNode({uri, title: '1'}); 146 const track2 = new TrackNode({uri, title: '2'}); 147 const track11 = new TrackNode({uri, title: '1.1'}); 148 const track12 = new TrackNode({uri, title: '1.2'}); 149 const track121 = new TrackNode({uri, title: '1.2.1'}); 150 const track21 = new TrackNode({uri, title: '2.1'}); 151 152 ctx.workspace.addChildInOrder(trackRoot); 153 trackRoot.addChildLast(track1); 154 trackRoot.addChildLast(track2); 155 track1.addChildLast(track11); 156 track1.addChildLast(track12); 157 track12.addChildLast(track121); 158 track2.addChildLast(track21); 159 160 ctx.commands.registerCommand({ 161 id: 'com.example.ExampleNestedTracks#CloneTracksToNewWorkspace', 162 name: 'Clone track to new workspace', 163 callback: () => { 164 const ws = ctx.workspaces.createEmptyWorkspace('New workspace'); 165 ws.addChildLast(trackRoot.clone()); 166 ctx.workspaces.switchWorkspace(ws); 167 }, 168 }); 169 170 ctx.commands.registerCommand({ 171 id: 'com.example.ExampleNestedTracks#DeepCloneTracksToNewWorkspace', 172 name: 'Clone all tracks to new workspace', 173 callback: () => { 174 const ws = ctx.workspaces.createEmptyWorkspace('Deep workspace'); 175 ws.addChildLast(trackRoot.clone(true)); 176 ctx.workspaces.switchWorkspace(ws); 177 }, 178 }); 179 } 180} 181