1/*
2 * Copyright (C) 2024 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 { type CpuFreqData, type RunningFreqData, type RunningData } from '../component/trace/sheet/frequsage/TabPaneFreqUsageConfig';
17
18let comPower = new Map<number, Map<number, unknown>>();
19let resultArray: Array<RunningFreqData> = [];
20let timeZones: number = 0;
21let maxCommpuPower: number = 0;
22
23/**
24 *
25 * @param args.runData 数据库查询上来的running数据,此函数会将数据整理成map结构,分组规则:'pid_tid'为键,running数据数字为值
26 * @returns 返回map对象及所有running数据的dur和,后续会依此计算百分比
27 */
28function orgnazitionMap(
29  args: {
30    runData: Array<RunningData>;
31    cpuFreqData: Array<CpuFreqData>;
32    leftNs: number;
33    rightNs: number;
34    cpuArray: number[];
35  }
36): Array<RunningFreqData> {
37  let result: Map<string, Array<RunningData>> = new Map();
38  let sum: number = 0;
39  // 循环分组
40  for (let i = 0; i < args.runData.length; i++) {
41    let mapKey: string = args.runData[i].pid + '_' + args.runData[i].tid;
42    // 该running数据若在map对象中不包含其'pid_tid'构成的键,则新加key-value值
43    if (!result.has(mapKey)) {
44      result.set(mapKey, new Array());
45    }
46    // 整理左右边界数据问题, 因为涉及多线程,所以必须放在循环里
47    if (
48      args.runData[i].ts < args.leftNs &&
49      args.runData[i].ts + args.runData[i].dur > args.leftNs
50    ) {
51      args.runData[i].dur = args.runData[i].ts + args.runData[i].dur - args.leftNs;
52      args.runData[i].ts = args.leftNs;
53    }
54    if (args.runData[i].ts + args.runData[i].dur > args.rightNs) {
55      args.runData[i].dur = args.rightNs - args.runData[i].ts;
56    }
57    // 特殊处理数据表中dur为负值的情况
58    if (args.runData[i].dur < 0) {
59      args.runData[i].dur = 0;
60    }
61    // 分组整理数据
62    result.get(mapKey)?.push({
63      pid: args.runData[i].pid,
64      tid: args.runData[i].tid,
65      cpu: args.runData[i].cpu,
66      dur: args.runData[i].dur,
67      ts: args.runData[i].ts,
68    });
69    sum += args.runData[i].dur;
70  }
71  return dealCpuFreqData(args.cpuFreqData, result, sum, args.cpuArray);
72}
73
74/**
75 *
76 * @param cpuFreqData cpu频点数据的数组
77 * @param result running数据的map对象
78 * @param sum running数据的时间和
79 * @returns 返回cpu频点数据map,'pid_tid'为键,频点算力值数据的数组为值
80 */
81function dealCpuFreqData(
82  cpuFreqData: Array<CpuFreqData>,
83  result: Map<string, Array<RunningData>>,
84  sum: number,
85  cpuList: number[]
86): Array<RunningFreqData> {
87  let runningFreqData: Map<string, Array<RunningFreqData>> = new Map();
88  result.forEach((item, key) => {
89    let resultList: Array<RunningFreqData> = new Array();
90    for (let i = 0; i < item.length; i++) {
91      for (let j = 0; j < cpuFreqData.length; j++) {
92        let flag: number;
93        if (item[i].cpu === cpuFreqData[j].cpu) {
94          // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间小于频点结束时间减去running数据开始时间的差值的情况
95          if (
96            item[i].ts > cpuFreqData[j].ts &&
97            item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur &&
98            item[i].dur < cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts
99          ) {
100            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 1))!);
101            item.splice(i, 1);
102            i--;
103            break;
104          }
105          if (
106            item[i].ts > cpuFreqData[j].ts &&
107            item[i].ts < cpuFreqData[j].ts + cpuFreqData[j].dur &&
108            item[i].dur >= cpuFreqData[j].ts + cpuFreqData[j].dur - item[i].ts
109          ) {
110            // 当running状态数据的开始时间大于频点数据开始时间,小于频点结束时间。且running数据的持续时间大于等于频点结束时间减去running数据开始时间的差值的情况
111            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 2))!);
112          }
113          // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值小于频点数据持续时间的情况
114          if (
115            item[i].ts <= cpuFreqData[j].ts &&
116            item[i].ts + item[i].dur > cpuFreqData[j].ts &&
117            item[i].dur + item[i].ts - cpuFreqData[j].ts < cpuFreqData[j].dur
118          ) {
119            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 3))!);
120            item.splice(i, 1);
121            i--;
122            break;
123          }
124          if (
125            item[i].ts <= cpuFreqData[j].ts &&
126            item[i].ts + item[i].dur > cpuFreqData[j].ts &&
127            item[i].dur + item[i].ts - cpuFreqData[j].ts >= cpuFreqData[j].dur
128          ) {
129            // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间大于频点开始时间。且running数据的持续时间减去频点数据开始时间的差值大于等于频点数据持续时间的情况
130            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 4))!);
131          }
132          if (
133            item[i].ts <= cpuFreqData[j].ts &&
134            item[i].ts + item[i].dur <= cpuFreqData[j].ts
135          ) {
136            // 当running状态数据的开始时间小于等于频点数据开始时间,结束时间小于等于频点开始时间的情况
137            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!);
138            item.splice(i, 1);
139            i--;
140            break;
141          }
142        } else {
143          if (!cpuList.includes(item[i].cpu)) {
144            resultList.push(returnObj(item[i], cpuFreqData[j], sum, (flag = 5))!);
145            item.splice(i, 1);
146            i--;
147            break;
148          }
149        }
150      }
151    }
152    runningFreqData.set(key, mergeSameData(resultList));
153  });
154  return dealTree(runningFreqData);
155}
156
157/**
158 *
159 * @param item running数据
160 * @param cpuFreqData 频点数据
161 * @param sum running总和
162 * @param flag 标志位,根据不同值返回不同结果
163 * @returns 返回新的对象
164 */
165function returnObj(
166  item: RunningData,
167  cpuFreqData: CpuFreqData,
168  sum: number,
169  flag: number
170): RunningFreqData | undefined {
171  const PERCENT: number = 100;
172  const FREQ_MUTIPLE: number = 1000;
173  //@ts-ignore
174  const computorPower: number = comPower ? comPower.get(item.cpu)?.mapData.get(cpuFreqData.value)! : 0;
175  let result;
176  switch (flag) {
177    case 1:
178      result = {
179        thread: item.pid + '_' + item.tid,
180        consumption: cpuFreqData.value * item.dur,
181        cpu: item.cpu,
182        frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
183        dur: item.dur,
184        percent: (item.dur / sum) * PERCENT,
185        consumpower: computorPower * item.dur,
186        cpuload: (computorPower * item.dur) / (timeZones * maxCommpuPower) * PERCENT
187      };
188      break;
189    case 2:
190      result = {
191        thread: item.pid + '_' + item.tid,
192        consumption: cpuFreqData.value * (cpuFreqData.ts + cpuFreqData.dur - item.ts),
193        cpu: item.cpu,
194        frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
195        dur: cpuFreqData.ts + cpuFreqData.dur - item.ts,
196        percent: ((cpuFreqData.ts + cpuFreqData.dur - item.ts) / sum) * PERCENT,
197        consumpower: computorPower * (cpuFreqData.ts + cpuFreqData.dur - item.ts),
198        cpuload: (computorPower * (cpuFreqData.ts + cpuFreqData.dur - item.ts)) / (timeZones * maxCommpuPower) * PERCENT
199      };
200      break;
201    case 3:
202      result = {
203        thread: item.pid + '_' + item.tid,
204        consumption: cpuFreqData.value * (item.dur + item.ts - cpuFreqData.ts),
205        cpu: item.cpu,
206        frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
207        dur: item.dur + item.ts - cpuFreqData.ts,
208        percent: ((item.dur + item.ts - cpuFreqData.ts) / sum) * PERCENT,
209        consumpower: computorPower * (item.dur + item.ts - cpuFreqData.ts),
210        cpuload: (computorPower * (item.dur + item.ts - cpuFreqData.ts)) / (timeZones * maxCommpuPower) * PERCENT
211      };
212      break;
213    case 4:
214      result = {
215        thread: item.pid + '_' + item.tid,
216        consumption: cpuFreqData.value * cpuFreqData.dur,
217        cpu: item.cpu,
218        frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
219        dur: cpuFreqData.dur,
220        percent: (cpuFreqData.dur / sum) * PERCENT,
221        consumpower: computorPower * cpuFreqData.dur,
222        cpuload: (computorPower * cpuFreqData.dur) / (timeZones * maxCommpuPower) * PERCENT
223      };
224      break;
225    case 5:
226      result = {
227        thread: item.pid + '_' + item.tid,
228        consumption: 0,
229        cpu: item.cpu,
230        frequency: 'unknown',
231        dur: item.dur,
232        percent: (item.dur / sum) * PERCENT,
233        consumpower: 0,
234        cpuload: 0
235      };
236      break;
237  }
238  return result;
239}
240
241/**
242 *
243 * @param resultList 单线程内running数据与cpu频点数据整合成的数组
244 */
245function mergeSameData(
246  resultList: Array<RunningFreqData>
247): Array<RunningFreqData> {
248  let cpuFreqArr: Array<RunningFreqData> = [];
249  let cpuArr: Array<number> = [];
250  //合并同一线程内,当运行所在cpu和频点相同时,dur及percent进行累加求和
251  for (let i = 0; i < resultList.length; i++) {
252    if (!cpuArr.includes(resultList[i].cpu)) {
253      cpuArr.push(resultList[i].cpu);
254      cpuFreqArr.push(creatNewObj(resultList[i].cpu));
255    }
256    for (let j = i + 1; j < resultList.length; j++) {
257      if (
258        resultList[i].cpu === resultList[j].cpu &&
259        resultList[i].frequency === resultList[j].frequency
260      ) {
261        resultList[i].dur += resultList[j].dur;
262        resultList[i].percent += resultList[j].percent;
263        resultList[i].consumption += resultList[j].consumption;
264        resultList[i].consumpower += resultList[j].consumpower;
265        resultList[i].cpuload += resultList[j].cpuload;
266        resultList.splice(j, 1);
267        j--;
268      }
269    }
270    cpuFreqArr.find(function (item) {
271      if (item.cpu === resultList[i].cpu) {
272        item.children?.push(resultList[i]);
273        item.children?.sort((a, b) => b.consumption - a.consumption);
274        item.dur += resultList[i].dur;
275        item.percent += resultList[i].percent;
276        item.consumption += resultList[i].consumption;
277        item.consumpower += resultList[i].consumpower;
278        item.cpuload += resultList[i].cpuload;
279        item.thread = resultList[i].thread;
280      }
281    });
282  }
283  cpuFreqArr.sort((a, b) => a.cpu - b.cpu);
284  return cpuFreqArr;
285}
286
287/**
288 *
289 * @param params cpu层级的数据
290 * @returns 整理好的进程级数据
291 */
292function dealTree(
293  params: Map<string, Array<RunningFreqData>>
294): Array<RunningFreqData> {
295  let result: Array<RunningFreqData> = [];
296  params.forEach((item, key) => {
297    let process: RunningFreqData = creatNewObj(-1, false);
298    let thread: RunningFreqData = creatNewObj(-2);
299    for (let i = 0; i < item.length; i++) {
300      thread.children?.push(item[i]);
301      thread.dur += item[i].dur;
302      thread.percent += item[i].percent;
303      thread.consumption += item[i].consumption;
304      thread.consumpower += item[i].consumpower;
305      thread.cpuload += item[i].cpuload;
306      thread.thread = item[i].thread;
307    }
308    process.children?.push(thread);
309    process.dur += thread.dur;
310    process.percent += thread.percent;
311    process.consumption += thread.consumption;
312    process.consumpower += thread.consumpower;
313    process.cpuload += thread.cpuload;
314    process.thread = process.thread! + key.split('_')[0];
315    result.push(process);
316  });
317  for (let i = 0; i < result.length; i++) {
318    for (let j = i + 1; j < result.length; j++) {
319      if (result[i].thread === result[j].thread) {
320        result[i].children?.push(result[j].children![0]);
321        result[i].dur += result[j].dur;
322        result[i].percent += result[j].percent;
323        result[i].consumption += result[j].consumption;
324        result[i].consumpower += result[j].consumpower;
325        result[i].cpuload += result[j].cpuload;
326        result.splice(j, 1);
327        j--;
328      }
329    }
330  }
331  return result;
332}
333
334/**
335 *
336 * @param cpu 根据cpu值创建层级结构,cpu < 0为线程、进程层级,其余为cpu层级
337 * @returns
338 */
339function creatNewObj(cpu: number, flag: boolean = true): RunningFreqData {
340  return {
341    thread: flag ? '' : 'P',
342    consumption: 0,
343    cpu: cpu,
344    frequency: -1,
345    dur: 0,
346    percent: 0,
347    children: [],
348    consumpower: 0,
349    cpuload: 0
350  };
351}
352
353/**
354 *
355 * @param arr 需要整理汇总的频点级数据
356 * @returns 返回一个total->cpu->频点的三级树结构数组
357 */
358function fixTotal(arr: Array<RunningFreqData>): Array<RunningFreqData> {
359  let result: Array<RunningFreqData> = [];
360  let flag: number = -1;
361  // 数据入参的情况是,第一条为进程数据,其后是该进程下所有线程的数据。以进程数据做分割
362  for (let i = 0; i < arr.length; i++) {
363    // 判断如果是进程数据,则将其children的数组清空,并以其作为最顶层数据
364    if (arr[i].thread?.indexOf('P') !== -1) {
365      arr[i].children = [];
366      arr[i].thread = arr[i].thread + '-summary data';
367      result.push(arr[i]);
368      // 标志判定当前数组的长度,也可用.length判断
369      flag++;
370    } else {
371      // 非进程数据会进入到else中,去判断当前线程数据的cpu分组是否存在,不存在则进行创建
372      if (result[flag].children![arr[i].cpu] === undefined) {
373        result[flag].children![arr[i].cpu] = {
374          thread: 'summary data',
375          consumption: 0,
376          cpu: arr[i].cpu,
377          frequency: -1,
378          dur: 0,
379          percent: 0,
380          children: [],
381          consumpower: 0,
382          cpuload: 0
383        };
384      }
385      // 每有一条数据要放到cpu分组下时,则将该cpu分组的各项数据累和
386      result[flag].children![arr[i].cpu].consumption += arr[i].consumption;
387      result[flag].children![arr[i].cpu].consumpower += arr[i].consumpower;
388      result[flag].children![arr[i].cpu].cpuload += arr[i].cpuload;
389      result[flag].children![arr[i].cpu].dur += arr[i].dur;
390      result[flag].children![arr[i].cpu].percent += arr[i].percent;
391      // 查找当前cpu分组下是否存在与当前数据的频点相同的数据,返回相同数据的索引值
392      let index: number = result[flag].children![
393        arr[i].cpu
394      ].children?.findIndex((item) => item.frequency === arr[i].frequency)!;
395      // 若存在相同频点的数据,则进行合并,不同直接push
396      if (index === -1) {
397        arr[i].thread = 'summary data';
398        result[flag].children![arr[i].cpu].children?.push(arr[i]);
399      } else {
400        result[flag].children![arr[i].cpu].children![index].consumption += arr[i].consumption;
401        result[flag].children![arr[i].cpu].children![index].consumpower += arr[i].consumpower;
402        result[flag].children![arr[i].cpu].children![index].dur += arr[i].dur;
403        result[flag].children![arr[i].cpu].children![index].percent += arr[i].percent;
404        result[flag].children![arr[i].cpu].children![index].cpuload += arr[i].cpuload;
405      }
406    }
407  }
408  return result;
409}
410
411/**
412 *
413 * @param arr1 前次整理好的区分线程的数据
414 * @param arr2 不区分线程的Total数据
415 */
416function mergeTotal(
417  arr1: Array<RunningFreqData>,
418  arr2: Array<RunningFreqData>
419): void {
420  for (let i = 0; i < arr1.length; i++) {
421    const num: number = arr2.findIndex((item) =>
422      item.thread?.includes(arr1[i].thread!)
423    );
424    arr2[num].thread = 'summary data';
425    arr1[i].children?.unshift(arr2[num]);
426    arr2.splice(num, 1);
427  }
428}
429
430
431/**
432 *
433 * @param arr 待整理的数组,会经过递归取到最底层的数据
434 */
435function recursion(arr: Array<RunningFreqData>): void {
436  for (let idx = 0; idx < arr.length; idx++) {
437    if (arr[idx].cpu === -1) {
438      resultArray.push(arr[idx]);
439    }
440    if (arr[idx].children) {
441      recursion(arr[idx].children!);
442    } else {
443      resultArray.push(arr[idx]);
444    }
445  }
446}
447
448self.onmessage = (e: MessageEvent): void => {
449  comPower = e.data.comPower;
450  resultArray = [];
451  timeZones = e.data.rightNs - e.data.leftNs;
452  maxCommpuPower = 0;
453  if (comPower) {
454    comPower.forEach(item => {
455      let maxFreq = 0;
456      let commpuPower = 0;
457      //@ts-ignore
458      for (const i of item.mapData.entries()) {
459        if (i[0] > maxFreq) {
460          maxFreq = i[0];
461          commpuPower = i[1];
462        }
463      }
464      //@ts-ignore
465      maxCommpuPower += commpuPower * item.smtRate;
466    });
467  }
468  let result = orgnazitionMap(e.data);
469  recursion(result);
470  resultArray = JSON.parse(JSON.stringify(resultArray));
471  mergeTotal(result, fixTotal(resultArray));
472  self.postMessage(result);
473};