• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// @ts-nocheck
2/*
3 * Copyright (C) 2022 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {BaseElement, element} from "../../base-ui/BaseElement.js";
18import "./trace/TimerShaftElement.js";
19import "./trace/base/TraceRow.js";
20import "./trace/base/TraceRowRecyclerView.js"
21import {
22    getAsyncEvents,
23    getCpuUtilizationRate,
24    getFps,
25    getFunDataByTid,
26    queryCpuData,
27    queryCpuFreq,
28    queryCpuFreqData,
29    queryCpuMax,
30    queryCpuMaxFreq,
31    queryProcess,
32    queryProcessData,
33    queryProcessMem,
34    queryProcessMemData,
35    queryProcessThreads,
36    queryThreadData,
37    queryTotalTime,
38    threadPool
39} from "../database/SqlLite.js";
40import {TraceRow} from "./trace/base/TraceRow.js";
41import {TimerShaftElement} from "./trace/TimerShaftElement.js";
42import {TimeRange} from "./trace/timer-shaft/RangeRuler.js";
43import {CpuStruct} from "../bean/CpuStruct.js";
44import {CpuFreqStruct} from "../bean/CpuFreqStruct.js";
45import {ProcessStruct} from "../bean/ProcessStruct.js";
46import {ColorUtils} from "./trace/base/ColorUtils.js";
47import "./trace/base/TraceSheet.js";
48import {TraceSheet} from "./trace/base/TraceSheet.js";
49import {ThreadStruct} from "../bean/ThreadStruct.js";
50import {ProcessMemStruct} from "../bean/ProcessMemStruct.js";
51import {FuncStruct} from "../bean/FuncStruct.js";
52import {FpsStruct} from "../bean/FpsStruct.js";
53import {RangeSelect} from "./trace/base/RangeSelect.js";
54import {SelectionParam} from "../bean/BoxSelection.js";
55import {procedurePool} from "../database/Procedure.js";
56import {SportRuler} from "./trace/timer-shaft/SportRuler.js";
57import {TraceRowRecyclerView} from "./trace/base/TraceRowRecyclerView.js";
58import {TraceRowObject} from "./trace/base/TraceRowObject.js";
59import {Rect} from "./trace/timer-shaft/Rect.js";
60
61@element('sp-recycler-system-trace')
62export class SpRecyclerSystemTrace extends BaseElement {
63    static scrollViewWidth = 0
64    rowsEL: TraceRowRecyclerView | undefined | null;
65    private timerShaftEL: TimerShaftElement | null | undefined;
66    private range: TimeRange | undefined
67    private traceSheetEL: TraceSheet | undefined | null;
68    private rangeSelect!: RangeSelect;
69    private processThreads: Array<ThreadStruct> = []
70    private processAsyncEvent: Array<ProcessMemStruct> = []
71    private processMem: Array<any> = []
72
73    initElements(): void {
74        this.rowsEL = this.shadowRoot?.querySelector<TraceRowRecyclerView>('.rows')
75        this.timerShaftEL = this.shadowRoot?.querySelector('.timer-shaft')
76        this.traceSheetEL = this.shadowRoot?.querySelector('.trace-sheet')
77        this.rangeSelect = new RangeSelect(this.timerShaftEL);
78        this.rangeSelect.rowsEL = this.rowsEL;
79        document?.addEventListener("flag-change", (event: any) => {
80            let flag = event.detail;
81            this.timerShaftEL?.modifyFlagList(event.detail.type, event.detail.flagObj)
82            if (flag.hidden) {
83                this.traceSheetEL?.setAttribute("mode", 'hidden');
84            }
85        })
86        SpRecyclerSystemTrace.scrollViewWidth = this.getScrollWidth()
87        this.rangeSelect.selectHandler = (rows) => {
88            let selection = new SelectionParam();
89            // 框选的 cpu ,无则不传
90            selection.cpus = [];
91            // 框选的 线程 id,无则不传
92            selection.threadIds = [];
93            // 款选的函数的 线程id ,无则不传
94            selection.funTids = [];
95            // 框选的 内存 trackId ,无则不传
96            selection.processTrackIds = [];
97            // 框选的起始时间
98            selection.leftNs = 0;
99            // 框选的结束时间
100            selection.rightNs = 0;
101            rows.forEach(it => {
102                if (it.rowType == TraceRow.ROW_TYPE_CPU) {
103                    selection.cpus.push(parseInt(it.rowId!))
104                } else if (it.rowType == TraceRow.ROW_TYPE_THREAD) {
105                    selection.threadIds.push(parseInt(it.rowId!))
106                } else if (it.rowType == TraceRow.ROW_TYPE_FUNC) {
107                    selection.funTids.push(parseInt(it.rowId!))
108                } else if (it.rowType == TraceRow.ROW_TYPE_MEM) {
109                    selection.trackIds.push(parseInt(it.rowId!))
110                } else if (it.rowType == TraceRow.ROW_TYPE_FPS) {
111                    selection.hasFps = true;
112                } else if (it.rowType == TraceRow.ROW_TYPE_HEAP) {
113                    selection.heapIds.push(parseInt(it.rowId!))
114                }
115                if (it.rangeSelect && it.rangeSelect.startNS) {
116                    selection.leftNs = it.rangeSelect.startNS;
117                }
118                if (it.rangeSelect && it.rangeSelect.endNS) {
119                    selection.rightNs = it.rangeSelect.endNS;
120                }
121            })
122            this.traceSheetEL?.rangeSelect(selection)
123        }
124        // @ts-ignore
125        new ResizeObserver((entries) => {
126            let width = entries[0].contentRect.width - 1 - SpRecyclerSystemTrace.scrollViewWidth;
127            requestAnimationFrame(() => {
128                this.timerShaftEL?.updateWidth(width)
129                this.shadowRoot!.querySelectorAll<TraceRow<any>>("trace-row").forEach(it => it.updateWidth(width))
130            })
131        }).observe(this)
132        // @ts-ignore
133        new ResizeObserver((entries) => {
134            let width = this.clientWidth - 1 - SpRecyclerSystemTrace.scrollViewWidth
135            requestAnimationFrame(() => {
136                this.timerShaftEL?.updateWidth(width)
137                this.shadowRoot!.querySelectorAll<TraceRow<any>>("trace-row").forEach(it => it.updateWidth(width))
138            })
139            // this.shadowRoot!.querySelectorAll<TraceRow<any>>("trace-row").forEach(it=>it.updateWidth(width))
140        }).observe(window.document.body)
141    }
142
143    getScrollWidth() {
144        let noScroll, scroll, oDiv = document.createElement('div');
145        oDiv.style.cssText = 'position:absolute; top:-1000px;     width:100px; height:100px; overflow:hidden;';
146        noScroll = document.body.appendChild(oDiv).clientWidth;
147        oDiv.style.overflowY = 'scroll';
148        scroll = oDiv.clientWidth;
149        document.body.removeChild(oDiv);
150        return noScroll - scroll + 1;
151    }
152
153    getVisibleRows(): Array<TraceRow<any>> {
154        return [...this.rowsEL!.shadowRoot!.querySelectorAll<TraceRow<any>>("trace-row")];
155    }
156
157    timerShaftELRangeChange = (e: any) => {
158        this.range = e.detail;
159        TraceRow.range = this.range;
160        let scrollTop = this.rowsEL?.scrollTop || 0
161        let scrollHeight = this.rowsEL?.clientHeight || 0
162        //在rowsEL显示范围内的 trace-row组件将收到时间区间变化通知
163        this.getVisibleRows().forEach(it => {
164            it.dataListCache.length = 0;
165            this.hoverStructNull();
166            it.drawObject();
167        })
168    }
169
170    rowsElOnScroll = (e: any) => {
171        this.hoverStructNull();
172        let rows = this.getVisibleRows();
173        rows.forEach((it, index) => {
174            if (index == 0 || index == rows.length - 1) {
175                it.dataListCache.length = 0;
176                it.drawObject();
177            }
178        })
179    }
180
181    documentOnMouseDown = (ev: MouseEvent) => {
182        this.rangeSelect.mouseDown(ev)
183    }
184
185    documentOnMouseUp = (ev: MouseEvent) => {
186        this.rangeSelect.mouseUp(ev);
187    }
188
189    documentOnMouseMove = (ev: MouseEvent) => {
190        let rows = this.getVisibleRows();
191        this.rangeSelect.mouseMove(rows, ev)
192        rows.forEach(tr => {
193            let x = ev.offsetX - (tr.canvasContainer?.offsetLeft || 0);
194            let y = ev.offsetY - (tr.canvasContainer?.offsetTop || 0) + (this.rowsEL?.scrollTop || 0);
195            //判断鼠标是否在当前 trace-row
196            if (x > tr.frame.x && x < tr.frame.x + tr.frame.width && y > tr.frame.y && y < tr.frame.y + tr.frame.height) {
197                this.hoverStructNull();
198                if (tr.rowType === TraceRow.ROW_TYPE_CPU) {
199                    CpuStruct.hoverCpuStruct = tr.onMouseHover(x, y);
200                    if (CpuStruct.hoverCpuStruct) {
201                        tr.tip = `<span>P:${CpuStruct.hoverCpuStruct.processName || "Process"} [${CpuStruct.hoverCpuStruct.processId}]</span><span>T:${CpuStruct.hoverCpuStruct.name} [${CpuStruct.hoverCpuStruct.tid}]</span>`;
202                    }
203                    tr.setTipLeft(x, CpuStruct.hoverCpuStruct)
204                } else if (tr.rowType === TraceRow.ROW_TYPE_CPU_FREQ) {
205                    CpuFreqStruct.hoverCpuFreqStruct = tr.onMouseHover(x, y);
206                    if (CpuFreqStruct.hoverCpuFreqStruct) {
207                        tr.tip = `<span>${ColorUtils.formatNumberComma(CpuFreqStruct.hoverCpuFreqStruct.value!)} kHz</span>`
208                    }
209                    tr.setTipLeft(x, CpuFreqStruct.hoverCpuFreqStruct)
210                } else if (tr.rowType === TraceRow.ROW_TYPE_THREAD) {
211                    ThreadStruct.hoverThreadStruct = tr.onMouseHover(x, y, false);
212                } else if (tr.rowType === TraceRow.ROW_TYPE_FUNC) {
213                    FuncStruct.hoverFuncStruct = tr.onMouseHover(x, y, false)
214                } else if (tr.rowType === TraceRow.ROW_TYPE_HEAP) {
215                    HeapStruct.hoverHeapStruct = tr.onMouseHover(x, y, false)
216                    if (HeapStruct.hoverHeapStruct) {
217                        tr.tip = `<span>${ColorUtils.formatNumberComma(HeapStruct.hoverHeapStruct.heapsize!)} byte</span>`
218                    }
219                    tr.setTipLeft(x, HeapStruct.hoverHeapStruct)
220                } else {
221                    this.hoverStructNull();
222                }
223            } else {
224                tr.onMouseLeave(x, y);
225            }
226            tr.drawObject();
227        })
228    }
229
230    hoverStructNull() {
231        CpuStruct.hoverCpuStruct = undefined;
232        CpuFreqStruct.hoverCpuFreqStruct = undefined;
233        ThreadStruct.hoverThreadStruct = undefined;
234        FuncStruct.hoverFuncStruct = undefined;
235    }
236
237    selectStructNull() {
238        CpuStruct.selectCpuStruct = undefined;
239        CpuFreqStruct.selectCpuFreqStruct = undefined;
240        ThreadStruct.selectThreadStruct = undefined;
241        FuncStruct.selectFuncStruct = undefined;
242    }
243
244    documentOnClick = (ev: MouseEvent) => {
245        if (this.rangeSelect.isDrag()) {
246            return;
247        }
248        this.rowsEL?.querySelectorAll<TraceRow<any>>("trace-row").forEach(it => it.rangeSelect = undefined)
249        this.selectStructNull();
250        if (CpuStruct.hoverCpuStruct) {
251            CpuStruct.selectCpuStruct = CpuStruct.hoverCpuStruct
252            this.traceSheetEL?.displayCpuData(CpuStruct.hoverCpuStruct);
253        } else if (ThreadStruct.hoverThreadStruct) {
254            ThreadStruct.selectThreadStruct = ThreadStruct.hoverThreadStruct;
255            this.traceSheetEL?.displayThreadData(ThreadStruct.hoverThreadStruct)
256        } else if (FuncStruct.hoverFuncStruct) {
257            FuncStruct.selectFuncStruct = FuncStruct.hoverFuncStruct;
258            this.traceSheetEL?.displayFuncData(FuncStruct.hoverFuncStruct)
259        } else if (SportRuler.rulerFlagObj) {
260
261        } else {
262            this.traceSheetEL?.setAttribute("mode", 'hidden');
263        }
264        this.documentOnMouseMove(ev)
265    }
266
267    connectedCallback() {
268        /**
269         * 监听时间轴区间变化
270         */
271        this.timerShaftEL?.addEventListener('range-change', this.timerShaftELRangeChange)
272        /**
273         * 监听rowsEL的滚动时间,刷新可见区域的trace-row组件的时间区间(将触发trace-row组件重绘)
274         */
275        this.rowsEL?.addEventListener('scroll', this.rowsElOnScroll)
276        /**
277         * 监听document的mousemove事件 坐标通过换算后找到当前鼠标所在的trace-row组件,将坐标传入
278         */
279        document.addEventListener('mousemove', this.documentOnMouseMove)
280        document.addEventListener('mousedown', this.documentOnMouseDown)
281        document.addEventListener('mouseup', this.documentOnMouseUp)
282        document.addEventListener('click', this.documentOnClick)
283    }
284
285    disconnectedCallback() {
286        this.timerShaftEL?.removeEventListener('range-change', this.timerShaftELRangeChange);
287        this.rowsEL?.removeEventListener('scroll', this.rowsElOnScroll);
288        document.removeEventListener('mousemove', this.documentOnMouseMove);
289        document.removeEventListener('click', this.documentOnClick);
290    }
291
292    loadDatabaseUrl(url: string, complete?: Function) {
293        this.init({url: url}).then(() => {
294            let scrollTop = this.rowsEL?.scrollTop || 0
295            let scrollHeight = this.rowsEL?.clientHeight || 0
296            this.rowsEL?.querySelectorAll("trace-row").forEach((it: any) => {
297                let top = it.offsetTop - (this.rowsEL?.offsetTop || 0);
298                if (top + it.clientHeight > scrollTop && top + it.clientHeight < scrollTop + scrollHeight + it.clientHeight) {
299                    (it as TraceRow<any>).dataListCache.length = 0;
300                }
301            })
302            if (complete) {
303                complete();
304            }
305        })
306    }
307
308    loadDatabaseArrayBuffer(buf: ArrayBuffer, complete?: Function) {
309        this.init({buf}).then(() => {
310            let scrollTop = this.rowsEL?.scrollTop || 0
311            let scrollHeight = this.rowsEL?.clientHeight || 0
312            this.rowsEL?.querySelectorAll("trace-row").forEach((it: any) => {
313                let top = it.offsetTop - (this.rowsEL?.offsetTop || 0);
314                if (top + it.clientHeight > scrollTop && top + it.clientHeight < scrollTop + scrollHeight + it.clientHeight) {
315                    (it as TraceRow<any>).dataListCache.length = 0;
316                }
317            })
318            if (complete) {
319                complete();
320            }
321        })
322    }
323
324    init = async (param: { buf?: ArrayBuffer, url?: string }) => {
325        if (this.rowsEL) this.rowsEL.innerHTML = ''
326        this.traceSheetEL?.setAttribute("mode", "hidden")
327        this.timerShaftEL?.reset();
328        procedurePool.clearCache();
329        param.buf && await threadPool.initSqlite(param.buf);
330        param.url && await threadPool.initServer(param.url);
331        this.processThreads = await queryProcessThreads();
332        this.processMem = await queryProcessMem()
333        this.processAsyncEvent = await getAsyncEvents()
334        await this.initTotalTime();
335        let cpuObjs = await this.initCpu();
336        await this.initCpuRate();
337        let freqObjs = await this.initCpuFreq();
338        let fpsObjs = await this.initFPS();
339        let processObjs = await this.initProcess();
340        this.rowsEL.dataSource = [...cpuObjs, ...freqObjs, ...fpsObjs, ...processObjs]
341        this.getVisibleRows().forEach(it => it.drawObject());
342    }
343    initCpuRate = async () => {
344        let rates = await getCpuUtilizationRate(0, this.timerShaftEL?.totalNS || 0);
345        if (this.timerShaftEL) this.timerShaftEL.cpuUsage = rates;
346    }
347    initTotalTime = async () => {
348        let res = await queryTotalTime();
349        if (this.timerShaftEL) {
350            this.timerShaftEL.totalNS = res[0].total
351            this.timerShaftEL.loadComplete = true;
352        }
353    }
354    initCpu = async () => {
355        let objs = [];
356        let array = await queryCpuMax();
357        if (array && array.length > 0 && array[0]) {
358            let cpuMax = array[0].cpu
359            CpuStruct.cpuCount = cpuMax + 1;
360            for (let i1 = 0; i1 < CpuStruct.cpuCount; i1++) {
361                const cpuId = i1;
362                let traceRow = new TraceRowObject();
363                traceRow.rowId = `${cpuId}`
364                traceRow.rowType = TraceRow.ROW_TYPE_CPU
365                traceRow.rowParentId = ''
366                traceRow.rowHeight = 40
367                traceRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, traceRow.rowHeight)
368                traceRow.name = `Cpu ${cpuId}`
369                traceRow.supplier = () => queryCpuData(cpuId, 0, this.timerShaftEL?.totalNS || 0)
370                traceRow.onThreadHandler = ((row, ctx) => {
371                    procedurePool.submitWithName("cpu", `cpu${cpuId}`, {
372                        list: traceRow.must ? traceRow.dataList : undefined,
373                        startNS: TraceRow.range?.startNS || 0,
374                        endNS: TraceRow.range?.endNS || 0,
375                        totalNS: TraceRow.range?.totalNS || 0,
376                        frame: traceRow.frame
377                    }, (res: any) => {
378                        traceRow.dataListCache = res;
379                        traceRow.must = false;
380                        row.clearCanvas();
381                        row.c!.beginPath();
382                        row.drawLines();
383                        for (let i = 0; i < res.length; i++) {
384                            CpuStruct.draw(ctx, res[i])
385                        }
386                        row.drawSelection();
387                        row.c!.closePath();
388                    })
389                })
390                objs.push(traceRow)
391            }
392        }
393        return objs;
394    }
395
396    initCpuFreq = async () => {
397        let objs = [];
398        let freqList = await queryCpuFreq();
399        let freqMaxList = await queryCpuMaxFreq();
400        CpuFreqStruct.maxFreq = freqMaxList[0].maxFreq;
401        let math = () => {
402            let units: Array<string> = ["", "K", "M", "G", "T", "E"];
403            let sb = " ";
404            CpuFreqStruct.maxFreqName = " ";
405            if (CpuFreqStruct.maxFreq > 0) {
406                let log10: number = Math.ceil(Math.log10(CpuFreqStruct.maxFreq));
407                let pow10: number = Math.pow(10, log10);
408                let afterCeil: number = Math.ceil(CpuFreqStruct.maxFreq / (pow10 / 4)) * (pow10 / 4);
409                CpuFreqStruct.maxFreq = afterCeil;
410                let unitIndex: number = Math.floor(log10 / 3);
411                sb = `${afterCeil / Math.pow(10, unitIndex * 3)}${units[unitIndex + 1]}hz`
412            }
413            CpuFreqStruct.maxFreqName = sb.toString();
414        }
415        math();
416        for (let i = 0; i < freqList.length; i++) {
417            const it = freqList[i];
418            let traceRow = new TraceRowObject();
419            traceRow.rowId = `${it.cpu}`
420            traceRow.rowType = TraceRow.ROW_TYPE_CPU_FREQ
421            traceRow.rowParentId = ''
422            traceRow.rowHeight = 40
423            traceRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, traceRow.rowHeight)
424            traceRow.name = `Cpu ${it.cpu} Frequency`;
425            traceRow.supplier = () => queryCpuFreqData(it.cpu)
426            traceRow.onThreadHandler = (row, ctx) => {
427                procedurePool.submitWithName("freq", `freq${it.cpu}`, {
428                    list: traceRow.must ? traceRow.dataList : undefined,
429                    startNS: TraceRow.range?.startNS || 0,
430                    endNS: TraceRow.range?.endNS || 0,
431                    totalNS: TraceRow.range?.totalNS || 0,
432                    frame: traceRow.frame
433                }, (res: any) => {
434                    traceRow.dataListCache = res;
435                    traceRow.must = false;
436                    row.clearCanvas();
437                    row.drawLines();
438                    row.c!.beginPath();
439                    for (let i = 0; i < res.length; i++) {
440                        CpuFreqStruct.draw(ctx, res[i])
441                    }
442                    row.drawSelection();
443                    row.c!.closePath();
444                    let s = CpuFreqStruct.maxFreqName
445                    let textMetrics = ctx.measureText(s);
446                    row.c!.globalAlpha = 0.8
447                    row.c!.fillStyle = "#f0f0f0"
448                    row.c!.fillRect(0, 5, textMetrics.width + 8, 18)
449                    row.c!.globalAlpha = 1
450                    row.c!.fillStyle = "#333"
451                    ctx.textBaseline = "middle"
452                    ctx.fillText(maxFps, 4, 5 + 9)
453                })
454            }
455            objs.push(traceRow)
456        }
457        return objs;
458    }
459
460    initFPS = async () => {
461        let objs = [];
462        let fpsRow = new TraceRowObject();
463        fpsRow.rowId = `fps`
464        fpsRow.rowType = TraceRow.ROW_TYPE_FPS
465        fpsRow.rowParentId = ''
466        FpsStruct.maxFps = 0
467        fpsRow.rowHeight = 40
468        fpsRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, fpsRow.rowHeight)
469        fpsRow.name = "FPS"
470        fpsRow.supplier = () => getFps()
471        fpsRow.onDrawHandler = (row, ctx) => {
472            if (fpsRow.dataListCache.length > 0) {
473                for (let i = 0; i < fpsRow.dataListCache.length; i++) {
474                    FpsStruct.draw(ctx, fpsRow.dataListCache[i])
475                }
476            } else {
477                if (fpsRow.dataList) {
478                    for (let i = 0; i < fpsRow.dataList.length; i++) {
479                        let it = fpsRow.dataList[i];
480                        if ((it.fps || 0) > FpsStruct.maxFps) {
481                            FpsStruct.maxFps = it.fps || 0
482                        }
483                        if (i === fpsRow.dataList.length - 1) {
484                            it.dur = (TraceRow.range?.endNS || 0) - (it.startNS || 0)
485                        } else {
486                            it.dur = (fpsRow.dataList[i + 1].startNS || 0) - (it.startNS || 0)
487                        }
488                        if ((it.startNS || 0) + (it.dur || 0) > (TraceRow.range?.startNS || 0) && (it.startNS || 0) < (TraceRow.range?.endNS || 0)) {
489                            FpsStruct.setFrame(fpsRow.dataList[i], 5, TraceRow.range?.startNS || 0, TraceRow.range?.endNS || 0, TraceRow.range?.totalNS || 0, fpsRow.frame)
490                            if (i > 0 && ((fpsRow.dataList[i - 1].frame?.x || 0) == (fpsRow.dataList[i].frame?.x || 0) && (fpsRow.dataList[i - 1].frame?.width || 0) == (fpsRow.dataList[i].frame?.width || 0))) {
491
492                            } else {
493                                fpsRow.dataListCache.push(fpsRow.dataList[i])
494                                FpsStruct.draw(ctx, fpsRow.dataList[i])
495                            }
496                        }
497                    }
498                }
499            }
500            if (ctx) {
501                let maxFps = FpsStruct.maxFps + "FPS"
502                let textMetrics = ctx.measureText(maxFps);
503                ctx.globalAlpha = 0.8
504                ctx.fillStyle = "#f0f0f0"
505                ctx.fillRect(0, 5, textMetrics.width + 8, 18)
506                ctx.globalAlpha = 1
507                ctx.fillStyle = "#333"
508                ctx.textBaseline = "middle"
509                ctx.fillText(maxFps, 4, 5 + 9)
510            }
511        }
512        objs.push(fpsRow)
513        return objs;
514    }
515
516    /**
517     * 添加进程信息
518     */
519    initProcess = async () => {
520        let objs = [];
521        let processList = await queryProcess();
522        for (let i = 0; i < processList.length; i++) {
523            const it = processList[i];
524            let processRow = new TraceRowObject<ProcessStruct>();
525            processRow.rowId = `${it.pid}`
526            processRow.rowType = TraceRow.ROW_TYPE_PROCESS
527            processRow.rowParentId = ''
528            processRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, processRow.rowHeight);
529            processRow.folder = true;
530            processRow.name = `${it.processName || "Process"} ${it.pid}`;
531            processRow.supplier = () => queryProcessData(it.pid || -1, 0, TraceRow.range?.totalNS || 0)
532            processRow.onThreadHandler = (row, ctx) => {
533                procedurePool.submitWithName("process", `process ${it.pid} ${it.processName}`, {
534                    list: processRow.must ? processRow.dataList : undefined,
535                    startNS: TraceRow.range?.startNS || 0,
536                    endNS: TraceRow.range?.endNS || 0,
537                    totalNS: TraceRow.range?.totalNS || 0,
538                    frame: processRow.frame
539                }, (res: any) => {
540                    processRow.dataListCache = res;
541                    processRow.must = false;
542                    row.clearCanvas();
543                    row.drawLines();
544                    row.c!.beginPath();
545                    for (let i = 0; i < res.length; i++) {
546                        ProcessStruct.draw(ctx, res[i])
547                    }
548                    row.drawSelection();
549                    row.c!.closePath();
550                })
551            }
552            objs.push(processRow);
553
554            /**
555             * 添加进程内存信息
556             */
557            let processMem = this.processMem.filter(mem => mem.pid === it.pid);
558            processMem.forEach(mem => {
559                let row = new TraceRowObject<ProcessMemStruct>();
560                row.rowId = `${mem.trackId}`
561                row.rowType = TraceRow.ROW_TYPE_MEM
562                row.rowParentId = `${it.pid}`
563                row.rowHidden = !processRow.expansion
564                row.rowHeight = 40
565                row.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, row.rowHeight)
566                row.name = `${mem.trackName}`;
567                row.children = true;
568                row.supplier = () => queryProcessMemData(mem.trackId).then(res => {
569                    let maxValue = Math.max(...res.map(it => it.value || 0))
570                    for (let j = 0; j < res.length; j++) {
571                        res[j].maxValue = maxValue;
572                        if (j == res.length - 1) {
573                            res[j].duration = this.range?.totalNS || 0;
574                        } else {
575                            res[j].duration = (res[j + 1].startTime || 0) - (res[j].startTime || 0);
576                        }
577                        if (j > 0) {
578                            res[j].delta = (res[j].value || 0) - (res[j - 1].value || 0);
579                        } else {
580                            res[j].delta = 0;
581                        }
582                    }
583                    return res
584                });
585                row.onThreadHandler = (r, ctx) => {
586                    procedurePool.submitWithName("mem", `mem ${mem.trackId} ${mem.trackName}`, {
587                        list: row.must ? row.dataList : undefined,
588                        startNS: TraceRow.range?.startNS || 0,
589                        endNS: TraceRow.range?.endNS || 0,
590                        totalNS: TraceRow.range?.totalNS || 0,
591                        frame: row.frame
592                    }, (res: any) => {
593                        row.dataListCache = res;
594                        row.must = false;
595                        r.clearCanvas();
596                        r.drawLines();
597                        r.c!.beginPath();
598                        for (let i = 0; i < res.length; i++) {
599                            ProcessMemStruct.draw(ctx, res[i])
600                        }
601                        r.drawSelection();
602                        r.c!.closePath();
603                    })
604                }
605                objs.push(row);
606            });
607            /**
608             * 添加进程线程信息
609             */
610            let threads = this.processThreads.filter(thread => thread.pid === it.pid && thread.tid != 0 && thread.threadName != null);
611            threads.forEach((thread, i) => {
612                let threadRow = new TraceRowObject<ThreadStruct>();
613                threadRow.rowId = `${thread.tid}`
614                threadRow.rowType = TraceRow.ROW_TYPE_THREAD
615                threadRow.rowParentId = `${it.pid}`
616                threadRow.rowHidden = !processRow.expansion
617                threadRow.rowHeight = 40
618                threadRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, threadRow.rowHeight);
619                threadRow.name = `${thread.threadName} ${thread.tid}`;
620                threadRow.children = true;
621                threadRow.supplier = () => queryThreadData(thread.tid || 0).then(res => {
622                    getFunDataByTid(thread.tid || 0).then(funs => {
623                        if (funs.length > 0) {
624                            let maxHeight = (Math.max(...funs.map(it => it.depth || 0)) + 1) * 20 + 20;
625                            let funcRow = new TraceRowObject<FuncStruct>();
626                            funcRow.rowId = `${thread.tid}`
627                            funcRow.rowType = TraceRow.ROW_TYPE_FUNC
628                            funcRow.rowParentId = `${it.pid}`
629                            funcRow.rowHidden = !processRow.expansion
630                            funcRow.rowHeight = maxHeight;
631                            funcRow.frame = new Rect(0, 0, this.rowsEL.clientWidth - 248, funcRow.rowHeight);
632                            funcRow.name = `${thread.threadName} ${thread.tid}`;
633                            funcRow.children = true;
634                            funcRow.supplier = () => new Promise((resolve, reject) => resolve(funs))
635                            funcRow.onThreadHandler = (r, ctx) => {
636                                procedurePool.submitWithName("func", `func ${thread.tid} ${thread.threadName}`, {
637                                    list: funcRow.must ? funcRow.dataList : undefined,
638                                    startNS: TraceRow.range?.startNS || 0,
639                                    endNS: TraceRow.range?.endNS || 0,
640                                    totalNS: TraceRow.range?.totalNS || 0,
641                                    frame: threadRow.frame
642                                }, (res: any) => {
643                                    funcRow.must = false;
644                                    funcRow.dataListCache = res;
645                                    r.clearCanvas();
646                                    r.drawLines();
647                                    r.c!.beginPath();
648                                    for (let i = 0; i < res.length; i++) {
649                                        FuncStruct.draw(ctx, res[i])
650                                    }
651                                    r.drawSelection();
652                                    r.c!.closePath();
653                                })
654                            }
655                        }
656                    })
657                    return res;
658                })
659                threadRow.onThreadHandler = (r, ctx) => {
660                    procedurePool.submitWithName("thread", `thread ${thread.tid} ${thread.threadName}`, {
661                        list: threadRow.must ? threadRow.dataList : undefined,
662                        startNS: TraceRow.range?.startNS || 0,
663                        endNS: TraceRow.range?.endNS || 0,
664                        totalNS: TraceRow.range?.totalNS || 0,
665                        frame: threadRow.frame
666                    }, (res: any) => {
667                        threadRow.dataListCache = res;
668                        threadRow.must = false;
669                        r.clearCanvas();
670                        r.drawLines();
671                        r.c!.beginPath();
672                        for (let i = 0; i < res.length; i++) {
673                            ThreadStruct.draw(ctx, res[i])
674                        }
675                        r.drawSelection();
676                        r.c!.closePath();
677                    })
678                }
679                objs.push(threadRow);
680            })
681        }
682        return objs;
683    }
684
685    insertAfter(newEl: HTMLElement, targetEl: HTMLElement) {
686        let parentEl = targetEl.parentNode;
687        if (parentEl!.lastChild == targetEl) {
688            parentEl!.appendChild(newEl);
689        } else {
690            parentEl!.insertBefore(newEl, targetEl.nextSibling);
691        }
692    }
693
694    initHtml(): string {
695        return `
696        <style>
697        :host{
698            display: block;
699            width: 100%;
700            height: 100%;
701        }
702        .timer-shaft{
703            width: 100%;
704            z-index: 2;
705        }
706        .rows{
707            display: flex;
708            box-sizing: border-box;
709            flex-direction: column;
710            overflow-y: auto;
711            max-height: calc(100vh - 150px - 48px);
712            flex: 1;
713            width: 100%;
714        }
715        .container{
716            width: 100%;
717            box-sizing: border-box;
718            height: 100%;
719            display: grid;
720            grid-template-columns: 1fr;
721            grid-template-rows: min-content 1fr min-content;
722        }
723
724        </style>
725        <div class="container">
726            <timer-shaft-element class="timer-shaft">
727            </timer-shaft-element>
728            <trace-row-recycler-view class="rows">
729            </trace-row-recycler-view>
730            <trace-sheet class="trace-sheet" mode="hidden">
731            </trace-sheet>
732        </div>
733        `;
734    }
735
736}
737