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