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