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 {BigintMath} from '../../base/bigint_math'; 17import {sqliteString} from '../../base/string_utils'; 18import {exists} from '../../base/utils'; 19import {SliceDetails} from '../sql_utils/slice'; 20import {Anchor} from '../../widgets/anchor'; 21import {MenuItem, PopupMenu2} from '../../widgets/menu'; 22import {Section} from '../../widgets/section'; 23import {SqlRef} from '../../widgets/sql_ref'; 24import {Tree, TreeNode} from '../../widgets/tree'; 25import { 26 BreakdownByThreadState, 27 BreakdownByThreadStateTreeNode, 28} from './thread_state'; 29import {DurationWidget} from '../widgets/duration'; 30import {renderProcessRef} from '../widgets/process'; 31import {renderThreadRef} from '../widgets/thread'; 32import {Timestamp} from '../widgets/timestamp'; 33import {getSqlTableDescription} from '../widgets/sql/table/sql_table_registry'; 34import {assertExists} from '../../base/logging'; 35import {Trace} from '../../public/trace'; 36import {extensions} from '../extensions'; 37 38// Renders a widget storing all of the generic details for a slice from the 39// slice table. 40export function renderDetails( 41 trace: Trace, 42 slice: SliceDetails, 43 durationBreakdown?: BreakdownByThreadState, 44) { 45 return m( 46 Section, 47 {title: 'Details'}, 48 m( 49 Tree, 50 m(TreeNode, { 51 left: 'Name', 52 right: m( 53 PopupMenu2, 54 { 55 trigger: m(Anchor, slice.name), 56 }, 57 m(MenuItem, { 58 label: 'Slices with the same name', 59 onclick: () => { 60 extensions.addSqlTableTab(trace, { 61 table: assertExists(getSqlTableDescription('slice')), 62 filters: [ 63 { 64 op: (cols) => `${cols[0]} = ${sqliteString(slice.name)}`, 65 columns: ['name'], 66 }, 67 ], 68 }); 69 }, 70 }), 71 ), 72 }), 73 m(TreeNode, { 74 left: 'Category', 75 right: 76 !slice.category || slice.category === '[NULL]' 77 ? 'N/A' 78 : slice.category, 79 }), 80 m(TreeNode, { 81 left: 'Start time', 82 right: m(Timestamp, {ts: slice.ts}), 83 }), 84 exists(slice.absTime) && 85 m(TreeNode, {left: 'Absolute Time', right: slice.absTime}), 86 m( 87 TreeNode, 88 { 89 left: 'Duration', 90 right: m(DurationWidget, {dur: slice.dur}), 91 }, 92 exists(durationBreakdown) && 93 slice.dur > 0 && 94 m(BreakdownByThreadStateTreeNode, { 95 data: durationBreakdown, 96 dur: slice.dur, 97 }), 98 ), 99 renderThreadDuration(slice), 100 slice.thread && 101 m(TreeNode, { 102 left: 'Thread', 103 right: renderThreadRef(slice.thread), 104 }), 105 slice.process && 106 m(TreeNode, { 107 left: 'Process', 108 right: renderProcessRef(slice.process), 109 }), 110 slice.process && 111 exists(slice.process.uid) && 112 m(TreeNode, { 113 left: 'User ID', 114 right: slice.process.uid, 115 }), 116 slice.process && 117 slice.process.packageName && 118 m(TreeNode, { 119 left: 'Package name', 120 right: slice.process.packageName, 121 }), 122 slice.process && 123 exists(slice.process.versionCode) && 124 m(TreeNode, { 125 left: 'Version code', 126 right: slice.process.versionCode, 127 }), 128 m(TreeNode, { 129 left: 'SQL ID', 130 right: m(SqlRef, {table: 'slice', id: slice.id}), 131 }), 132 ), 133 ); 134} 135 136function renderThreadDuration(sliceInfo: SliceDetails) { 137 if (exists(sliceInfo.threadTs) && exists(sliceInfo.threadDur)) { 138 // If we have valid thread duration, also display a percentage of 139 // |threadDur| compared to |dur|. 140 const ratio = BigintMath.ratio(sliceInfo.threadDur, sliceInfo.dur); 141 const threadDurFractionSuffix = 142 sliceInfo.threadDur === -1n ? '' : ` (${(ratio * 100).toFixed(2)}%)`; 143 return m(TreeNode, { 144 left: 'Thread duration', 145 right: [ 146 m(DurationWidget, {dur: sliceInfo.threadDur}), 147 threadDurFractionSuffix, 148 ], 149 }); 150 } else { 151 return undefined; 152 } 153} 154