| // Copyright (C) 2023 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import m from 'mithril'; |
| import {BigintMath} from '../base/bigint_math'; |
| import {sqliteString} from '../base/string_utils'; |
| import {exists} from '../base/utils'; |
| import {SliceDetails} from '../trace_processor/sql_utils/slice'; |
| import {Anchor} from '../widgets/anchor'; |
| import {MenuItem, PopupMenu2} from '../widgets/menu'; |
| import {Section} from '../widgets/section'; |
| import {SqlRef} from '../widgets/sql_ref'; |
| import {Tree, TreeNode} from '../widgets/tree'; |
| import { |
| BreakdownByThreadState, |
| BreakdownByThreadStateTreeNode, |
| } from './sql/thread_state'; |
| import {DurationWidget} from './widgets/duration'; |
| import {renderProcessRef} from './widgets/process'; |
| import {renderThreadRef} from './widgets/thread'; |
| import {Timestamp} from './widgets/timestamp'; |
| import {getSqlTableDescription} from './widgets/sql/table/sql_table_registry'; |
| import {assertExists} from '../base/logging'; |
| import {Trace} from '../public/trace'; |
| import {extensions} from '../public/lib/extensions'; |
| |
| // Renders a widget storing all of the generic details for a slice from the |
| // slice table. |
| export function renderDetails( |
| trace: Trace, |
| slice: SliceDetails, |
| durationBreakdown?: BreakdownByThreadState, |
| ) { |
| return m( |
| Section, |
| {title: 'Details'}, |
| m( |
| Tree, |
| m(TreeNode, { |
| left: 'Name', |
| right: m( |
| PopupMenu2, |
| { |
| trigger: m(Anchor, slice.name), |
| }, |
| m(MenuItem, { |
| label: 'Slices with the same name', |
| onclick: () => { |
| extensions.addSqlTableTab(trace, { |
| table: assertExists(getSqlTableDescription('slice')), |
| filters: [ |
| { |
| op: (cols) => `${cols[0]} = ${sqliteString(slice.name)}`, |
| columns: ['name'], |
| }, |
| ], |
| }); |
| }, |
| }), |
| ), |
| }), |
| m(TreeNode, { |
| left: 'Category', |
| right: |
| !slice.category || slice.category === '[NULL]' |
| ? 'N/A' |
| : slice.category, |
| }), |
| m(TreeNode, { |
| left: 'Start time', |
| right: m(Timestamp, {ts: slice.ts}), |
| }), |
| exists(slice.absTime) && |
| m(TreeNode, {left: 'Absolute Time', right: slice.absTime}), |
| m( |
| TreeNode, |
| { |
| left: 'Duration', |
| right: m(DurationWidget, {dur: slice.dur}), |
| }, |
| exists(durationBreakdown) && |
| slice.dur > 0 && |
| m(BreakdownByThreadStateTreeNode, { |
| data: durationBreakdown, |
| dur: slice.dur, |
| }), |
| ), |
| renderThreadDuration(slice), |
| slice.thread && |
| m(TreeNode, { |
| left: 'Thread', |
| right: renderThreadRef(slice.thread), |
| }), |
| slice.process && |
| m(TreeNode, { |
| left: 'Process', |
| right: renderProcessRef(slice.process), |
| }), |
| slice.process && |
| exists(slice.process.uid) && |
| m(TreeNode, { |
| left: 'User ID', |
| right: slice.process.uid, |
| }), |
| slice.process && |
| slice.process.packageName && |
| m(TreeNode, { |
| left: 'Package name', |
| right: slice.process.packageName, |
| }), |
| slice.process && |
| exists(slice.process.versionCode) && |
| m(TreeNode, { |
| left: 'Version code', |
| right: slice.process.versionCode, |
| }), |
| m(TreeNode, { |
| left: 'SQL ID', |
| right: m(SqlRef, {table: 'slice', id: slice.id}), |
| }), |
| ), |
| ); |
| } |
| |
| function renderThreadDuration(sliceInfo: SliceDetails) { |
| if (exists(sliceInfo.threadTs) && exists(sliceInfo.threadDur)) { |
| // If we have valid thread duration, also display a percentage of |
| // |threadDur| compared to |dur|. |
| const ratio = BigintMath.ratio(sliceInfo.threadDur, sliceInfo.dur); |
| const threadDurFractionSuffix = |
| sliceInfo.threadDur === -1n ? '' : ` (${(ratio * 100).toFixed(2)}%)`; |
| return m(TreeNode, { |
| left: 'Thread duration', |
| right: [ |
| m(DurationWidget, {dur: sliceInfo.threadDur}), |
| threadDurFractionSuffix, |
| ], |
| }); |
| } else { |
| return undefined; |
| } |
| } |