• 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 {
17  BaseStruct,
18  drawFlagLine,
19  drawLines,
20  drawLoading,
21  drawSelection,
22  PerfRender,
23  RequestMessage,
24} from './ProcedureWorkerCommon.js';
25import { TraceRow } from '../../component/trace/base/TraceRow.js';
26
27export class FileSystemRender extends PerfRender {
28  renderMainThread(
29    req: {
30      context: CanvasRenderingContext2D;
31      useCache: boolean;
32      type: string;
33      chartColor: string;
34    },
35    row: TraceRow<FileSysChartStruct>
36  ) {
37    let list = row.dataList;
38    let filter = row.dataListCache;
39    let groupBy10MS = (TraceRow.range?.scale || 50) > 40_000_000;
40    let isDiskIO: boolean = req.type.includes('disk-io');
41    if (list && row.dataList2.length == 0) {
42      row.dataList2 = isDiskIO
43        ? FileSysChartStruct.groupBy10MSWithMaxLatency(list)
44        : FileSysChartStruct.groupBy10MSWithCount(list);
45    }
46    fileSysChart(
47      list,
48      row.dataList2,
49      req.type,
50      filter,
51      TraceRow.range?.startNS ?? 0,
52      TraceRow.range?.endNS ?? 0,
53      TraceRow.range?.totalNS ?? 0,
54      row.frame,
55      groupBy10MS,
56      isDiskIO,
57      req.useCache || (TraceRow.range?.refresh ?? false)
58    );
59    req.context.beginPath();
60    let find = false;
61    let hoverRect: FileSysChartStruct | undefined = undefined;
62    for (let re of filter) {
63      if (row.isHover && re.frame && row.hoverX >= re.frame.x && row.hoverX <= re.frame.x + re.frame.width) {
64        if (hoverRect == undefined || re.size! > hoverRect.size!) {
65          hoverRect = re;
66          find = true;
67        }
68      }
69      if (re.frame && re.frame!.x > row.hoverX + 3) {
70        break;
71      }
72    }
73    if (hoverRect) {
74      FileSysChartStruct.hoverFileSysStruct = hoverRect;
75    }
76    for (let re of filter) {
77      FileSysChartStruct.draw(req.context, re, req.chartColor);
78    }
79    if (!find && row.isHover) FileSysChartStruct.hoverFileSysStruct = undefined;
80    req.context.closePath();
81  }
82
83  render(fileSysRequest: RequestMessage, list: Array<any>, filter: Array<any>, dataList2: Array<any>) {
84    let groupBy10MS = fileSysRequest.scale > 20_000_000;
85    let isDiskIO: boolean = fileSysRequest.type!.includes('disk-io');
86    if (isDiskIO) {
87      groupBy10MS = true;
88    }
89    if (fileSysRequest.lazyRefresh) {
90      fileSysChart(
91        list,
92        dataList2,
93        fileSysRequest.type!,
94        filter,
95        fileSysRequest.startNS,
96        fileSysRequest.endNS,
97        fileSysRequest.totalNS,
98        fileSysRequest.frame,
99        groupBy10MS,
100        isDiskIO,
101        fileSysRequest.useCache || !fileSysRequest.range.refresh
102      );
103    } else {
104      if (!fileSysRequest.useCache) {
105        fileSysChart(
106          list,
107          dataList2,
108          fileSysRequest.type!,
109          filter,
110          fileSysRequest.startNS,
111          fileSysRequest.endNS,
112          fileSysRequest.totalNS,
113          fileSysRequest.frame,
114          groupBy10MS,
115          isDiskIO,
116          false
117        );
118      }
119    }
120    let hoverStruct: FileSysChartStruct | undefined;
121    if (fileSysRequest.canvas) {
122      fileSysRequest.context.clearRect(0, 0, fileSysRequest.frame.width, fileSysRequest.frame.height);
123      let arr = filter;
124      if (arr.length > 0 && !fileSysRequest.range.refresh && !fileSysRequest.useCache && fileSysRequest.lazyRefresh) {
125        drawLoading(
126          fileSysRequest.context,
127          fileSysRequest.startNS,
128          fileSysRequest.endNS,
129          fileSysRequest.totalNS,
130          fileSysRequest.frame,
131          arr[0].startNS,
132          arr[arr.length - 1].startNS + arr[arr.length - 1].dur
133        );
134      }
135      drawLines(fileSysRequest.context, fileSysRequest.xs, fileSysRequest.frame.height, fileSysRequest.lineColor);
136      fileSysRequest.context.stroke();
137      fileSysRequest.context.beginPath();
138      if (fileSysRequest.isHover) {
139        let offset = groupBy10MS ? 0 : 3;
140        for (let re of filter) {
141          if (
142            re.frame &&
143            fileSysRequest.hoverX >= re.frame.x - offset &&
144            fileSysRequest.hoverX <= re.frame.x + re.frame.width + offset
145          ) {
146            hoverStruct = re;
147            break;
148          }
149        }
150      }
151      for (let re of filter) {
152        FileSysChartStruct.draw(fileSysRequest.context, re, fileSysRequest.chartColor);
153      }
154      drawSelection(fileSysRequest.context, fileSysRequest.params);
155      fileSysRequest.context.closePath();
156      drawFlagLine(
157        fileSysRequest.context,
158        fileSysRequest.flagMoveInfo,
159        fileSysRequest.flagSelectedInfo,
160        fileSysRequest.startNS,
161        fileSysRequest.endNS,
162        fileSysRequest.totalNS,
163        fileSysRequest.frame,
164        fileSysRequest.slicesTime
165      );
166    }
167    let msg = {
168      id: fileSysRequest.id,
169      type: fileSysRequest.type,
170      results: fileSysRequest.canvas ? undefined : filter,
171      hover: hoverStruct,
172    };
173    self.postMessage(msg);
174  }
175}
176
177export function fileSysChart(
178  arr: Array<any>,
179  arr2: Array<any>,
180  type: string,
181  res: Array<any>,
182  startNS: number,
183  endNS: number,
184  totalNS: number,
185  frame: any,
186  groupBy10MS: boolean,
187  isDiskIO: boolean,
188  use: boolean
189) {
190  if (use && res.length > 0) {
191    //&& !groupBy10MS
192    let pns = (endNS - startNS) / frame.width;
193    let y = frame.y;
194    for (let i = 0; i < res.length; i++) {
195      let it = res[i];
196      if ((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS) {
197        if (!it.frame) {
198          it.frame = {};
199          it.frame.y = y;
200        }
201        it.frame.height = it.height;
202        FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame);
203      } else {
204        it.frame = null;
205      }
206    }
207    return;
208  }
209  res.length = 0;
210  if (arr) {
211    let list: Array<any> = [];
212    let pns = (endNS - startNS) / frame.width;
213    let y = frame.y;
214    if (groupBy10MS) {
215      list = arr2.filter((it) => (it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS);
216      let groups = list
217        .map((it) => {
218          if (!it.frame) {
219            it.frame = {};
220            it.frame.y = y;
221          }
222          it.frame.height = it.height;
223          FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame);
224          return it;
225        })
226        .reduce((pre, current, index, arr) => {
227          (pre[`${current.frame.x}`] = pre[`${current.frame.x}`] || []).push(current);
228          return pre;
229        }, {});
230      Reflect.ownKeys(groups).map((kv) => {
231        res.push(groups[kv][0]);
232      });
233    } else {
234      let filter = arr.filter((it) => (it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS);
235      list = isDiskIO
236        ? FileSysChartStruct.computeHeightNoGroupLatency(filter, totalNS)
237        : FileSysChartStruct.computeHeightNoGroup(filter, totalNS);
238      list.map((it) => {
239        if (!it.frame) {
240          it.frame = {};
241          it.frame.y = y;
242        }
243        it.frame.height = it.height;
244        FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame);
245        res.push(it);
246      });
247    }
248  }
249}
250
251export class FileSysChartStruct extends BaseStruct {
252  static hoverFileSysStruct: FileSysChartStruct | undefined;
253  startNS: number | undefined;
254  endNS: number | undefined;
255  dur: number | undefined;
256  size: number | undefined;
257  height: number | undefined;
258  group10Ms: boolean | undefined;
259
260  static draw(ctx: CanvasRenderingContext2D, data: FileSysChartStruct, chartColor: string) {
261    if (data.frame) {
262      ctx.fillStyle = chartColor;
263      ctx.strokeStyle = chartColor;
264      ctx.fillRect(data.frame.x, 40 - (data.height || 0), data.frame.width, data.height || 0);
265    }
266  }
267
268  static setFrame(fileSystemNode: any, pns: number, startNS: number, endNS: number, frame: any) {
269    if ((fileSystemNode.startNS || 0) < startNS) {
270      fileSystemNode.frame.x = 0;
271    } else {
272      fileSystemNode.frame.x = Math.floor(((fileSystemNode.startNS || 0) - startNS) / pns);
273    }
274    if ((fileSystemNode.startNS || 0) + (fileSystemNode.dur || 0) > endNS) {
275      fileSystemNode.frame.width = frame.width - fileSystemNode.frame.x;
276    } else {
277      fileSystemNode.frame.width = Math.ceil(
278        ((fileSystemNode.startNS || 0) + (fileSystemNode.dur || 0) - startNS) / pns - fileSystemNode.frame.x
279      );
280    }
281    if (fileSystemNode.frame.width < 1) {
282      fileSystemNode.frame.width = 1;
283    }
284  }
285
286  static computeHeightNoGroup(array: Array<any>, totalNS: number): Array<any> {
287    if (array.length > 0) {
288      let time: Array<{ time: number; type: number }> = [];
289      array.map((item) => {
290        time.push({ time: item.startNS, type: 1 });
291        time.push({ time: item.endNS || totalNS, type: -1 });
292      });
293      time = time.sort((a, b) => a.time - b.time);
294      let arr: Array<any> = [];
295      let first = {
296        startNS: time[0].time ?? 0,
297        dur: 0,
298        size: 1,
299        group10Ms: false,
300        height: 1,
301      };
302      arr.push(first);
303      let max = 2;
304      for (let i = 1, len = time.length; i < len; i++) {
305        let heap = {
306          startNS: time[i].time,
307          dur: 0,
308          size: 0,
309          group10Ms: false,
310          height: 0,
311        };
312        arr[i - 1].dur = heap.startNS - arr[i - 1].startNS;
313        if (i == len - 1) {
314          heap.dur = totalNS - heap.startNS;
315        }
316        heap.size = arr[i - 1].size + time[i].type;
317        heap.height = Math.floor((heap.size / 6) * 36);
318        max = max > heap.size ? max : heap.size;
319        arr.push(heap);
320      }
321      arr.map((it) => (it.height = Math.floor((it.size / max) * 36)));
322      return arr;
323    } else {
324      return [];
325    }
326  }
327
328  static groupBy10MSWithCount(array: Array<any>): Array<any> {
329    let obj = array
330      .map((it) => {
331        it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0;
332        return it;
333      })
334      .reduce((pre, current) => {
335        (pre[current['timestamp_group']] = pre[current['timestamp_group']] || []).push(current);
336        return pre;
337      }, {});
338    let arr: any[] = [];
339    let max = 1;
340    for (let aKey in obj) {
341      max = obj[aKey].length > max ? obj[aKey].length : max;
342    }
343    for (let aKey in obj) {
344      let ns = parseInt(aKey);
345      let height: number = Math.floor((obj[aKey].length / max) * 36);
346      arr.push({
347        startNS: ns,
348        dur: 1_000_000_0,
349        group10Ms: true,
350        size: obj[aKey].length,
351        height: height < 1 ? 1 : height,
352      });
353    }
354    return arr;
355  }
356
357  static computeHeightNoGroupLatency(array: Array<any>, totalNS: number): Array<any> {
358    if (array.length > 0) {
359      let max = 0;
360      let arr: Array<any> = [];
361      for (let io of array) {
362        let ioItem = {
363          startNS: io.startNS,
364          dur: io.endNS > totalNS ? totalNS - io.startNS : io.endNS - io.startNS,
365          size: io.dur,
366          group10Ms: false,
367          height: 0,
368        };
369        max = max > ioItem.size ? max : ioItem.size;
370        arr.push(ioItem);
371      }
372      arr.map((it) => {
373        let height = Math.floor((it.size / max) * 36);
374        it.height = height < 1 ? 1 : height;
375      });
376      return arr;
377    } else {
378      return [];
379    }
380  }
381
382  static groupBy10MSWithMaxLatency(array: Array<any>): Array<any> {
383    let obj = array
384      .map((it) => {
385        it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0;
386        return it;
387      })
388      .reduce((pre, current) => {
389        if (pre[current['timestamp_group']] == undefined || pre[current['timestamp_group']] == null) {
390          pre[current['timestamp_group']] = [];
391        }
392        if (pre[current['timestamp_group']].length > 0) {
393          let p = pre[current['timestamp_group']][0];
394          if (p.dur < current.dur) {
395            pre[current['timestamp_group']][0] = current;
396          }
397        } else {
398          pre[current['timestamp_group']][0] = current;
399        }
400        return pre;
401      }, {});
402    let arr: any[] = [];
403    let max = 1;
404    for (let aKey in obj) {
405      max = obj[aKey][0].dur > max ? obj[aKey][0].dur : max;
406    }
407    for (let aKey in obj) {
408      let ns = parseInt(aKey);
409      let height: number = Math.floor((obj[aKey][0].dur / max) * 36);
410      arr.push({
411        startNS: ns,
412        dur: 1_000_000_0,
413        group10Ms: true,
414        size: obj[aKey][0].dur,
415        height: height < 1 ? 1 : height,
416      });
417    }
418    return arr;
419  }
420}
421