• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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