• 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, getByteWithUnit,
18    getProbablyTime,
19    getTimeString,
20    LogicHandler,
21    MerageBean,
22    merageBeanDataSplit,
23    postMessage,
24    setFileName
25} from "./ProcedureLogicWorkerCommon.js";
26
27export let FILE_TYPE_MAP = {
28    '0': 'OPEN',
29    '1': 'CLOSE',
30    '2': 'READ',
31    '3': 'WRITE',
32};
33
34export let DISKIO_TYPE_MAP = {
35    '0': 'OPEN',
36    '1': 'CLOSE',
37    '2': 'READ',
38    '3': 'WRITE',
39};
40
41export let VM_TYPE_MAP = {
42    '1': 'File Backed In',
43    '2': 'Page Cache Hit',
44    '3': 'Swap From Zram',
45    '4': 'Swap From Disk',
46    '5': 'Zero Fill Page',
47    '6': 'Zero FAKE Page',
48    '7': 'Copy On Write',
49};
50
51export class ProcedureLogicWorkerFileSystem extends LogicHandler {
52    tab:string = "";
53    data_dict: Map<number, string> = new Map<number, string>();
54    currentEventId: string = ""
55    currentTreeMapData: any = {}
56    samplesData: FileSample[] = []
57    splitMapData: any = {}
58    currentTreeList: any[] = []
59    searchValue: string = ""
60    callChainsMap: Map<number, FileCallChain[]> = new Map<number, FileCallChain[]>()
61    allProcess: FileMerageBean[] = []
62    dataSource: FileMerageBean[] = []
63    currentDataType:string = ""
64
65    handle(data: any): void {
66        this.currentEventId = data.id
67        if (data && data.type) {
68            switch (data.type) {
69                case "fileSystem-init":
70                    this.data_dict = data.params as Map<number, string>
71                    this.initCallchains();
72                    break
73                case "fileSystem-queryCallchains":
74                    let callChains = convertJSON(data.params.list) || [];
75                    this.initCallChainTopDown(callChains)
76                    // @ts-ignore
77                    self.postMessage({id: data.id, action: data.action, results: []});
78                    break;
79                case "fileSystem-queryFileSamples":
80                case "fileSystem-queryIoSamples":
81                case "fileSystem-queryVirtualMemorySamples":
82                    this.currentDataType = data.type
83                    this.samplesData = convertJSON(data.params.list) || []
84                    self.postMessage({
85                        id: data.id, action: data.action, results: this.resolvingAction([{
86                            funcName: "getCallChainsBySampleIds",
87                            funcArgs: [true]
88                        }])
89                    });
90                    break;
91                case "fileSystem-action":
92                    if (data.params) {
93                        let filter = data.params.filter((item: any) => item.funcName == "getCurrentDataFromDb");
94                        if (filter.length == 0) {
95                            // @ts-ignore
96                            self.postMessage({
97                                id: data.id,
98                                action: data.action,
99                                results: this.resolvingAction(data.params)
100                            });
101                        } else {
102                            this.resolvingAction(data.params)
103                        }
104                    }
105                    break;
106                case "fileSystem-queryStack":
107                    let res = this.getStacksByCallchainId(data.params.callchainId)
108                    self.postMessage({id: data.id, action: data.action, results: res})
109                    break;
110                case "fileSystem-queryFileSysEvents":
111                    if (data.params.list) {
112                        let res = convertJSON(data.params.list) || []
113                        postMessage(data.id, data.action, this.supplementFileSysEvents(res, this.tab));
114                    } else {
115                        this.tab = data.params.tab;
116                        this.queryFileSysEvents(data.params.leftNs, data.params.rightNs, data.params.typeArr, data.params.tab)
117                    }
118                    break;
119                case "fileSystem-queryVMEvents":
120                    if (data.params.list) {
121                        let res = convertJSON(data.params.list) || []
122                        postMessage(data.id, data.action, this.supplementVMEvents(res));
123                    } else {
124                        this.queryVMEvents(data.params.leftNs, data.params.rightNs, data.params.typeArr)
125                    }
126                    break
127                case "fileSystem-queryIOEvents":
128                    if (data.params.list) {
129                        let res = convertJSON(data.params.list) || []
130                        postMessage(data.id, data.action, this.supplementIoEvents(res));
131                    } else {
132                        this.queryIOEvents(data.params.leftNs, data.params.rightNs, data.params.typeArr)
133                    }
134                    break
135            }
136        }
137    }
138
139    queryFileSysEvents(leftNs: number, rightNs: number, typeArr: Array<number>, tab: string) {
140        let types = Array.from(typeArr).join(",");
141        let sql = "";
142        if (tab == "events") {
143            sql = `
144            select
145                A.callchain_id as callchainId,
146                (A.start_ts - B.start_ts) as startTs,
147                dur,
148                A.type,
149                ifnull(C.name,'Process') || '[' || C.pid || ']' as process,
150                ifnull(D.name,'Thread') || '[' || D.tid || ']' as thread,
151                first_argument as firstArg,
152                second_argument as secondArg,
153                third_argument as thirdArg,
154                fourth_argument as fourthArg,
155                return_value as returnValue,
156                error_code as error
157            from file_system_sample A,trace_range B
158            left join process C on A.ipid = C.id
159            left join thread D on A.itid = D.id
160            where A.type in (${types})
161            and(
162                (A.end_ts - B.start_ts) between $leftNS and $rightNS
163            )
164            order by A.end_ts;
165        `
166        } else if (tab == "history") {
167            sql = `
168            select
169                A.callchain_id as callchainId,
170                (A.start_ts - B.start_ts) as startTs,
171                dur,
172                fd,
173                A.type,
174                ifnull(C.name,'Process') || '[' || C.pid || ']' as process
175            from file_system_sample A,trace_range B
176            left join process C on A.ipid = C.id
177            where A.type in (${types})
178            and fd not null
179            and(
180                (A.start_ts - B.start_ts) between $leftNS and $rightNS
181            )
182            order by A.end_ts;
183        `
184        } else {
185            sql = `
186            select TB.callchain_id                                  as callchainId,
187                (TB.start_ts - TR.start_ts)                         as startTs,
188                (${rightNs} - TB.start_ts)                          as dur,
189                TB.fd,
190                TB.type,
191                ifnull(TC.name, 'Process') || '[' || TC.pid || ']'  as process
192            from (
193                select fd,
194                    max(case when type = 0 then A.end_ts else 0 end) as openTs,
195                    max(case when type = 1 then A.end_ts else 0 end) as closeTs
196                from file_system_sample A
197                where type in (0, 1) and A.end_ts between $leftNS and $rightNS group by fd
198                ) TA
199            left join file_system_sample TB on TA.fd = TB.fd and TA.openTs = TB.end_ts
200            left join process TC on TB.ipid = TC.ipid
201            left join trace_range TR
202            where startTs not null and TB.fd not null and TA.closeTs < TA.openTs
203            order by TB.end_ts;  `
204        }
205        this.queryData("fileSystem-queryFileSysEvents", sql, {$leftNS: leftNs, $rightNS: rightNs})
206    }
207
208    queryVMEvents(leftNs: number, rightNs: number, typeArr: Array<number>){
209        let types = Array.from(typeArr).join(",");
210        let sql = `select
211                A.callchain_id as callchainId,
212                (A.start_ts - B.start_ts) as startTs,
213                dur,
214                addr as address,
215                C.pid,
216                T.tid,
217                size,
218                A.type,
219                ifnull(T.name,'Thread') || '[' || T.tid || ']' as thread,
220                ifnull(C.name,'Process') || '[' || C.pid || ']' as process
221            from virtual_memory_sample A,trace_range B
222            left join process C on A.ipid = C.id
223            left join thread T on T.id = A.itid
224            where (
225                (A.end_ts - B.start_ts) between $leftNS and $rightNS
226            );`;
227        this.queryData("fileSystem-queryVMEvents", sql, {$leftNS: leftNs, $rightNS: rightNs})
228    }
229
230    queryIOEvents(leftNs: number, rightNs: number, typeArr: Array<number>){
231        let types = Array.from(typeArr).join(",");
232        let sql = `select
233                A.callchain_id as callchainId,
234                (A.start_ts - B.start_ts) as startTs,
235                latency_dur as dur,
236                path,
237                dur_per_4k as durPer4k,
238                tier,
239                size,
240                A.type,
241                block_number as blockNumber,
242                ifnull(T.name,'Thread') || '[' || T.tid || ']' as thread,
243                ifnull(C.name,'Process') || '[' || C.pid || ']' as process
244            from io_latency_sample A,trace_range B
245            left join process C on A.ipid = C.id
246            left join thread T on T.id = A.itid
247            where (
248                (A.start_ts - B.start_ts) between $leftNS and $rightNS
249                or
250                (A.start_ts + A.latency_dur - B.start_ts) between $leftNS and $rightNS
251            );`;
252        this.queryData("fileSystem-queryIOEvents", sql, {$leftNS: leftNs, $rightNS: rightNs})
253    }
254
255    getStacksByCallchainId(id: number) {
256        let stacks = this.callChainsMap.get(id) ?? [];
257        let arr: Array<Stack> = [];
258        for (let s of stacks) {
259            let st: Stack = new Stack()
260            st.path = (this.data_dict.get(s.pathId) ?? "Unknown Path").split("/").reverse()[0];
261            st.symbol = `${s.symbolsId == null ? s.ip : this.data_dict.get(s.symbolsId) ?? ''} (${st.path})`;
262            st.type = (st.path.endsWith(".so.1") || st.path.endsWith(".dll") || st.path.endsWith(".so")) ? 0 : 1;
263            arr.push(st);
264        }
265        return arr;
266    }
267
268    supplementIoEvents(res: Array<IoCompletionTimes>){
269        return res.map((event) => {
270            event.startTsStr = getTimeString(event.startTs)
271            event.durPer4kStr = getProbablyTime(event.durPer4k)
272            event.sizeStr = getByteWithUnit(event.size)
273            event.durStr = getProbablyTime(event.dur)
274            // @ts-ignore
275            event.operation = DISKIO_TYPE_MAP[`${event.type}`]||"UNKNOW"
276            let stacks = this.callChainsMap.get(event.callchainId)||[];
277            if(stacks.length > 0){
278                let stack = stacks[0]
279                event.backtrace = [stack.symbolsId == null ? stack.ip : this.data_dict.get(stack.symbolsId) ?? "", `(${stacks.length} other frames)`];
280            }else {
281                event.backtrace = [];
282            }
283            return event
284        })
285    }
286
287    supplementVMEvents(res: Array<VirtualMemoryEvent>){
288        return res.map((event) => {
289            event.startTsStr = getTimeString(event.startTs)
290            event.sizeStr = getByteWithUnit(event.size*4096)
291            event.durStr = getProbablyTime(event.dur)
292            // @ts-ignore
293            event.operation = VM_TYPE_MAP[`${event.type}`]||"UNKNOW"
294            return event
295        })
296    }
297
298
299    supplementFileSysEvents(res: Array<FileSysEvent>, tab: string) {
300        res.map((r) => {
301            let stacks = this.callChainsMap.get(r.callchainId);
302            r.startTsStr = getTimeString(r.startTs);
303            r.durStr = getProbablyTime(r.dur);
304            if (tab == "events") {
305                r.firstArg = r.firstArg ?? "0x0"
306                r.secondArg = r.secondArg ?? "0x0"
307                r.thirdArg = r.thirdArg ?? "0x0"
308                r.fourthArg = r.fourthArg ?? "0x0"
309                r.returnValue = r.returnValue ?? "0x0"
310                r.error = r.error ?? "0x0"
311            }
312            // @ts-ignore
313            r.typeStr = FILE_TYPE_MAP[`${r.type}`] ?? "";
314            if (stacks && stacks.length > 0) {
315                let stack = stacks[0]
316                r.depth = stacks.length;
317                r.symbol = stack.symbolsId == null ? stack.ip : this.data_dict.get(stack.symbolsId) ?? ""
318                if (tab != "events") {
319                    r.path = this.data_dict.get(stack.pathId) ?? ""
320                }
321                r.backtrace = [r.symbol, `(${r.depth} other frames)`];
322            } else {
323                r.depth = 0;
324                r.symbol = "";
325                r.path = "";
326                r.backtrace = [];
327            }
328        })
329        return res;
330    }
331
332    initCallchains() {
333        this.clearAll()
334        this.queryData("fileSystem-queryCallchains", `select callchain_id as callChainId,depth,symbols_id as symbolsId,file_path_id as pathId,ip from ebpf_callstack`, {})
335    }
336
337    queryCallchainsSamples(selectionParam: any){
338        if(selectionParam.queryFuncName!=undefined){
339            switch (selectionParam.queryFuncName) {
340                case "fileSystem":
341                    this.queryFileSamples(selectionParam)
342                    break;
343                case "io":
344                    this.queryIOSamples(selectionParam)
345                    break;
346                case "virtualMemory":
347                    this.queryEpbfSamples(selectionParam)
348                    break;
349            }
350        }
351    }
352
353    queryFileSamples(selectionParam: any) {
354        let sql = '';
355        if (selectionParam.fileSystemType != undefined && selectionParam.fileSystemType.length > 0) {
356            sql += " and s.type in ("
357            sql += selectionParam.fileSystemType.join(",")
358            sql += ")"
359        }
360        this.queryData("fileSystem-queryFileSamples", `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
361left join process p on p.id = s.ipid
362left join thread h on h.id = s.itid
363where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`
364            , {$startTime: selectionParam.leftNs, $endTime: selectionParam.rightNs})
365
366    }
367
368    queryIOSamples(selectionParam: any){
369        let sql = '';
370        this.queryData("fileSystem-queryIoSamples", `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 io_latency_sample s,trace_range t
371left join process p on p.id = s.ipid
372left join thread h on h.id = s.itid
373where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`
374            , {$startTime: selectionParam.leftNs, $endTime: selectionParam.rightNs})
375    }
376
377    queryEpbfSamples(selectionParam: any){
378        let sql = '';
379        this.queryData("fileSystem-queryVirtualMemorySamples", `select s.callchain_id as callChainId,h.tid,h.name as threadName,s.dur,s.type,p.pid,p.name as processName from virtual_memory_sample s,trace_range t
380left join process p on p.id = s.ipid
381left join thread h on h.id = s.itid
382where s.end_ts between $startTime + t.start_ts and $endTime + t.start_ts ${sql} and callchain_id != -1;`
383            , {$startTime: selectionParam.leftNs, $endTime: selectionParam.rightNs})
384    }
385
386    initCallChainTopDown(list: any[]) {
387        list.forEach((callchain: FileCallChain) => {
388            if (this.callChainsMap.has(callchain.callChainId)) {
389                this.callChainsMap.get(callchain.callChainId)!.push(callchain)
390            } else {
391                this.callChainsMap.set(callchain.callChainId, [callchain])
392            }
393        })
394    }
395
396    freshCurrentCallchains(samples: FileSample[], isTopDown: boolean) {
397        this.currentTreeMapData = {}
398        this.currentTreeList = []
399        this.allProcess = [];
400        this.dataSource = []
401        let totalCount = 0
402        samples.forEach((sample) => {
403            totalCount += sample.dur;
404            let callChains = this.createThreadAndType(sample)
405            let topIndex = isTopDown ? 0 : (callChains.length - 1);
406            if (callChains.length > 1) {
407                let root = this.currentTreeMapData[callChains[topIndex].symbolsId + "" + callChains[topIndex].pathId + sample.pid];
408                if (root == undefined) {
409                    root = new FileMerageBean();
410                    this.currentTreeMapData[callChains[topIndex].symbolsId + "" + callChains[topIndex].pathId + sample.pid] = root;
411                    this.currentTreeList.push(root)
412                }
413                FileMerageBean.merageCallChainSample(root, callChains[topIndex], sample, false);
414                this.merageChildrenByIndex(root, callChains, topIndex, sample, isTopDown);
415            }
416        })
417        let rootMerageMap: any = {}
418        Object.values(this.currentTreeMapData).forEach((merageData: any) => {
419            if (rootMerageMap[merageData.pid] == undefined) {
420                let processMerageData = new FileMerageBean()//新增进程的节点数据
421                processMerageData.canCharge = false
422                processMerageData.symbolName = merageData.processName
423                processMerageData.symbol = processMerageData.symbolName
424                processMerageData.children.push(merageData)
425                processMerageData.initChildren.push(merageData)
426                processMerageData.dur = merageData.dur;
427                processMerageData.count = merageData.count;
428                processMerageData.total = totalCount;
429                rootMerageMap[merageData.pid] = processMerageData
430            } else {
431                rootMerageMap[merageData.pid].children.push(merageData)
432                rootMerageMap[merageData.pid].initChildren.push(merageData)
433                rootMerageMap[merageData.pid].dur += merageData.dur;
434                rootMerageMap[merageData.pid].count += merageData.count;
435                rootMerageMap[merageData.pid].total = totalCount;
436            }
437            merageData.parentNode = rootMerageMap[merageData.pid]//子节点添加父节点的引用
438        })
439        let id = 0;
440        this.currentTreeList.forEach((node) => {
441            node.total = totalCount;
442            this.setMerageName(node)
443            if (node.id == "") {
444                node.id = id + ""
445                id++
446            }
447            if (node.parentNode) {
448                if (node.parentNode.id == "") {
449                    node.parentNode.id = id + ""
450                    id++
451                }
452                node.parentId = node.parentNode.id
453            }
454        })
455        this.allProcess = Object.values(rootMerageMap)
456    }
457
458    createThreadAndType(sample: FileSample){
459        let typeCallchain = new FileCallChain();
460        typeCallchain.callChainId = sample.callChainId
461        let map:any = {}
462        if(this.currentDataType == "fileSystem-queryFileSamples"){
463            map = FILE_TYPE_MAP
464        }else if(this.currentDataType == "fileSystem-queryIoSamples"){
465            map = DISKIO_TYPE_MAP
466        }else if(this.currentDataType == "fileSystem-queryVirtualMemorySamples"){
467            map = VM_TYPE_MAP
468        }
469        // @ts-ignore
470        typeCallchain.ip = map[sample.type.toString()]||"";
471        typeCallchain.symbolsId = sample.type
472        typeCallchain.pathId = -1;
473        let threadCallChain = new FileCallChain();
474        threadCallChain.callChainId = sample.callChainId
475        threadCallChain.ip = (sample.threadName||"Thread")+`-${sample.tid}`
476        threadCallChain.symbolsId = sample.tid;
477        threadCallChain.pathId = -1;
478        return [typeCallchain,threadCallChain,...(this.callChainsMap.get(sample.callChainId)||[])]
479    }
480
481
482    merageChildrenByIndex(currentNode: FileMerageBean, callChainDataList: any[], index: number, sample: FileSample, isTopDown: boolean) {
483        isTopDown ? index++ : index--;
484        let isEnd = isTopDown ? (callChainDataList.length == index + 1) : (index == 0)
485        let node;
486        if (currentNode.initChildren.filter((child: any) => {
487            if (child.ip == callChainDataList[index]?.ip||(child.symbolsId == callChainDataList[index]?.symbolsId&&child.pathId == callChainDataList[index]?.pathId)) {
488                node = child;
489                FileMerageBean.merageCallChainSample(child, callChainDataList[index], sample, isEnd)
490                return true;
491            }
492            return false;
493        }).length == 0) {
494            node = new FileMerageBean()
495            FileMerageBean.merageCallChainSample(node, callChainDataList[index], sample, isEnd)
496            currentNode.children.push(node)
497            currentNode.initChildren.push(node)
498            this.currentTreeList.push(node)
499            node.parentNode = currentNode
500        }
501        if (node && !isEnd) this.merageChildrenByIndex(node, callChainDataList, index, sample, isTopDown)
502    }
503
504    setMerageName(currentNode: FileMerageBean) {
505        if (currentNode.pathId == -1) {
506            currentNode.canCharge = false;
507            currentNode.symbol = currentNode.ip;
508            currentNode.symbolName = currentNode.symbol;
509            currentNode.libName = "";
510            currentNode.path = "";
511        } else {
512            currentNode.symbol = this.data_dict.get(currentNode.symbolsId) || currentNode.ip || 'unkown'
513            currentNode.path = this.data_dict.get(currentNode.pathId) || 'unkown'
514            currentNode.libName = setFileName(currentNode.path)
515            currentNode.symbolName = `${currentNode.symbol} (${currentNode.libName})`
516        }
517    }
518
519    clearAll() {
520        this.samplesData = []
521        this.splitMapData = {}
522        this.currentTreeMapData = {}
523        this.currentTreeList = []
524        this.searchValue = ""
525        this.allProcess = []
526        this.dataSource = []
527        this.callChainsMap = new Map<number, FileCallChain[]>()
528        this.splitMapData = {}
529        this.searchValue = ""
530        this.currentDataType = ""
531    }
532
533    clearSplitMapData(symbolName: string) {
534        delete this.splitMapData[symbolName]
535    }
536
537    resolvingAction(params: any[]) {
538        if (params.length > 0) {
539            params.forEach((item) => {
540                if (item.funcName && item.funcArgs) {
541                    switch (item.funcName) {
542                        case "getCallChainsBySampleIds":
543                            this.freshCurrentCallchains(this.samplesData, item.funcArgs[0])
544                            break
545                        case "getCurrentDataFromDb":
546                            this.queryCallchainsSamples(item.funcArgs[0]);
547                            break
548                        case "hideSystemLibrary":
549                            merageBeanDataSplit.hideSystemLibrary(this.allProcess, this.splitMapData);
550                            break
551                        case "hideNumMaxAndMin":
552                            merageBeanDataSplit.hideNumMaxAndMin(this.allProcess, this.splitMapData, item.funcArgs[0], item.funcArgs[1])
553                            break
554                        case "splitAllProcess":
555                            merageBeanDataSplit.splitAllProcess(this.allProcess, this.splitMapData, item.funcArgs[0])
556                            break
557                        case "resetAllNode":
558                            merageBeanDataSplit.resetAllNode(this.allProcess, this.currentTreeList, this.searchValue)
559                            break
560                        case "resotreAllNode":
561                            merageBeanDataSplit.resotreAllNode(this.splitMapData, item.funcArgs[0])
562                            break
563                        case "clearSplitMapData":
564                            this.clearSplitMapData(item.funcArgs[0])
565                            break
566                        case "splitTree":
567                            merageBeanDataSplit.splitTree(this.splitMapData, this.allProcess, item.funcArgs[0], item.funcArgs[1], item.funcArgs[2], this.currentTreeList, this.searchValue);
568                            break
569                        case "setSearchValue":
570                            this.searchValue = item.funcArgs[0]
571                            break
572                    }
573                }
574            })
575            this.dataSource = this.allProcess.filter((process) => {
576                return process.children && process.children.length > 0
577            })
578        }
579        return this.dataSource
580    }
581
582    queryData(queryName: string, sql: string, args: any) {
583        self.postMessage({
584            id: this.currentEventId,
585            type: queryName,
586            isQuery: true,
587            args: args,
588            sql: sql
589        })
590    }
591
592}
593
594class FileCallChain {
595    callChainId: number = 0;
596    depth: number = 0;
597    symbolsId: number = 0;
598    pathId: number = 0;
599    ip: string = ""
600}
601
602class FileSample {
603    type:number = 0
604    callChainId:number = 0;
605    dur:number = 0;
606    pid:number = 0;
607    tid:number = 0;
608    threadName:string = "";
609    processName:string = ""
610}
611
612export class FileMerageBean extends MerageBean {
613    ip: string = ""
614    symbolsId: number = 0;
615    pathId: number = 0;
616    processName: string = "";
617    type: number = 0
618
619    static merageCallChainSample(currentNode: FileMerageBean, callChain: FileCallChain, sample: FileSample, isEnd: boolean) {
620        if (currentNode.processName == "") {
621            currentNode.ip = callChain.ip
622            currentNode.pid = sample.pid
623            currentNode.canCharge = true
624            currentNode.pathId = callChain.pathId;
625            currentNode.symbolsId = callChain.symbolsId;
626            currentNode.processName = sample.processName || `Process(${sample.pid})`
627        }
628        if (isEnd) {
629            currentNode.selfDur += sample.dur;
630            currentNode.self = getProbablyTime(currentNode.selfDur)
631        }
632        currentNode.dur += sample.dur;
633        currentNode.count++;
634    }
635}
636
637export class Stack {
638    type: number = 0;
639    symbol: string = "";
640    path: string = "";
641}
642
643export class FileSysEvent {
644    isSelected:boolean = false;
645    id: number = 0;
646    callchainId: number = 0;
647    startTs: number = 0;
648    startTsStr: string = "";
649    durStr: string = "";
650    dur: number = 0;
651    process: string = "";
652    thread: string = "";
653    type: number = 0;
654    typeStr: string = "";
655    fd: number = 0;
656    size: number = 0;
657    depth: number = 0;
658    firstArg: string = "";
659    secondArg: string = "";
660    thirdArg: string = "";
661    fourthArg: string = "";
662    returnValue: string = "";
663    error: string = "";
664    path: string = "";
665    symbol: string = "";
666    backtrace: Array<string> = [];
667}
668
669export class IoCompletionTimes{
670    isSelected:boolean = false;
671    type:number = 0;
672    callchainId: number = 0;
673    startTs: number = 0;
674    startTsStr: string = "";
675    durStr: string = "";
676    dur: number = 0;
677    process: string = "";
678    thread: string = "";
679    path: string = "";
680    operation: string = "";
681    size: number = 0;
682    sizeStr:string = "";
683    blockNumber:string = "";
684    tier:number = 0;
685    backtrace: Array<string> = [];
686    durPer4kStr: string = ""
687    durPer4k: number = 0;
688}
689
690export class VirtualMemoryEvent{
691    isSelected:boolean = false;
692    callchainId: number = 0;
693    startTs: number = 0;
694    startTsStr: string = "";
695    durStr: string = "";
696    dur: number = 0;
697    process: string = "";
698    thread: string = "";
699    address:string = "";
700    size:number = 0;
701    sizeStr:string = "";
702    type:number = 0;
703    tid:number = 0;
704    pid:number = 0;
705    operation: string = "";
706}
707