* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type CpuFreqData, type RunningFreqData, type RunningData } from '../component/trace/sheet/frequsage/TabPaneFreqUsageConfig';
let comPower = new Map<number, Map<number, unknown>>();
let timeZones: number = 0;
let maxCommpuPower: number = 0;
let tempIndex: number = 0;
*
* @param args.runData 数据库查询上来的running数据,此函数会将数据整理成map结构,分组规则:'cpu'为键,running数据数字为值
*
*/
function orgnazitionMap(
args: {
runData: Array<RunningData>;
cpuFreqData: Array<CpuFreqData>;
leftNs: number;
rightNs: number;
cpuArray: number[];
broCpuData: unknown[];
recordStartNS: number;
}
): Array<RunningFreqData> {
let result: Map<number, Array<RunningData>> = new Map();
let sum: number = 0;
for (let i = 0; i < args.runData.length; i++) {
let mapKey: number = args.runData[i].cpu;
if (!result.has(mapKey)) {
result.set(mapKey, new Array());
}
if (
args.runData[i].ts < args.leftNs - args.recordStartNS &&
args.runData[i].ts + args.runData[i].dur > args.leftNs - args.recordStartNS
) {
args.runData[i].dur = args.runData[i].ts + args.runData[i].dur - (args.leftNs - args.recordStartNS);
args.runData[i].ts = args.leftNs - args.recordStartNS;
}
if (args.runData[i].ts + args.runData[i].dur > args.rightNs - args.recordStartNS) {
args.runData[i].dur = args.rightNs - args.recordStartNS - args.runData[i].ts;
}
if (args.runData[i].dur < 0) {
args.runData[i].dur = 0;
}
result.get(mapKey)?.push({
pid: args.runData[i].pid,
tid: args.runData[i].tid,
cpu: args.runData[i].cpu,
dur: args.runData[i].dur,
ts: args.runData[i].ts
});
sum += args.runData[i].dur;
}
let cpuFreqArr = args.cpuFreqData.filter((item) => {
return (!(item.ts > args.rightNs || item.ts + item.dur < args.leftNs)) && args.cpuArray.indexOf(item.cpu) !== -1;
});
if (cpuFreqArr.length === 0) {
return [];
}
let cpuFreqMap = takeCpuFreqToMap(cpuFreqArr, args.leftNs, args.rightNs);
return dealRunningData(cpuFreqMap, result, sum, args.cpuArray, args.broCpuData, args.recordStartNS)
}
function dealRunningData(
cpuFreqData: Map<number, Array<CpuFreqData>>,
result: Map<number, Array<RunningData>>,
sum: number,
cpuList: number[],
broCpuData: unknown[],
recordStartNS: number
): Array<RunningFreqData> {
let returnResult: Map<number, Array<RunningFreqData>> = new Map();
result.forEach((item, key) => {
let itemCpuFreqData: Array<CpuFreqData> = cpuFreqData.get(key);
let resultList: Array<RunningFreqData> = new Array();
for (let i = 0; i < item.length; i++) {
for (let j = 0; j < itemCpuFreqData.length; j++) {
let flag: number;
if (
item[i].ts > itemCpuFreqData[j].ts - recordStartNS &&
item[i].ts < itemCpuFreqData[j].ts - recordStartNS + itemCpuFreqData[j].dur &&
item[i].dur < itemCpuFreqData[j].ts - recordStartNS + itemCpuFreqData[j].dur - item[i].ts
) {
if (Array.isArray(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 1), broCpuData, recordStartNS)!)) {
resultList = resultList.concat(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 1), broCpuData, recordStartNS)!);
} else {
resultList.push(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 1), broCpuData, recordStartNS)!);
}
item.splice(i, 1);
i--;
break;
}
if (
item[i].ts > itemCpuFreqData[j].ts - recordStartNS &&
item[i].ts < itemCpuFreqData[j].ts - recordStartNS + itemCpuFreqData[j].dur &&
item[i].dur >= itemCpuFreqData[j].ts - recordStartNS + itemCpuFreqData[j].dur - item[i].ts
) {
if (Array.isArray(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 2), broCpuData, recordStartNS)!)) {
resultList = resultList.concat(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 2), broCpuData, recordStartNS)!);
} else {
resultList.push(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 2), broCpuData, recordStartNS)!);
}
}
if (
item[i].ts <= itemCpuFreqData[j].ts - recordStartNS &&
item[i].ts + item[i].dur > itemCpuFreqData[j].ts - recordStartNS &&
item[i].dur + item[i].ts - (itemCpuFreqData[j].ts - recordStartNS) < itemCpuFreqData[j].dur
) {
if (Array.isArray(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 3), broCpuData, recordStartNS)!)) {
resultList = resultList.concat(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 3), broCpuData, recordStartNS)!);
} else {
resultList.push(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 3), broCpuData, recordStartNS)!);
}
item.splice(i, 1);
i--;
break;
}
if (
item[i].ts <= itemCpuFreqData[j].ts - recordStartNS &&
item[i].ts + item[i].dur > itemCpuFreqData[j].ts - recordStartNS &&
item[i].dur + item[i].ts - (itemCpuFreqData[j].ts - recordStartNS) >= itemCpuFreqData[j].dur
) {
if (Array.isArray(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 4), broCpuData, recordStartNS)!)) {
resultList = resultList.concat(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 4), broCpuData, recordStartNS)!);
} else {
resultList.push(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 4), broCpuData, recordStartNS)!);
}
}
if (
item[i].ts <= itemCpuFreqData[j].ts - recordStartNS &&
item[i].ts + item[i].dur <= itemCpuFreqData[j].ts - recordStartNS
) {
if (Array.isArray(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 5), broCpuData, recordStartNS)!)) {
resultList = resultList.concat(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 5), broCpuData, recordStartNS)!);
} else {
resultList.push(returnObj(item[i], itemCpuFreqData[j], sum, (flag = 5), broCpuData, recordStartNS)!);
}
item.splice(i, 1);
i--;
break;
}
}
}
if (!returnResult.has(key)) {
returnResult.set(key, new Array());
}
returnResult.get(key)?.push(...resultList);
})
return dealTree(returnResult);
}
function insertSortedArray(arr: any[], newArr: any[]) {
const newValue = newArr[0];
let left = 0;
let right = arr.length - 1;
let index = arr.length;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid][0] < newValue) {
left = mid + 1;
} else {
right = mid - 1;
}
}
index = left;
tempIndex = index;
arr.splice(index, 0, newArr);
return arr;
}
function returnObj(
item: RunningData,
cpuFreqData: CpuFreqData,
sum: number,
flag: number,
broCpuData: Array<unknown>,
recordStartNS: number
): RunningFreqData | undefined {
const PERCENT: number = 100;
const FREQ_MUTIPLE: number = 1000;
let computorPower: number = 0;
if (comPower && !comPower.get(item.cpu)?.mapData.has(cpuFreqData.value)) {
const mapDataArr = Array.from(comPower.get(item.cpu)?.mapData.entries()).sort((a, b) => a[0] - b[0]);
if (Number(cpuFreqData.value) < Number(mapDataArr[0][0])) {
computorPower = mapDataArr[0][1] - ((mapDataArr[1][1] - mapDataArr[0][1]) * (mapDataArr[0][0] - cpuFreqData.value) / (mapDataArr[1][0] - mapDataArr[0][0]));
} else if (Number(cpuFreqData.value) > Number(mapDataArr[mapDataArr.length - 1][0])) {
let lastKey = mapDataArr[mapDataArr.length - 1][0];
let lastValue = mapDataArr[mapDataArr.length - 1][1];
let lastTwoKey = mapDataArr[mapDataArr.length - 2][0];
let lastTwoValue = mapDataArr[mapDataArr.length - 2][1];
computorPower = ((lastValue - lastTwoValue) * (cpuFreqData.value - lastKey) / (lastKey - lastTwoKey)) + lastValue;
} else {
let tempArr = insertSortedArray(mapDataArr, [cpuFreqData.value, null]);
computorPower = ((tempArr[tempIndex + 1][1] - tempArr[tempIndex - 1][1]) * (cpuFreqData.value - tempArr[tempIndex - 1][0]) / (tempArr[tempIndex + 1][0] - tempArr[tempIndex - 1][0])) + tempArr[tempIndex - 1][1];
}
} else {
computorPower = comPower ? comPower.get(item.cpu)?.mapData.get(cpuFreqData.value)! : 0;
}
let result: RunningFreqData | undefined;
switch (flag) {
case 1:
result = {
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * item.dur,
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
dur: item.dur,
percent: (item.dur / sum) * PERCENT,
consumpower: computorPower * item.dur,
cpuload: maxCommpuPower ? (computorPower * item.dur) / (timeZones * maxCommpuPower) * PERCENT : 0.00,
ts: item.ts,
pid: item.pid,
tid: item.tid
};
break;
case 2:
result = {
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * (cpuFreqData.ts - recordStartNS + cpuFreqData.dur - item.ts),
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
dur: cpuFreqData.ts - recordStartNS + cpuFreqData.dur - item.ts,
percent: ((cpuFreqData.ts - recordStartNS + cpuFreqData.dur - item.ts) / sum) * PERCENT,
consumpower: computorPower * (cpuFreqData.ts - recordStartNS + cpuFreqData.dur - item.ts),
cpuload: maxCommpuPower ? (computorPower * (cpuFreqData.ts - recordStartNS + cpuFreqData.dur - item.ts)) / (timeZones * maxCommpuPower) * PERCENT : 0.00,
ts: item.ts,
pid: item.pid,
tid: item.tid
};
break;
case 3:
result = {
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * (item.dur + item.ts - (cpuFreqData.ts - recordStartNS)),
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
dur: item.dur + item.ts - (cpuFreqData.ts - recordStartNS),
percent: ((item.dur + item.ts - (cpuFreqData.ts - recordStartNS)) / sum) * PERCENT,
consumpower: computorPower * (item.dur + item.ts - (cpuFreqData.ts - recordStartNS)),
cpuload: maxCommpuPower ? (computorPower * (item.dur + item.ts - (cpuFreqData.ts - recordStartNS))) / (timeZones * maxCommpuPower) * PERCENT : 0.00,
ts: cpuFreqData.ts - recordStartNS,
pid: item.pid,
tid: item.tid
};
break;
case 4:
result = {
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * cpuFreqData.dur,
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
dur: cpuFreqData.dur,
percent: (cpuFreqData.dur / sum) * PERCENT,
consumpower: computorPower * cpuFreqData.dur,
cpuload: maxCommpuPower ? (computorPower * cpuFreqData.dur) / (timeZones * maxCommpuPower) * PERCENT : 0.00,
ts: cpuFreqData.ts - recordStartNS,
pid: item.pid,
tid: item.tid
};
break;
case 5:
result = {
thread: item.pid + '_' + item.tid,
consumption: 0,
cpu: item.cpu,
frequency: 'unknown',
dur: item.dur,
percent: (item.dur / sum) * PERCENT,
consumpower: 0,
cpuload: 0,
ts: item.ts - recordStartNS,
pid: item.pid,
tid: item.tid
};
break;
}
if (comPower && comPower!.get(item.cpu) && (comPower!.get(item.cpu).broId || comPower!.get(item.cpu).broId === 0) && comPower!.get(item.cpu).smtRate) {
let broCpuDataList = broCpuData.filter((e) => (e.cpu === comPower!.get(item.cpu).broId) &&
!(e.startTime >= result!.ts + result!.dur || e.endTime <= result!.ts));
let parallelDur = 0;
broCpuDataList.forEach((e) => {
if (e.startTime <= result!.ts && e.endTime >= result!.ts + result!.dur) {
parallelDur += result!.dur;
} else if (e.startTime <= result!.ts && e.endTime < result!.ts + result!.dur && e.endTime >= result!.ts) {
parallelDur += e.endTime - result!.ts;
} else if (e.startTime > result!.ts && e.endTime >= result!.ts + result!.dur && e.startTime <= result!.ts + result!.dur) {
parallelDur += result!.ts + result!.dur - e.startTime;
} else {
parallelDur += e.dur;
}
});
if (parallelDur === 0) {
return result;
} else if (parallelDur === result!.dur) {
result.consumpower = result.consumpower * comPower!.get(item.cpu).smtRate;
result.frequency = cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower * comPower!.get(item.cpu).smtRate + '*';
result.cpuload = result.consumpower / (timeZones * maxCommpuPower) * PERCENT;
return result;
} else {
let resultArr = [
{
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * (result!.dur - parallelDur),
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower : cpuFreqData.value / FREQ_MUTIPLE,
dur: result!.dur - parallelDur,
percent: ((result!.dur - parallelDur) / sum) * PERCENT,
consumpower: computorPower * (result!.dur - parallelDur),
cpuload: (computorPower * (result!.dur - parallelDur)) / (timeZones * maxCommpuPower) * PERCENT,
ts: item.ts,
pid: item.pid,
tid: item.tid
},
{
thread: item.pid + '_' + item.tid,
consumption: cpuFreqData.value * parallelDur,
cpu: item.cpu,
frequency: computorPower ? cpuFreqData.value / FREQ_MUTIPLE + ': ' + computorPower * comPower!.get(item.cpu).smtRate + '*' : cpuFreqData.value / FREQ_MUTIPLE,
dur: parallelDur,
percent: (parallelDur / sum) * PERCENT,
consumpower: computorPower * parallelDur * comPower!.get(item.cpu).smtRate,
cpuload: (computorPower * parallelDur * comPower!.get(item.cpu).smtRate) / (timeZones * maxCommpuPower) * PERCENT,
ts: item.ts,
pid: item.pid,
tid: item.tid
}
];
return resultArr;
}
} else {
return result;
}
}
function takeCpuFreqToMap(cpuFreqData: Array<CpuFreqData>, leftNs: number, rightNs: number): Map<number, Array<CpuFreqData>> {
let map: Map<number, Array<CpuFreqData>> = new Map();
for (let i = 0; i < cpuFreqData.length; i++) {
if (!map.has(cpuFreqData[i].cpu)) {
map.set(cpuFreqData[i].cpu, new Array());
}
map.get(cpuFreqData[i].cpu)?.push({
ts: cpuFreqData[i].ts,
cpu: cpuFreqData[i].cpu,
value: cpuFreqData[i].value,
dur: cpuFreqData[i].dur
})
}
return map;
}
function dealTree(
params: Map<number, Array<RunningFreqData>>
): Array<RunningFreqData> {
let result: Array<RunningFreqData> = [];
params.forEach((item, key) => {
item = item.filter((e) => e.frequency !== 'unknown')
let cpuData: RunningFreqData = creatNewObj(item[0].cpu, false);
for (let i = 0; i < item.length; i++) {
item[i].cpuThreadName = `${item[i].cpu}/[${item[i].tid}]`;
item[i].process = item[i].pid;
if (item.length < 10) {
cpuData.children?.push(item[i]);
};
cpuData.cpuThreadName = item[i].cpu;
cpuData.dur += item[i].dur;
cpuData.percent += item[i].percent;
cpuData.consumption += item[i].consumption;
cpuData.consumpower += item[i].consumpower;
cpuData.cpuload += item[i].cpuload;
cpuData.thread = '';
}
if (item.length > 10) {
item.sort((a, b) => b.consumpower - a.consumpower);
let top10Arr = item.slice(0, 10);
top10Arr.forEach(e => cpuData.children?.push(e));
}
result.push(cpuData);
})
result.sort((a, b) => a.cpu - b.cpu);
return result;
}
function creatNewObj(cpu: number, flag: boolean = true): RunningFreqData {
return {
thread: flag ? '' : 'P',
consumption: 0,
cpu: cpu,
frequency: 0,
dur: 0,
percent: 0,
children: [],
consumpower: 0,
cpuload: 0
};
}
self.onmessage = (e: MessageEvent): void => {
comPower = e.data.comPower;
timeZones = e.data.rightNs - e.data.leftNs;
maxCommpuPower = 0;
if (comPower) {
comPower.forEach(item => {
let maxFreq = 0;
let commpuPower = 0;
for (const i of item.mapData.entries()) {
if (i[0] > maxFreq) {
maxFreq = i[0];
commpuPower = i[1];
}
}
maxCommpuPower += commpuPower * item.smtRate;
});
}
let result = orgnazitionMap(e.data);
self.postMessage(result);
}