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