• 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 {duration, Time, time} from '../../base/time';
16import {exists} from '../../base/utils';
17import {Engine} from '../../trace_processor/engine';
18import {
19  LONG,
20  LONG_NULL,
21  NUM,
22  NUM_NULL,
23  STR,
24  STR_NULL,
25} from '../../trace_processor/query_result';
26import {
27  constraintsToQuerySuffix,
28  SQLConstraints,
29} from '../../trace_processor/sql_utils';
30import {
31  asArgSetId,
32  asSliceSqlId,
33  asUpid,
34  asUtid,
35  SliceSqlId,
36  Upid,
37  Utid,
38} from './core_types';
39import {Arg, getArgs} from './args';
40import {getThreadInfo, ThreadInfo} from './thread';
41import {getProcessInfo, ProcessInfo} from './process';
42
43// Basic information about a slice.
44export interface SliceDetails {
45  id: SliceSqlId;
46  name: string;
47  ts: time;
48  absTime?: string;
49  dur: duration;
50  parentId?: SliceSqlId;
51  trackId: number;
52  depth: number;
53  thread?: ThreadInfo;
54  process?: ProcessInfo;
55  threadTs?: time;
56  threadDur?: duration;
57  category?: string;
58  args?: Arg[];
59}
60
61async function getUtidAndUpid(
62  engine: Engine,
63  sqlTrackId: number,
64): Promise<{utid?: Utid; upid?: Upid}> {
65  const {upid, utid} = (
66    await engine.query(`
67      SELECT
68        extract_arg(dimension_arg_set_id, 'upid') as upid,
69        extract_arg(dimension_arg_set_id, 'utid') as utid
70      FROM track
71      WHERE id = ${sqlTrackId}
72    `)
73  ).firstRow({upid: NUM_NULL, utid: NUM_NULL});
74  return {upid: asUpid(upid ?? undefined), utid: asUtid(utid ?? undefined)};
75}
76
77export async function getSliceFromConstraints(
78  engine: Engine,
79  constraints: SQLConstraints,
80): Promise<SliceDetails[]> {
81  const query = await engine.query(`
82    SELECT
83      id,
84      name,
85      ts,
86      dur,
87      track_id as trackId,
88      depth,
89      parent_id as parentId,
90      thread_dur as threadDur,
91      thread_ts as threadTs,
92      category,
93      arg_set_id as argSetId,
94      ABS_TIME_STR(ts) as absTime
95    FROM slice
96    ${constraintsToQuerySuffix(constraints)}`);
97  const it = query.iter({
98    id: NUM,
99    name: STR,
100    ts: LONG,
101    dur: LONG,
102    trackId: NUM,
103    depth: NUM,
104    parentId: NUM_NULL,
105    threadDur: LONG_NULL,
106    threadTs: LONG_NULL,
107    category: STR_NULL,
108    argSetId: NUM_NULL,
109    absTime: STR_NULL,
110  });
111
112  const result: SliceDetails[] = [];
113  for (; it.valid(); it.next()) {
114    const {utid, upid} = await getUtidAndUpid(engine, it.trackId);
115
116    const thread: ThreadInfo | undefined =
117      utid === undefined ? undefined : await getThreadInfo(engine, utid);
118    const process: ProcessInfo | undefined =
119      thread !== undefined
120        ? thread.process
121        : upid === undefined
122          ? undefined
123          : await getProcessInfo(engine, upid);
124
125    result.push({
126      id: asSliceSqlId(it.id),
127      name: it.name,
128      ts: Time.fromRaw(it.ts),
129      dur: it.dur,
130      trackId: it.trackId,
131      depth: it.depth,
132      parentId: asSliceSqlId(it.parentId ?? undefined),
133      thread,
134      process,
135      threadDur: it.threadDur ?? undefined,
136      threadTs: exists(it.threadTs) ? Time.fromRaw(it.threadTs) : undefined,
137      category: it.category ?? undefined,
138      args: exists(it.argSetId)
139        ? await getArgs(engine, asArgSetId(it.argSetId))
140        : undefined,
141      absTime: it.absTime ?? undefined,
142    });
143  }
144  return result;
145}
146
147export async function getSlice(
148  engine: Engine,
149  id: SliceSqlId,
150): Promise<SliceDetails | undefined> {
151  const result = await getSliceFromConstraints(engine, {
152    filters: [`id=${id}`],
153  });
154  if (result.length > 1) {
155    throw new Error(`slice table has more than one row with id ${id}`);
156  }
157  if (result.length === 0) {
158    return undefined;
159  }
160  return result[0];
161}
162
163// A slice tree node, combining the information about the given slice with
164// information about its descendants.
165export interface SliceTreeNode extends SliceDetails {
166  children: SliceTreeNode[];
167  parent?: SliceTreeNode;
168}
169
170// Get all descendants for a given slice in a tree form.
171export async function getDescendantSliceTree(
172  engine: Engine,
173  id: SliceSqlId,
174): Promise<SliceTreeNode | undefined> {
175  const slice = await getSlice(engine, id);
176  if (slice === undefined) {
177    return undefined;
178  }
179  const descendants = await getSliceFromConstraints(engine, {
180    filters: [
181      `track_id=${slice.trackId}`,
182      `depth >= ${slice.depth}`,
183      `ts >= ${slice.ts}`,
184      // TODO(altimin): consider making `dur` undefined here instead of -1.
185      slice.dur >= 0 ? `ts <= (${slice.ts} + ${slice.dur})` : undefined,
186    ],
187    orderBy: ['ts', 'depth'],
188  });
189  const slices: {[key: SliceSqlId]: SliceTreeNode} = Object.fromEntries(
190    descendants.map((slice) => [
191      slice.id,
192      {
193        children: [],
194        ...slice,
195      },
196    ]),
197  );
198  for (const [_, slice] of Object.entries(slices)) {
199    if (slice.parentId !== undefined) {
200      const parent = slices[slice.parentId];
201      slice.parent = parent;
202      parent.children.push(slice);
203    }
204  }
205  return slices[id];
206}
207