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 16 17export class ChartStruct { 18 depth: number = 0; 19 symbol: string = ''; 20 lib: string = ''; 21 path: string = ''; 22 addr: string = ''; 23 size: number = 0; 24 count: number = 0; 25 eventCount: number = 0; 26 eventPercent: string = ''; 27 dur: number = 0; 28 parent: ChartStruct | undefined; 29 children: Array<ChartStruct> = []; 30 isSearch: boolean = false; 31 tsArray: Array<number> = []; // 每个绘制的函数由哪些时间点的样本组成 32 countArray: Array<number> = []; // native hook统计模式下一个时间点有多次分配 33 durArray: Array<number> = []; 34 isThread: boolean = false; 35 isProcess: boolean = false; 36} 37 38export class Msg { 39 tag: string = ''; 40 index: number = 0; 41 isSending: boolean = false; 42 data: Array<unknown> = []; 43} 44 45export class HiPerfSymbol { 46 id: number = 0; 47 startTime: number = 0; 48 eventCount: number = 0; 49 endTime: number = 0; 50 totalTime: number = 0; 51 fileId: number = 0; 52 symbolId: number = 0; 53 cpu_id: number = 0; 54 depth: number = 0; 55 children?: Array<HiPerfSymbol>; 56 callchain_id: number = 0; 57 thread_id: number = 0; 58 name: string = ''; 59 60 public clone(): HiPerfSymbol { 61 const cloneSymbol = new HiPerfSymbol(); 62 cloneSymbol.children = []; 63 cloneSymbol.depth = this.depth; 64 return cloneSymbol; 65 } 66} 67 68export class MerageBean extends ChartStruct { 69 #parentNode: MerageBean | undefined = undefined; 70 #total = 0; 71 parent: MerageBean | undefined = undefined; 72 id: string = ''; 73 parentId: string = ''; 74 self?: string = '0s'; 75 weight?: string; 76 weightPercent?: string; 77 selfDur: number = 0; 78 dur: number = 0; 79 pid: number = 0; 80 canCharge: boolean = true; 81 isStore = 0; 82 isSelected: boolean = false; 83 searchShow: boolean = true; 84 children: MerageBean[] = []; 85 initChildren: MerageBean[] = []; 86 type: number = 0; 87 set parentNode(data: MerageBean | undefined) { 88 this.parent = data; 89 this.#parentNode = data; 90 } 91 92 get parentNode(): MerageBean | undefined { 93 return this.#parentNode; 94 } 95 96 set total(data: number) { 97 this.#total = data; 98 this.weight = `${getProbablyTime(this.dur)}`; 99 this.weightPercent = `${((this.dur / data) * 100).toFixed(1)}%`; 100 } 101 102 get total(): number { 103 return this.#total; 104 } 105} 106 107class MerageBeanDataSplit { 108 systmeRuleName = '/system/'; 109 numRuleName = '/max/min/'; 110 111 //所有的操作都是针对整个树结构的, 不区分特定的数据 112 splitTree( 113 splitMapData: unknown, 114 data: MerageBean[], 115 name: string, 116 isCharge: boolean, 117 isSymbol: boolean, 118 currentTreeList: ChartStruct[], 119 searchValue: string 120 ): void { 121 data.forEach((process) => { 122 process.children = []; 123 if (isCharge) { 124 this.recursionChargeInitTree(splitMapData, process, name, isSymbol); 125 } else { 126 this.recursionPruneInitTree(splitMapData, process, name, isSymbol); 127 } 128 }); 129 this.resetAllNode(data, currentTreeList, searchValue); 130 } 131 132 recursionChargeInitTree(splitMapData: unknown, node: MerageBean, symbolName: string, isSymbol: boolean): void { 133 if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) { 134 //@ts-ignore 135 (splitMapData[symbolName] = splitMapData[symbolName] || []).push(node); 136 node.isStore++; 137 } 138 if (node.initChildren.length > 0) { 139 node.initChildren.forEach((child) => { 140 this.recursionChargeInitTree(splitMapData, child, symbolName, isSymbol); 141 }); 142 } 143 } 144 145 recursionPruneInitTree(splitMapData: unknown, node: MerageBean, symbolName: string, isSymbol: boolean): void { 146 if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) { 147 //@ts-ignore 148 (splitMapData[symbolName] = splitMapData[symbolName] || []).push(node); 149 node.isStore++; 150 this.pruneChildren(splitMapData, node, symbolName); 151 } else if (node.initChildren.length > 0) { 152 node.initChildren.forEach((child) => { 153 this.recursionPruneInitTree(splitMapData, child, symbolName, isSymbol); 154 }); 155 } 156 } 157 158 //symbol lib prune 159 recursionPruneTree(node: MerageBean, symbolName: string, isSymbol: boolean): void { 160 if ((isSymbol && node.symbol === symbolName) || (!isSymbol && node.lib === symbolName)) { 161 node.parent && node.parent.children.splice(node.parent.children.indexOf(node), 1); 162 } else { 163 node.children.forEach((child) => { 164 this.recursionPruneTree(child, symbolName, isSymbol); 165 }); 166 } 167 } 168 169 recursionChargeByRule( 170 splitMapData: unknown, 171 node: MerageBean, 172 ruleName: string, 173 rule: (node: MerageBean) => boolean 174 ): void { 175 if (node.initChildren.length > 0) { 176 node.initChildren.forEach((child) => { 177 if (rule(child)) { 178 //@ts-ignore 179 (splitMapData[ruleName] = splitMapData[ruleName] || []).push(child); 180 child.isStore++; 181 } 182 this.recursionChargeByRule(splitMapData, child, ruleName, rule); 183 }); 184 } 185 } 186 187 pruneChildren(splitMapData: unknown, node: MerageBean, symbolName: string): void { 188 if (node.initChildren.length > 0) { 189 node.initChildren.forEach((child) => { 190 child.isStore++; 191 //@ts-ignore 192 (splitMapData[symbolName] = splitMapData[symbolName] || []).push(child); 193 this.pruneChildren(splitMapData, child, symbolName); 194 }); 195 } 196 } 197 198 hideSystemLibrary(allProcess: MerageBean[], splitMapData: unknown): void { 199 allProcess.forEach((item) => { 200 item.children = []; 201 this.recursionChargeByRule(splitMapData, item, this.systmeRuleName, (node) => { 202 return node.path.startsWith(this.systmeRuleName); 203 }); 204 }); 205 } 206 207 hideNumMaxAndMin(allProcess: MerageBean[], splitMapData: unknown, startNum: number, endNum: string): void { 208 let max = endNum === '∞' ? Number.POSITIVE_INFINITY : parseInt(endNum); 209 allProcess.forEach((item) => { 210 item.children = []; 211 this.recursionChargeByRule(splitMapData, item, this.numRuleName, (node) => { 212 return node.count < startNum || node.count > max; 213 }); 214 }); 215 } 216 217 resotreAllNode(splitMapData: unknown, symbols: string[]): void { 218 symbols.forEach((symbol) => { 219 //@ts-ignore 220 let list = splitMapData[symbol]; 221 if (list !== undefined) { 222 list.forEach((item: unknown) => { 223 //@ts-ignore 224 item.isStore--; 225 }); 226 } 227 }); 228 } 229 230 resetAllNode(data: MerageBean[], currentTreeList: ChartStruct[], searchValue: string): void { 231 this.clearSearchNode(currentTreeList); 232 data.forEach((process) => { 233 process.searchShow = true; 234 process.isSearch = false; 235 }); 236 this.resetNewAllNode(data, currentTreeList); 237 if (searchValue !== '') { 238 this.findSearchNode(data, searchValue, false); 239 this.resetNewAllNode(data, currentTreeList); 240 } 241 } 242 243 resetNewAllNode(data: MerageBean[], currentTreeList: ChartStruct[]): void { 244 data.forEach((process) => { 245 process.children = []; 246 }); 247 let values = currentTreeList.map((item: ChartStruct) => { 248 item.children = []; 249 return item; 250 }); 251 values.forEach((item: unknown) => { 252 //@ts-ignore 253 if (item.parentNode !== undefined) { 254 //@ts-ignore 255 if (item.isStore === 0 && item.searchShow) { 256 //@ts-ignore 257 let parentNode = item.parentNode; 258 while (parentNode !== undefined && !(parentNode.isStore === 0 && parentNode.searchShow)) { 259 parentNode = parentNode.parentNode; 260 } 261 if (parentNode) { 262 //@ts-ignore 263 item.currentTreeParentNode = parentNode; 264 parentNode.children.push(item); 265 } 266 } 267 } 268 }); 269 } 270 271 findSearchNode(data: MerageBean[], search: string, parentSearch: boolean): void { 272 search = search.toLocaleLowerCase(); 273 data.forEach((item) => { 274 if ((item.symbol && item.symbol.toLocaleLowerCase().includes(search)) || parentSearch) { 275 item.searchShow = true; 276 item.isSearch = item.symbol !== undefined && item.symbol.toLocaleLowerCase().includes(search); 277 let parentNode = item.parent; 278 while (parentNode && !parentNode.searchShow) { 279 parentNode.searchShow = true; 280 parentNode = parentNode.parent; 281 } 282 } else { 283 item.searchShow = false; 284 item.isSearch = false; 285 } 286 if (item.children.length > 0) { 287 this.findSearchNode(item.children, search, item.searchShow); 288 } 289 }); 290 } 291 292 clearSearchNode(currentTreeList: ChartStruct[]): void { 293 currentTreeList.forEach((node) => { 294 //@ts-ignore 295 node.searchShow = true; 296 node.isSearch = false; 297 }); 298 } 299 300 splitAllProcess(allProcess: unknown[], splitMapData: unknown, list: unknown): void { 301 //@ts-ignore 302 list.forEach((item: unknown) => { 303 allProcess.forEach((process) => { 304 //@ts-ignore 305 if (item.select === '0') { 306 //@ts-ignore 307 this.recursionChargeInitTree(splitMapData, process, item.name, item.type === 'symbol'); 308 } else { 309 //@ts-ignore 310 this.recursionPruneInitTree(splitMapData, process, item.name, item.type === 'symbol'); 311 } 312 }); 313 //@ts-ignore 314 if (!item.checked) { 315 //@ts-ignore 316 this.resotreAllNode(splitMapData, [item.name]); 317 } 318 }); 319 } 320} 321 322export let merageBeanDataSplit = new MerageBeanDataSplit(); 323 324export abstract class LogicHandler { 325 abstract handle(data: unknown): void; 326 queryData(eventId: string, queryName: string, sql: string, args: unknown): void { 327 self.postMessage({ 328 id: eventId, 329 type: queryName, 330 isQuery: true, 331 args: args, 332 sql: sql, 333 }); 334 } 335 336 abstract clearAll(): void; 337} 338 339let dec = new TextDecoder(); 340 341export let setFileName = (path: string): string => { 342 let fileName = ''; 343 if (path) { 344 let number = path.lastIndexOf('/'); 345 if (number > 0) { 346 fileName = path.substring(number + 1); 347 return fileName; 348 } 349 } 350 return path; 351}; 352 353let pagination = (page: number, pageSize: number, source: Array<unknown>): unknown[] => { 354 let offset = (page - 1) * pageSize; 355 return offset + pageSize >= source.length 356 ? source.slice(offset, source.length) 357 : source.slice(offset, offset + pageSize); 358}; 359 360const PAGE_SIZE: number = 50_0000; 361export let postMessage = (id: unknown, action: string, results: Array<unknown>, pageSize: number = PAGE_SIZE): void => { 362 if (results.length > pageSize) { 363 let pageCount = Math.ceil(results.length / pageSize); 364 for (let i = 1; i <= pageCount; i++) { 365 let tag = 'start'; 366 if (i === 1) { 367 tag = 'start'; 368 } else if (i === pageCount) { 369 tag = 'end'; 370 } else { 371 tag = 'sending'; 372 } 373 let msg = new Msg(); 374 msg.tag = tag; 375 msg.index = i; 376 msg.isSending = tag !== 'end'; 377 msg.data = pagination(i, PAGE_SIZE, results); 378 self.postMessage({ 379 id: id, 380 action: action, 381 isSending: msg.tag !== 'end', 382 results: msg, 383 }); 384 } 385 results.length = 0; 386 } else { 387 let msg = new Msg(); 388 msg.tag = 'end'; 389 msg.index = 0; 390 msg.isSending = false; 391 msg.data = results; 392 self.postMessage({ id: id, action: action, results: msg }); 393 results.length = 0; 394 } 395}; 396export let translateJsonString = (str: string): string => { 397 return str // .padding 398 .replace(/[\t|\r|\n]/g, '') 399 .replace(/\\/g, '\\\\'); 400}; 401 402export let convertJSON = (arrBuf: ArrayBuffer | Array<unknown>): unknown[] => { 403 if (arrBuf instanceof ArrayBuffer) { 404 let string = dec.decode(arrBuf); 405 let jsonArray = []; 406 string = string.substring(string.indexOf('\n') + 1); 407 if (!string) { 408 } else { 409 let parse; 410 let tansStr = translateJsonString(string); 411 try { 412 parse = JSON.parse(translateJsonString(string)); 413 } catch { 414 tansStr = tansStr.replace(/[^\x20-\x7E]/g, '?'); //匹配乱码字符,将其转换为? 415 parse = JSON.parse(tansStr); 416 } 417 let columns = parse.columns; 418 let values = parse.values; 419 for (let i = 0; i < values.length; i++) { 420 let object = {}; 421 for (let j = 0; j < columns.length; j++) { 422 //@ts-ignore 423 object[columns[j]] = values[i][j]; 424 } 425 jsonArray.push(object); 426 } 427 } 428 return jsonArray; 429 } else { 430 return arrBuf; 431 } 432}; 433 434export let getByteWithUnit = (bytes: number): string => { 435 if (bytes < 0) { 436 return '-' + getByteWithUnit(Math.abs(bytes)); 437 } 438 let currentBytes = bytes; 439 let kb1 = 1 << 10; 440 let mb = (1 << 10) << 10; 441 let gb = ((1 << 10) << 10) << 10; // 1 gb 442 let res = ''; 443 if (currentBytes > gb) { 444 res += (currentBytes / gb).toFixed(2) + ' GB'; 445 } else if (currentBytes > mb) { 446 res += (currentBytes / mb).toFixed(2) + ' MB'; 447 } else if (currentBytes > kb1) { 448 res += (currentBytes / kb1).toFixed(2) + ' KB'; 449 } else { 450 res += Math.round(currentBytes) + ' byte'; 451 } 452 return res; 453}; 454 455export let getTimeString = (ns: number): string => { 456 let currentNs = ns; 457 let hour1 = 3600_000_000_000; 458 let minute1 = 60_000_000_000; 459 let second1 = 1_000_000_000; 460 let millisecond1 = 1_000_000; 461 let microsecond1 = 1_000; 462 let res = ''; 463 if (currentNs >= hour1) { 464 res += Math.floor(currentNs / hour1) + 'h '; 465 currentNs = currentNs - Math.floor(currentNs / hour1) * hour1; 466 } 467 if (currentNs >= minute1) { 468 res += Math.floor(currentNs / minute1) + 'm '; 469 currentNs = currentNs - Math.floor(ns / minute1) * minute1; 470 } 471 if (currentNs >= second1) { 472 res += Math.floor(currentNs / second1) + 's '; 473 currentNs = currentNs - Math.floor(currentNs / second1) * second1; 474 } 475 if (currentNs >= millisecond1) { 476 res += Math.floor(currentNs / millisecond1) + 'ms '; 477 currentNs = currentNs - Math.floor(currentNs / millisecond1) * millisecond1; 478 } 479 if (currentNs >= microsecond1) { 480 res += Math.floor(currentNs / microsecond1) + 'μs '; 481 currentNs = currentNs - Math.floor(currentNs / microsecond1) * microsecond1; 482 } 483 if (currentNs > 0) { 484 res += currentNs + 'ns '; 485 } 486 if (res === '') { 487 res = ns + ''; 488 } 489 return res; 490}; 491 492export function getProbablyTime(ns: number): string { 493 let currentNs = ns; 494 let hour1 = 3600_000_000_000; 495 let minute1 = 60_000_000_000; 496 let second1 = 1_000_000_000; 497 let millisecond1 = 1_000_000; 498 let microsecond1 = 1_000; 499 let res = ''; 500 if (currentNs >= hour1) { 501 res += (currentNs / hour1).toFixed(2) + 'h '; 502 } else if (currentNs >= minute1) { 503 res += (currentNs / minute1).toFixed(2) + 'm '; 504 } else if (currentNs >= second1) { 505 res += (currentNs / second1).toFixed(2) + 's '; 506 } else if (currentNs >= millisecond1) { 507 res += (currentNs / millisecond1).toFixed(2) + 'ms '; 508 } else if (currentNs >= microsecond1) { 509 res += (currentNs / microsecond1).toFixed(2) + 'μs '; 510 } else if (currentNs > 0) { 511 res += currentNs.toFixed(0) + 'ns '; 512 } else if (res === '') { 513 res = ns + ''; 514 } 515 return res; 516} 517 518export function getThreadUsageProbablyTime(ns: number): string { 519 let currentNs = ns; 520 let microsecond1 = 1_000; 521 let res = ''; 522 if (currentNs > 0) { 523 res += (currentNs / microsecond1).toFixed(2); 524 } else if (res === '') { 525 res = ns + ''; 526 } 527 return res; 528} 529 530export function timeMsFormat2p(timeNs: number): string { 531 let currentNs = timeNs; 532 let oneHour = 3600_000; 533 let oneMinute1 = 60_000; 534 let oneSecond = 1_000; // 1 second 535 let commonResult = ''; 536 if (currentNs >= oneHour) { 537 commonResult += Math.floor(currentNs / oneHour).toFixed(2) + 'h'; 538 return commonResult; 539 } 540 if (currentNs >= oneMinute1) { 541 commonResult += Math.floor(currentNs / oneMinute1).toFixed(2) + 'min'; 542 return commonResult; 543 } 544 if (currentNs >= oneSecond) { 545 commonResult += Math.floor(currentNs / oneSecond).toFixed(2) + 's'; 546 return commonResult; 547 } 548 if (currentNs > 0) { 549 commonResult += currentNs.toFixed(2) + 'ms'; 550 return commonResult; 551 } 552 if (commonResult === '') { 553 commonResult = '0s'; 554 } 555 return commonResult; 556} 557 558export function formatRealDate(date: Date, fmt: string): string { 559 let obj = { 560 'M+': date.getMonth() + 1, 561 'd+': date.getDate(), 562 'h+': date.getHours(), 563 'm+': date.getMinutes(), 564 's+': date.getSeconds(), 565 'q+': Math.floor((date.getMonth() + 3) / 3), 566 S: date.getMilliseconds(), 567 }; 568 if (/(y+)/.test(fmt)) { 569 fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); 570 } 571 for (let key in obj) { 572 if (new RegExp('(' + key + ')').test(fmt)) { 573 // @ts-ignore 574 fmt = fmt.replace( 575 RegExp.$1, 576 // @ts-ignore 577 RegExp.$1.length === 1 ? obj[key] : ('00' + obj[key]).substr(('' + obj[key]).length) 578 ); 579 } 580 } 581 return fmt; 582} 583 584export function formatRealDateMs(timeNs: number): string { 585 return formatRealDate(new Date(timeNs / 1000000), 'MM-dd hh:mm:ss.S'); 586} 587 588export class JsProfilerSymbol { 589 id: number = 0; 590 nameId: number = 0; 591 name: string = ''; 592 scriptId: number = 0; 593 urlId: number = 0; 594 url: string = ''; 595 line: number = 0; 596 column: number = 0; 597 hitCount: number = 0; 598 childrenString?: string; 599 childrenIds: Array<number> = []; 600 children?: Array<JsProfilerSymbol>; 601 parentId: number = 0; 602 depth: number = -1; 603 cpuProfilerData?: JsProfilerSymbol; 604 605 public clone(): JsProfilerSymbol { 606 const cloneSymbol = new JsProfilerSymbol(); 607 cloneSymbol.name = this.name; 608 cloneSymbol.url = this.url; 609 cloneSymbol.hitCount = this.hitCount; 610 cloneSymbol.children = []; 611 cloneSymbol.childrenIds = []; 612 cloneSymbol.parentId = this.parentId; 613 cloneSymbol.depth = this.depth; 614 cloneSymbol.cpuProfilerData = this.cpuProfilerData; 615 return cloneSymbol; 616 } 617} 618 619export class HeapTreeDataBean { 620 MoudleName: string | undefined; 621 AllocationFunction: string | undefined; 622 symbolId: number = 0; 623 fileId: number = 0; 624 startTs: number = 0; 625 endTs: number = 0; 626 eventType: string | undefined; 627 depth: number = 0; 628 heapSize: number = 0; 629 eventId: number = 0; 630 addr: string = ''; 631 callChinId: number = 0; 632} 633 634export class PerfCall { 635 sampleId: number = 0; 636 depth: number = 0; 637 name: string = ''; 638} 639 640export class FileCallChain { 641 callChainId: number = 0; 642 depth: number = 0; 643 symbolsId: number = 0; 644 pathId: number = 0; 645 ip: string = ''; 646 isThread: boolean = false; 647} 648 649export class DataCache { 650 public static instance: DataCache | undefined; 651 public dataDict = new Map<number, string>(); 652 public eBpfCallChainsMap = new Map<number, Array<FileCallChain>>(); 653 public nmFileDict = new Map<number, string>(); 654 public nmHeapFrameMap = new Map<number, Array<HeapTreeDataBean>>(); 655 public perfCountToMs = 1; // 1000 / freq 656 public perfCallChainMap: Map<number, PerfCall> = new Map<number, PerfCall>(); 657 public jsCallChain: Array<JsProfilerSymbol> | undefined; 658 public jsSymbolMap = new Map<number, JsProfilerSymbol>(); 659 660 public static getInstance(): DataCache { 661 if (!this.instance) { 662 this.instance = new DataCache(); 663 } 664 return this.instance; 665 } 666 667 public clearAll(): void { 668 if (this.dataDict) { 669 this.dataDict.clear(); 670 } 671 this.clearEBpf(); 672 this.clearNM(); 673 this.clearPerf(); 674 this.clearJsCache(); 675 } 676 677 public clearNM(): void { 678 this.nmFileDict.clear(); 679 this.nmHeapFrameMap.clear(); 680 } 681 682 public clearEBpf(): void { 683 this.eBpfCallChainsMap.clear(); 684 } 685 686 public clearJsCache(): void { 687 if (this.jsCallChain) { 688 this.jsCallChain.length = 0; 689 } 690 this.jsSymbolMap.clear(); 691 } 692 693 public clearPerf(): void { 694 this.perfCallChainMap.clear(); 695 } 696} 697 698export class InitAnalysis { 699 public static instance: InitAnalysis | undefined; 700 public isInitAnalysis: boolean = true; 701 public static getInstance(): InitAnalysis { 702 if (!this.instance) { 703 this.instance = new InitAnalysis(); 704 } 705 return this.instance; 706 } 707} 708 709interface perfAsyncList { 710 tid?: number; 711 pid?: number; 712 time?: number; 713 symbol?: string; 714 traceid?: string; 715 eventCount?: number; 716 sampleCount?: number; 717 jsFuncName?: string; 718 callerCallchainid?: number; 719 calleeCallchainid?: number; 720 asyncFuncName?: string; 721 eventType?: string; 722 children?: Array<perfAsyncList>; 723 eventTypeId?: number; 724 symbolName?: string; 725 callerCallStack?: Array<perfAsyncList>; 726 calleeCallStack?: Array<perfAsyncList>; 727 callStackList?: Array<perfAsyncList>; 728 parent?: perfAsyncList; 729 isProcess?: boolean; 730 isThread?: boolean; 731 depth?: number; 732 isSearch?: boolean; 733 isJsStack?: boolean; 734 lib?: string; 735 isChartSelectParent?: boolean; 736 isChartSelect?: boolean; 737 isDraw?: boolean; 738 drawDur?: number; 739 drawEventCount?: number; 740 drawCount?: number; 741 drawSize?: number; 742 searchEventCount?: number; 743 searchCount?: number; 744 searchDur?: number; 745 searchSize?: number; 746 size?: number; 747 count?: number; 748 dur?: number; 749 tsArray?: Array<number>; 750 isCharged?: boolean; 751 addr?: string; 752} 753 754export function dealAsyncData( 755 arr: Array<perfAsyncList>, 756 perfCallChain: object, 757 nmCallChain: Map<number, Array<{ addr: string, depth: number, eventId: number, fileId: number, symbolId: number }>>, 758 dataDict: Map<number, string>, 759 searchValue: string 760): Array<perfAsyncList> { 761 // 转换为小写字符 762 searchValue = searchValue.toLocaleLowerCase(); 763 // 循环遍历每一条数据 764 for (let i = 0; i < arr.length; i++) { 765 let flag: boolean = false; 766 // 定义每条数据的调用栈与被调用栈数组 767 arr[i].calleeCallStack! = []; 768 arr[i].callerCallStack! = []; 769 // 从前端缓存的perfcallchain表与native_hook_frame表中拿到calleeId与callerId对应的数据 770 // @ts-ignore 771 let calleeCallChain = perfCallChain[arr[i].calleeCallchainid]; 772 let callerCallChain = nmCallChain.get(arr[i].callerCallchainid!)!; 773 // 循环被调用栈数组,拿到该条采样数据对应的所有被调用栈信息 774 for (let j = 0; j < calleeCallChain.length; j++) { 775 let calleeStack: perfAsyncList = {}; 776 // 拿到每一层被调用栈栈名 777 calleeStack.symbolName = dataDict.get(calleeCallChain[j].name)!; 778 // 判断该条采样数据的被调用栈链中是否包含用户筛选字段 779 if (calleeStack.symbolName.toLocaleLowerCase().indexOf(searchValue) !== -1) { 780 flag = true; 781 } 782 // 获取calleeCallchainid、depth、eventTypeId、lib、addr 783 calleeStack.calleeCallchainid = arr[i].calleeCallchainid!; 784 calleeStack.depth = calleeCallChain[j].depth; 785 calleeStack.eventTypeId = arr[i].eventTypeId!; 786 calleeStack.lib = calleeCallChain[j].fileName; 787 calleeStack.addr = `${'0x'}${calleeCallChain[j].vaddrInFile.toString(16)}`; 788 // 填充到该条数据的被调用栈数组中 789 arr[i].calleeCallStack!.push(calleeStack); 790 } 791 for (let z = 0; z < callerCallChain.length; z++) { 792 let callerStack: perfAsyncList = {}; 793 // 拿到每一层被调用栈栈名 794 callerStack.symbolName = dataDict.get(callerCallChain[z].symbolId)!; 795 // 判断该条采样数据的调用栈链中是否包含用户筛选字段 796 if (callerStack.symbolName.toLocaleLowerCase().indexOf(searchValue) !== -1) { 797 flag = true; 798 } 799 // 获取callerCallchainid、depth、eventTypeId、lib、addr 800 callerStack.callerCallchainid = arr[i].callerCallchainid!; 801 callerStack.depth = callerCallChain[z].depth; 802 callerStack.eventTypeId = arr[i].eventTypeId!; 803 callerStack.addr = callerCallChain[z].addr; 804 callerStack.lib = setFileName(dataDict.get(callerCallChain[z].fileId)!); 805 // 填充到该条数据的调用栈数组中 806 arr[i].callerCallStack!.push(callerStack); 807 } 808 // 若存在用户筛选字段内容,数据进行保留。若不存在,则在返回给前端的数据中删除此条数据,减少前端处理的数据量 809 if (!flag) { 810 arr.splice(i, 1); 811 i--; 812 } 813 } 814 return arr; 815}