1// Copyright (C) 2021 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 {LogFilteringCriteria, LogPanel} from './logs_panel'; 17import {ANDROID_LOGS_TRACK_KIND} from '../../public/track_kinds'; 18import {Trace} from '../../public/trace'; 19import {PerfettoPlugin} from '../../public/plugin'; 20import {NUM} from '../../trace_processor/query_result'; 21import {AndroidLogTrack} from './logs_track'; 22import {exists} from '../../base/utils'; 23import {TrackNode} from '../../public/workspace'; 24 25const VERSION = 1; 26 27const DEFAULT_STATE: AndroidLogPluginState = { 28 version: VERSION, 29 filter: { 30 // The first two log priorities are ignored. 31 minimumLevel: 2, 32 tags: [], 33 textEntry: '', 34 hideNonMatching: true, 35 }, 36}; 37 38interface AndroidLogPluginState { 39 version: number; 40 filter: LogFilteringCriteria; 41} 42 43export default class implements PerfettoPlugin { 44 static readonly id = 'dev.perfetto.AndroidLog'; 45 async onTraceLoad(ctx: Trace): Promise<void> { 46 const store = ctx.mountStore<AndroidLogPluginState>((init) => { 47 return exists(init) && (init as {version: unknown}).version === VERSION 48 ? (init as AndroidLogPluginState) 49 : DEFAULT_STATE; 50 }); 51 52 const result = await ctx.engine.query( 53 `select count(1) as cnt from android_logs`, 54 ); 55 const logCount = result.firstRow({cnt: NUM}).cnt; 56 const uri = 'perfetto.AndroidLog'; 57 const title = 'Android logs'; 58 if (logCount > 0) { 59 ctx.tracks.registerTrack({ 60 uri, 61 title, 62 tags: {kind: ANDROID_LOGS_TRACK_KIND}, 63 track: new AndroidLogTrack(ctx.engine), 64 }); 65 const track = new TrackNode({title, uri}); 66 ctx.workspace.addChildInOrder(track); 67 } 68 69 const androidLogsTabUri = 'perfetto.AndroidLog#tab'; 70 71 // Eternal tabs should always be available even if there is nothing to show 72 const filterStore = store.createSubStore( 73 ['filter'], 74 (x) => x as LogFilteringCriteria, 75 ); 76 77 ctx.tabs.registerTab({ 78 isEphemeral: false, 79 uri: androidLogsTabUri, 80 content: { 81 render: () => m(LogPanel, {filterStore: filterStore, trace: ctx}), 82 getTitle: () => 'Android Logs', 83 }, 84 }); 85 86 if (logCount > 0) { 87 ctx.tabs.addDefaultTab(androidLogsTabUri); 88 } 89 90 ctx.commands.registerCommand({ 91 id: 'perfetto.AndroidLog#ShowLogsTab', 92 name: 'Show android logs tab', 93 callback: () => { 94 ctx.tabs.showTab(androidLogsTabUri); 95 }, 96 }); 97 } 98} 99