• 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 {
16  ComputeMetricArgs,
17  ComputeMetricResult,
18  RawQueryArgs,
19  RawQueryResult
20} from './protos';
21import {TimeSpan} from './time';
22
23export interface LoadingTracker {
24  beginLoading(): void;
25  endLoading(): void;
26}
27
28export class NullLoadingTracker implements LoadingTracker {
29  beginLoading(): void {}
30  endLoading(): void {}
31}
32
33/**
34 * Abstract interface of a trace proccessor.
35 * This is the TypeScript equivalent of src/trace_processor/rpc.h.
36 *
37 * Engine also defines helpers for the most common service methods
38 * (e.g. query).
39 */
40export abstract class Engine {
41  abstract readonly id: string;
42  private _cpus?: number[];
43  private _numGpus?: number;
44  private loadingTracker: LoadingTracker;
45
46  constructor(tracker?: LoadingTracker) {
47    this.loadingTracker = tracker ? tracker : new NullLoadingTracker();
48  }
49
50  /**
51   * Push trace data into the engine. The engine is supposed to automatically
52   * figure out the type of the trace (JSON vs Protobuf).
53   */
54  abstract parse(data: Uint8Array): Promise<void>;
55
56  /**
57   * Notify the engine no more data is coming.
58   */
59  abstract notifyEof(): void;
60
61  /**
62   * Resets the trace processor state by destroying any table/views created by
63   * the UI after loading.
64   */
65  abstract restoreInitialTables(): void;
66
67  /*
68   * Performs a SQL query and retruns a proto-encoded RawQueryResult object.
69   */
70  abstract rawQuery(rawQueryArgs: Uint8Array): Promise<Uint8Array>;
71
72  /*
73   * Performs computation of metrics and returns a proto-encoded TraceMetrics
74   * object.
75   */
76  abstract rawComputeMetric(computeMetricArgs: Uint8Array): Promise<Uint8Array>;
77
78  /**
79   * Shorthand for sending a SQL query to the engine.
80   * Deals with {,un}marshalling of request/response args.
81   */
82  async query(sqlQuery: string, userQuery = false): Promise<RawQueryResult> {
83    this.loadingTracker.beginLoading();
84    try {
85      const args = new RawQueryArgs();
86      args.sqlQuery = sqlQuery;
87      args.timeQueuedNs = Math.floor(performance.now() * 1e6);
88      const argsEncoded = RawQueryArgs.encode(args).finish();
89      const respEncoded = await this.rawQuery(argsEncoded);
90      const result = RawQueryResult.decode(respEncoded);
91      if (!result.error || userQuery) return result;
92      // Query failed, throw an error since it was not a user query
93      throw new Error(`Query error "${sqlQuery}": ${result.error}`);
94    } finally {
95      this.loadingTracker.endLoading();
96    }
97  }
98
99  /**
100   * Shorthand for sending a compute metrics request to the engine.
101   * Deals with {,un}marshalling of request/response args.
102   */
103  async computeMetric(metrics: string[]): Promise<ComputeMetricResult> {
104    const args = new ComputeMetricArgs();
105    args.metricNames = metrics;
106    const argsEncoded = ComputeMetricArgs.encode(args).finish();
107    const respEncoded = await this.rawComputeMetric(argsEncoded);
108    return ComputeMetricResult.decode(respEncoded);
109  }
110
111  async queryOneRow(query: string): Promise<number[]> {
112    const result = await this.query(query);
113    const res: number[] = [];
114    if (result.numRecords === 0) return res;
115    for (const col of result.columns) {
116      if (col.longValues!.length === 0) {
117        console.error(
118            `queryOneRow should only be used for queries that return long values
119             : ${query}`);
120        throw new Error(
121            `queryOneRow should only be used for queries that return long values
122             : ${query}`);
123      }
124      res.push(+col.longValues![0]);
125    }
126    return res;
127  }
128
129  // TODO(hjd): When streaming must invalidate this somehow.
130  async getCpus(): Promise<number[]> {
131    if (!this._cpus) {
132      const result =
133          await this.query('select distinct(cpu) from sched order by cpu;');
134      if (result.numRecords === 0) return [];
135      this._cpus = result.columns[0].longValues!.map(n => +n);
136    }
137    return this._cpus;
138  }
139
140  async getNumberOfGpus(): Promise<number> {
141    if (!this._numGpus) {
142      const result = await this.query(`
143        select count(distinct(gpu_id)) as gpuCount
144        from gpu_counter_track
145        where name = 'gpufreq';
146      `);
147      this._numGpus = +result.columns[0].longValues![0];
148    }
149    return this._numGpus;
150  }
151
152  // TODO: This should live in code that's more specific to chrome, instead of
153  // in engine.
154  async getNumberOfProcesses(): Promise<number> {
155    const result = await this.query('select count(*) from process;');
156    return +result.columns[0].longValues![0];
157  }
158
159  async getTraceTimeBounds(): Promise<TimeSpan> {
160    const query = `select start_ts, end_ts from trace_bounds`;
161    const res = (await this.queryOneRow(query));
162    return new TimeSpan(res[0] / 1e9, res[1] / 1e9);
163  }
164}
165