• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
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 */
15
16import { SpSystemTrace } from '../SpSystemTrace.js';
17import { TraceRow } from '../trace/base/TraceRow.js';
18import { procedurePool } from '../../database/Procedure.js';
19import {
20  queryHiPerfCpuData,
21  queryHiPerfCpuMergeData,
22  queryHiPerfCpuMergeData2,
23  queryHiPerfEventData,
24  queryHiPerfEventList,
25  queryHiPerfEventListData,
26  queryHiPerfProcessData,
27  queryHiPerfThreadData,
28  queryPerfCmdline,
29  queryPerfThread,
30} from '../../database/SqlLite.js';
31import { Utils } from '../trace/base/Utils.js';
32import { PerfThread } from '../../bean/PerfProfile.js';
33import { HiperfCpuRender, HiPerfCpuStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfCPU.js';
34import { HiperfThreadRender, HiPerfThreadStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfThread.js';
35import { HiperfProcessRender, HiPerfProcessStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfProcess.js';
36import { info } from '../../../log/Log.js';
37import { HiperfEventRender, HiPerfEventStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfEvent.js';
38import { perfDataQuery } from './PerfDataQuery.js';
39import { renders } from '../../database/ui-worker/ProcedureWorker.js';
40import { EmptyRender } from '../../database/ui-worker/ProcedureWorkerCPU.js';
41import { HiperfReportRender, HiPerfReportStruct } from '../../database/ui-worker/ProcedureWorkerHiPerfReport.js';
42
43
44export interface ResultData {
45  existA: boolean | null | undefined;
46  existF: boolean | null | undefined;
47  fValue: number;
48}
49
50export class SpHiPerf {
51  static selectCpuStruct: HiPerfCpuStruct | undefined;
52  static selectProcessStruct: HiPerfProcessStruct | undefined;
53  static selectThreadStruct: HiPerfThreadStruct | undefined;
54  static stringResult: ResultData | undefined;
55
56  private cpuData: Array<any> | undefined;
57  public maxCpuId: number = 0;
58  private rowFolder!: TraceRow<any>;
59  private perfThreads: Array<PerfThread> | undefined;
60  private trace: SpSystemTrace;
61  private group: any;
62  private rowList: TraceRow<any>[] | undefined;
63  private eventTypeList: Array<{ id: number; report_value: string }> = [];
64
65  constructor(trace: SpSystemTrace) {
66    this.trace = trace;
67  }
68
69  async init() {
70    await this.initCmdLine();
71    this.rowList = [];
72    this.perfThreads = await queryPerfThread();
73    this.eventTypeList = await queryHiPerfEventList();
74    info('PerfThread Data size is: ', this.perfThreads!.length);
75    this.group = Utils.groupBy(this.perfThreads || [], 'pid');
76    this.cpuData = await queryHiPerfCpuMergeData2();
77    this.maxCpuId = this.cpuData.length > 0 ? this.cpuData[0].cpu_id : -Infinity;
78    if (this.cpuData.length > 0) {
79      await this.initFolder();
80      await this.initCpuMerge();
81      await this.initCpu();
82      await this.initProcess();
83    }
84    info('HiPerf Data initialized');
85  }
86
87  getStringResult(s: string = '') {
88    let list = s.split(' ').filter((e) => e);
89    let sA = list.findIndex((item) => item == '-a');
90    let sF = list.findIndex((item) => item == '-f');
91    SpHiPerf.stringResult = {
92      existA: sA !== -1,
93      existF: sF !== -1,
94      fValue: Number((1000 / (sF !== -1 ? parseInt(list[sF + 1]) : 1000)).toFixed(2)),
95    };
96  }
97
98  async initCmdLine() {
99    let perfCmdLines = await queryPerfCmdline();
100    if (perfCmdLines.length > 0) {
101      this.getStringResult(perfCmdLines[0].report_value);
102    } else {
103      SpHiPerf.stringResult = {
104        existA: true,
105        existF: false,
106        fValue: 1,
107      };
108    }
109  }
110
111  async initFolder() {
112    let row = TraceRow.skeleton();
113    row.setAttribute('disabled-check', '');
114    row.rowId = `HiPerf`;
115    row.index = 0;
116    row.rowType = TraceRow.ROW_TYPE_HIPERF;
117    row.rowParentId = '';
118    row.folder = true;
119    row.style.height = '40px';
120    if (SpHiPerf.stringResult?.existA === true) {
121      row.name = `HiPerf (All)`;
122    } else {
123      let names = Reflect.ownKeys(this.group)
124        .map((pid: any) => {
125          let array = this.group[pid] as Array<PerfThread>;
126          let process = array.filter((th) => th.pid === th.tid)[0];
127          return process.processName;
128        })
129        .join(',');
130      row.name = `HiPerf (${names})`;
131    }
132    row.supplier = () => new Promise<Array<any>>((resolve) => resolve([]));
133    row.onThreadHandler = (useCache) => {
134      row.canvasSave(this.trace.canvasPanelCtx!);
135      if (row.expansion) {
136        this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height);
137      } else {
138        (renders['empty'] as EmptyRender).renderMainThread(
139          {
140            context: this.trace.canvasPanelCtx,
141            useCache: useCache,
142            type: ``,
143          },
144          row
145        );
146      }
147      row.canvasRestore(this.trace.canvasPanelCtx!);
148    };
149    this.rowFolder = row;
150    this.trace.rowsEL?.appendChild(row);
151  }
152
153  async initCpuMerge() {
154    let cpuMergeRow = TraceRow.skeleton<HiPerfCpuStruct>();
155    cpuMergeRow.rowId = `HiPerf-cpu-merge`;
156    cpuMergeRow.index = 0;
157    cpuMergeRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU;
158    cpuMergeRow.rowParentId = 'HiPerf';
159    cpuMergeRow.rowHidden = !this.rowFolder.expansion;
160    cpuMergeRow.folder = false;
161    cpuMergeRow.name = `HiPerf`;
162    cpuMergeRow.style.height = '40px';
163    cpuMergeRow.setAttribute('children', '');
164    cpuMergeRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
165    cpuMergeRow.selectChangeHandler = this.trace.selectChangeHandler;
166    cpuMergeRow.supplier = () => queryHiPerfCpuMergeData();
167    cpuMergeRow.focusHandler = () => this.hoverTip(cpuMergeRow, HiPerfCpuStruct.hoverStruct);
168    cpuMergeRow.onThreadHandler = (useCache) => {
169      let context = cpuMergeRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
170      cpuMergeRow.canvasSave(context);
171      (renders['HiPerf-Cpu'] as HiperfCpuRender).renderMainThread(
172        {
173          context: context,
174          useCache: useCache,
175          scale: TraceRow.range?.scale || 50,
176          type: `HiPerf-Cpu-Merge`,
177          maxCpu: this.maxCpuId + 1,
178          intervalPerf: SpHiPerf.stringResult?.fValue || 1,
179          range: TraceRow.range,
180        },
181        cpuMergeRow
182      );
183      cpuMergeRow.canvasRestore(context);
184    };
185    this.rowFolder.addChildTraceRow(cpuMergeRow);
186    this.rowList?.push(cpuMergeRow);
187  }
188
189  async initCpu() {
190    for (let i = 0; i <= this.maxCpuId; i++) {
191      let perfCpuRow = TraceRow.skeleton<HiPerfCpuStruct>();
192      perfCpuRow.rowId = `HiPerf-cpu-${i}`;
193      perfCpuRow.index = i;
194      perfCpuRow.rowType = TraceRow.ROW_TYPE_HIPERF_CPU;
195      perfCpuRow.rowParentId = 'HiPerf';
196      perfCpuRow.rowHidden = !this.rowFolder.expansion;
197      perfCpuRow.folder = false;
198      perfCpuRow.name = `Cpu ${i}`;
199      perfCpuRow.setAttribute('children', '');
200      perfCpuRow.favoriteChangeHandler = this.trace.favoriteChangeHandler;
201      perfCpuRow.selectChangeHandler = this.trace.selectChangeHandler;
202      perfCpuRow.style.height = '40px';
203      perfCpuRow.supplier = () => queryHiPerfCpuData(i);
204      perfCpuRow.focusHandler = () => this.hoverTip(perfCpuRow, HiPerfCpuStruct.hoverStruct);
205      perfCpuRow.onThreadHandler = (useCache) => {
206        let context = perfCpuRow.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
207        perfCpuRow.canvasSave(context);
208        (renders['HiPerf-Cpu'] as HiperfCpuRender).renderMainThread(
209          {
210            context: context,
211            useCache: useCache,
212            scale: TraceRow.range?.scale || 50,
213            type: `HiPerf-Cpu-${i}`,
214            maxCpu: this.maxCpuId + 1,
215            intervalPerf: SpHiPerf.stringResult?.fValue || 1,
216            range: TraceRow.range,
217          },
218          perfCpuRow
219        );
220        perfCpuRow.canvasRestore(context);
221      };
222      this.rowFolder.addChildTraceRow(perfCpuRow);
223      this.rowList?.push(perfCpuRow);
224    }
225  }
226
227  async initReport() {
228    this.eventTypeList.forEach((it, index) => {
229      let fold = TraceRow.skeleton<HiPerfReportStruct>();
230      fold.rowId = `Perf-Report-${it.id}-${it.report_value}`;
231      fold.index = index;
232      fold.rowType = TraceRow.ROW_TYPE_HIPERF_REPORT;
233      fold.rowParentId = 'HiPerf';
234      fold.rowHidden = !this.rowFolder.expansion;
235      fold.folder = true;
236      fold.name = `Event :${it.report_value}`;
237      fold.folderPaddingLeft = 6;
238      fold.favoriteChangeHandler = this.trace.favoriteChangeHandler;
239      fold.selectChangeHandler = this.trace.selectChangeHandler;
240      fold.supplier = () => queryHiPerfEventListData(it.id);
241      fold.onThreadHandler = (useCache) => {
242        let context = fold.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
243        fold.canvasSave(context);
244        (renders['HiPerf-Report-Fold'] as HiperfReportRender).renderMainThread(
245          {
246            context: context,
247            useCache: useCache,
248            scale: TraceRow.range?.scale || 50,
249            type: `HiPerf-Report-Fold-${it.report_value}-${it.id}`,
250            maxCpu: this.maxCpuId + 1,
251            intervalPerf: SpHiPerf.stringResult?.fValue || 1,
252            range: TraceRow.range,
253          },
254          fold
255        );
256        fold.canvasRestore(context);
257      };
258      this.trace.rowsEL?.appendChild(fold);
259      this.rowList?.push(fold);
260      for (let i = 0; i <= this.maxCpuId; i++) {
261        let row = TraceRow.skeleton<HiPerfEventStruct>();
262        row.rowId = `HiPerf-Report-Event-${it.report_value}-${i}`;
263        row.index = i;
264        row.rowType = TraceRow.ROW_TYPE_HIPERF_EVENT;
265        row.rowParentId = fold.rowId;
266        row.rowHidden = !fold.expansion;
267        row.folder = false;
268        row.name = `Cpu ${i}`;
269        row.style.height = '40px';
270        row.setAttribute('children', '');
271        row.favoriteChangeHandler = this.trace.favoriteChangeHandler;
272        row.selectChangeHandler = this.trace.selectChangeHandler;
273        row.supplier = () => queryHiPerfEventData(it.id, row.index);
274        row.focusHandler = () => this.hoverTip(row, HiPerfEventStruct.hoverStruct);
275        row.onThreadHandler = (useCache) => {
276          let context = fold.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
277          fold.canvasSave(context);
278          (renders['HiPerf-Report-Event'] as HiperfEventRender).renderMainThread(
279            {
280              context: context,
281              useCache: useCache,
282              scale: TraceRow.range?.scale || 50,
283              type: `HiPerf-Report-Event-${it.report_value}-${i}`,
284              maxCpu: this.maxCpuId + 1,
285              intervalPerf: SpHiPerf.stringResult?.fValue || 1,
286              range: TraceRow.range,
287            },
288            row
289          );
290          fold.canvasRestore(context);
291        };
292        this.trace.rowsEL?.appendChild(row);
293        this.rowList?.push(row);
294      }
295    });
296  }
297
298  async initProcess() {
299    Reflect.ownKeys(this.group).forEach((key, index) => {
300      let array = this.group[key] as Array<PerfThread>;
301      let process = array.filter((th) => th.pid === th.tid)[0];
302      let row = TraceRow.skeleton<HiPerfProcessStruct>();
303      row.rowId = `${process.pid}-Perf-Process`;
304      row.index = index;
305      row.rowType = TraceRow.ROW_TYPE_HIPERF_PROCESS;
306      row.rowParentId = 'HiPerf';
307      row.rowHidden = !this.rowFolder.expansion;
308      row.folder = true;
309      row.name = `${process.processName || 'Process'} [${process.pid}]`;
310      row.folderPaddingLeft = 6;
311      row.style.height = '40px';
312      row.favoriteChangeHandler = this.trace.favoriteChangeHandler;
313      row.selectChangeHandler = this.trace.selectChangeHandler;
314      row.supplier = () => queryHiPerfProcessData(process.pid);
315      row.focusHandler = () => this.hoverTip(row, HiPerfProcessStruct.hoverStruct);
316      row.onThreadHandler = (useCache) => {
317        let context = this.trace.canvasPanelCtx!;
318        row.canvasSave(context);
319        if (row.expansion) {
320          this.trace.canvasPanelCtx?.clearRect(0, 0, row.frame.width, row.frame.height);
321        } else {
322          (renders['HiPerf-Process'] as HiperfProcessRender).renderMainThread(
323            {
324              context: context,
325              useCache: useCache,
326              scale: TraceRow.range?.scale || 50,
327              type: `HiPerf-Process-${row.index}`,
328              intervalPerf: SpHiPerf.stringResult?.fValue || 1,
329              range: TraceRow.range,
330            },
331            row
332          );
333        }
334        row.canvasRestore(context);
335      };
336      this.rowFolder.addChildTraceRow(row);
337      this.rowList?.push(row);
338      array.forEach((thObj, thIdx) => {
339        let thread = TraceRow.skeleton<HiPerfThreadStruct>();
340        thread.rowId = `${thObj.tid}-Perf-Thread`;
341        thread.index = thIdx;
342        thread.rowType = TraceRow.ROW_TYPE_HIPERF_THREAD;
343        thread.rowParentId = row.rowId;
344        thread.rowHidden = !row.expansion;
345        thread.folder = false;
346        thread.name = `${thObj.threadName || 'Thread'} [${thObj.tid}]`;
347        thread.setAttribute('children', '');
348        thread.folderPaddingLeft = 0;
349        thread.style.height = '40px';
350        thread.favoriteChangeHandler = this.trace.favoriteChangeHandler;
351        thread.selectChangeHandler = this.trace.selectChangeHandler;
352        thread.supplier = () => queryHiPerfThreadData(thObj.tid);
353        thread.focusHandler = () => this.hoverTip(thread, HiPerfThreadStruct.hoverStruct);
354        thread.onThreadHandler = (useCache) => {
355          let context = thread.collect ? this.trace.canvasFavoritePanelCtx! : this.trace.canvasPanelCtx!;
356          thread.canvasSave(context);
357          (renders['HiPerf-Thread'] as HiperfThreadRender).renderMainThread(
358            {
359              context: context,
360              useCache: useCache,
361              scale: TraceRow.range?.scale || 50,
362              type: `HiPerf-Thread-${row.index}-${thread.index}`,
363              intervalPerf: SpHiPerf.stringResult?.fValue || 1,
364              range: TraceRow.range,
365            },
366            thread
367          );
368          thread.canvasRestore(context);
369        };
370        row.addChildTraceRow(thread);
371        this.rowList?.push(thread);
372      });
373    });
374  }
375
376  updateChartData() {
377    this.rowList?.forEach((it) => {
378      it.dataList = [];
379      it.dataList2 = [];
380      it.dataListCache = [];
381      it.isComplete = false;
382    });
383  }
384
385  hoverTip(
386    row: TraceRow<any>,
387    struct:
388      | HiPerfThreadStruct
389      | HiPerfProcessStruct
390      | HiPerfEventStruct
391      | HiPerfReportStruct
392      | HiPerfCpuStruct
393      | undefined
394  ) {
395    let tip = '';
396    if (struct) {
397      let num = 0;
398      if (struct instanceof HiPerfEventStruct) {
399        num = Math.trunc(((struct.sum || 0) / (struct.max || 0)) * 100);
400      } else {
401        num = Math.trunc(((struct.height || 0) / 40) * 100);
402      }
403      if (num > 0) {
404        tip = `<span>${num * (this.maxCpuId + 1)}% (10.00ms)</span>`;
405      } else {
406        let perfCall = perfDataQuery.callChainMap.get(struct.callchain_id || 0);
407        tip = `<span>${perfCall ? perfCall.name : ''} (${perfCall ? perfCall.depth : '0'} other frames)</span>`;
408      }
409    }
410    this.trace?.displayTip(row, struct, tip);
411  }
412}
413