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 */ 15import { 16 convertJSON, 17 DataCache, 18 getByteWithUnit, 19 HeapTreeDataBean, 20 LogicHandler, 21 MerageBean, 22 merageBeanDataSplit, 23 postMessage, 24 setFileName, 25} from './ProcedureLogicWorkerCommon.js'; 26 27export class ProcedureLogicWorkerNativeMemory extends LogicHandler { 28 selectTotalSize = 0; 29 selectTotalCount = 0; 30 stackCount = 0; 31 NATIVE_MEMORY_DATA: Array<NativeEvent> = []; 32 currentTreeMapData: any = {}; 33 currentTreeList: any[] = []; 34 queryAllCallchainsSamples: NativeHookStatistics[] = []; 35 currentSamples: NativeHookStatistics[] = []; 36 allThreads: NativeHookCallInfo[] = []; 37 splitMapData: any = {}; 38 searchValue: string = ''; 39 currentEventId: string = ''; 40 chartComplete: Map<number, boolean> = new Map<number, boolean>(); 41 realTimeDif: number = 0; 42 responseTypes: { key: number; value: string }[] = []; 43 totalNS: number = 0; 44 isAnalysis: boolean = false; 45 isStatistic: boolean = false; 46 boxRangeNativeHook: Array<NativeMemory> = []; 47 clearBoxSelectionData: boolean = false; 48 nativeMemoryArgs?: Map<string, any> 49 private dataCache = DataCache.getInstance(); 50 51 handle(data: any): void { 52 this.currentEventId = data.id; 53 if (data && data.type) { 54 switch (data.type) { 55 case 'native-memory-init': 56 this.clearAll(); 57 if (data.params.isRealtime) { 58 this.realTimeDif = data.params.realTimeDif; 59 } 60 this.dataCache.dataDict = data.params.dataDict; 61 this.initNMChartData(); 62 break; 63 case 'native-memory-queryNMChartData': 64 this.NATIVE_MEMORY_DATA = convertJSON(data.params.list) || []; 65 this.initNMFrameData(); 66 break; 67 case 'native-memory-queryNMFrameData': 68 let arr = convertJSON(data.params.list) || []; 69 this.initNMStack(arr); 70 arr = []; 71 self.postMessage({ 72 id: data.id, 73 action: 'native-memory-init', 74 results: [], 75 }); 76 break; 77 case 'native-memory-queryCallchainsSamples': 78 this.searchValue = ''; 79 if (data.params.list) { 80 let callchainsSamples = convertJSON(data.params.list) || []; 81 this.queryAllCallchainsSamples = callchainsSamples; 82 this.freshCurrentCallchains(callchainsSamples, true); 83 // @ts-ignore 84 self.postMessage({ 85 id: data.id, 86 action: data.action, 87 results: this.allThreads, 88 }); 89 } else { 90 this.queryCallchainsSamples( 91 'native-memory-queryCallchainsSamples', 92 data.params.leftNs, 93 data.params.rightNs, 94 data.params.types 95 ); 96 } 97 break; 98 case 'native-memory-queryStatisticCallchainsSamples': 99 this.searchValue = ''; 100 if (data.params.list) { 101 let samples = convertJSON(data.params.list) || []; 102 this.queryAllCallchainsSamples = samples; 103 this.freshCurrentCallchains(samples, true); 104 // @ts-ignore 105 self.postMessage({ 106 id: data.id, 107 action: data.action, 108 results: this.allThreads, 109 }); 110 } else { 111 this.queryStatisticCallchainsSamples( 112 'native-memory-queryStatisticCallchainsSamples', 113 data.params.leftNs, 114 data.params.rightNs, 115 data.params.types 116 ); 117 } 118 break; 119 case 'native-memory-queryAnalysis': 120 if (data.params.list) { 121 let samples = convertJSON(data.params.list) || []; 122 this.queryAllCallchainsSamples = samples; 123 self.postMessage({ 124 id: data.id, 125 action: data.action, 126 results: this.combineStatisticAndCallChain(samples), 127 }); 128 } else { 129 if (data.params.isStatistic) { 130 this.isStatistic = true; 131 this.queryStatisticCallchainsSamples( 132 'native-memory-queryAnalysis', 133 data.params.leftNs, 134 data.params.rightNs, 135 data.params.types 136 ); 137 } else { 138 this.isStatistic = false; 139 this.queryCallchainsSamples( 140 'native-memory-queryAnalysis', 141 data.params.leftNs, 142 data.params.rightNs, 143 data.params.types 144 ); 145 } 146 } 147 break; 148 case 'native-memory-queryNativeHookEvent': 149 if (data.params) { 150 if (data.params.list) { 151 this.boxRangeNativeHook = convertJSON(data.params.list); 152 if (this.nativeMemoryArgs?.get('refresh')) { 153 this.clearBoxSelectionData = this.boxRangeNativeHook.length > 100_0000; 154 } 155 this.supplementNativeHoodData(); 156 postMessage(data.id, data.action, this.resolvingActionNativeMemory(this.nativeMemoryArgs!), 100_0000); 157 if (this.clearBoxSelectionData) { 158 this.boxRangeNativeHook = []; 159 } 160 } else if (data.params.get('refresh') || this.boxRangeNativeHook.length === 0) { 161 this.nativeMemoryArgs = data.params; 162 let leftNs = data.params.get('leftNs'); 163 let rightNs = data.params.get('rightNs'); 164 let types = data.params.get('types'); 165 this.boxRangeNativeHook = []; 166 this.queryNativeHookEvent(leftNs, rightNs, types); 167 } else { 168 this.nativeMemoryArgs = data.params; 169 postMessage(data.id, data.action, this.resolvingActionNativeMemory(this.nativeMemoryArgs!), 100_0000); 170 if (this.clearBoxSelectionData) { 171 this.boxRangeNativeHook = []; 172 } 173 } 174 } 175 break; 176 case 'native-memory-action': 177 if (data.params) { 178 self.postMessage({ 179 id: data.id, 180 action: data.action, 181 results: this.resolvingAction(data.params), 182 }); 183 } 184 break; 185 case 'native-memory-chart-action': 186 if (data.params) { 187 postMessage(data.id, data.action, this.resolvingActionNativeMemoryChartData(data.params)); 188 } 189 break; 190 case 'native-memory-calltree-action': 191 if (data.params) { 192 self.postMessage({ 193 id: data.id, 194 action: data.action, 195 results: this.resolvingNMCallAction(data.params), 196 }); 197 } 198 break; 199 case 'native-memory-init-responseType': 200 this.initResponseTypeList(data.params); 201 self.postMessage({ 202 id: data.id, 203 action: data.action, 204 results: [], 205 }); 206 break; 207 case 'native-memory-get-responseType': 208 self.postMessage({ 209 id: data.id, 210 action: data.action, 211 results: this.responseTypes, 212 }); 213 break; 214 case 'native-memory-queryNativeHookStatistic': 215 if (data.params.list) { 216 let arr = this.statisticDataHandler(convertJSON(data.params.list)); 217 postMessage(data.id, data.action, this.handleNativeHookStatisticData(arr)); 218 } else { 219 this.totalNS = data.params.totalNS; 220 this.queryNativeHookStatistic(data.params.type); 221 } 222 break; 223 } 224 } 225 } 226 227 initNMChartData() { 228 this.queryData( 229 this.currentEventId, 230 'native-memory-queryNMChartData', 231 ` 232 select * from ( 233 select 234 h.start_ts - t.start_ts as startTime, 235 h.heap_size as heapSize, 236 (case when h.event_type = 'AllocEvent' then 0 else 1 end) as eventType 237 from native_hook h ,trace_range t 238 where h.start_ts between t.start_ts and t.end_ts 239 and (h.event_type = 'AllocEvent' or h.event_type = 'MmapEvent') 240 union all 241 select 242 h.end_ts - t.start_ts as startTime, 243 h.heap_size as heapSize, 244 (case when h.event_type = 'AllocEvent' then 2 else 3 end) as eventType 245 from native_hook h ,trace_range t 246 where 247 h.start_ts between t.start_ts and t.end_ts 248 and h.end_ts between t.start_ts and t.end_ts 249 and (h.event_type = 'AllocEvent' or h.event_type = 'MmapEvent') 250 ) 251 order by startTime; 252 `, 253 {} 254 ); 255 } 256 257 queryNativeHookStatistic(type: number) { 258 let condition: string; 259 if (type === 0) { 260 condition = 'and type = 0'; 261 } else if (type === 1) { 262 condition = 'and type > 0'; 263 } else { 264 condition = ''; 265 } 266 let sql = ` 267select callchain_id callchainId, 268 ts - start_ts as ts, 269 apply_count applyCount, 270 apply_size applySize, 271 release_count releaseCount, 272 release_size releaseSize 273from native_hook_statistic,trace_range 274where ts between start_ts and end_ts ${condition}; 275 `; 276 this.queryData(this.currentEventId, 'native-memory-queryNativeHookStatistic', sql, {}); 277 } 278 279 queryNativeHookEvent(leftNs: number, rightNs: number, types: Array<string>) { 280 let condition = types.length === 1 ? `and A.event_type = ${types[0]}` : `and (A.event_type = 'AllocEvent' or A.event_type = 'MmapEvent')`; 281 let libId = this.nativeMemoryArgs?.get('filterResponseType'); 282 let allocType = this.nativeMemoryArgs?.get('filterAllocType'); 283 let eventType = this.nativeMemoryArgs?.get('filterEventType'); 284 if (libId !== undefined && libId !== -1) { 285 condition = `${condition} and last_lib_id = ${libId}`;// filter lib 286 } 287 if (eventType === '1') { 288 condition = `${condition} and event_type = 'AllocEvent'`; 289 } 290 if (eventType === '2') { 291 condition = `${condition} and event_type = 'MmapEvent'`; 292 } 293 if (allocType === '1') { 294 condition = `${condition} and ((A.end_ts - B.start_ts) > ${rightNs} or A.end_ts is null)`; 295 } 296 if (allocType === '2') { 297 condition = `${condition} and (A.end_ts - B.start_ts) <= ${rightNs}`; 298 } 299 let sql = ` 300 select 301 callchain_id as eventId, 302 event_type as eventType, 303 heap_size as heapSize, 304 ('0x' || addr) as addr, 305 (A.start_ts - B.start_ts) as startTs, 306 (A.end_ts - B.start_ts) as endTs, 307 tid as threadId, 308 sub_type_id as subTypeId, 309 ifnull(last_lib_id,0) as lastLibId 310 from 311 native_hook A, 312 trace_range B 313 left join 314 thread t 315 on 316 A.itid = t.id 317 where 318 A.start_ts - B.start_ts between ${leftNs} and ${rightNs} ${condition} 319 `; 320 this.queryData(this.currentEventId, 'native-memory-queryNativeHookEvent', sql, {}); 321 } 322 323 supplementNativeHoodData() { 324 let len = this.boxRangeNativeHook.length; 325 for(let i = 0, j = len -1; i <= j; i++, j--){ 326 this.fillNativeHook(this.boxRangeNativeHook[i], i); 327 if (i !== j) { 328 this.fillNativeHook(this.boxRangeNativeHook[j], j); 329 } 330 } 331 } 332 333 fillNativeHook(memory: NativeMemory, index: number) { 334 if (memory.subTypeId !== null && memory.subType === undefined) { 335 memory.subType = this.dataCache.dataDict.get(memory.subTypeId) || '-'; 336 } 337 memory.index = index; 338 let arr = this.dataCache.nmHeapFrameMap.get(memory.eventId) || []; 339 let frame = Array.from(arr) 340 .reverse() 341 .find((item) => { 342 let fileName = this.dataCache.dataDict.get(item.fileId); 343 return !((fileName ?? '').includes('libc++') || (fileName ?? '').includes('musl')); 344 }); 345 if (frame === null || frame === undefined) { 346 if (arr.length > 0) { 347 frame = arr[0]; 348 } 349 } 350 if (frame !== null && frame !== undefined) { 351 memory.symbol = this.groupCutFilePath(frame.symbolId, this.dataCache.dataDict.get(frame.symbolId) || ''); 352 memory.library = this.groupCutFilePath( 353 frame.fileId, 354 this.dataCache.dataDict.get(frame.fileId) || 'Unknown Path' 355 ); 356 } else { 357 memory.symbol = '-'; 358 memory.library = '-'; 359 } 360 } 361 362 statisticDataHandler(arr: Array<any>) { 363 let callGroupMap: Map<number, any[]> = new Map<number, any[]>(); 364 let obj = {}; 365 for (let hook of arr) { 366 if ((obj as any)[hook.ts]) { 367 let data = (obj as any)[hook.ts] as any; 368 data.startTime = hook.ts; 369 data.dur = 0; 370 if (callGroupMap.has(hook.callchainId)) { 371 let calls = callGroupMap.get(hook.callchainId); 372 let last = calls![calls!.length - 1]; 373 data.heapsize += hook.applySize - last.applySize - (hook.releaseSize - last.releaseSize); 374 data.density += hook.applyCount - last.applyCount - (hook.releaseCount - last.releaseCount); 375 calls!.push(hook); 376 } else { 377 data.heapsize += hook.applySize - hook.releaseSize; 378 data.density += hook.applyCount - hook.releaseCount; 379 callGroupMap.set(hook.callchainId, [hook]); 380 } 381 } else { 382 let data: any = {}; 383 data.startTime = hook.ts; 384 data.dur = 0; 385 if (callGroupMap.has(hook.callchainId)) { 386 let calls = callGroupMap.get(hook.callchainId); 387 let last = calls![calls!.length - 1]; 388 data.heapsize = hook.applySize - last.applySize - (hook.releaseSize - last.releaseSize); 389 data.density = hook.applyCount - last.applyCount - (hook.releaseCount - last.releaseCount); 390 calls!.push(hook); 391 } else { 392 data.heapsize = hook.applySize - hook.releaseSize; 393 data.density = hook.applyCount - hook.releaseCount; 394 callGroupMap.set(hook.callchainId, [hook]); 395 } 396 (obj as any)[hook.ts] = data; 397 } 398 } 399 return Object.values(obj) as { 400 startTime: number; 401 heapsize: number; 402 density: number; 403 dur: number; 404 }[]; 405 } 406 407 handleNativeHookStatisticData( 408 arr: { 409 startTime: number; 410 heapsize: number; 411 density: number; 412 dur: number; 413 }[] 414 ) { 415 let maxSize = 0, 416 maxDensity = 0, 417 minSize = 0, 418 minDensity = 0; 419 for (let i = 0, len = arr.length; i < len; i++) { 420 if (i == len - 1) { 421 arr[i].dur = this.totalNS - arr[i].startTime; 422 } else { 423 arr[i + 1].heapsize = arr[i].heapsize + arr[i + 1].heapsize; 424 arr[i + 1].density = arr[i].density + arr[i + 1].density; 425 arr[i].dur = arr[i + 1].startTime - arr[i].startTime; 426 } 427 maxSize = Math.max(maxSize, arr[i].heapsize); 428 maxDensity = Math.max(maxDensity, arr[i].density); 429 minSize = Math.min(minSize, arr[i].heapsize); 430 minDensity = Math.min(minDensity, arr[i].density); 431 } 432 return arr.map((it) => { 433 (it as any).maxHeapSize = maxSize; 434 (it as any).maxDensity = maxDensity; 435 (it as any).minHeapSize = minSize; 436 (it as any).minDensity = minDensity; 437 return it; 438 }); 439 } 440 initResponseTypeList(list: any[]) { 441 this.responseTypes = [ 442 { 443 key: -1, 444 value: 'ALL', 445 }, 446 ]; 447 list.forEach((item) => { 448 if (item.lastLibId == null) { 449 this.responseTypes.push({ 450 key: 0, 451 value: '-', 452 }); 453 } else { 454 this.responseTypes.push({ 455 key: item.lastLibId, 456 value: this.groupCutFilePath(item.lastLibId, item.value) || '-', 457 }); 458 } 459 }); 460 } 461 initNMFrameData() { 462 this.queryData( 463 this.currentEventId, 464 'native-memory-queryNMFrameData', 465 ` 466 select h.symbol_id as symbolId, h.file_id as fileId, h.depth, h.callchain_id as eventId, h.vaddr as addr 467 from native_hook_frame h 468 `, 469 {} 470 ); 471 } 472 initNMStack(frameArr: Array<HeapTreeDataBean>) { 473 frameArr.map((frame) => { 474 let frameEventId = frame.eventId; 475 if (this.dataCache.nmHeapFrameMap.has(frameEventId)) { 476 this.dataCache.nmHeapFrameMap.get(frameEventId)!.push(frame); 477 } else { 478 this.dataCache.nmHeapFrameMap.set(frameEventId, [frame]); 479 } 480 }); 481 } 482 resolvingAction(paramMap: Map<string, any>): Array<NativeHookCallInfo | NativeMemory | HeapStruct> { 483 let actionType = paramMap.get('actionType'); 484 if (actionType === 'memory-stack') { 485 return this.resolvingActionNativeMemoryStack(paramMap); 486 } else if (actionType === 'native-memory-state-change') { 487 let startTs = paramMap.get('startTs'); 488 let currentSelection = this.boxRangeNativeHook.filter((item) => { 489 return item.startTs === startTs; 490 }); 491 if (currentSelection.length > 0) { 492 currentSelection[0].isSelected = true; 493 } 494 return []; 495 } else { 496 return []; 497 } 498 } 499 resolvingActionNativeMemoryChartData(paramMap: Map<string, any>): Array<HeapStruct> { 500 let nativeMemoryType: number = paramMap.get('nativeMemoryType') as number; 501 let totalNS: number = paramMap.get('totalNS') as number; 502 let arr: Array<HeapStruct> = []; 503 let nmMaxSize = 0; 504 let nmMaxDensity = 0; 505 let nmMinSize = 0; 506 let nmMinDensity = 0; 507 let nmTempSize = 0; 508 let nmTempDensity = 0; 509 let nmFilterLen = 0; 510 let nmFilterLevel = 0; 511 let putArr = (ne: NativeEvent, filterLevel: number, finish: boolean) => { 512 let nmHeapStruct = new HeapStruct(); 513 nmHeapStruct.startTime = ne.startTime; 514 if (arr.length == 0) { 515 if (ne.eventType == 0 || ne.eventType == 1) { 516 nmHeapStruct.density = 1; 517 nmHeapStruct.heapsize = ne.heapSize; 518 } else { 519 nmHeapStruct.density = -1; 520 nmHeapStruct.heapsize = 0 - ne.heapSize; 521 } 522 nmMaxSize = nmHeapStruct.heapsize; 523 nmMaxDensity = nmHeapStruct.density; 524 nmMinSize = nmHeapStruct.heapsize; 525 nmMinDensity = nmHeapStruct.density; 526 arr.push(nmHeapStruct); 527 } else { 528 let last = arr[arr.length - 1]; 529 last.dur = nmHeapStruct.startTime! - last.startTime!; 530 if (last.dur > filterLevel || finish) { 531 if (ne.eventType == 0 || ne.eventType == 1) { 532 nmHeapStruct.density = last.density! + nmTempDensity + 1; 533 nmHeapStruct.heapsize = last.heapsize! + nmTempSize + ne.heapSize; 534 } else { 535 nmHeapStruct.density = last.density! + nmTempDensity - 1; 536 nmHeapStruct.heapsize = last.heapsize! + nmTempSize - ne.heapSize; 537 } 538 nmTempDensity = 0; 539 nmTempSize = 0; 540 if (nmHeapStruct.density > nmMaxDensity) { 541 nmMaxDensity = nmHeapStruct.density; 542 } 543 if (nmHeapStruct.density < nmMinDensity) { 544 nmMinDensity = nmHeapStruct.density; 545 } 546 if (nmHeapStruct.heapsize > nmMaxSize) { 547 nmMaxSize = nmHeapStruct.heapsize; 548 } 549 if (nmHeapStruct.heapsize < nmMinSize) { 550 nmMinSize = nmHeapStruct.heapsize; 551 } 552 arr.push(nmHeapStruct); 553 } else { 554 if (ne.eventType == 0 || ne.eventType == 1) { 555 nmTempDensity = nmTempDensity + 1; 556 nmTempSize = nmTempSize + ne.heapSize; 557 } else { 558 nmTempDensity = nmTempDensity - 1; 559 nmTempSize = nmTempSize - ne.heapSize; 560 } 561 } 562 } 563 }; 564 if (nativeMemoryType == 1) { 565 let temp = this.NATIVE_MEMORY_DATA.filter((ne) => ne.eventType === 0 || ne.eventType === 2); 566 nmFilterLen = temp.length; 567 nmFilterLevel = this.getFilterLevel(nmFilterLen); 568 temp.map((ne, index) => putArr(ne, nmFilterLevel, index === nmFilterLen - 1)); 569 temp.length = 0; 570 } else if (nativeMemoryType == 2) { 571 let temp = this.NATIVE_MEMORY_DATA.filter((ne) => ne.eventType === 1 || ne.eventType === 3); 572 nmFilterLen = temp.length; 573 nmFilterLevel = this.getFilterLevel(nmFilterLen); 574 temp.map((ne, index) => putArr(ne, nmFilterLevel, index === nmFilterLen - 1)); 575 temp.length = 0; 576 } else { 577 nmFilterLen = this.NATIVE_MEMORY_DATA.length; 578 let filterLevel = this.getFilterLevel(nmFilterLen); 579 this.NATIVE_MEMORY_DATA.map((ne, index) => putArr(ne, filterLevel, index === nmFilterLen - 1)); 580 } 581 if (arr.length > 0) { 582 arr[arr.length - 1].dur = totalNS - arr[arr.length - 1].startTime!; 583 } 584 arr.map((heapStruct) => { 585 heapStruct.maxHeapSize = nmMaxSize; 586 heapStruct.maxDensity = nmMaxDensity; 587 heapStruct.minHeapSize = nmMinSize; 588 heapStruct.minDensity = nmMinDensity; 589 }); 590 this.chartComplete.set(nativeMemoryType, true); 591 if (this.chartComplete.has(0) && this.chartComplete.has(1) && this.chartComplete.has(2)) { 592 this.NATIVE_MEMORY_DATA = []; 593 } 594 return arr; 595 } 596 resolvingActionNativeMemoryStack(paramMap: Map<string, any>) { 597 let eventId = paramMap.get('eventId'); 598 let frameArr = this.dataCache.nmHeapFrameMap.get(eventId) || []; 599 let arr: Array<NativeHookCallInfo> = []; 600 frameArr.map((frame) => { 601 let target = new NativeHookCallInfo(); 602 target.eventId = frame.eventId; 603 target.depth = frame.depth; 604 target.addr = frame.addr; 605 target.symbol = this.groupCutFilePath(frame.symbolId, this.dataCache.dataDict.get(frame.symbolId) || '') ?? ''; 606 target.library = this.groupCutFilePath(frame.fileId, this.dataCache.dataDict.get(frame.fileId) || '') ?? ''; 607 target.title = `[ ${target.symbol} ] ${target.library}`; 608 target.type = 609 target.library.endsWith('.so.1') || target.library.endsWith('.dll') || target.library.endsWith('.so') ? 0 : 1; 610 arr.push(target); 611 }); 612 return arr; 613 } 614 615 resolvingActionNativeMemory(paramMap: Map<string, any>): Array<NativeMemory> { 616 let filterAllocType = paramMap.get('filterAllocType'); 617 let filterEventType = paramMap.get('filterEventType'); 618 let filterResponseType = paramMap.get('filterResponseType'); 619 let leftNs = paramMap.get('leftNs'); 620 let rightNs = paramMap.get('rightNs'); 621 let sortColumn = paramMap.get('sortColumn'); 622 let sortType = paramMap.get('sortType'); 623 let statisticsSelection = paramMap.get('statisticsSelection'); 624 let filter = this.boxRangeNativeHook; 625 if ((filterAllocType !== undefined && filterAllocType !== 0) || 626 (filterEventType !== undefined && filterEventType !== 0) || 627 (filterResponseType !== undefined && filterResponseType !== -1) 628 ) { 629 filter = this.boxRangeNativeHook.filter((item) => { 630 let filterAllocation = true; 631 let freed = item.endTs > leftNs && 632 item.endTs <= rightNs && 633 item.endTs !== 0 && 634 item.endTs !== null; 635 if (filterAllocType === '1') { 636 filterAllocation = !freed; 637 } else if (filterAllocType == '2') { 638 filterAllocation = freed; 639 } 640 let filterNative = this.getTypeFromIndex(parseInt(filterEventType), item, statisticsSelection); 641 let filterLastLib = filterResponseType == -1 ? true : filterResponseType == item.lastLibId; 642 return filterAllocation && filterNative && filterLastLib; 643 }); 644 } 645 if (sortColumn !== undefined && sortType !== undefined && sortColumn !== '' && sortType !== 0) { 646 return this.sortByNativeMemoryColumn(sortColumn, sortType, filter); 647 } else { 648 return filter; 649 } 650 } 651 652 sortByNativeMemoryColumn(nmMemoryColumn: string, nmMemorySort: number, list: Array<NativeMemory>) { 653 if (nmMemorySort === 0) { 654 return list; 655 } else { 656 return list.sort((memoryLeftData: any, memoryRightData: any) => { 657 if (nmMemoryColumn === 'index' || nmMemoryColumn === 'startTs' || nmMemoryColumn === 'heapSize') { 658 return nmMemorySort == 1 659 ? memoryLeftData[nmMemoryColumn] - memoryRightData[nmMemoryColumn] 660 : memoryRightData[nmMemoryColumn] - memoryLeftData[nmMemoryColumn]; 661 } else { 662 if (nmMemorySort == 1) { 663 if (memoryLeftData[nmMemoryColumn] > memoryRightData[nmMemoryColumn]) { 664 return 1; 665 } else if (memoryLeftData[nmMemoryColumn] === memoryRightData[nmMemoryColumn]) { 666 return 0; 667 } else { 668 return -1; 669 } 670 } else { 671 if (memoryRightData[nmMemoryColumn] > memoryLeftData[nmMemoryColumn]) { 672 return 1; 673 } else if (memoryLeftData[nmMemoryColumn] == memoryRightData[nmMemoryColumn]) { 674 return 0; 675 } else { 676 return -1; 677 } 678 } 679 } 680 }); 681 } 682 } 683 684 groupCutFilePath(fileId: number, path: string): string { 685 let name: string; 686 if (this.dataCache.nmFileDict.has(fileId)) { 687 name = this.dataCache.nmFileDict.get(fileId) ?? ''; 688 } else { 689 let currentPath = path.substring(path.lastIndexOf('/') + 1); 690 this.dataCache.nmFileDict.set(fileId, currentPath); 691 name = currentPath; 692 } 693 return name == '' ? '-' : name; 694 } 695 696 traverseSampleTree(stack: NativeHookCallInfo, hook: NativeHookStatistics) { 697 stack.count += 1; 698 stack.countValue = `${stack.count}`; 699 stack.countPercent = `${((stack.count / this.selectTotalCount) * 100).toFixed(1)}%`; 700 stack.size += hook.heapSize; 701 stack.tid = hook.tid; 702 stack.threadName = hook.threadName; 703 stack.heapSizeStr = `${getByteWithUnit(stack.size)}`; 704 stack.heapPercent = `${((stack.size / this.selectTotalSize) * 100).toFixed(1)}%`; 705 if (stack.children.length > 0) { 706 stack.children.map((child) => { 707 this.traverseSampleTree(child as NativeHookCallInfo, hook); 708 }); 709 } 710 } 711 traverseTree(stack: NativeHookCallInfo, hook: NativeHookStatistics) { 712 stack.count = 1; 713 stack.countValue = `${stack.count}`; 714 stack.countPercent = `${((stack!.count / this.selectTotalCount) * 100).toFixed(1)}%`; 715 stack.size = hook.heapSize; 716 stack.tid = hook.tid; 717 stack.threadName = hook.threadName; 718 stack.heapSizeStr = `${getByteWithUnit(stack!.size)}`; 719 stack.heapPercent = `${((stack!.size / this.selectTotalSize) * 100).toFixed(1)}%`; 720 if (stack.children.length > 0) { 721 stack.children.map((child) => { 722 this.traverseTree(child as NativeHookCallInfo, hook); 723 }); 724 } 725 } 726 getTypeFromIndex( 727 indexOf: number, 728 item: NativeHookStatistics | NativeMemory, 729 statisticsSelection: Array<StatisticsSelection> 730 ): boolean { 731 if (indexOf == -1) { 732 return false; 733 } 734 if (indexOf < 3) { 735 if (indexOf == 0) { 736 return true; 737 } else if (indexOf == 1) { 738 return item.eventType == 'AllocEvent'; 739 } else if (indexOf == 2) { 740 return item.eventType == 'MmapEvent'; 741 } 742 } else if (indexOf - 3 < statisticsSelection.length) { 743 let selectionElement = statisticsSelection[indexOf - 3]; 744 if (selectionElement.memoryTap != undefined && selectionElement.max != undefined) { 745 if (selectionElement.memoryTap.indexOf('Malloc') != -1) { 746 return item.eventType == 'AllocEvent' && item.heapSize == selectionElement.max; 747 } else if (selectionElement.memoryTap.indexOf('Mmap') != -1) { 748 return item.eventType == 'MmapEvent' && item.heapSize == selectionElement.max && item.subTypeId === null; 749 } else { 750 return item.subType == selectionElement.memoryTap; 751 } 752 } 753 if (selectionElement.max === undefined && typeof selectionElement.memoryTap === 'number') { 754 return item.subTypeId === selectionElement.memoryTap; 755 } 756 } 757 return false; 758 } 759 clearAll() { 760 this.dataCache.clearNM(); 761 this.splitMapData = {}; 762 this.currentSamples = []; 763 this.allThreads = []; 764 this.queryAllCallchainsSamples = []; 765 this.NATIVE_MEMORY_DATA = []; 766 this.chartComplete.clear(); 767 this.realTimeDif = 0; 768 this.currentTreeMapData = {}; 769 this.currentTreeList.length = 0; 770 this.responseTypes.length = 0; 771 this.boxRangeNativeHook = []; 772 this.nativeMemoryArgs?.clear(); 773 } 774 775 queryCallchainsSamples(action: string, leftNs: number, rightNs: number, types: Array<string>) { 776 this.queryData( 777 this.currentEventId, 778 action, 779 `select A.id, 780 callchain_id as eventId, 781 event_type as eventType, 782 heap_size as heapSize, 783 (A.start_ts - B.start_ts) as startTs, 784 (A.end_ts - B.start_ts) as endTs, 785 tid, 786 ifnull(last_lib_id,0) as lastLibId, 787 t.name as threadName, 788 A.addr, 789 A.sub_type_id as subTypeId 790 from 791 native_hook A, 792 trace_range B 793 left join 794 thread t 795 on 796 A.itid = t.id 797 where 798 A.start_ts - B.start_ts 799 between ${leftNs} and ${rightNs} and A.event_type in (${types.join(',')}) 800 `, 801 {} 802 ); 803 } 804 queryStatisticCallchainsSamples(action: string, leftNs: number, rightNs: number, types: Array<number>) { 805 let condition = ''; 806 if (types.length === 1) { 807 if (types[0] === 0) { 808 condition = 'and type = 0'; 809 } else { 810 condition = 'and type != 0'; 811 } 812 } 813 this.queryData( 814 this.currentEventId, 815 action, 816 `select A.id, 817 0 as tid, 818 callchain_id as eventId, 819 (case when type = 0 then 'AllocEvent' else 'MmapEvent' end) as eventType, 820 (case when sub_type_id not null then sub_type_id else type end) as subTypeId, 821 apply_size as heapSize, 822 release_size as freeSize, 823 apply_count as count, 824 release_count as freeCount, 825 (max(A.ts) - B.start_ts) as startTs 826 from 827 native_hook_statistic A, 828 trace_range B 829 where 830 A.ts - B.start_ts 831 between ${leftNs} and ${rightNs} 832 ${condition} 833 group by callchain_id; 834 `, 835 {} 836 ); 837 } 838 839 combineStatisticAndCallChain(samples: NativeHookStatistics[]) { 840 samples.sort((a, b) => a.id - b.id); 841 const analysisSampleList = new Array<AnalysisSample>(); 842 const applyAllocSamples = new Array<AnalysisSample>(); 843 const applyMmapSamples = new Array<AnalysisSample>(); 844 845 for (const sample of samples) { 846 const count = this.isStatistic ? sample.count : 1; 847 const analysisSample = new AnalysisSample(sample.id, sample.heapSize, count, sample.eventType, sample.startTs); 848 849 if (this.isStatistic) { 850 analysisSample.releaseCount = sample.freeCount; 851 analysisSample.releaseSize = sample.freeSize; 852 switch (sample.subTypeId) { 853 case 1: 854 analysisSample.subType = 'MmapEvent'; 855 break; 856 case 2: 857 analysisSample.subType = 'FILE_PAGE_MSG'; 858 break; 859 case 3: 860 analysisSample.subType = 'MEMORY_USING_MSG'; 861 break; 862 default: 863 analysisSample.subType = this.dataCache.dataDict.get(sample.subTypeId); 864 } 865 } else { 866 let subType = undefined; 867 if (sample.subTypeId) { 868 subType = this.dataCache.dataDict.get(sample.subTypeId); 869 } 870 analysisSample.endTs = sample.endTs; 871 analysisSample.addr = sample.addr; 872 analysisSample.tid = sample.tid; 873 analysisSample.subType = subType; 874 } 875 876 if (['FreeEvent', 'MunmapEvent'].includes(sample.eventType)) { 877 if (sample.eventType === 'FreeEvent') { 878 this.setApplyIsRelease(analysisSample, applyAllocSamples); 879 } else { 880 this.setApplyIsRelease(analysisSample, applyMmapSamples); 881 } 882 continue; 883 } else { 884 if (sample.eventType === 'AllocEvent') { 885 applyAllocSamples.push(analysisSample); 886 } else { 887 applyMmapSamples.push(analysisSample); 888 } 889 } 890 891 const callChains = this.dataCache.nmHeapFrameMap.get(sample.eventId) || []; 892 if (!callChains || callChains.length === 0) { 893 return; 894 } 895 let index = callChains.length - 1; 896 let lastFilterCallChain: HeapTreeDataBean | undefined | null; 897 while (true) { 898 // if all call stack is musl or libc++. use stack top lib 899 if (index < 0) { 900 lastFilterCallChain = callChains[callChains.length - 1]; 901 break; 902 } 903 904 lastFilterCallChain = callChains[index]; 905 const libPath = this.dataCache.dataDict.get(lastFilterCallChain.fileId); 906 //ignore musl and libc++ so 907 if (libPath?.includes('musl') || libPath?.includes('libc++')) { 908 index--; 909 } else { 910 lastFilterCallChain = lastFilterCallChain; 911 break; 912 } 913 } 914 915 const filePath = this.dataCache.dataDict.get(lastFilterCallChain.fileId)!; 916 let libName = ''; 917 if (filePath) { 918 const path = filePath.split('/'); 919 libName = path[path.length - 1]; 920 } 921 const symbolName = 922 this.dataCache.dataDict.get(lastFilterCallChain.symbolId) || libName + ' (' + sample.addr + ')'; 923 924 analysisSample.libId = lastFilterCallChain.fileId; 925 analysisSample.libName = libName; 926 analysisSample.symbolId = lastFilterCallChain.symbolId; 927 analysisSample.symbolName = symbolName; 928 929 analysisSampleList.push(analysisSample); 930 } 931 return analysisSampleList; 932 } 933 934 setApplyIsRelease(sample: AnalysisSample, arr: Array<AnalysisSample>) { 935 let idx = arr.length - 1; 936 for (idx; idx >= 0; idx--) { 937 let item = arr[idx]; 938 if (item.endTs === sample.startTs && item.addr === sample.addr) { 939 arr.splice(idx, 1); 940 item.isRelease = true; 941 return; 942 } 943 } 944 } 945 946 freshCurrentCallchains(samples: NativeHookStatistics[], isTopDown: boolean) { 947 this.currentTreeMapData = {}; 948 this.currentTreeList = []; 949 let totalSize = 0; 950 let totalCount = 0; 951 samples.forEach((nativeHookSample) => { 952 if (nativeHookSample.eventId == -1) { 953 return; 954 } 955 totalSize += nativeHookSample.heapSize; 956 totalCount += nativeHookSample.count || 1; 957 let callChains = this.createThreadSample(nativeHookSample); 958 let topIndex = isTopDown ? 0 : callChains.length - 1; 959 if (callChains.length > 0) { 960 let root = 961 this.currentTreeMapData[ 962 nativeHookSample.tid + '-' + (callChains[topIndex].symbolId || '') + '-' + (callChains[topIndex].fileId || '') 963 ]; 964 if (root == undefined) { 965 root = new NativeHookCallInfo(); 966 root.threadName = nativeHookSample.threadName; 967 this.currentTreeMapData[ 968 nativeHookSample.tid + '-' + (callChains[topIndex].symbolId || '') + '-' + (callChains[topIndex].fileId || '') 969 ] = root; 970 this.currentTreeList.push(root); 971 } 972 NativeHookCallInfo.merageCallChainSample(root, callChains[topIndex], nativeHookSample); 973 if (callChains.length > 1) { 974 this.merageChildrenByIndex(root, callChains, topIndex, nativeHookSample, isTopDown); 975 } 976 } 977 }); 978 let rootMerageMap: any = {}; 979 // @ts-ignore 980 let threads = Object.values(this.currentTreeMapData); 981 threads.forEach((merageData: any) => { 982 if (rootMerageMap[merageData.tid] == undefined) { 983 let threadMerageData = new NativeHookCallInfo(); //新增进程的节点数据 984 threadMerageData.canCharge = false; 985 threadMerageData.type = -1; 986 threadMerageData.symbolName = `${merageData.threadName || 'Thread'} [${merageData.tid}]`; 987 threadMerageData.symbol = threadMerageData.symbolName; 988 threadMerageData.children.push(merageData); 989 threadMerageData.initChildren.push(merageData); 990 threadMerageData.count = merageData.count || 1; 991 threadMerageData.heapSize = merageData.heapSize; 992 threadMerageData.totalCount = totalCount; 993 threadMerageData.totalSize = totalSize; 994 rootMerageMap[merageData.tid] = threadMerageData; 995 } else { 996 rootMerageMap[merageData.tid].children.push(merageData); 997 rootMerageMap[merageData.tid].initChildren.push(merageData); 998 rootMerageMap[merageData.tid].count += merageData.count || 1; 999 rootMerageMap[merageData.tid].heapSize += merageData.heapSize; 1000 rootMerageMap[merageData.tid].totalCount = totalCount; 1001 rootMerageMap[merageData.tid].totalSize = totalSize; 1002 } 1003 merageData.parentNode = rootMerageMap[merageData.tid]; //子节点添加父节点的引用 1004 }); 1005 let id = 0; 1006 this.currentTreeList.forEach((nmTreeNode) => { 1007 nmTreeNode.totalCount = totalCount; 1008 nmTreeNode.totalSize = totalSize; 1009 this.setMerageName(nmTreeNode); 1010 if (nmTreeNode.id == '') { 1011 nmTreeNode.id = id + ''; 1012 id++; 1013 } 1014 if (nmTreeNode.parentNode) { 1015 if (nmTreeNode.parentNode.id == '') { 1016 nmTreeNode.parentNode.id = id + ''; 1017 id++; 1018 } 1019 nmTreeNode.parentId = nmTreeNode.parentNode.id; 1020 } 1021 }); 1022 // @ts-ignore 1023 this.allThreads = Object.values(rootMerageMap) as NativeHookCallInfo[]; 1024 } 1025 groupCallchainSample(paramMap: Map<string, any>) { 1026 let groupMap: any = {}; 1027 let filterAllocType = paramMap.get('filterAllocType'); 1028 let filterEventType = paramMap.get('filterEventType'); 1029 let filterResponseType = paramMap.get('filterResponseType'); 1030 let leftNs = paramMap.get('leftNs'); 1031 let rightNs = paramMap.get('rightNs'); 1032 let nativeHookType = paramMap.get('nativeHookType'); 1033 let statisticsSelection = paramMap.get('statisticsSelection'); 1034 if (filterAllocType == '0' && filterEventType == '0' && filterResponseType == -1) { 1035 this.currentSamples = this.queryAllCallchainsSamples; 1036 return; 1037 } 1038 let filter = this.queryAllCallchainsSamples.filter((item) => { 1039 let filterAllocation = true; 1040 if (nativeHookType === 'native-hook') { 1041 if (filterAllocType == '1') { 1042 filterAllocation = 1043 item.startTs >= leftNs && 1044 item.startTs <= rightNs && 1045 (item.endTs > rightNs || item.endTs == 0 || item.endTs == null); 1046 } else if (filterAllocType == '2') { 1047 filterAllocation = 1048 item.startTs >= leftNs && 1049 item.startTs <= rightNs && 1050 item.endTs <= rightNs && 1051 item.endTs != 0 && 1052 item.endTs != null; 1053 } 1054 } else { 1055 if (filterAllocType == '1') { 1056 filterAllocation = item.heapSize > item.freeSize; 1057 } else if (filterAllocType == '2') { 1058 filterAllocation = item.heapSize === item.freeSize; 1059 } 1060 } 1061 let filterLastLib = filterResponseType == -1 ? true : filterResponseType == item.lastLibId; 1062 let filterNative = this.getTypeFromIndex(parseInt(filterEventType), item, statisticsSelection); 1063 return filterAllocation && filterNative && filterLastLib; 1064 }); 1065 filter.forEach((sample) => { 1066 let currentNode = groupMap[sample.tid + '-' + sample.eventId] || new NativeHookStatistics(); 1067 if (currentNode.count == 0) { 1068 Object.assign(currentNode, sample); 1069 if (filterAllocType == '1' && nativeHookType !== 'native-hook') { 1070 currentNode.heapSize = sample.heapSize - sample.freeSize; 1071 currentNode.count = sample.count - sample.freeCount; 1072 } 1073 if (currentNode.count === 0) { 1074 currentNode.count++; 1075 } 1076 } else { 1077 currentNode.count++; 1078 currentNode.heapSize += sample.heapSize; 1079 } 1080 groupMap[sample.tid + '-' + sample.eventId] = currentNode; 1081 }); 1082 // @ts-ignore 1083 this.currentSamples = Object.values(groupMap); 1084 } 1085 createThreadSample(sample: NativeHookStatistics) { 1086 return this.dataCache.nmHeapFrameMap.get(sample.eventId) || []; 1087 } 1088 merageChildrenByIndex( 1089 currentNode: NativeHookCallInfo, 1090 callChainDataList: any[], 1091 index: number, 1092 sample: NativeHookStatistics, 1093 isTopDown: boolean 1094 ) { 1095 isTopDown ? index++ : index--; 1096 let isEnd = isTopDown ? callChainDataList.length == index + 1 : index == 0; 1097 let node; 1098 if ( 1099 currentNode.initChildren.filter((child: any) => { 1100 if (child.symbolId == callChainDataList[index]?.symbolId && child.fileId == callChainDataList[index]?.fileId) { 1101 node = child; 1102 NativeHookCallInfo.merageCallChainSample(child, callChainDataList[index], sample); 1103 return true; 1104 } 1105 return false; 1106 }).length == 0 1107 ) { 1108 node = new NativeHookCallInfo(); 1109 NativeHookCallInfo.merageCallChainSample(node, callChainDataList[index], sample); 1110 currentNode.children.push(node); 1111 currentNode.initChildren.push(node); 1112 this.currentTreeList.push(node); 1113 node.parentNode = currentNode; 1114 } 1115 if (node && !isEnd) this.merageChildrenByIndex(node, callChainDataList, index, sample, isTopDown); 1116 } 1117 setMerageName(currentNode: NativeHookCallInfo) { 1118 currentNode.symbol = 1119 this.groupCutFilePath(currentNode.symbolId, this.dataCache.dataDict.get(currentNode.symbolId) || '') ?? 'unknown'; 1120 currentNode.path = this.dataCache.dataDict.get(currentNode.fileId) || 'unknown'; 1121 currentNode.libName = setFileName(currentNode.path); 1122 currentNode.lib = currentNode.path; 1123 currentNode.symbolName = `[${currentNode.symbol}] ${currentNode.libName}`; 1124 currentNode.type = 1125 currentNode.libName.endsWith('.so.1') || 1126 currentNode.libName.endsWith('.dll') || 1127 currentNode.libName.endsWith('.so') 1128 ? 0 1129 : 1; 1130 } 1131 clearSplitMapData(symbolName: string) { 1132 delete this.splitMapData[symbolName]; 1133 } 1134 resolvingNMCallAction(params: any[]) { 1135 if (params.length > 0) { 1136 params.forEach((item) => { 1137 if (item.funcName && item.funcArgs) { 1138 switch (item.funcName) { 1139 case 'groupCallchainSample': 1140 this.groupCallchainSample(item.funcArgs[0] as Map<string, any>); 1141 break; 1142 case 'getCallChainsBySampleIds': 1143 this.freshCurrentCallchains(this.currentSamples, item.funcArgs[0]); 1144 break; 1145 case 'hideSystemLibrary': 1146 merageBeanDataSplit.hideSystemLibrary(this.allThreads, this.splitMapData); 1147 break; 1148 case 'hideNumMaxAndMin': 1149 merageBeanDataSplit.hideNumMaxAndMin( 1150 this.allThreads, 1151 this.splitMapData, 1152 item.funcArgs[0], 1153 item.funcArgs[1] 1154 ); 1155 break; 1156 case 'splitAllProcess': 1157 merageBeanDataSplit.splitAllProcess(this.allThreads, this.splitMapData, item.funcArgs[0]); 1158 break; 1159 case 'resetAllNode': 1160 merageBeanDataSplit.resetAllNode(this.allThreads, this.currentTreeList, this.searchValue); 1161 break; 1162 case 'resotreAllNode': 1163 merageBeanDataSplit.resotreAllNode(this.splitMapData, item.funcArgs[0]); 1164 break; 1165 case 'splitTree': 1166 merageBeanDataSplit.splitTree( 1167 this.splitMapData, 1168 this.allThreads, 1169 item.funcArgs[0], 1170 item.funcArgs[1], 1171 item.funcArgs[2], 1172 this.currentTreeList, 1173 this.searchValue 1174 ); 1175 break; 1176 case 'setSearchValue': 1177 this.searchValue = item.funcArgs[0]; 1178 break; 1179 case 'clearSplitMapData': 1180 this.clearSplitMapData(item.funcArgs[0]); 1181 break; 1182 } 1183 } 1184 }); 1185 } 1186 return this.allThreads.filter((thread) => { 1187 return thread.children && thread.children.length > 0; 1188 }); 1189 } 1190 getFilterLevel(len: number): number { 1191 if (len > 300_0000) { 1192 return 50_0000; 1193 } else if (len > 200_0000) { 1194 return 30_0000; 1195 } else if (len > 100_0000) { 1196 return 10_0000; 1197 } else if (len > 50_0000) { 1198 return 5_0000; 1199 } else if (len > 30_0000) { 1200 return 2_0000; 1201 } else if (len > 15_0000) { 1202 return 1_0000; 1203 } else { 1204 return 0; 1205 } 1206 } 1207} 1208 1209export class NativeHookStatistics { 1210 id: number = 0; 1211 eventId: number = 0; 1212 eventType: string = ''; 1213 subType: string = ''; 1214 subTypeId: number = 0; 1215 heapSize: number = 0; 1216 freeSize: number = 0; 1217 addr: string = ''; 1218 startTs: number = 0; 1219 endTs: number = 0; 1220 sumHeapSize: number = 0; 1221 max: number = 0; 1222 count: number = 0; 1223 freeCount: number = 0; 1224 tid: number = 0; 1225 threadName: string = ''; 1226 lastLibId: number = 0; 1227 isSelected: boolean = false; 1228} 1229export class NativeHookCallInfo extends MerageBean { 1230 #totalCount: number = 0; 1231 #totalSize: number = 0; 1232 library: string = ''; 1233 symbolId: number = 0; 1234 fileId: number = 0; 1235 title: string = ''; 1236 count: number = 0; 1237 countValue: string = ''; 1238 countPercent: string = ''; 1239 type: number = 0; 1240 heapSize: number = 0; 1241 heapPercent: string = ''; 1242 heapSizeStr: string = ''; 1243 eventId: number = 0; 1244 tid: number = 0; 1245 threadName: string = ''; 1246 eventType: string = ''; 1247 isSelected: boolean = false; 1248 set totalCount(total: number) { 1249 this.#totalCount = total; 1250 this.countValue = this.count + ''; 1251 this.size = this.heapSize; 1252 this.countPercent = `${((this.count / total) * 100).toFixed(1)}%`; 1253 } 1254 get totalCount() { 1255 return this.#totalCount; 1256 } 1257 set totalSize(total: number) { 1258 this.#totalSize = total; 1259 this.heapSizeStr = `${getByteWithUnit(this.heapSize)}`; 1260 this.heapPercent = `${((this.heapSize / total) * 100).toFixed(1)}%`; 1261 } 1262 get totalSize() { 1263 return this.#totalSize; 1264 } 1265 static merageCallChainSample( 1266 currentNode: NativeHookCallInfo, 1267 callChain: HeapTreeDataBean, 1268 sample: NativeHookStatistics 1269 ) { 1270 if (currentNode.symbol == undefined || currentNode.symbol == '') { 1271 currentNode.symbol = callChain.AllocationFunction || ''; 1272 currentNode.addr = callChain.addr; 1273 currentNode.eventId = sample.eventId; 1274 currentNode.eventType = sample.eventType; 1275 currentNode.symbolId = callChain.symbolId; 1276 currentNode.fileId = callChain.fileId; 1277 currentNode.tid = sample.tid; 1278 } 1279 currentNode.count += sample.count || 1; 1280 currentNode.heapSize += sample.heapSize; 1281 } 1282} 1283export class NativeMemory { 1284 index: number = 0; 1285 eventId: number = 0; 1286 eventType: string = ''; 1287 subType: string = ''; 1288 subTypeId: number = 0; 1289 addr: string = ''; 1290 startTs: number = 0; 1291 endTs: number = 0; 1292 heapSize: number = 0; 1293 symbol: string = ''; 1294 library: string = ''; 1295 lastLibId: number = 0; 1296 isSelected: boolean = false; 1297 threadId: number = 0; 1298} 1299 1300export class HeapStruct { 1301 startTime: number | undefined; 1302 endTime: number | undefined; 1303 dur: number | undefined; 1304 density: number | undefined; 1305 heapsize: number | undefined; 1306 maxHeapSize: number = 0; 1307 maxDensity: number = 0; 1308 minHeapSize: number = 0; 1309 minDensity: number = 0; 1310} 1311export class NativeEvent { 1312 startTime: number = 0; 1313 heapSize: number = 0; 1314 eventType: number = 0; 1315} 1316export class StatisticsSelection { 1317 memoryTap: string = ''; 1318 max: number = 0; 1319} 1320 1321class AnalysisSample { 1322 id: number; 1323 count: number; 1324 size: number; 1325 type: number; 1326 startTs: number; 1327 1328 isRelease: boolean; 1329 releaseCount?: number; 1330 releaseSize?: number; 1331 1332 endTs?: number; 1333 subType?: string; 1334 tid?: number; 1335 addr?: string; 1336 1337 libId!: number; 1338 libName!: string; 1339 symbolId!: number; 1340 symbolName!: string; 1341 1342 constructor(id: number, size: number, count: number, type: number | string, startTs: number) { 1343 this.id = id; 1344 this.size = size; 1345 this.count = count; 1346 this.startTs = startTs; 1347 switch (type) { 1348 case 'AllocEvent': 1349 case '0': 1350 this.type = 0; 1351 this.isRelease = false; 1352 break; 1353 case 'MmapEvent': 1354 case '1': 1355 this.isRelease = false; 1356 this.type = 1; 1357 break; 1358 case 'FreeEvent': 1359 this.isRelease = true; 1360 this.type = 2; 1361 break; 1362 case 'MunmapEvent': 1363 this.isRelease = true; 1364 this.type = 3; 1365 break; 1366 default: 1367 this.isRelease = false; 1368 this.type = -1; 1369 } 1370 } 1371} 1372