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};