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