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