• 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 { ColorUtils } from '../../../component/trace/base/ColorUtils';
17import {
18  BaseStruct,
19  dataFilterHandler,
20  drawFlagLine,
21  drawLines,
22  drawLoadingFrame,
23  drawSelection,
24  drawWakeUp,
25  drawWakeUpList,
26  Rect,
27  Render,
28  RequestMessage,
29} from '../ProcedureWorkerCommon';
30import { TraceRow } from '../../../component/trace/base/TraceRow';
31import { SpSystemTrace } from '../../../component/SpSystemTrace';
32
33export class EmptyRender extends Render {
34  // @ts-ignore
35  renderMainThread(req: unknown, row: TraceRow<unknown>): void {
36    //@ts-ignore
37    drawLoadingFrame(req.context, [], row);
38  }
39  render(cpuReqMessage: RequestMessage, list: Array<CpuStruct>, filter: Array<CpuStruct>): void {
40    if (cpuReqMessage.canvas) {
41      cpuReqMessage.context.clearRect(0, 0, cpuReqMessage.frame.width, cpuReqMessage.frame.height);
42      cpuReqMessage.context.beginPath();
43      drawLines(cpuReqMessage.context, cpuReqMessage.xs!, cpuReqMessage.frame.height, cpuReqMessage.lineColor);
44      drawSelection(cpuReqMessage.context, cpuReqMessage.params);
45      cpuReqMessage.context.closePath();
46      drawFlagLine(
47        cpuReqMessage.context,
48        cpuReqMessage.flagMoveInfo!,
49        cpuReqMessage.flagSelectedInfo!,
50        cpuReqMessage.startNS,
51        cpuReqMessage.endNS,
52        cpuReqMessage.totalNS,
53        cpuReqMessage.frame,
54        cpuReqMessage.slicesTime
55      );
56    }
57    // @ts-ignore
58    self.postMessage({
59      id: cpuReqMessage.id,
60      type: cpuReqMessage.type,
61      results: cpuReqMessage.canvas ? undefined : filter,
62      hover: null,
63    });
64  }
65}
66
67export class CpuRender {
68  renderMainThread(
69    req: {
70      ctx: CanvasRenderingContext2D;
71      useCache: boolean;
72      type: string;
73      translateY: number;
74    },
75    row: TraceRow<CpuStruct>
76  ): void {
77    let cpuList = row.dataList;
78    let cpuFilter = row.dataListCache;
79    let startNS = TraceRow.range!.startNS ?? 0;
80    let endNS = TraceRow.range!.endNS ?? 0;
81    let totalNS = TraceRow.range!.totalNS ?? 0;
82    dataFilterHandler(cpuList, cpuFilter, {
83      startKey: 'startTime',
84      durKey: 'dur',
85      startNS: startNS,
86      endNS: endNS,
87      totalNS: totalNS,
88      frame: row.frame,
89      paddingTop: 3,
90      useCache: req.useCache || !(TraceRow.range?.refresh ?? false),
91    });
92    drawLoadingFrame(req.ctx, cpuFilter, row);
93    req.ctx.beginPath();
94    req.ctx.font = '11px sans-serif';
95    cpuFilter.forEach((re) => {
96      re.translateY = req.translateY;
97      CpuStruct.draw(req.ctx, re, req.translateY);
98    });
99    req.ctx.closePath();
100    let currentCpu = parseInt(req.type!.replace('cpu-data-', ''));
101    let wakeup = req.type === `cpu-data-${CpuStruct.selectCpuStruct?.cpu || 0}` ? CpuStruct.selectCpuStruct : undefined; // @ts-ignore
102    drawWakeUp(req.ctx, CpuStruct.wakeupBean, startNS, endNS, totalNS, row.frame, wakeup, currentCpu, true);
103    for (let i = 0; i < SpSystemTrace.wakeupList.length; i++) {
104      if (i + 1 === SpSystemTrace.wakeupList.length) {
105        return;
106      }
107      let wake = SpSystemTrace.wakeupList[i + 1];
108      let wakeupListItem =
109        req.type === `cpu-data-${SpSystemTrace.wakeupList[i]?.cpu || 0}` ? SpSystemTrace.wakeupList[i] : undefined; // @ts-ignore
110      drawWakeUpList(req.ctx, wake, startNS, endNS, totalNS, row.frame, wakeupListItem, currentCpu, true);
111    }
112  }
113
114  cpu(
115    cpuList: Array<CpuStruct>,
116    cpuRes: Array<CpuStruct>,
117    startNS: number,
118    endNS: number,
119    totalNS: number,
120    frame: Rect,
121    use: boolean
122  ): void {
123    if (use && cpuRes.length > 0) {
124      this.setFrameCpuByRes(cpuRes, startNS, endNS, frame);
125      return;
126    }
127    if (cpuList) {
128      this.setFrameCpuByList(cpuRes, startNS, endNS, frame, cpuList);
129    }
130  }
131
132  setFrameCpuByRes(cpuRes: Array<CpuStruct>, startNS: number, endNS: number, frame: Rect): void {
133    let pns = (endNS - startNS) / frame.width;
134    let y = frame.y + 5;
135    let height = frame.height - 10;
136    for (let i = 0, len = cpuRes.length; i < len; i++) {
137      let it = cpuRes[i];
138      if ((it.startTime || 0) + (it.dur || 0) > startNS && (it.startTime || 0) < endNS) {
139        if (!cpuRes[i].frame) {
140          cpuRes[i].frame = new Rect(0, 0, 0, 0);
141          cpuRes[i].frame!.y = y;
142          cpuRes[i].frame!.height = height;
143        }
144        CpuStruct.setCpuFrame(cpuRes[i], pns, startNS, endNS, frame);
145      } else {
146        cpuRes[i].frame = undefined;
147      }
148    }
149  }
150
151  setFrameCpuByList(
152    cpuRes: Array<CpuStruct>,
153    startNS: number,
154    endNS: number,
155    frame: Rect,
156    cpuList: Array<CpuStruct>
157  ): void {
158    cpuRes.length = 0;
159    let pns = (endNS - startNS) / frame.width; //每个像素多少ns
160    let y = frame.y + 5;
161    let height = frame.height - 10;
162    let left = 0;
163    let right = 0;
164    for (let i = 0, j = cpuList.length - 1, ib = true, jb = true; i < cpuList.length, j >= 0; i++, j--) {
165      if (cpuList[j].startTime! <= endNS && jb) {
166        right = j;
167        jb = false;
168      }
169      if (cpuList[i].startTime! + cpuList[i].dur! >= startNS && ib) {
170        left = i;
171        ib = false;
172      }
173      if (!ib && !jb) {
174        break;
175      }
176    }
177    let slice = cpuList.slice(left, right + 1);
178    let sum = 0;
179    for (let i = 0; i < slice.length; i++) {
180      if (!slice[i].frame) {
181        slice[i].frame = new Rect(0, 0, 0, 0);
182        slice[i].frame!.y = y;
183        slice[i].frame!.height = height;
184      }
185      if (slice[i].dur! >= pns) {
186        slice[i].v = true;
187        CpuStruct.setCpuFrame(slice[i], pns, startNS, endNS, frame);
188      } else {
189        if (i > 0) {
190          let c = slice[i].startTime! - slice[i - 1].startTime! - slice[i - 1].dur!;
191          if (c < pns && sum < pns) {
192            sum += c + slice[i - 1].dur!;
193            slice[i].v = false;
194          } else {
195            slice[i].v = true;
196            CpuStruct.setCpuFrame(slice[i], pns, startNS, endNS, frame);
197            sum = 0;
198          }
199        }
200      }
201    }
202    cpuRes.push(...slice.filter((it) => it.v));
203  }
204}
205export function CpuStructOnClick(rowType: string, sp: SpSystemTrace, cpuClickHandler: unknown): Promise<unknown> {
206  return new Promise((resolve, reject) => {
207    if (rowType === TraceRow.ROW_TYPE_CPU && CpuStruct.hoverCpuStruct) {
208      CpuStruct.selectCpuStruct = CpuStruct.hoverCpuStruct;
209      sp.timerShaftEL?.drawTriangle(CpuStruct.selectCpuStruct!.startTime || 0, 'inverted');
210      sp.traceSheetEL?.displayCpuData(
211        CpuStruct.selectCpuStruct,
212        (wakeUpBean) => {
213          CpuStruct.wakeupBean = wakeUpBean;
214          sp.refreshCanvas(false);
215        },
216        //@ts-ignore
217        cpuClickHandler
218      );
219      sp.timerShaftEL?.modifyFlagList(undefined);
220      reject(new Error());
221    } else {
222      resolve(null);
223    }
224  });
225}
226export class CpuStruct extends BaseStruct {
227  static cpuCount: number = 1; //最大cpu数量
228  static hoverCpuStruct: CpuStruct | undefined;
229  static selectCpuStruct: CpuStruct | undefined;
230  static wakeupBean: WakeupBean | null | undefined = null;
231  cpu: number | undefined;
232  dur: number | undefined;
233  end_state: string | undefined;
234  state: string | undefined;
235  id: number | undefined;
236  tid: number | undefined;
237  name: string | undefined;
238  priority: number | undefined;
239  processCmdLine: string | undefined;
240  processId: number | undefined;
241  processName: string | undefined;
242  displayProcess: string | undefined;
243  displayThread: string | undefined;
244  measurePWidth: number = 0;
245  measureTWidth: number = 0;
246  startTime: number | undefined;
247  argSetID: number | undefined;
248  type: string | undefined;
249  v: boolean = false;
250  nofinish: boolean = false;
251  ts: number | undefined;
252  itid: number | undefined;
253  process: string | undefined;
254  pid: number | undefined;
255  thread: string | undefined;
256  isKeyPath?: number;
257
258  static draw(ctx: CanvasRenderingContext2D, data: CpuStruct, translateY: number): void {
259    if (data.frame) {
260      let pid = data.processId || 0;
261      let tid = data.tid || 0;
262      let width = data.frame.width || 0;
263      if (data.tid === CpuStruct.hoverCpuStruct?.tid || !CpuStruct.hoverCpuStruct) {
264        ctx.globalAlpha = 1;
265        ctx.fillStyle = ColorUtils.colorForTid(pid > 0 ? pid : tid);
266      } else if (data.processId === CpuStruct.hoverCpuStruct?.processId) {
267        ctx.globalAlpha = 0.6;
268        ctx.fillStyle = ColorUtils.colorForTid(pid > 0 ? pid : tid);
269      } else {
270        ctx.globalAlpha = 1;
271        ctx.fillStyle = '#e0e0e0';
272      }
273      ctx.fillRect(data.frame.x, data.frame.y, width, data.frame.height);
274      ctx.globalAlpha = 1;
275      CpuStruct.drawText(ctx, data, width, pid, tid);
276      CpuStruct.drawRim(ctx, data, width);
277    }
278  }
279  static drawText(ctx: CanvasRenderingContext2D, data: CpuStruct, width: number, pid: number, tid: number): void {
280    let textFillWidth = width - textPadding * 2;
281    if (data.frame && textFillWidth > 3) {
282      if (data.displayProcess === undefined) {
283        data.displayProcess = `${data.processName || 'Process'} [${data.processId}]`;
284        data.measurePWidth = ctx.measureText(data.displayProcess).width;
285      }
286      if (data.displayThread === undefined) {
287        data.displayThread = `${data.name || 'Thread'} [${data.tid}] [Prio:${data.priority || 0}]`;
288        data.measureTWidth = ctx.measureText(data.displayThread).width;
289      }
290      let processCharWidth = Math.round(data.measurePWidth / data.displayProcess.length);
291      let threadCharWidth = Math.round(data.measureTWidth / data.displayThread.length);
292      ctx.fillStyle = ColorUtils.funcTextColor(ColorUtils.colorForTid(pid > 0 ? pid : tid));
293      ctx.font = '9px sans-serif';
294      ctx.textBaseline = 'bottom';
295      let y = data.frame.height / 2 + data.frame.y;
296      if (data.measurePWidth < textFillWidth) {
297        let x1 = Math.floor(width / 2 - data.measurePWidth / 2 + data.frame.x + textPadding);
298        ctx.fillText(data.displayProcess, x1, y, textFillWidth);
299      } else {
300        if (textFillWidth >= processCharWidth) {
301          let chatNum = textFillWidth / processCharWidth;
302          let x1 = data.frame.x + textPadding;
303          if (chatNum < 2) {
304            ctx.fillText(data.displayProcess.substring(0, 1), x1, y, textFillWidth);
305          } else {
306            ctx.fillText(data.displayProcess.substring(0, chatNum - 1) + '...', x1, y, textFillWidth);
307          }
308        }
309      }
310      ctx.textBaseline = 'top';
311      if (data.measureTWidth < textFillWidth) {
312        let x2 = Math.floor(width / 2 - data.measureTWidth / 2 + data.frame.x + textPadding);
313        ctx.fillText(data.displayThread, x2, y + 2, textFillWidth);
314      } else {
315        if (textFillWidth >= threadCharWidth) {
316          let chatNum = textFillWidth / threadCharWidth;
317          let x1 = data.frame.x + textPadding;
318          if (chatNum < 2) {
319            ctx.fillText(data.displayThread.substring(0, 1), x1, y + 2, textFillWidth);
320          } else {
321            ctx.fillText(data.displayThread.substring(0, chatNum - 1) + '...', x1, y + 2, textFillWidth);
322          }
323        }
324      }
325    }
326  }
327  static drawRim(ctx: CanvasRenderingContext2D, data: CpuStruct, width: number): void {
328    if (data.frame) {
329      if (data.nofinish && width > 4) {
330        ctx.fillStyle = '#fff';
331        let ruptureWidth = 4;
332        let ruptureNode = 8;
333        ctx.moveTo(data.frame.x + data.frame.width - 1, data.frame.y);
334        for (let i = 1; i <= ruptureNode; i++) {
335          ctx.lineTo(
336            data.frame.x + data.frame.width - 1 - (i % 2 === 0 ? 0 : ruptureWidth),
337            data.frame.y + (data.frame.height / ruptureNode) * i
338          );
339        }
340        ctx.closePath();
341        ctx.fill();
342      }
343      if (data.isKeyPath) {
344        const gradient = ctx.createLinearGradient(0, 0, 0, 40);
345        gradient.addColorStop(0, '#000000');
346        gradient.addColorStop(0.5, '#0000FF');
347        gradient.addColorStop(1, '#FF0000');
348        ctx.strokeStyle = gradient;
349        ctx.lineWidth = 4;
350        ctx.strokeRect(data.frame.x, data.frame.y - 2, width - 2, data.frame.height + 2);
351      }
352      if (CpuStruct.selectCpuStruct && CpuStruct.equals(CpuStruct.selectCpuStruct, data)) {
353        ctx.strokeStyle = '#232c5d';
354        ctx.lineWidth = 2;
355        ctx.strokeRect(data.frame.x, data.frame.y, width - 2, data.frame.height);
356      }
357    }
358  }
359
360  static setCpuFrame(cpuNode: CpuStruct, pns: number, startNS: number, endNS: number, frame: Rect): void {
361    if (!cpuNode.frame) {
362      return;
363    }
364    if ((cpuNode.startTime || 0) < startNS) {
365      cpuNode.frame.x = 0;
366    } else {
367      cpuNode.frame.x = Math.floor(((cpuNode.startTime || 0) - startNS) / pns);
368    }
369    if ((cpuNode.startTime || 0) + (cpuNode.dur || 0) > endNS) {
370      cpuNode.frame.width = frame.width - cpuNode.frame.x;
371    } else {
372      cpuNode.frame.width = Math.ceil(
373        ((cpuNode.startTime || 0) + (cpuNode.dur || 0) - startNS) / pns - cpuNode.frame.x
374      );
375    }
376    if (cpuNode.frame.width < 1) {
377      cpuNode.frame.width = 1;
378    }
379  }
380
381  static equals(d1: CpuStruct, d2: CpuStruct): boolean {
382    return (
383      d1 &&
384      d2 &&
385      d1.cpu === d2.cpu &&
386      d1.tid === d2.tid &&
387      d1.processId === d2.processId &&
388      d1.startTime === d2.startTime &&
389      d1.dur === d2.dur
390    );
391  }
392}
393
394export class WakeupBean {
395  wakeupTime: number | undefined;
396  cpu: number | undefined;
397  process: string | undefined;
398  pid: number | undefined;
399  thread: string | undefined;
400  dur: number | null | undefined;
401  tid: number | undefined;
402  schedulingLatency: number | undefined;
403  ts: number | undefined;
404  schedulingDesc: string | undefined;
405  itid: number | undefined;
406  state: string | undefined;
407  argSetID: number | undefined;
408}
409
410const textPadding = 2;
411