• 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 {SpApplication} from "../SpApplication";
17
18export function ns2s(ns: number): string {
19    let second1 = 1_000_000_000; // 1 second
20    let millisecond1 = 1_000_000; // 1 millisecond
21    let microsecond1 = 1_000; // 1 microsecond
22    let nanosecond1 = 1000.0;
23    let res;
24    if (ns >= second1) {
25        res = (ns / 1000 / 1000 / 1000).toFixed(1) + " s";
26    } else if (ns >= millisecond1) {
27        res = (ns / 1000 / 1000).toFixed(1) + " ms";
28    } else if (ns >= microsecond1) {
29        res = (ns / 1000).toFixed(1) + " μs";
30    } else if (ns > 0) {
31        res = ns.toFixed(1) + " ns";
32    } else {
33        res = ns.toFixed(1) + " s";
34    }
35    return res;
36}
37
38export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: any) {
39    // @ts-ignore
40    if (endNS == 0) {
41        endNS = duration;
42    }
43    let xSize: number = (ns - startNS) * rect.width / (endNS - startNS);
44    if (xSize < 0) {
45        xSize = 0;
46    } else if (xSize > rect.width) {
47        xSize = rect.width;
48    }
49    return xSize;
50}
51
52export class Rect {
53    x: number = 0
54    y: number = 0
55    width: number = 0
56    height: number = 0
57
58    constructor(x: number, y: number, width: number, height: number) {
59        this.x = x;
60        this.y = y;
61        this.width = width;
62        this.height = height;
63    }
64
65    static contains(rect: Rect, x: number, y: number): boolean {
66        return rect.x <= x && x <= rect.x + rect.width && rect.y <= y && y <= rect.y + rect.height;
67    }
68
69    static containsWithPadding(rect: Rect, x: number, y: number, paddingLeftRight: number, paddingTopBottom: number): boolean {
70        return rect.x + paddingLeftRight <= x
71            && x <= rect.x + rect.width - paddingLeftRight
72            && rect.y + paddingTopBottom <= y
73            && y <= rect.y + rect.height - paddingTopBottom;
74    }
75
76    static containsWithMargin(rect: Rect, x: number, y: number, t: number, r: number, b: number, l: number): boolean {
77        return rect.x - l <= x
78            && x <= rect.x + rect.width + r
79            && rect.y - t <= y
80            && y <= rect.y + rect.height + b;
81    }
82
83    static intersect(r1: Rect, rect: Rect): boolean {
84        let maxX = r1.x + r1.width >= rect.x + rect.width ? r1.x + r1.width : rect.x + rect.width;
85        let maxY = r1.y + r1.height >= rect.y + rect.height ? r1.y + r1.height : rect.y + rect.height;
86        let minX = r1.x <= rect.x ? r1.x : rect.x;
87        let minY = r1.y <= rect.y ? r1.y : rect.y;
88        if (maxX - minX <= rect.width + r1.width && maxY - minY <= r1.height + rect.height) {
89            return true;
90        } else {
91            return false;
92        }
93    }
94
95    contains(x: number, y: number): boolean {
96        return this.x <= x && x <= this.x + this.width && this.y <= y && y <= this.y + this.height;
97    }
98
99    containsWithPadding(x: number, y: number, paddingLeftRight: number, paddingTopBottom: number): boolean {
100        return this.x + paddingLeftRight <= x
101            && x <= this.x + this.width - paddingLeftRight
102            && this.y + paddingTopBottom <= y
103            && y <= this.y + this.height - paddingTopBottom;
104    }
105
106    containsWithMargin(x: number, y: number, t: number, r: number, b: number, l: number): boolean {
107        return this.x - l <= x
108            && x <= this.x + this.width + r
109            && this.y - t <= y
110            && y <= this.y + this.height + b;
111    }
112
113    /**
114     * 判断是否相交
115     * @param rect
116     */
117    intersect(rect: Rect): boolean {
118        let maxX = this.x + this.width >= rect.x + rect.width ? this.x + this.width : rect.x + rect.width;
119        let maxY = this.y + this.height >= rect.y + rect.height ? this.y + this.height : rect.y + rect.height;
120        let minX = this.x <= rect.x ? this.x : rect.x;
121        let minY = this.y <= rect.y ? this.y : rect.y;
122        if (maxX - minX <= rect.width + this.width && maxY - minY <= this.height + rect.height) {
123            return true;
124        } else {
125            return false;
126        }
127    }
128}
129
130export class Point {
131    x: number = 0
132    y: number = 0
133
134    constructor(x: number, y: number) {
135        this.x = x;
136        this.y = y;
137    }
138}
139
140export class BaseStruct {
141    frame: Rect | undefined
142    isHover: boolean = false;
143}
144
145
146export class ColorUtils {
147    public static GREY_COLOR: string = "#f0f0f0"
148    /**
149     * Color array of all current columns
150     */
151    public static MD_PALETTE: Array<string> = [
152        "#3391ff",// red
153        "#0076ff",// pink
154        "#66adff",// purple
155        "#2db3aa",// deep purple
156        "#008078",// indigo
157        "#73e6de",// blue
158        "#535da6",// light blue
159        "#38428c", // cyan
160        "#7a84cc",// teal
161        "#ff9201",// green
162        "#ff7500",// light green
163        "#ffab40",// lime
164        "#2db4e2",// amber 0xffc105
165        "#0094c6", // orange
166        "#7cdeff",// deep orange
167        "#ffd44a", // brown
168        "#fbbf00",// blue gray
169        "#ffe593",// yellow 0xffec3d
170    ];
171    public static FUNC_COLOR: Array<string> = [
172        "#46B1E3",
173        "#0094c6", // orange
174        "#38428c",
175        "#38778c",
176        "#90708c",
177        "#9c27b0",
178        "#0076ff",// pink
179        "#66adff",// purple
180        "#2db4e2",
181        "#7cdeff",// deep orange
182        "#73e6de",// blue
183        "#bcaaa4",
184        "#2db3aa", // deep purple
185        "#008078", // blue
186        "#795548",
187        // "#ffab40",// lime
188        // "#c0ca33",
189        "#ff7500",// light green
190        "#ec407a",
191    ];
192
193    /**
194     * Get the color value according to the length of the string
195     *
196     * @param str str
197     * @param max max
198     * @return int
199     */
200    public static hash(str: string, max: number): number {
201        let colorA: number = 0x811c9dc5;
202        let colorB: number = 0xfffffff;
203        let colorC: number = 16777619;
204        let colorD: number = 0xffffffff;
205        let hash: number = colorA & colorB;
206
207        for (let index: number = 0; index < str.length; index++) {
208            hash ^= str.charCodeAt(index);
209            hash = (hash * colorC) & colorD;
210        }
211        return Math.abs(hash) % max;
212    }
213
214    public static hashFunc(str: string, depth: number, max: number): number {
215        let colorA: number = 0x811c9dc5;
216        let colorB: number = 0xfffffff;
217        let colorC: number = 16777619;
218        let colorD: number = 0xffffffff;
219        let hash: number = colorA & colorB;
220        let st = str.replace(/[0-9]+/g, "");
221        for (let index: number = 0; index < st.length; index++) {
222            hash ^= st.charCodeAt(index);
223            hash = (hash * colorC) & colorD;
224        }
225        return (Math.abs(hash) + depth) % max;
226    }
227
228    /**
229     * Get color according to tid
230     *
231     * @param tid tid
232     * @return Color
233     */
234    public static colorForTid(tid: number): string {
235        let colorIdx: number = ColorUtils.hash(`${tid}`, ColorUtils.MD_PALETTE.length);
236        return ColorUtils.MD_PALETTE[colorIdx];
237    }
238
239    public static formatNumberComma(str: number): string {
240        let l = str.toString().split("").reverse();
241        let t: string = "";
242        for (let i = 0; i < l.length; i++) {
243            t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "");
244        }
245        return t.split("").reverse().join("")
246    }
247}
248
249export function drawLines(ctx: CanvasRenderingContext2D, xs: Array<any>, height: number, lineColor: string) {
250    if (ctx) {
251        ctx.lineWidth = 1;
252        ctx.strokeStyle = lineColor || "#dadada";
253        xs?.forEach(it => {
254            ctx.moveTo(Math.floor(it), 0)
255            ctx.lineTo(Math.floor(it), height)
256        })
257        ctx.stroke();
258    }
259}
260
261export function drawFlagLine(ctx: any, hoverFlag: any, selectFlag: any, startNS: number, endNS: number, totalNS: number, frame: any, slicesTime: { startTime: number | null, endTime: number | null, color: string | null }) {
262    if (ctx) {
263        if (hoverFlag) {
264            ctx.beginPath();
265            ctx.lineWidth = 2;
266            ctx.strokeStyle = hoverFlag?.color || "#dadada";
267            ctx.moveTo(Math.floor(hoverFlag.x), 0)
268            ctx.lineTo(Math.floor(hoverFlag.x), frame.height)
269            ctx.stroke();
270            ctx.closePath();
271        }
272        if (selectFlag) {
273            ctx.beginPath();
274            ctx.lineWidth = 2;
275            ctx.strokeStyle = selectFlag?.color || "#dadada";
276            selectFlag.x = ns2x(selectFlag.time, startNS, endNS, totalNS, frame);
277            ctx.moveTo(Math.floor(selectFlag.x), 0)
278            ctx.lineTo(Math.floor(selectFlag.x), frame.height)
279            ctx.stroke();
280            ctx.closePath();
281        }
282        if (slicesTime && slicesTime.startTime && slicesTime.endTime) {
283            ctx.beginPath();
284            ctx.lineWidth = 1;
285            ctx.strokeStyle = slicesTime.color || "#dadada";
286            let x1 = ns2x(slicesTime.startTime, startNS, endNS, totalNS, frame);
287            let x2 = ns2x(slicesTime.endTime, startNS, endNS, totalNS, frame);
288            ctx.moveTo(Math.floor(x1), 0)
289            ctx.lineTo(Math.floor(x1), frame.height)
290            ctx.moveTo(Math.floor(x2), 0)
291            ctx.lineTo(Math.floor(x2), frame.height)
292            ctx.stroke();
293            ctx.closePath();
294        }
295    }
296}
297
298/**
299 * get framechart color by percent
300 * @param widthPercentage proportion of function
301 * @returns rbg
302 */
303export function getHeatColor(widthPercentage: number) {
304    return {
305        r: Math.floor(245 + 10 * (1 - widthPercentage)),
306        g: Math.floor(110 + 105 * (1 - widthPercentage)),
307        b: 100,
308    };
309}
310
311export enum ChartMode {
312    Call,
313    Byte,
314    Count,
315}
316
317export class ChartStruct extends BaseStruct {
318    static hoverFuncStruct: ChartStruct | undefined;
319    static selectFuncStruct: ChartStruct | undefined;
320    static lastSelectFuncStruct: ChartStruct | undefined;
321    static padding: number = 1;
322    needShow = false;
323    depth: number = 0;
324    symbol: string = '';
325    size: number = 0;
326    count: number = 0;
327    type: ChartMode = ChartMode.Call;
328    parent: ChartStruct | undefined;
329    children: Array<ChartStruct> = [];
330
331    /**
332     * set function position
333     * @param node current function struct
334     * @param canvas_frame canvas
335     * @param total all rect size
336     */
337    static setFuncFrame(node: ChartStruct, canvas_frame: Rect, total: number, mode: ChartMode) {
338        if (!node.frame) {
339            node.frame = new Rect(0, 0, 0, 0);
340        }
341        // filter depth is 0
342        if (node.parent) {
343            let idx = node.parent.children.indexOf(node);
344            if (idx == 0) {
345                node.frame!.x = node.parent.frame!.x;
346            } else {
347                // set x by left frame. left frame is parent.children[idx - 1]
348                node.frame.x = node.parent.children[idx - 1].frame!.x + node.parent.children[idx - 1].frame!.width
349            }
350            if (mode == ChartMode.Byte) {
351                node.frame!.width = Math.floor(node.size / total * canvas_frame.width);
352            } else {
353                node.frame!.width = Math.floor(node.count / total * canvas_frame.width);
354            }
355            node.frame!.y = node.parent.frame!.y + 20;
356            node.frame!.height = 20;
357        }
358    }
359
360    /**
361     * draw rect
362     * @param ctx CanvasRenderingContext2D
363     * @param data rect which is need draw
364     * @param percent function size or count / total size or count
365     */
366    static draw(ctx: CanvasRenderingContext2D, data: ChartStruct, percent: number) {
367        let spApplication = <SpApplication>document.getElementsByTagName("sp-application")[0]
368        if (data.frame) {
369            // draw rect
370            let miniHeight = 20;
371            if (ChartStruct.isSelected(data)) {
372                ctx.fillStyle = `rgba(${82}, ${145}, ${255}, 0.9)`;
373            } else {
374                let color = getHeatColor(percent);
375                ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, 0.9)`;
376            }
377            ctx.fillRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - ChartStruct.padding * 2);
378            //draw border
379            if (ChartStruct.isHover(data)) {
380                if (spApplication.dark) {
381                    ctx.strokeStyle = "#fff";
382                } else {
383                    ctx.strokeStyle = "#000";
384                }
385            } else {
386                if (spApplication.dark) {
387                    ctx.strokeStyle = "#000";
388                } else {
389                    ctx.strokeStyle = "#fff";
390                }
391            }
392            ctx.lineWidth = 0.4;
393            ctx.strokeRect(data.frame.x, data.frame.y, data.frame.width, miniHeight - ChartStruct.padding * 2);
394
395            //draw symbol name
396            if (data.frame.width > 10) {
397                if (percent > 0.6 || ChartStruct.isSelected(data)) {
398                    ctx.fillStyle = "#fff";
399                } else {
400                    ctx.fillStyle = "#000";
401                }
402                ChartStruct.drawString(ctx, data.symbol || '', 5, data.frame);
403            }
404
405        }
406    }
407
408    /**
409     * draw function string in rect
410     * @param ctx CanvasRenderingContext2D
411     * @param str function Name
412     * @param textPadding textPadding
413     * @param frame canvas area
414     * @returns is draw
415     */
416    static drawString(ctx: CanvasRenderingContext2D, str: string, textPadding: number, frame: Rect): boolean {
417        let textMetrics = ctx.measureText(str);
418        let charWidth = Math.round(textMetrics.width / str.length)
419        if (textMetrics.width < frame.width - textPadding * 2) {
420            let x2 = Math.floor(frame.width / 2 - textMetrics.width / 2 + frame.x + textPadding)
421            ctx.fillText(str, x2, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2)
422            return true;
423        }
424        if (frame.width - textPadding * 2 > charWidth * 4) {
425            let chatNum = (frame.width - textPadding * 2) / charWidth;
426            let x1 = frame.x + textPadding
427            ctx.fillText(str.substring(0, chatNum - 4) + '...', x1, Math.floor(frame.y + frame.height / 2 + 2), frame.width - textPadding * 2)
428            return true;
429        }
430        return false;
431    }
432
433    static isHover(data: ChartStruct): boolean {
434        return ChartStruct.hoverFuncStruct == data;
435    }
436
437    static isSelected(data: ChartStruct): boolean {
438        return ChartStruct.lastSelectFuncStruct == data;
439    }
440
441
442}
443