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