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