1/* 2 * Copyright (C) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { 17 convertJSON, 18 DataCache, 19 FileCallChain, 20 getByteWithUnit, 21 getProbablyTime, 22 getTimeString, 23 LogicHandler, 24 MerageBean, 25 merageBeanDataSplit, 26 postMessage, 27 setFileName, 28} from './ProcedureLogicWorkerCommon.js'; 29 30export let FILE_TYPE_MAP = { 31 '0': 'OPEN', 32 '1': 'CLOSE', 33 '2': 'READ', 34 '3': 'WRITE', 35}; 36 37export let DISKIO_TYPE_MAP = { 38 '1': 'DATA_READ', 39 '2': 'DATA_WRITE', 40 '3': 'METADATA_READ', 41 '4': 'METADATA_WRITE', 42 '5': 'PAGE_IN', 43 '6': 'PAGE_OUT', 44}; 45 46export let VM_TYPE_MAP = { 47 '1': 'File Backed In', 48 '2': 'Page Cache Hit', 49 '3': 'Swap From Zram', 50 '4': 'Swap From Disk', 51 '5': 'Zero Fill Page', 52 '6': 'Zero FAKE Page', 53 '7': 'Copy On Write', 54}; 55 56const FS_TYPE = 0; 57const PF_TYPE = 1; 58const BIO_TYPE = 2; 59 60export class ProcedureLogicWorkerFileSystem extends LogicHandler { 61 private dataCache = DataCache.getInstance(); 62 handlerMap: Map<string, any> = new Map<string, any>(); 63 currentEventId: string = ''; 64 tab: string = ''; 65 isAnalysis = false; 66 67 handle(data: any): void { 68 if (data.id) { 69 this.currentEventId = data.id; 70 for (let handle of this.handlerMap.values()) { 71 handle.setEventId(this.currentEventId); 72 } 73 } 74 if (data && data.type) { 75 switch (data.type) { 76 case 'fileSystem-init': 77 this.dataCache.dataDict = data.params; 78 this.initCallchains(); 79 break; 80 case 'fileSystem-queryCallchains': 81 let callChains = convertJSON(data.params.list) || []; 82 this.dataCache.clearEBpf(); 83 this.initCallChainTopDown(callChains); 84 // @ts-ignore 85 self.postMessage({ 86 id: data.id, 87 action: 'fileSystem-init', 88 results: [], 89 }); 90 break; 91 case 'fileSystem-queryFileSamples': 92 this.handlerMap.get('fileSystem').samplesList = convertJSON(data.params.list) || []; 93 if (this.isAnalysis) { 94 this.isAnalysis = false; 95 self.postMessage({ 96 id: this.currentEventId, 97 action: data.action, 98 results: this.fileSystemAnalysis(FS_TYPE, this.handlerMap.get('fileSystem').samplesList), 99 }); 100 } else { 101 self.postMessage({ 102 id: this.currentEventId, 103 action: data.action, 104 results: this.handlerMap.get('fileSystem').resolvingAction([ 105 { 106 funcName: 'getCallChainsBySampleIds', 107 funcArgs: [true], 108 }, 109 ]), 110 }); 111 } 112 113 break; 114 case 'fileSystem-queryIoSamples': 115 this.handlerMap.get('io').samplesList = convertJSON(data.params.list) || []; 116 if (this.isAnalysis) { 117 this.isAnalysis = false; 118 self.postMessage({ 119 id: this.currentEventId, 120 action: data.action, 121 results: this.fileSystemAnalysis(BIO_TYPE, this.handlerMap.get('io').samplesList), 122 }); 123 } else { 124 self.postMessage({ 125 id: this.currentEventId, 126 action: data.action, 127 results: this.handlerMap.get('io').resolvingAction([ 128 { 129 funcName: 'getCallChainsBySampleIds', 130 funcArgs: [true], 131 }, 132 ]), 133 }); 134 } 135 136 break; 137 case 'fileSystem-queryVirtualMemorySamples': 138 this.handlerMap.get('virtualMemory').samplesList = convertJSON(data.params.list) || []; 139 if (this.isAnalysis) { 140 this.isAnalysis = false; 141 self.postMessage({ 142 id: this.currentEventId, 143 action: data.action, 144 results: this.fileSystemAnalysis(PF_TYPE, this.handlerMap.get('virtualMemory').samplesList), 145 }); 146 } else { 147 self.postMessage({ 148 id: this.currentEventId, 149 action: data.action, 150 results: this.handlerMap.get('virtualMemory').resolvingAction([ 151 { 152 funcName: 'getCallChainsBySampleIds', 153 funcArgs: [true], 154 }, 155 ]), 156 }); 157 } 158 break; 159 case 'fileSystem-action': 160 if (data.params) { 161 let filter = data.params.args.filter((item: any) => item.funcName == 'getCurrentDataFromDb'); 162 if (filter.length == 0) { 163 // @ts-ignore 164 self.postMessage({ 165 id: data.id, 166 action: data.action, 167 results: this.handlerMap.get(data.params.callType).resolvingAction(data.params.args), 168 }); 169 } else { 170 if (data.params.isAnalysis) { 171 this.isAnalysis = true; 172 } 173 this.handlerMap.get(data.params.callType).resolvingAction(data.params.args); 174 } 175 } 176 break; 177 case 'fileSystem-queryStack': 178 let res = this.getStacksByCallchainId(data.params.callchainId); 179 self.postMessage({ 180 id: data.id, 181 action: data.action, 182 results: res, 183 }); 184 break; 185 case 'fileSystem-queryFileSysEvents': 186 if (data.params.list) { 187 let res = convertJSON(data.params.list) || []; 188 postMessage(data.id, data.action, this.supplementFileSysEvents(res, this.tab)); 189 } else { 190 this.tab = data.params.tab; 191 this.queryFileSysEvents(data.params.leftNs, data.params.rightNs, data.params.typeArr, data.params.tab); 192 } 193 break; 194 case 'fileSystem-queryVMEvents': 195 if (data.params.list) { 196 let res = convertJSON(data.params.list) || []; 197 postMessage(data.id, data.action, this.supplementVMEvents(res)); 198 } else { 199 this.queryVMEvents(data.params.leftNs, data.params.rightNs, data.params.typeArr); 200 } 201 break; 202 case 'fileSystem-queryIOEvents': 203 if (data.params.list) { 204 let res = convertJSON(data.params.list) || []; 205 postMessage(data.id, data.action, this.supplementIoEvents(res)); 206 } else { 207 this.queryIOEvents(data.params.leftNs, data.params.rightNs, data.params.diskIOipids); 208 } 209 break; 210 } 211 } 212 } 213 214 clearAll() { 215 this.dataCache.clearEBpf(); 216 for (let key of this.handlerMap.keys()) { 217 if (this.handlerMap.get(key).clear) { 218 this.handlerMap.get(key).clear(); 219 } 220 } 221 this.handlerMap.clear(); 222 } 223 224 queryFileSysEvents(leftNs: number, rightNs: number, typeArr: Array<number>, tab: string) { 225 let types = Array.from(typeArr).join(','); 226 let sql = ''; 227 if (tab == 'events') { 228 sql = ` 229 select 230 A.callchain_id as callchainId, 231 (A.start_ts - B.start_ts) as startTs, 232 dur, 233 A.type, 234 ifnull(C.name,'Process') || '[' || C.pid || ']' as process, 235 ifnull(D.name,'Thread') || '[' || D.tid || ']' as thread, 236 first_argument as firstArg, 237 second_argument as secondArg, 238 third_argument as thirdArg, 239 fourth_argument as fourthArg, 240 return_value as returnValue, 241 fd, 242 file_id as fileId, 243 error_code as error 244 from file_system_sample A,trace_range B 245 left join process C on A.ipid = C.id 246 left join thread D on A.itid = D.id 247 where A.type in (${types}) 248 and( 249 (A.end_ts - B.start_ts) between $leftNS and $rightNS 250 ) 251 order by A.end_ts; 252 `; 253 } else if (tab == 'history') { 254 sql = ` 255 select 256 A.callchain_id as callchainId, 257 (A.start_ts - B.start_ts) as startTs, 258 dur, 259 fd, 260 A.type, 261 A.file_id as fileId, 262 ifnull(C.name,'Process') || '[' || C.pid || ']' as process 263 from file_system_sample A,trace_range B 264 left join process C on A.ipid = C.id 265 where A.type in (${types}) 266 and fd not null 267 and( 268 (A.start_ts - B.start_ts) between $leftNS and $rightNS 269 ) 270 order by A.end_ts; 271 `; 272 } else { 273 sql = ` 274 select TB.callchain_id as callchainId, 275 (TB.start_ts - TR.start_ts) as startTs, 276 (${rightNs} - TB.start_ts) as dur, 277 TB.fd, 278 TB.type, 279 TB.file_id as fileId, 280 ifnull(TC.name, 'Process') || '[' || TC.pid || ']' as process 281 from ( 282 select fd,ipid, 283 max(case when type = 0 then A.end_ts else 0 end) as openTs, 284 max(case when type = 1 then A.end_ts else 0 end) as closeTs 285 from file_system_sample A 286 where type in (0, 1) and A.end_ts between $leftNS and $rightNS group by fd,ipid 287 ) TA 288 left join file_system_sample TB on TA.fd = TB.fd and TA.ipid = TB.ipid and TA.openTs = TB.end_ts 289 left join process TC on TB.ipid = TC.ipid 290 left join trace_range TR 291 where startTs not null and TB.fd not null and TA.closeTs < TA.openTs 292 order by TB.end_ts; `; 293 } 294 this.queryData(this.currentEventId, 'fileSystem-queryFileSysEvents', sql, { 295 $leftNS: leftNs, 296 $rightNS: rightNs, 297 }); 298 } 299 300 queryVMEvents(leftNs: number, rightNs: number, typeArr: Array<number>) { 301 let types = Array.from(typeArr).join(','); 302 let sql = `select 303 A.callchain_id as callchainId, 304 (A.start_ts - B.start_ts) as startTs, 305 dur, 306 addr as address, 307 C.pid, 308 T.tid, 309 size, 310 A.type, 311 ifnull(T.name,'Thread') || '[' || T.tid || ']' as thread, 312 ifnull(C.name,'Process') || '[' || C.pid || ']' as process 313 from paged_memory_sample A,trace_range B 314 left join process C on A.ipid = C.id 315 left join thread T on T.id = A.itid 316 where ( 317 (A.end_ts - B.start_ts) between $leftNS and $rightNS 318 );`; 319 this.queryData(this.currentEventId, 'fileSystem-queryVMEvents', sql, { 320 $leftNS: leftNs, 321 $rightNS: rightNs, 322 }); 323 } 324 325 queryIOEvents(leftNs: number, rightNs: number, diskIOipids: Array<number>) { 326 let ipidsSql = ''; 327 if (diskIOipids.length > 0) { 328 ipidsSql += `and A.ipid in (${diskIOipids.join(',')})`; 329 } 330 let sql = `select 331 A.callchain_id as callchainId, 332 (A.start_ts - B.start_ts) as startTs, 333 latency_dur as dur, 334 path_id as pathId, 335 dur_per_4k as durPer4k, 336 tier, 337 size, 338 A.type, 339 block_number as blockNumber, 340 T.tid, 341 C.pid, 342 ifnull(T.name,'Thread') || '[' || T.tid || ']' as thread, 343 ifnull(C.name,'Process') || '[' || C.pid || ']' as process 344 from bio_latency_sample A,trace_range B 345 left join process C on A.ipid = C.id 346 left join thread T on T.id = A.itid 347 where ( 348 (A.end_ts - B.start_ts) between $leftNS and $rightNS 349 ) ${ipidsSql};`; 350 this.queryData(this.currentEventId, 'fileSystem-queryIOEvents', sql, { 351 $leftNS: leftNs, 352 $rightNS: rightNs, 353 }); 354 } 355 356 getStacksByCallchainId(id: number) { 357 let stacks = this.dataCache.eBpfCallChainsMap.get(id) ?? []; 358 let arr: Array<Stack> = []; 359 for (let s of stacks) { 360 let st: Stack = new Stack(); 361 st.path = (this.dataCache.dataDict?.get(s.pathId) ?? 'Unknown Path').split('/').reverse()[0]; 362 st.symbol = `${s.symbolsId == null ? s.ip : this.dataCache.dataDict?.get(s.symbolsId) ?? ''} (${st.path})`; 363 st.type = st.path.endsWith('.so.1') || st.path.endsWith('.dll') || st.path.endsWith('.so') ? 0 : 1; 364 arr.push(st); 365 } 366 return arr; 367 } 368 369 supplementIoEvents(res: Array<IoCompletionTimes>) { 370 return res.map((event) => { 371 if (typeof event.pathId == 'string') { 372 event.pathId = parseInt(event.pathId); 373 } 374 event.startTsStr = getTimeString(event.startTs); 375 event.durPer4kStr = event.durPer4k == 0 ? '-' : getProbablyTime(event.durPer4k); 376 event.sizeStr = getByteWithUnit(event.size); 377 event.durStr = getProbablyTime(event.dur); 378 event.path = event.pathId ? this.dataCache.dataDict?.get(event.pathId) ?? '-' : '-'; 379 // @ts-ignore 380 event.operation = DISKIO_TYPE_MAP[`${event.type}`] || 'UNKNOWN'; 381 let stacks = this.dataCache.eBpfCallChainsMap.get(event.callchainId) || []; 382 if (stacks.length > 0) { 383 let stack = stacks[0]; 384 event.backtrace = [ 385 stack.symbolsId == null ? stack.ip : this.dataCache.dataDict?.get(stack.symbolsId) ?? '', 386 `(${stacks.length} other frames)`, 387 ]; 388 } else { 389 event.backtrace = []; 390 } 391 return event; 392 }); 393 } 394 395 supplementVMEvents(res: Array<VirtualMemoryEvent>) { 396 return res.map((event) => { 397 event.startTsStr = getTimeString(event.startTs); 398 event.sizeStr = getByteWithUnit(event.size * 4096); 399 event.durStr = getProbablyTime(event.dur); 400 // @ts-ignore 401 event.operation = VM_TYPE_MAP[`${event.type}`] || 'UNKNOWNN'; 402 return event; 403 }); 404 } 405 406 supplementFileSysEvents(res: Array<FileSysEvent>, tab: string) { 407 res.map((r) => { 408 let stacks = this.dataCache.eBpfCallChainsMap.get(r.callchainId); 409 r.startTsStr = getTimeString(r.startTs); 410 r.durStr = getProbablyTime(r.dur); 411 if (tab == 'events') { 412 r.firstArg = r.firstArg ?? '0x0'; 413 r.secondArg = r.secondArg ?? '0x0'; 414 r.thirdArg = r.thirdArg ?? '0x0'; 415 r.fourthArg = r.fourthArg ?? '0x0'; 416 r.returnValue = r.returnValue ?? '0x0'; 417 r.error = r.error ?? '0x0'; 418 r.path = this.dataCache.dataDict?.get(r.fileId) ?? '-'; 419 } 420 // @ts-ignore 421 r.typeStr = FILE_TYPE_MAP[`${r.type}`] ?? ''; 422 if (stacks && stacks.length > 0) { 423 let stack = stacks[0]; 424 r.depth = stacks.length; 425 r.symbol = stack.symbolsId == null ? stack.ip : this.dataCache.dataDict?.get(stack.symbolsId) ?? ''; 426 if (tab != 'events') { 427 r.path = this.dataCache.dataDict?.get(r.fileId) ?? '-'; 428 } 429 r.backtrace = [r.symbol, `(${r.depth} other frames)`]; 430 } else { 431 r.depth = 0; 432 r.symbol = ''; 433 r.path = ''; 434 r.backtrace = []; 435 } 436 }); 437 return res; 438 } 439 440 initCallchains() { 441 if (this.handlerMap.size > 0) { 442 this.handlerMap.forEach((value) => { 443 value.clearAll(); 444 }); 445 this.handlerMap.clear(); 446 } 447 this.handlerMap.set('fileSystem', new FileSystemCallTreeHandler('fileSystem', this.queryData)); 448 this.handlerMap.set('io', new FileSystemCallTreeHandler('io', this.queryData)); 449 this.handlerMap.set('virtualMemory', new FileSystemCallTreeHandler('virtualMemory', this.queryData)); 450 this.queryData( 451 this.currentEventId, 452 'fileSystem-queryCallchains', 453 `select callchain_id as callChainId,depth,symbols_id as symbolsId,file_path_id as pathId,ip from ebpf_callstack`, 454 {} 455 ); 456 } 457 458 initCallChainTopDown(list: any[]) { 459 const callChainsMap = this.dataCache.eBpfCallChainsMap; 460 list.forEach((callchain: FileCallChain) => { 461 if (callChainsMap.has(callchain.callChainId)) { 462 callChainsMap.get(callchain.callChainId)!.push(callchain); 463 } else { 464 callChainsMap.set(callchain.callChainId, [callchain]); 465 } 466 }); 467 } 468 469 fileSystemAnalysis(type: number, samplesList: Array<FileSample>): Array<FileAnalysisSample> { 470 let analysisSampleList = new Array<FileAnalysisSample>(); 471 for (let sample of samplesList) { 472 let analysisSample = new FileAnalysisSample(sample); 473 let callChainList = this.dataCache.eBpfCallChainsMap.get(sample.callChainId); 474 if (!callChainList || callChainList.length === 0) { 475 continue; 476 } 477 let depth = callChainList.length - 1; 478 let lastCallChain: FileCallChain | undefined | null; 479 //let lastFilter 480 while (true) { 481 if (depth < 0) { 482 lastCallChain = callChainList[depth]; 483 break; 484 } 485 lastCallChain = callChainList[depth]; 486 if (type === BIO_TYPE) { 487 let symbolName = this.dataCache.dataDict?.get(lastCallChain.symbolsId); 488 if (symbolName?.includes('submit_bio')) { 489 depth--; 490 } else { 491 break; 492 } 493 } else { 494 let libPath = this.dataCache.dataDict?.get(lastCallChain.pathId); 495 if (libPath?.includes('musl') || libPath?.includes('libc++')) { 496 depth--; 497 } else { 498 break; 499 } 500 } 501 } 502 if (!lastCallChain) { 503 continue; 504 } 505 analysisSample.libId = lastCallChain.pathId; 506 analysisSample.symbolId = lastCallChain.symbolsId; 507 let libPath = this.dataCache.dataDict?.get(analysisSample.libId) || ''; 508 let pathArray = libPath.split('/'); 509 analysisSample.libName = pathArray[pathArray.length - 1]; 510 let symbolName = this.dataCache.dataDict?.get(analysisSample.symbolId); 511 if (!symbolName) { 512 symbolName = lastCallChain.ip + ' (' + analysisSample.libName + ')'; 513 } 514 analysisSample.symbolName = symbolName; 515 analysisSampleList.push(analysisSample); 516 } 517 return analysisSampleList; 518 } 519} 520 521class FileSystemCallTreeHandler { 522 currentTreeMapData: any = {}; 523 allProcess: FileMerageBean[] = []; 524 dataSource: FileMerageBean[] = []; 525 currentDataType: string = ''; 526 currentTreeList: any[] = []; 527 samplesList: FileSample[] = []; 528 splitMapData: any = {}; 529 searchValue: string = ''; 530 currentEventId: string = ''; 531 queryData = (eventId: string, action: string, sql: string, args: any) => {}; 532 533 constructor(type: string, queryData: any) { 534 this.currentDataType = type; 535 this.queryData = queryData; 536 } 537 538 clear() { 539 this.allProcess.length = 0; 540 this.dataSource.length = 0; 541 this.currentTreeList.length = 0; 542 this.samplesList.length = 0; 543 this.splitMapData = {}; 544 } 545 546 setEventId(eventId: string) { 547 this.currentEventId = eventId; 548 } 549 queryCallChainsSamples(selectionParam: any) { 550 switch (this.currentDataType) { 551 case 'fileSystem': 552 this.queryFileSamples(selectionParam); 553 break; 554 case 'io': 555 this.queryIOSamples(selectionParam); 556 break; 557 case 'virtualMemory': 558 this.queryPageFaultSamples(selectionParam); 559 break; 560 } 561 } 562 563 queryFileSamples(selectionParam: any) { 564 let sql = ''; 565 if (selectionParam.fileSystemType != undefined && selectionParam.fileSystemType.length > 0) { 566 sql += ' and s.type in ('; 567 sql += selectionParam.fileSystemType.join(','); 568 sql += ')'; 569 } 570 if ( 571 selectionParam.diskIOipids.length > 0 && 572 !selectionParam.diskIOLatency && 573 selectionParam.fileSystemType.length == 0 574 ) { 575 sql += ` and s.ipid in (${selectionParam.diskIOipids.join(',')})`; 576 } 577 this.queryData( 578 this.currentEventId, 579 'fileSystem-queryFileSamples', 580 `select s.callchain_id as callChainId,h.tid,h.name as threadName,s.dur,s.type,p.pid,p.name as processName from file_system_sample s,trace_range t 581left join process p on p.id = s.ipid 582left join thread h on h.id = s.itid 583where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`, 584 { 585 $startTime: selectionParam.leftNs, 586 $endTime: selectionParam.rightNs, 587 } 588 ); 589 } 590 591 queryIOSamples(selectionParam: any) { 592 let sql = ''; 593 if (selectionParam.diskIOipids.length > 0) { 594 sql += `and (s.ipid in (${selectionParam.diskIOipids.join(',')}) and s.type in (5,6)) `; 595 } 596 if (selectionParam.diskIOReadIds.length > 0) { 597 sql += `or (s.ipid in (${selectionParam.diskIOReadIds.join(',')}) and s.type in (1,3)) `; 598 } 599 if (selectionParam.diskIOWriteIds.length > 0) { 600 sql += `or (s.ipid in (${selectionParam.diskIOWriteIds.join(',')}) and s.type in (2,4)) `; 601 } 602 this.queryData( 603 this.currentEventId, 604 'fileSystem-queryIoSamples', 605 `select s.callchain_id as callChainId,h.tid,h.name as threadName,s.latency_dur as dur,s.type,p.pid,p.name as processName from bio_latency_sample s,trace_range t 606left join process p on p.id = s.ipid 607left join thread h on h.id = s.itid 608where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`, 609 { 610 $startTime: selectionParam.leftNs, 611 $endTime: selectionParam.rightNs, 612 } 613 ); 614 } 615 616 queryPageFaultSamples(selectionParam: any) { 617 let sql = ''; 618 if ( 619 selectionParam.diskIOipids.length > 0 && 620 !selectionParam.diskIOLatency && 621 !selectionParam.fileSysVirtualMemory 622 ) { 623 sql += ` and s.ipid in (${selectionParam.diskIOipids.join(',')})`; 624 } 625 this.queryData( 626 this.currentEventId, 627 'fileSystem-queryVirtualMemorySamples', 628 `select s.callchain_id as callChainId,h.tid,h.name as threadName,s.dur,s.type,p.pid,p.name as processName from paged_memory_sample s,trace_range t 629left join process p on p.id = s.ipid 630left join thread h on h.id = s.itid 631where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`, 632 { 633 $startTime: selectionParam.leftNs, 634 $endTime: selectionParam.rightNs, 635 } 636 ); 637 } 638 639 freshCurrentCallChains(samples: FileSample[], isTopDown: boolean) { 640 this.currentTreeMapData = {}; 641 this.currentTreeList = []; 642 this.allProcess = []; 643 this.dataSource = []; 644 let totalCount = 0; 645 646 samples.forEach((sample) => { 647 totalCount += sample.dur; 648 let callChains = this.createThreadAndType(sample); 649 if (callChains.length == 2) { 650 return; 651 } 652 let topIndex = isTopDown ? 0 : callChains.length - 1; 653 if (callChains.length > 1) { 654 let root = 655 this.currentTreeMapData[callChains[topIndex].symbolsId + '' + callChains[topIndex].pathId + sample.pid]; 656 if (root == undefined) { 657 root = new FileMerageBean(); 658 this.currentTreeMapData[callChains[topIndex].symbolsId + '' + callChains[topIndex].pathId + sample.pid] = 659 root; 660 this.currentTreeList.push(root); 661 } 662 FileMerageBean.merageCallChainSample(root, callChains[topIndex], sample, false); 663 this.merageChildrenByIndex(root, callChains, topIndex, sample, isTopDown); 664 } 665 }); 666 let rootMerageMap: any = {}; 667 // @ts-ignore 668 Object.values(this.currentTreeMapData).forEach((merageData: any) => { 669 if (rootMerageMap[merageData.pid] == undefined) { 670 let fileMerageBean = new FileMerageBean(); //新增进程的节点数据 671 fileMerageBean.canCharge = false; 672 fileMerageBean.symbolName = merageData.processName; 673 fileMerageBean.symbol = fileMerageBean.symbolName; 674 fileMerageBean.children.push(merageData); 675 fileMerageBean.initChildren.push(merageData); 676 fileMerageBean.dur = merageData.dur; 677 fileMerageBean.count = merageData.count; 678 fileMerageBean.total = totalCount; 679 rootMerageMap[merageData.pid] = fileMerageBean; 680 } else { 681 rootMerageMap[merageData.pid].children.push(merageData); 682 rootMerageMap[merageData.pid].initChildren.push(merageData); 683 rootMerageMap[merageData.pid].dur += merageData.dur; 684 rootMerageMap[merageData.pid].count += merageData.count; 685 rootMerageMap[merageData.pid].total = totalCount; 686 } 687 merageData.parentNode = rootMerageMap[merageData.pid]; //子节点添加父节点的引用 688 }); 689 let id = 0; 690 this.currentTreeList.forEach((currentNode) => { 691 currentNode.total = totalCount; 692 this.setMerageName(currentNode); 693 if (currentNode.id == '') { 694 currentNode.id = id + ''; 695 id++; 696 } 697 if (currentNode.parentNode) { 698 if (currentNode.parentNode.id == '') { 699 currentNode.parentNode.id = id + ''; 700 id++; 701 } 702 currentNode.parentId = currentNode.parentNode.id; 703 } 704 }); 705 // @ts-ignore 706 this.allProcess = Object.values(rootMerageMap); 707 } 708 709 createThreadAndType(sample: FileSample) { 710 let typeCallChain = new FileCallChain(); 711 typeCallChain.callChainId = sample.callChainId; 712 let map: any = {}; 713 if (this.currentDataType == 'fileSystem') { 714 map = FILE_TYPE_MAP; 715 } else if (this.currentDataType == 'io') { 716 map = DISKIO_TYPE_MAP; 717 } else if (this.currentDataType == 'virtualMemory') { 718 map = VM_TYPE_MAP; 719 } 720 // @ts-ignore 721 typeCallChain.ip = map[sample.type.toString()] || 'UNKNOWN'; 722 typeCallChain.symbolsId = sample.type; 723 typeCallChain.pathId = -1; 724 let threadCallChain = new FileCallChain(); 725 threadCallChain.callChainId = sample.callChainId; 726 threadCallChain.ip = (sample.threadName || 'Thread') + `-${sample.tid}`; 727 threadCallChain.symbolsId = sample.tid; 728 threadCallChain.pathId = -1; 729 const eBpfCallChainsMap = DataCache.getInstance().eBpfCallChainsMap; 730 return [typeCallChain, threadCallChain, ...(eBpfCallChainsMap.get(sample.callChainId) || [])]; 731 } 732 733 merageChildrenByIndex( 734 currentNode: FileMerageBean, 735 callChainDataList: any[], 736 index: number, 737 sample: FileSample, 738 isTopDown: boolean 739 ) { 740 isTopDown ? index++ : index--; 741 let isEnd = isTopDown ? callChainDataList.length == index + 1 : index == 0; 742 let node; 743 if ( 744 currentNode.initChildren.filter((child: any) => { 745 if ( 746 child.ip == callChainDataList[index]?.ip || 747 (child.symbolsId != null && 748 child.symbolsId == callChainDataList[index]?.symbolsId && 749 child.pathId == callChainDataList[index]?.pathId) 750 ) { 751 node = child; 752 FileMerageBean.merageCallChainSample(child, callChainDataList[index], sample, isEnd); 753 return true; 754 } 755 return false; 756 }).length == 0 757 ) { 758 node = new FileMerageBean(); 759 FileMerageBean.merageCallChainSample(node, callChainDataList[index], sample, isEnd); 760 currentNode.children.push(node); 761 currentNode.initChildren.push(node); 762 this.currentTreeList.push(node); 763 node.parentNode = currentNode; 764 } 765 if (node && !isEnd) this.merageChildrenByIndex(node, callChainDataList, index, sample, isTopDown); 766 } 767 768 setMerageName(currentNode: FileMerageBean) { 769 if (currentNode.pathId == -1) { 770 currentNode.canCharge = false; 771 currentNode.symbol = currentNode.ip; 772 currentNode.symbolName = currentNode.symbol; 773 currentNode.libName = ''; 774 currentNode.path = ''; 775 } else { 776 const dataCache = DataCache.getInstance(); 777 currentNode.symbol = dataCache.dataDict?.get(currentNode.symbolsId) || currentNode.ip || 'unknown'; 778 currentNode.path = dataCache.dataDict?.get(currentNode.pathId) || 'unknown'; 779 currentNode.libName = setFileName(currentNode.path); 780 currentNode.lib = currentNode.libName; 781 currentNode.addr = currentNode.ip; 782 currentNode.symbolName = `${currentNode.symbol} (${currentNode.libName})`; 783 } 784 } 785 resolvingAction(params: any[]) { 786 if (params.length > 0) { 787 params.forEach((paramItem) => { 788 if (paramItem.funcName && paramItem.funcArgs) { 789 switch (paramItem.funcName) { 790 case 'getCallChainsBySampleIds': 791 this.freshCurrentCallChains(this.samplesList, paramItem.funcArgs[0]); 792 break; 793 case 'getCurrentDataFromDb': 794 this.queryCallChainsSamples(paramItem.funcArgs[0]); 795 break; 796 case 'hideSystemLibrary': 797 merageBeanDataSplit.hideSystemLibrary(this.allProcess, this.splitMapData); 798 break; 799 case 'hideNumMaxAndMin': 800 merageBeanDataSplit.hideNumMaxAndMin( 801 this.allProcess, 802 this.splitMapData, 803 paramItem.funcArgs[0], 804 paramItem.funcArgs[1] 805 ); 806 break; 807 case 'splitAllProcess': 808 merageBeanDataSplit.splitAllProcess(this.allProcess, this.splitMapData, paramItem.funcArgs[0]); 809 break; 810 case 'resetAllNode': 811 merageBeanDataSplit.resetAllNode(this.allProcess, this.currentTreeList, this.searchValue); 812 break; 813 case 'resotreAllNode': 814 merageBeanDataSplit.resotreAllNode(this.splitMapData, paramItem.funcArgs[0]); 815 break; 816 case 'clearSplitMapData': 817 this.clearSplitMapData(paramItem.funcArgs[0]); 818 break; 819 case 'splitTree': 820 merageBeanDataSplit.splitTree( 821 this.splitMapData, 822 this.allProcess, 823 paramItem.funcArgs[0], 824 paramItem.funcArgs[1], 825 paramItem.funcArgs[2], 826 this.currentTreeList, 827 this.searchValue 828 ); 829 break; 830 case 'setSearchValue': 831 this.searchValue = paramItem.funcArgs[0]; 832 break; 833 } 834 } 835 }); 836 this.dataSource = this.allProcess.filter((process) => { 837 return process.children && process.children.length > 0; 838 }); 839 } 840 return this.dataSource; 841 } 842 843 clearAll() { 844 this.samplesList = []; 845 this.splitMapData = {}; 846 this.currentTreeMapData = {}; 847 this.currentTreeList = []; 848 this.searchValue = ''; 849 this.allProcess = []; 850 this.dataSource = []; 851 this.splitMapData = {}; 852 this.currentDataType = ''; 853 } 854 855 clearSplitMapData(symbolName: string) { 856 delete this.splitMapData[symbolName]; 857 } 858} 859 860class FileSample { 861 type: number = 0; 862 callChainId: number = 0; 863 dur: number = 0; 864 pid: number = 0; 865 tid: number = 0; 866 threadName: string = ''; 867 processName: string = ''; 868} 869 870class FileAnalysisSample extends FileSample { 871 libId = 0; 872 symbolId = 0; 873 libName = ''; 874 symbolName = ''; 875 constructor(fileSample: FileSample) { 876 super(); 877 this.type = fileSample.type; 878 this.callChainId = fileSample.callChainId; 879 this.dur = fileSample.dur; 880 this.pid = fileSample.pid; 881 this.tid = fileSample.tid; 882 this.threadName = fileSample.threadName; 883 this.processName = fileSample.processName; 884 } 885} 886 887export class FileMerageBean extends MerageBean { 888 ip: string = ''; 889 symbolsId: number = 0; 890 pathId: number = 0; 891 processName: string = ''; 892 type: number = 0; 893 894 static merageCallChainSample( 895 currentNode: FileMerageBean, 896 callChain: FileCallChain, 897 sample: FileSample, 898 isEnd: boolean 899 ) { 900 if (currentNode.processName == '') { 901 currentNode.ip = callChain.ip; 902 currentNode.pid = sample.pid; 903 currentNode.canCharge = true; 904 currentNode.pathId = callChain.pathId; 905 currentNode.symbolsId = callChain.symbolsId; 906 currentNode.processName = sample.processName || `Process(${sample.pid})`; 907 } 908 if (isEnd) { 909 currentNode.selfDur += sample.dur; 910 currentNode.self = getProbablyTime(currentNode.selfDur); 911 } 912 currentNode.dur += sample.dur; 913 currentNode.count++; 914 } 915} 916 917export class Stack { 918 type: number = 0; 919 symbol: string = ''; 920 path: string = ''; 921} 922 923export class FileSysEvent { 924 isSelected: boolean = false; 925 id: number = 0; 926 callchainId: number = 0; 927 startTs: number = 0; 928 startTsStr: string = ''; 929 durStr: string = ''; 930 dur: number = 0; 931 process: string = ''; 932 thread: string = ''; 933 type: number = 0; 934 typeStr: string = ''; 935 fd: number = 0; 936 size: number = 0; 937 depth: number = 0; 938 firstArg: string = ''; 939 secondArg: string = ''; 940 thirdArg: string = ''; 941 fourthArg: string = ''; 942 returnValue: string = ''; 943 error: string = ''; 944 path: string = ''; 945 symbol: string = ''; 946 backtrace: Array<string> = []; 947 fileId: number = 0; 948} 949 950export class IoCompletionTimes { 951 isSelected: boolean = false; 952 type: number = 0; 953 callchainId: number = 0; 954 startTs: number = 0; 955 startTsStr: string = ''; 956 durStr: string = ''; 957 dur: number = 0; 958 tid: number = 0; 959 pid: number = 0; 960 process: string = ''; 961 thread: string = ''; 962 path: string = ''; 963 pathId: number = 0; 964 operation: string = ''; 965 size: number = 0; 966 sizeStr: string = ''; 967 blockNumber: string = ''; 968 tier: number = 0; 969 backtrace: Array<string> = []; 970 durPer4kStr: string = ''; 971 durPer4k: number = 0; 972} 973 974export class VirtualMemoryEvent { 975 isSelected: boolean = false; 976 callchainId: number = 0; 977 startTs: number = 0; 978 startTsStr: string = ''; 979 durStr: string = ''; 980 dur: number = 0; 981 process: string = ''; 982 thread: string = ''; 983 address: string = ''; 984 size: number = 0; 985 sizeStr: string = ''; 986 type: number = 0; 987 tid: number = 0; 988 pid: number = 0; 989 operation: string = ''; 990} 991