1// Copyright (C) 2023 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 m from 'mithril'; 16import {FtraceExplorer, FtraceExplorerCache} from './ftrace_explorer'; 17import {Engine} from '../../trace_processor/engine'; 18import {Trace} from '../../public/trace'; 19import {PerfettoPlugin} from '../../public/plugin'; 20import {NUM} from '../../trace_processor/query_result'; 21import {FtraceFilter, FtracePluginState} from './common'; 22import {FtraceRawTrack} from './ftrace_track'; 23import {TrackNode} from '../../public/workspace'; 24 25const VERSION = 1; 26 27const DEFAULT_STATE: FtracePluginState = { 28 version: VERSION, 29 filter: { 30 excludeList: [], 31 }, 32}; 33 34export default class implements PerfettoPlugin { 35 static readonly id = 'dev.perfetto.Ftrace'; 36 async onTraceLoad(ctx: Trace): Promise<void> { 37 const store = ctx.mountStore<FtracePluginState>((init: unknown) => { 38 if ( 39 typeof init === 'object' && 40 init !== null && 41 'version' in init && 42 init.version === VERSION 43 ) { 44 return init as {} as FtracePluginState; 45 } else { 46 return DEFAULT_STATE; 47 } 48 }); 49 ctx.trash.use(store); 50 51 const filterStore = store.createSubStore( 52 ['filter'], 53 (x) => x as FtraceFilter, 54 ); 55 ctx.trash.use(filterStore); 56 57 const cpus = await this.lookupCpuCores(ctx.engine); 58 const group = new TrackNode({ 59 title: 'Ftrace Events', 60 sortOrder: -5, 61 isSummary: true, 62 }); 63 64 for (const cpuNum of cpus) { 65 const uri = `/ftrace/cpu${cpuNum}`; 66 const title = `Ftrace Track for CPU ${cpuNum}`; 67 68 ctx.tracks.registerTrack({ 69 uri, 70 title, 71 tags: { 72 cpu: cpuNum, 73 groupName: 'Ftrace Events', 74 }, 75 track: new FtraceRawTrack(ctx.engine, cpuNum, filterStore), 76 }); 77 78 const track = new TrackNode({uri, title}); 79 group.addChildInOrder(track); 80 } 81 82 if (group.children.length) { 83 ctx.workspace.addChildInOrder(group); 84 } 85 86 const cache: FtraceExplorerCache = { 87 state: 'blank', 88 counters: [], 89 }; 90 91 const ftraceTabUri = 'perfetto.FtraceRaw#FtraceEventsTab'; 92 93 ctx.tabs.registerTab({ 94 uri: ftraceTabUri, 95 isEphemeral: false, 96 content: { 97 render: () => 98 m(FtraceExplorer, { 99 filterStore, 100 cache, 101 trace: ctx, 102 }), 103 getTitle: () => 'Ftrace Events', 104 }, 105 }); 106 107 ctx.commands.registerCommand({ 108 id: 'perfetto.FtraceRaw#ShowFtraceTab', 109 name: 'Show ftrace tab', 110 callback: () => { 111 ctx.tabs.showTab(ftraceTabUri); 112 }, 113 }); 114 } 115 116 private async lookupCpuCores(engine: Engine): Promise<number[]> { 117 const query = 'select distinct cpu from ftrace_event order by cpu'; 118 119 const result = await engine.query(query); 120 const it = result.iter({cpu: NUM}); 121 122 const cpuCores: number[] = []; 123 124 for (; it.valid(); it.next()) { 125 cpuCores.push(it.cpu); 126 } 127 128 return cpuCores; 129 } 130} 131