• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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