1// Copyright (c) 2021 Huawei Device Co., Ltd.
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14import { Args } from './CommonArgs';
15import { cpuList, processList, sliceList, threadStateList } from './utils/AllMemoryCache';
16const dataCache: {
17  argSetID: number | null | undefined;
18  cpu: number | null | undefined;
19  dur: number | null | undefined;
20  itid: number | null | undefined;
21  pid: number | null | undefined;
22  state: string | null | undefined;
23  tid: number | null | undefined;
24  startTime: number | null | undefined;
25} = {
26  argSetID: null,
27  cpu: null,
28  dur: null,
29  itid: null,
30  pid: null,
31  state: null,
32  tid: null,
33  startTime: null
34};
35export const sliceSqlMem = (args: Args): string => {
36  return `
37      SELECT B.pid,                        
38             B.cpu,
39             B.tid,
40             B.itid                       as id,
41             B.dur                        AS dur,
42             B.state,
43             B.ts - ${args.recordStartNS} AS startTime,
44             ifnull(B.arg_setid, -1)      as argSetId
45      from thread_state AS B
46      where B.itid is not null and B.ts + ifnull(B.dur, 0) < ${args.recordEndNS}`;
47};
48
49export function sliceReceiver(data: unknown, proc: Function): void {
50  let count = {
51    cpu: new Map<number, number>(),
52  };
53  // 存储线程及其状态耗时总和;用以线程泳道排序
54  let threadMap = new Map<string, number>();
55  let processRowSortMap = new Map<string, number>();
56  sliceList.clear();
57  cpuList.clear();
58  processList.clear();
59  threadStateList.clear(); //@ts-ignore
60  let list: unknown[] = proc(sliceSqlMem(data.params));
61  sliceList.set(0, list);
62  for (let i = 0; i < list.length; i++) {
63    let slice = list[i]; //@ts-ignore
64    // @ts-ignore
65    if (slice.pid > 0 && typeof slice.pid === 'number' && slice.state === 'Running') {
66      // @ts-ignore
67      if (!processRowSortMap.has(slice.pid)) {
68        // @ts-ignore
69        processRowSortMap.set(slice.pid, slice.dur);
70      } else {
71        // @ts-ignore
72        let val = processRowSortMap.get(slice.pid);
73        // @ts-ignore
74        processRowSortMap.set(slice.pid, val + slice.dur);
75      }
76    }// @ts-ignore
77    if (slice.cpu !== null && slice.cpu !== undefined) {
78      //@ts-ignore
79      if (cpuList.has(slice.cpu)) {
80        //@ts-ignore
81        let arr = cpuList.get(slice.cpu) || [];
82        let last = arr[arr.length - 1];
83        //@ts-ignore
84        if (last && (last.dur === -1 || last.dur === null || last.dur === undefined)) {
85          //@ts-ignore
86          last.dur = slice.startTime - last.startTime;
87        } //@ts-ignore
88        cpuList.get(slice.cpu)!.push(slice);
89      } else {
90        //@ts-ignore
91        cpuList.set(slice.cpu, [slice]);
92      }
93    } //@ts-ignore
94    if (slice.pid >= 0 && slice.cpu !== null && slice.cpu !== undefined) {
95      //@ts-ignore
96      if (processList.has(slice.pid)) {
97        //@ts-ignore
98        processList.get(slice.pid)!.push(slice);
99      } else {
100        //@ts-ignore
101        processList.set(slice.pid, [slice]);
102      }
103    } //@ts-ignore
104    if (slice.pid >= 0 && slice.tid >= 0) {
105      //@ts-ignore
106      let key = `${slice.pid}-${slice.tid}`;
107      if (threadStateList.has(key)) {
108        threadStateList.get(key)!.push(slice);
109      } else {
110        threadStateList.set(key, [slice]);
111      }
112      // @ts-ignore
113      if (slice.state === 'S' || typeof slice.dur !== 'number' || slice.tid === 0) {
114        continue;
115      } else {
116        // @ts-ignore
117        if (!threadMap.has(key)) {
118          // @ts-ignore
119          threadMap.set(key, slice.dur);
120        } else {
121          // @ts-ignore
122          let val = threadMap.get(key);
123          // @ts-ignore
124          threadMap.set(key, val + slice.dur);
125        }
126      }
127    }
128  }
129  for (let key of cpuList.keys()) {
130    let arr = cpuList.get(key) || [];
131    count.cpu.set(key, arr.length);
132  }
133  //处理threadList最后一个符合条件的dur
134  for (let key of threadStateList.keys()) {
135    let arr = threadStateList.get(key) || [];
136    let last = arr[arr.length - 1];
137    if (!last) {
138      continue;
139    }
140    //@ts-ignore
141    switch (last.state) {
142      case 'S':
143      case 'I':
144      case 'T':
145      case 'X':
146        break;
147      default:
148        //@ts-ignore
149        if (last.dur === -1 || last.dur === null || last.dur === undefined) {
150          //@ts-ignore
151          let totalNs = data.params.recordEndNS - data.params.recordStartNS; //@ts-ignore
152          last.dur = totalNs - last.startTime;
153        }
154        break;
155    }
156  }
157  //处理热点数据
158  //@ts-ignore
159  let cpuUtiliRateArray = getCpuUtiliRate(cpuList, data.params);
160  postMsg(data, { count, threadMap, processRowSortMap, cpuUtiliRateArray });
161}
162
163function getCpuUtiliRate(cpulist: Map<number, Array<unknown>>, args: Args): Array<unknown> {
164  // cpu进行排序  
165  let cpuListArray = Array.from(cpulist.entries());
166  //@ts-ignore
167  cpuListArray.sort((a: unknown, b: unknown) => parseInt(a[0]) - parseInt(b[0]));
168  let cpuListMap = new Map(cpuListArray);
169  let cpuUtiliRateArray = new Array();
170  let cell = Math.floor((args.recordEndNS - args.recordStartNS) / 100);//分成100个格子,cell每个格子的持续时间
171  for (const [cpu, list] of cpuListMap.entries()) {
172    let ro = 0;
173    let index = 0;
174    let sumTime = 0;
175    //@ts-ignore
176    let sliceSt = list[0].startTime;//起始时间
177    let cellSt = ro * cell;//每个格子起始时间
178    let cellEt = (ro + 1) * cell;//每个格子的结束时间
179    //@ts-ignore
180    while (index < list.length && ro <= 99) {
181      let isGoNextRo = false;//标志位,当下格子区间内的cpu切片持续时间是否统计结束,如统计结束true可跳转至下一个格子区间,反之false
182      //@ts-ignore
183      let sliceEt = list[index].startTime + list[index].dur;//cpu结束时间
184      if (sliceSt >= cellSt && sliceEt <= cellEt) {//包含在ro内
185        //@ts-ignore
186        sumTime += (sliceEt - sliceSt);//cpu dur累加
187        //@ts-ignore
188        sliceSt = index + 1 >= list.length ? sliceSt : list[index + 1].startTime;//处理最后一条cpu数据
189        index++;
190      } else if (sliceSt >= cellSt && sliceSt < cellEt && sliceEt > cellEt) {//部分包含在ro内
191        sumTime = sumTime + (cellEt - sliceSt);
192        sliceSt = cellEt;
193        isGoNextRo = true;
194      } else if (sliceSt >= cellEt) {//不包含在ro内
195        isGoNextRo = true;
196      } else {//保护逻辑
197        //@ts-ignore
198        sliceSt = index + 1 >= list.length ? sliceSt : list[index + 1].startTime;
199        index++;
200      }
201      if (isGoNextRo) {//格子内cpu dur累加结束 跳转至下一个Ro 处理比率 储存处理后的数据
202        ro++;
203        cellSt = ro * cell;//下一个格子的起始时间
204        //第99个格子结束时间为recordEndNS,确保覆盖整个时间范围
205        if (ro < 99) {
206          cellEt = (ro + 1) * cell;//下一个格子结束时间
207        } else {
208          cellEt = args.recordEndNS - args.recordStartNS;
209        }
210        //处理比率 储存处理后的数据
211        if (sumTime !== 0) {
212          let rate = sumTime / cell;
213          cpuUtiliRateArray.push({ ro: ro - 1, cpu, rate });
214          sumTime = 0;
215        }
216      }
217    }
218    //处理最后一个sumTime不为0区间的数据
219    if (sumTime !== 0) {
220      let rate = sumTime / cell;
221      cpuUtiliRateArray.push({ ro, cpu, rate });
222    }
223  }
224  return cpuUtiliRateArray;
225}
226
227export function sliceSPTReceiver(data: unknown): void {
228  //@ts-ignore
229  if (data && data.params.func) {
230    //@ts-ignore
231    switch (data.params.func) {
232      case 'spt-getPTS':
233        getPTS(data);
234        break;
235      case 'spt-getSPT':
236        getSPT(data);
237        break;
238      case 'spt-getCpuPriorityByTime':
239        sptGetCpuPriorityByTime(data);
240        break;
241      case 'state-box':
242        getChildBoxDb(data);
243        break;
244      case 'near-data':
245        seacrhThreadNearData(data);
246        break;
247    }
248  }
249}
250
251function postMsg(data: unknown, res: unknown): void {
252  (self as unknown as Worker).postMessage(
253    {
254      //@ts-ignore
255      id: data.id, //@ts-ignore
256      action: data.action,
257      results: res, //@ts-ignore
258      len: res.length,
259      transfer: false,
260    },
261    []
262  );
263}
264
265function getSPT(data: unknown): void {
266  let threadSlice = sliceList.get(0) || [];
267  let sptFilter = threadSlice.filter(
268    (it) =>
269      //@ts-ignore
270      Math.max(data.params.leftNs, it.startTime!) < Math.min(data.params.rightNs, it.startTime! + it.dur!) &&
271      //@ts-ignore
272      (it.cpu === null || it.cpu === undefined || data.params.cpus.includes(it.cpu))
273  );
274  let group: unknown = {};
275  sptFilter.forEach((slice) => {
276    let item = {
277      //@ts-ignore
278      title: `T-${slice.tid}`,
279      count: 1, //@ts-ignore
280      state: slice.state, //@ts-ignore
281      pid: slice.pid, //@ts-ignore
282      tid: slice.tid, //@ts-ignore
283      minDuration: slice.dur || 0, //@ts-ignore
284      maxDuration: slice.dur || 0, //@ts-ignore
285      wallDuration: slice.dur || 0, //@ts-ignore
286      avgDuration: `${slice.dur}`,
287    }; //@ts-ignore
288    if (group[`${slice.state}`]) {
289      setSPTData(group, slice, item);
290    } else {
291      //@ts-ignore
292      group[`${slice.state}`] = {
293        //@ts-ignore
294        title: `S-${slice.state}`,
295        count: 1, //@ts-ignore
296        state: slice.state, //@ts-ignore
297        minDuration: slice.dur || 0, //@ts-ignore
298        maxDuration: slice.dur || 0, //@ts-ignore
299        wallDuration: slice.dur || 0, //@ts-ignore
300        avgDuration: `${slice.dur}`,
301        children: [
302          {
303            //@ts-ignore
304            title: `P-${slice.pid}`,
305            count: 1, //@ts-ignore
306            state: slice.state, //@ts-ignore
307            pid: slice.pid, //@ts-ignore
308            minDuration: slice.dur || 0, //@ts-ignore
309            maxDuration: slice.dur || 0, //@ts-ignore
310            wallDuration: slice.dur || 0, //@ts-ignore
311            avgDuration: `${slice.dur}`,
312            children: [item],
313          },
314        ],
315      };
316    }
317  }); //@ts-ignore
318  postMsg(data, Object.values(group));
319}
320
321function getPTS(data: unknown): void {
322  let threadSlice = sliceList.get(0) || [];
323  let ptsFilter = threadSlice.filter(
324    (it) =>
325      //@ts-ignore
326      Math.max(data.params.leftNs, it.startTime!) < Math.min(data.params.rightNs, it.startTime! + it.dur!) && //@ts-ignore
327      (it.cpu === null || it.cpu === undefined || data.params.cpus.includes(it.cpu))
328  );
329  let group: unknown = {};
330  ptsFilter.forEach((slice) => {
331    //@ts-ignore
332    let title = `S-${slice.state}`;
333    let item = setStateData(slice, title); //@ts-ignore
334    if (group[`${slice.pid}`]) {
335      //@ts-ignore
336      let process = group[`${slice.pid}`];
337      process.count += 1; //@ts-ignore
338      process.wallDuration += slice.dur; //@ts-ignore
339      process.minDuration = Math.min(process.minDuration, slice.dur!); //@ts-ignore
340      process.maxDuration = Math.max(process.maxDuration, slice.dur!);
341      process.avgDuration = (process.wallDuration / process.count).toFixed(2); //@ts-ignore
342      let thread = process.children.find((child: unknown) => child.title === `T-${slice.tid}`);
343      if (thread) {
344        thread.count += 1; //@ts-ignore
345        thread.wallDuration += slice.dur; //@ts-ignore
346        thread.minDuration = Math.min(thread.minDuration, slice.dur!); //@ts-ignore
347        thread.maxDuration = Math.max(thread.maxDuration, slice.dur!);
348        thread.avgDuration = (thread.wallDuration / thread.count).toFixed(2); //@ts-ignore
349        let state = thread.children.find((child: unknown) => child.title === `S-${slice.state}`);
350        if (state) {
351          state.count += 1; //@ts-ignore
352          state.wallDuration += slice.dur; //@ts-ignore
353          state.minDuration = Math.min(state.minDuration, slice.dur!); //@ts-ignore
354          state.maxDuration = Math.max(state.maxDuration, slice.dur!);
355          state.avgDuration = (state.wallDuration / state.count).toFixed(2);
356        } else {
357          thread.children.push(item);
358        }
359      } else {
360        let processChild = setThreadData(slice, item);
361        process.children.push(processChild);
362      }
363    } else {
364      //@ts-ignore
365      group[`${slice.pid}`] = setProcessData(slice, item);
366    }
367  }); //@ts-ignore
368  postMsg(data, Object.values(group));
369}
370
371function sptGetCpuPriorityByTime(data: unknown): void {
372  let threadSlice = sliceList.get(0) || [];
373  const result = threadSlice.filter((item: unknown) => {
374    //@ts-ignore
375    return !(item.startTime + item.dur < data.params.leftNs || item.startTime! > data.params.rightNs);
376  });
377  postMsg(data, result);
378}
379
380//处理跳转子页面的数据
381function getChildBoxDb(data: unknown): void {
382  let threadSlice = sliceList.get(0) || [];
383  let setProcessId;
384  let setThreadId;
385  let setCpu = new Set();
386  //处理processId数据
387  //@ts-ignore
388  setProcessId = handleIdParams(data.params.processId);
389  //处理threadId数据
390  //@ts-ignore
391  setThreadId = handleIdParams(data.params.threadId);
392  //处理Cpu数据
393  //@ts-ignore
394  if (data.params.cpus) {//@ts-ignore
395    if (data.params.cpus.length > 0) {
396      //@ts-ignore
397      setCpu = new Set(data.params.cpus);
398    }
399  }
400
401  let childBoxDb = threadSlice.filter((it: unknown) => {
402    let condition = true;
403    // 检查进程ID  
404    if (setProcessId.size !== 0) {
405      //@ts-ignore
406      condition = condition && setProcessId.has(it.pid);
407    }
408    // 检查线程ID  
409    if (setThreadId.size !== 0) {
410      //@ts-ignore
411      condition = condition && setThreadId.has(it.tid);
412    }
413    // 检查CPU(如果存在且不是null/undefined,或在setCpu中)  
414    if (setCpu.size !== 0) {
415      //@ts-ignore
416      condition = condition && (it.cpu === null || it.cpu === undefined || setCpu.has(it.cpu));
417    }
418    // 检查状态
419    //@ts-ignore  
420    if (data.params.state) {
421      //@ts-ignore  
422      condition = condition && it.state === data.params.state;
423    }
424    //@ts-ignore
425    return condition && Math.max(data.params.leftNs, it.startTime!) < Math.min(data.params.rightNs, it.startTime! + it.dur!);
426  });
427  postMsg(data, childBoxDb);
428}
429//处理processId和threadId
430function handleIdParams(id: number | number[] | undefined): Set<unknown> {
431  let setId: Set<unknown>;
432  if (Array.isArray(id) || typeof id === 'number') {
433    setId = new Set(Array.isArray(id) ? id : [id]);
434  } else {
435    setId = new Set();
436  }
437  return setId;
438}
439
440//查找点击的thread块前后相邻块信息
441function seacrhThreadNearData(target: unknown): void {
442  //@ts-ignore
443  let key = `${target.params.pid}-${target.params.tid}`;
444  let preData;
445  let nextData;
446  let threadSlice = threadStateList.get(key) || [];
447  //@ts-ignore
448  let dbIndex = threadSlice.findIndex(it => it.startTime === target.params.startTime);
449  if (dbIndex !== -1) {
450    preData = threadSlice[dbIndex - 1] ? threadSlice[dbIndex - 1] : dataCache;
451    nextData = threadSlice[dbIndex + 1] ? threadSlice[dbIndex + 1] : dataCache;
452  } else {
453    preData = dataCache;
454    nextData = dataCache;
455  }
456  postMsg(target, [preData, nextData]);
457}
458
459function setStateData(slice: unknown, title: string): unknown {
460  return {
461    title: title,
462    count: 1, //@ts-ignore
463    state: slice.state, //@ts-ignore
464    tid: slice.tid, //@ts-ignore
465    pid: slice.pid, //@ts-ignore
466    minDuration: slice.dur || 0, //@ts-ignore
467    maxDuration: slice.dur || 0, //@ts-ignore
468    wallDuration: slice.dur || 0, //@ts-ignore
469    avgDuration: `${slice.dur}`,
470  };
471}
472
473function setThreadData(slice: unknown, item: unknown): unknown {
474  return {
475    //@ts-ignore
476    title: `T-${slice.tid}`,
477    count: 1, //@ts-ignore
478    tid: slice.tid, //@ts-ignore
479    pid: slice.pid, //@ts-ignore
480    minDuration: slice.dur || 0, //@ts-ignore
481    maxDuration: slice.dur || 0, //@ts-ignore
482    wallDuration: slice.dur || 0, //@ts-ignore
483    avgDuration: `${slice.dur}`,
484    children: [item],
485  };
486}
487
488function setProcessData(slice: unknown, item: unknown): unknown {
489  return {
490    //@ts-ignore
491    title: `P-${slice.pid}`,
492    count: 1, //@ts-ignore
493    pid: slice.pid, //@ts-ignore
494    minDuration: slice.dur || 0, //@ts-ignore
495    maxDuration: slice.dur || 0, //@ts-ignore
496    wallDuration: slice.dur || 0, //@ts-ignore
497    avgDuration: `${slice.dur}`,
498    children: [
499      {
500        //@ts-ignore
501        title: `T-${slice.tid}`,
502        count: 1, //@ts-ignore
503        pid: slice.pid, //@ts-ignore
504        tid: slice.tid, //@ts-ignore
505        minDuration: slice.dur || 0, //@ts-ignore
506        maxDuration: slice.dur || 0, //@ts-ignore
507        wallDuration: slice.dur || 0, //@ts-ignore
508        avgDuration: `${slice.dur}`,
509        children: [item],
510      },
511    ],
512  };
513}
514
515function setSPTData(group: unknown, slice: unknown, item: unknown): void {
516  //@ts-ignore
517  let state = group[`${slice.state}`];
518  state.count += 1; //@ts-ignore
519  state.wallDuration += slice.dur; //@ts-ignore
520  state.minDuration = Math.min(state.minDuration, slice.dur!); //@ts-ignore
521  state.maxDuration = Math.max(state.maxDuration, slice.dur!);
522  state.avgDuration = (state.wallDuration / state.count).toFixed(2); //@ts-ignore
523  let process = state.children.find((child: unknown) => child.title === `P-${slice.pid}`);
524  if (process) {
525    process.count += 1; //@ts-ignore
526    process.wallDuration += slice.dur; //@ts-ignore
527    process.minDuration = Math.min(process.minDuration, slice.dur!); //@ts-ignore
528    process.maxDuration = Math.max(process.maxDuration, slice.dur!);
529    process.avgDuration = (process.wallDuration / process.count).toFixed(2); //@ts-ignore
530    let thread = process.children.find((child: unknown) => child.title === `T-${slice.tid}`);
531    if (thread) {
532      thread.count += 1; //@ts-ignore
533      thread.wallDuration += slice.dur; //@ts-ignore
534      thread.minDuration = Math.min(thread.minDuration, slice.dur!); //@ts-ignore
535      thread.maxDuration = Math.max(thread.maxDuration, slice.dur!);
536      thread.avgDuration = (thread.wallDuration / thread.count).toFixed(2);
537    } else {
538      process.children.push(item);
539    }
540  } else {
541    state.children.push({
542      //@ts-ignore
543      title: `P-${slice.pid}`,
544      count: 1, //@ts-ignore
545      state: slice.state, //@ts-ignore
546      pid: slice.pid, //@ts-ignore
547      minDuration: slice.dur || 0, //@ts-ignore
548      maxDuration: slice.dur || 0, //@ts-ignore
549      wallDuration: slice.dur || 0, //@ts-ignore
550      avgDuration: `${slice.dur}`,
551      children: [item],
552    });
553  }
554}
555