xref: /aosp_15_r20/external/perfetto/ui/src/components/details/slice_details.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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