• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 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 {assertFalse, assertTrue} from '../base/logging';
16import * as protos from '../gen/protos';
17
18// Aliases protos to avoid the super nested namespaces.
19// See https://www.typescriptlang.org/docs/handbook/namespaces.html#aliases
20import AndroidLogConfig = protos.perfetto.protos.AndroidLogConfig;
21import AndroidPowerConfig = protos.perfetto.protos.AndroidPowerConfig;
22import AndroidLogId = protos.perfetto.protos.AndroidLogId;
23import BatteryCounters =
24    protos.perfetto.protos.AndroidPowerConfig.BatteryCounters;
25import BufferConfig = protos.perfetto.protos.TraceConfig.BufferConfig;
26import DataSourceConfig = protos.perfetto.protos.DataSourceConfig;
27import FtraceConfig = protos.perfetto.protos.FtraceConfig;
28import IAndroidPowerConfig = protos.perfetto.protos.IAndroidPowerConfig;
29import IBufferConfig = protos.perfetto.protos.TraceConfig.IBufferConfig;
30import IProcessStatsConfig = protos.perfetto.protos.IProcessStatsConfig;
31import IRawQueryArgs = protos.perfetto.protos.IRawQueryArgs;
32import ISysStatsConfig = protos.perfetto.protos.ISysStatsConfig;
33import ITraceConfig = protos.perfetto.protos.ITraceConfig;
34import MeminfoCounters = protos.perfetto.protos.MeminfoCounters;
35import ProcessStatsConfig = protos.perfetto.protos.ProcessStatsConfig;
36import RawQueryArgs = protos.perfetto.protos.RawQueryArgs;
37import RawQueryResult = protos.perfetto.protos.RawQueryResult;
38import StatCounters = protos.perfetto.protos.SysStatsConfig.StatCounters;
39import SysStatsConfig = protos.perfetto.protos.SysStatsConfig;
40import TraceConfig = protos.perfetto.protos.TraceConfig;
41import TraceProcessor = protos.perfetto.protos.TraceProcessor;
42import VmstatCounters = protos.perfetto.protos.VmstatCounters;
43
44// TODO(hjd): Maybe these should go in their own file.
45export interface Row { [key: string]: number|string; }
46
47const COLUMN_TYPE_STR = RawQueryResult.ColumnDesc.Type.STRING;
48const COLUMN_TYPE_DOUBLE = RawQueryResult.ColumnDesc.Type.DOUBLE;
49const COLUMN_TYPE_LONG = RawQueryResult.ColumnDesc.Type.LONG;
50
51function getCell(result: RawQueryResult, column: number, row: number): number|
52    string|null {
53  const values = result.columns[column];
54  if (values.isNulls![row]) return null;
55  switch (result.columnDescriptors[column].type) {
56    case COLUMN_TYPE_LONG:
57      return +values.longValues![row];
58    case COLUMN_TYPE_DOUBLE:
59      return +values.doubleValues![row];
60    case COLUMN_TYPE_STR:
61      return values.stringValues![row];
62    default:
63      throw new Error('Unhandled type!');
64  }
65}
66
67export function rawQueryResultColumns(result: RawQueryResult): string[] {
68  // Two columns can conflict on the same name, e.g.
69  // select x.foo, y.foo from x join y. In that case store them using the
70  // full table.column notation.
71  const res = [] as string[];
72  const uniqColNames = new Set<string>();
73  const colNamesToDedupe = new Set<string>();
74  for (const col of result.columnDescriptors) {
75    const colName = col.name || '';
76    if (uniqColNames.has(colName)) {
77      colNamesToDedupe.add(colName);
78    }
79    uniqColNames.add(colName);
80  }
81  for (let i = 0; i < result.columnDescriptors.length; i++) {
82    const colName = result.columnDescriptors[i].name || '';
83    if (colNamesToDedupe.has(colName)) {
84      res.push(`${colName}.${i + 1}`);
85    } else {
86      res.push(colName);
87    }
88  }
89  return res;
90}
91
92export function* rawQueryResultIter(result: RawQueryResult) {
93  const columns: Array<[string, number]> = rawQueryResultColumns(result).map(
94      (name, i): [string, number] => [name, i]);
95  for (let rowNum = 0; rowNum < result.numRecords; rowNum++) {
96    const row: Row = {};
97    for (const [name, colNum] of columns) {
98      const cell = getCell(result, colNum, rowNum);
99      row[name] = cell === null ? '[NULL]' : cell;
100    }
101    yield row;
102  }
103}
104
105export const NUM = 0;
106export const STR = 'str';
107export const NUM_NULL: number|null = 1;
108export const STR_NULL: string|null = 'str_null';
109
110/**
111 * This function allows for type safe use of RawQueryResults.
112 * The input is a RawQueryResult (|raw|) and a "spec".
113 * A spec is an object where the keys are column names and the values
114 * are constants representing the types. For example:
115 * {
116 *   upid: NUM,
117 *   pid: NUM_NULL,
118 *   processName: STR_NULL,
119 * }
120 * The output is a iterable of rows each row looks like the given spec:
121 * {
122 *   upid: 1,
123 *   pid: 42,
124 *   processName: null,
125 * }
126 * Each row has an appropriate typescript type based on the spec so there
127 * is no need to use ! or cast when using the result of rawQueryToRows.
128 * Note: type checking to ensure that the RawQueryResult matches the spec
129 * happens at runtime, so if a query can return null and this is not reflected
130 * in the spec this will still crash at runtime.
131 */
132export function*
133    rawQueryToRows<T>(raw: RawQueryResult, spec: T): IterableIterator<T> {
134  const allColumns = rawQueryResultColumns(raw);
135  const columns: Array<[string, (row: number) => string | number | null]> = [];
136  for (const [key, columnSpec] of Object.entries(spec)) {
137    const i = allColumns.indexOf(key);
138    assertTrue(i !== -1, `Expected column "${key}" (cols ${allColumns})`);
139
140    const column = raw.columns[i];
141    const isNulls = column.isNulls!;
142    const columnType = raw.columnDescriptors[i].type;
143
144    if (columnSpec === NUM || columnSpec === STR) {
145      for (let j = 0; j < raw.numRecords; j++) {
146        assertFalse(column.isNulls![i], `Unexpected null in ${key} row ${j}`);
147      }
148    }
149
150    if (columnSpec === NUM || columnSpec === NUM_NULL) {
151      if (columnType === COLUMN_TYPE_STR) {
152        throw new Error(`Expected numbers in column ${key} found strings`);
153      }
154    } else if (columnSpec === STR || columnSpec === STR_NULL) {
155      if (columnType === COLUMN_TYPE_LONG ||
156          columnType === COLUMN_TYPE_DOUBLE) {
157        throw new Error(`Expected strings in column ${key} found numbers`);
158      }
159    }
160
161    let accessor;
162    switch (columnType) {
163      case COLUMN_TYPE_LONG: {
164        const values = column.longValues!;
165        accessor = (i: number) => isNulls[i] ? null : +values[i];
166        break;
167      }
168      case COLUMN_TYPE_DOUBLE: {
169        const values = column.doubleValues!;
170        accessor = (i: number) => isNulls[i] ? null : values[i];
171        break;
172      }
173      case COLUMN_TYPE_STR: {
174        const values = column.stringValues!;
175        accessor = (i: number) => isNulls[i] ? null : values[i];
176        break;
177      }
178      default:
179        // We can only reach here if the column is completely null.
180        accessor = (_: number) => null;
181        break;
182    }
183    columns.push([key, accessor]);
184  }
185
186  for (let i = 0; i < raw.numRecords; i++) {
187    const row: {[_: string]: number | string | null} = {};
188    for (const [name, accessor] of columns) {
189      row[name] = accessor(i);
190    }
191    yield row as {} as T;
192  }
193}
194
195export {
196  AndroidLogConfig,
197  AndroidLogId,
198  AndroidPowerConfig,
199  BatteryCounters,
200  BufferConfig,
201  DataSourceConfig,
202  FtraceConfig,
203  IAndroidPowerConfig,
204  IBufferConfig,
205  IProcessStatsConfig,
206  IRawQueryArgs,
207  ISysStatsConfig,
208  ITraceConfig,
209  MeminfoCounters,
210  ProcessStatsConfig,
211  RawQueryArgs,
212  RawQueryResult,
213  StatCounters,
214  SysStatsConfig,
215  TraceConfig,
216  TraceProcessor,
217  VmstatCounters,
218};
219