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 {duration, Time, time} from '../../base/time'; 17import {DurationWidget} from '../../components/widgets/duration'; 18import {Timestamp} from '../../components/widgets/timestamp'; 19import {LONG, NUM, STR, STR_NULL} from '../../trace_processor/query_result'; 20import {DetailsShell} from '../../widgets/details_shell'; 21import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; 22import {Section} from '../../widgets/section'; 23import {SqlRef} from '../../widgets/sql_ref'; 24import {dictToTreeNodes, Tree} from '../../widgets/tree'; 25import {asUpid, Upid} from '../../components/sql_utils/core_types'; 26import {Trace} from '../../public/trace'; 27import {TrackEventDetailsPanel} from '../../public/details_panel'; 28 29interface Data { 30 startupId: number; 31 eventName: string; 32 startupBeginTs: time; 33 durToFirstVisibleContent: duration; 34 launchCause?: string; 35 upid: Upid; 36} 37 38export class StartupDetailsPanel implements TrackEventDetailsPanel { 39 private data?: Data; 40 41 constructor( 42 private readonly trace: Trace, 43 private readonly id: number, 44 ) {} 45 46 async load() { 47 const queryResult = await this.trace.engine.query(` 48 SELECT 49 activity_id AS startupId, 50 name, 51 startup_begin_ts AS startupBeginTs, 52 CASE 53 WHEN first_visible_content_ts IS NULL THEN 0 54 ELSE first_visible_content_ts - startup_begin_ts 55 END AS durTofirstVisibleContent, 56 launch_cause AS launchCause, 57 browser_upid AS upid 58 FROM chrome_startups 59 WHERE id = ${this.id}; 60 `); 61 62 const iter = queryResult.firstRow({ 63 startupId: NUM, 64 name: STR, 65 startupBeginTs: LONG, 66 durTofirstVisibleContent: LONG, 67 launchCause: STR_NULL, 68 upid: NUM, 69 }); 70 71 this.data = { 72 startupId: iter.startupId, 73 eventName: iter.name, 74 startupBeginTs: Time.fromRaw(iter.startupBeginTs), 75 durToFirstVisibleContent: iter.durTofirstVisibleContent, 76 upid: asUpid(iter.upid), 77 }; 78 79 if (iter.launchCause) { 80 this.data.launchCause = iter.launchCause; 81 } 82 } 83 84 private getDetailsDictionary() { 85 const details: {[key: string]: m.Child} = {}; 86 if (this.data === undefined) return details; 87 details['Activity ID'] = this.data.startupId; 88 details['Browser Upid'] = this.data.upid; 89 details['Startup Event'] = this.data.eventName; 90 details['Startup Timestamp'] = m(Timestamp, {ts: this.data.startupBeginTs}); 91 details['Duration to First Visible Content'] = m(DurationWidget, { 92 dur: this.data.durToFirstVisibleContent, 93 }); 94 if (this.data.launchCause) { 95 details['Launch Cause'] = this.data.launchCause; 96 } 97 details['SQL ID'] = m(SqlRef, { 98 table: 'chrome_startups', 99 id: this.id, 100 }); 101 return details; 102 } 103 104 render() { 105 if (!this.data) { 106 return m('h2', 'Loading'); 107 } 108 109 return m( 110 DetailsShell, 111 { 112 title: 'Chrome Startup', 113 }, 114 m( 115 GridLayout, 116 m( 117 GridLayoutColumn, 118 m( 119 Section, 120 {title: 'Details'}, 121 m(Tree, dictToTreeNodes(this.getDetailsDictionary())), 122 ), 123 ), 124 ), 125 ); 126 } 127} 128