1/* 2 * Copyright (C) 2023 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 { BaseElement, element } from '../../../../../base-ui/BaseElement'; 17import '../../../../../base-ui/radiobox/LitRadioBox'; 18import { LitTable, RedrawTreeForm } from '../../../../../base-ui/table/lit-table'; 19import '../../../../../base-ui/popover/LitPopoverV'; 20import { LitPopover } from '../../../../../base-ui/popover/LitPopoverV'; 21import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; 22import { queryCoreRunningThread } from '../../../../database/sql/ProcessThread.sql'; 23import { MtSettingHtml } from '../TabPaneMt.html'; 24import { LitCheckBox } from '../../../../../base-ui/checkbox/LitCheckBox'; 25import '../../../../../base-ui/checkbox/LitCheckBox'; 26import { MeterHeaderClick, HanldParalLogic } from './ParallelUtil'; 27import { TabPaneFilter } from '../TabPaneFilter'; 28import { Utils } from '../../base/Utils'; 29 30const UNIT: number = 1000000.0; 31const NUM_DIGITS: number = 3; 32const CORE_NUM: number = 12; 33const LITTLE_CPU_NUM: Array<number> = [0, 1, 2, 3]; 34const MID_CPU_NUM12: Array<number> = [4, 5, 6, 7, 8, 9]; 35const BIG_CPU_NUM12: Array<number> = [10, 11]; 36const CORE_JSON = { 37 'group1': [4, 5], 38 'group2': [6, 7], 39 'group3': [8, 9], 40 'group4': [10, 11], 41}; 42export class CpuStatus { 43 cpu: number = 0; 44 little: boolean = false; 45 medium: boolean = false; 46 big: boolean = false; 47} 48@element('tabpane-mt-parallel') 49export class TabPaneMtParallel extends BaseElement { 50 private parallelTable: LitTable | null | undefined; 51 private litSettingPopoverEl: LitPopover | null | undefined; 52 private litGourpPopoverEl: LitPopover | null | undefined; 53 private cpuTbl: HTMLDivElement | null | undefined; 54 private groupContentDiv: HTMLDivElement | null | undefined; 55 private selectionParam: SelectionParam | undefined; 56 private dataSourceMap: Map<string, unknown> = new Map<string, unknown>(); 57 private leftStartNs: number = 0; 58 private rightEndNs: number = 0; 59 private midCores: Array<number> = []; 60 private bigCores: Array<number> = []; 61 private littleCores: Array<number> = []; 62 private isCreateCpu: boolean = true; 63 private isCreateGroup: boolean = true; 64 private coreTypeMap: Map<string, unknown> = new Map<string, unknown>(); 65 private bottomFilterEl: TabPaneFilter | null | undefined; 66 private addGroupArr: Array<number> = []; 67 // @ts-ignore 68 private bufferGroupMap: Map<string, Array<unknown>> = new Map<string, unknown>(); 69 private isReset: boolean = true; 70 71 set data(threadStatesParam: SelectionParam) { 72 if (this.selectionParam === threadStatesParam) { return; }; 73 this.selectionParam = threadStatesParam; 74 this.leftStartNs = this.selectionParam!.leftNs + this.selectionParam!.recordStartNs; 75 this.rightEndNs = this.selectionParam!.rightNs + this.selectionParam!.recordStartNs; 76 this.isCreateCpu = true; 77 this.parallelTable!.recycleDataSource = []; 78 this.initDefaultConfig(); 79 this.resetSomeConfig(false); 80 } 81 initElements(): void { 82 this.parallelTable = this.shadowRoot!.querySelector<LitTable>('#tb-parallel'); 83 this.bottomFilterEl = this.shadowRoot?.querySelector('#filter'); 84 this.litSettingPopoverEl = this.bottomFilterEl?.shadowRoot?.querySelector('#data-core-popover'); 85 this.litGourpPopoverEl = this.bottomFilterEl?.shadowRoot?.querySelector('#group-mining-popover'); 86 this.cpuTbl = this.litGourpPopoverEl!.querySelector<HTMLDivElement>('#tb_cpu'); 87 this.groupContentDiv = this.litGourpPopoverEl!.querySelector<HTMLDivElement>('.add_content'); 88 this.cpuSettingElListener(); 89 this.groupSettingElListener(); 90 } 91 //Cpu Setting 气泡相关按钮监听 92 cpuSettingElListener(): void { 93 this.litSettingPopoverEl!.querySelector<HTMLDivElement>('#core-mining')!.onclick = (e): void => { 94 if (this.isCreateCpu) { 95 this.initDefaultConfig(); 96 this.isCreateCpu = false; 97 this.bottomFilterEl!.setCoreConfigList(Utils.getInstance().getWinCpuCount(), this.littleCores, this.midCores, this.bigCores); 98 }; 99 }; 100 this.litSettingPopoverEl!.querySelector<HTMLDivElement>('.confirm-button')!.addEventListener('click', (e: unknown) => { 101 this.resetSomeConfig(true); 102 }); 103 this.litSettingPopoverEl!.querySelector<HTMLDivElement>('.reset-button')!.addEventListener('click', (e: unknown) => { 104 this.isCreateCpu = true; 105 this.initDefaultConfig(); 106 this.resetSomeConfig(false); 107 }); 108 } 109 //Group Setting 气泡相关按钮监听 110 groupSettingElListener(): void { 111 this.litGourpPopoverEl!.querySelector<HTMLDivElement>('#group-mining')!.addEventListener('click', (e: unknown) => { 112 this.addGroupArr = []; 113 if (this.isCreateGroup) { 114 this.groupContentDiv!.innerHTML = ''; 115 this.getGroupTableLine(); 116 //如果核数为12,默认配置分组 117 if (Utils.getInstance().getWinCpuCount() === CORE_NUM && this.isReset) { 118 this.isReset = false; 119 const myMap = new Map(Object.entries(CORE_JSON)); 120 for (const val of myMap.values()) { 121 this.initGroupFn(val); 122 } 123 } 124 } else { 125 this.getGroupTableLine(); 126 } 127 }); 128 this.litGourpPopoverEl!.querySelector<HTMLDivElement>('.add_group_button')!.addEventListener('click', (e: unknown) => { 129 this.initGroupFn(this.addGroupArr); 130 //每次需要添加的数组,在每次添加完后清空 131 this.addGroupArr = []; 132 }); 133 this.litGourpPopoverEl!.querySelector<HTMLDivElement>('.cut_group_button')!.addEventListener('click', (e: unknown) => { 134 //支持撤回已配置好的分组 135 if (!this.groupContentDiv!.childNodes.length) { return }; 136 let parts: unknown = this.groupContentDiv!.lastChild!.textContent?.split(':'); 137 // @ts-ignore 138 if (this.bufferGroupMap.has(parts[0])) { this.bufferGroupMap.delete(parts[0]) }; 139 this.groupContentDiv!.removeChild(this.groupContentDiv!.lastChild!); 140 this.addGroupArr = []; 141 this.getGroupTableLine('cut'); 142 }); 143 this.litGourpPopoverEl!.querySelector<HTMLDivElement>('.confirm-group-button')!.addEventListener('click', (e: unknown) => { 144 this.updateDataSource(true); 145 // @ts-ignore 146 this.litGourpPopoverEl!.visible = false; 147 }); 148 this.litGourpPopoverEl!.querySelector<HTMLDivElement>('.reset-group-button')!.addEventListener('click', (e: unknown) => { 149 this.resetGroup(false); 150 this.isCreateCpu = true; 151 this.initDefaultConfig(); 152 this.updateDataSource(false); 153 // @ts-ignore 154 this.litGourpPopoverEl!.visible = false; 155 }); 156 } 157 //group setting 重置 158 resetGroup(isRest: boolean): void { 159 this.isCreateGroup = true; 160 this.bufferGroupMap.clear(); 161 this.isReset = !isRest; 162 } 163 //当cpu setting点击重置或者确认时,需要重置Group setting的数据 164 resetSomeConfig(isRest: boolean): void { 165 this.resetGroup(isRest); 166 this.updateDataSource(isRest); 167 // @ts-ignore 168 this.litSettingPopoverEl!.visible = false; 169 } 170 //更新treeData 171 updateDataSource(flag: boolean): void { 172 let param = flag ? this.bufferGroupMap.size !== 0 : Utils.getInstance().getWinCpuCount() === CORE_NUM; 173 let value = flag ? this.bufferGroupMap : new Map(Object.entries(CORE_JSON)); 174 if ((this.midCores.length || this.bigCores.length || this.littleCores.length) && param) { 175 this.coreTypeMap.clear(); 176 this.dataSourceMap.clear(); 177 this.parallelTable!.loading = true; 178 this.getMtParallelData(value).then(() => { 179 this.parallelTable!.recycleDataSource = [...this.dataSourceMap.values()]; 180 this.parallelTable!.loading = false; 181 MeterHeaderClick(this.parallelTable, [...this.dataSourceMap.values()]); 182 }); 183 } else { 184 this.parallelTable!.recycleDataSource = []; 185 MeterHeaderClick(this.parallelTable, []); 186 } 187 } 188 async getMtParallelData(obj: Map<string, unknown>) :Promise<void> { 189 let cpuObj: unknown = { 'B': this.bigCores, 'M': this.midCores, 'L': this.littleCores }; 190 let processIds: Array<number> = [...new Set(this.selectionParam!.processIds)]; 191 for (const [key, cpuGroup] of obj.entries()) { 192 //判断配的的组是否在同一个核分类中,如果在,返回是那个核分类,反之,返回null 193 // @ts-ignore 194 let core = this.handleSamePhysicsCore(cpuGroup, cpuObj); 195 if (core === null) { continue }; 196 // @ts-ignore 197 let res: unknown = await queryCoreRunningThread(processIds, this.selectionParam!.threadIds, cpuGroup, this.leftStartNs, this.rightEndNs); 198 // @ts-ignore 199 this.handleTreeProcessData(res, core, key, cpuGroup); 200 } 201 //计算根节点数据并处理第二层数据的单位及保留位数 202 for (const [i, item] of this.coreTypeMap) { 203 // @ts-ignore 204 if (this.dataSourceMap.has(`${item.pid}`)) { 205 // @ts-ignore 206 let obj = this.dataSourceMap.get(`${item.pid}`); 207 // @ts-ignore 208 item.allParallel = ((item.parallelDur * item.parallelNum / item.dur) * 100).toFixed(NUM_DIGITS); 209 // @ts-ignore 210 obj.dur += item.dur; 211 // @ts-ignore 212 obj.parallelDur += item.parallelDur; 213 // @ts-ignore 214 obj.load += item.load; 215 // @ts-ignore 216 obj.parallelNum = item.parallelNum; 217 // @ts-ignore 218 item.dur = (item.dur / UNIT).toFixed(NUM_DIGITS); 219 // @ts-ignore 220 item.parallelDur = (item.parallelDur / UNIT).toFixed(NUM_DIGITS); 221 // @ts-ignore 222 item.load = item.load.toFixed(NUM_DIGITS); 223 // @ts-ignore 224 obj.children.push(item); 225 } 226 } 227 //处理根节点数据的单位及保留位数 228 for (const [i, item] of this.dataSourceMap) { 229 // @ts-ignore 230 item.allParallel = ((item.parallelDur * item.parallelNum / item.dur) * 100).toFixed(NUM_DIGITS); 231 // @ts-ignore 232 item.dur = (item.dur / UNIT).toFixed(NUM_DIGITS); 233 // @ts-ignore 234 item.parallelDur = (item.parallelDur / UNIT).toFixed(NUM_DIGITS); 235 // @ts-ignore 236 item.load = item.load.toFixed(NUM_DIGITS); 237 } 238 } 239 240 //判断自配的相同物理核是否符合计算MT并行度的要求 241 handleSamePhysicsCore(arr: unknown, obj: { 'B': Array<number>; 'M': Array<number>; 'S': Array<number> }): string | null { 242 let core = null; 243 // @ts-ignore 244 if (arr.length > 2) { return null } 245 for (const [key, val] of Object.entries(obj)) { 246 // @ts-ignore 247 let isSet = val.includes(arr[0]) && val.includes(arr[1]); 248 if (isSet) { 249 core = key; 250 } 251 } 252 return core; 253 } 254 255 handleTreeProcessData(result: unknown, key: string, gourpKey: string, gourp: Array<number>): void { 256 let coreMap: Map<string, unknown> = new Map<string, unknown>(); 257 // @ts-ignore 258 for (let i = 0; i < result.length; i++) { 259 // @ts-ignore 260 let stateItem = result[i]; 261 //处理框选区域前后的边界ts 262 if (stateItem.ts < this.leftStartNs) { 263 stateItem.ts = this.leftStartNs; 264 } 265 if (stateItem.endTs > this.rightEndNs) { 266 stateItem.endTs = this.rightEndNs; 267 } 268 let dur = stateItem.endTs - stateItem.ts; 269 //以pid为key值添加MTTable数据源的最外层数据结构 270 if (!this.dataSourceMap.has(`${stateItem.pid}`)) { 271 this.dataSourceMap.set(`${stateItem.pid}`, { 272 pid: stateItem.pid, 273 tid: stateItem.tid, 274 title: stateItem.pName ? `${stateItem.pName} ${stateItem.pid}` : `[NULL] ${stateItem.pid}`, 275 group: '', 276 dur: null, 277 parallelNum: null, 278 parallelDur: null, 279 allParallel: null, 280 load: null, 281 tCount: null, 282 children: [] 283 }); 284 }; 285 if (coreMap.has(`${stateItem.pid}`)) { 286 let obj = coreMap.get(`${stateItem.pid}`); 287 // @ts-ignore 288 let setArr = new Set(obj.tidArr); 289 if (!(setArr.has(stateItem.tid))) { 290 setArr.add(stateItem.tid); 291 // @ts-ignore 292 obj.tidArr.push(stateItem.tid); 293 } 294 // @ts-ignore 295 obj.gourpDur += dur; 296 // @ts-ignore 297 obj.stateItem.push(stateItem); 298 } else { 299 coreMap.set(`${stateItem.pid}`, { 300 pid: stateItem.pid, 301 tid: stateItem.tid, 302 gourpDur: dur, 303 tidArr: [stateItem.tid], 304 stateItem: [stateItem], 305 }); 306 }; 307 }; 308 this.mergeTreeCoreData(coreMap, key, gourpKey, gourp); 309 } 310 //处理树结构最终需要的信息数据 311 // @ts-ignore 312 mergeTreeCoreData(map: Map<string, unknown>, coreKey: string, gourpKey: string, gourp: Array<number>): void { 313 let str = gourp.join(','); 314 for (const [key, value] of map) { 315 let pDur: number = 0; 316 // @ts-ignore 317 pDur = HanldParalLogic(this.hanldMapLogic, value, pDur); 318 // @ts-ignore 319 let paral = (pDur * gourp.length / value.gourpDur) * 100; 320 // @ts-ignore 321 let load = value.gourpDur / ((100 * UNIT) * Utils.getInstance().getWinCpuCount()); 322 let groupObj = { 323 // @ts-ignore 324 pid: value.pid, 325 // @ts-ignore 326 tid: value.tid, 327 title: '', 328 group: `${gourpKey}:${str}`, 329 // @ts-ignore 330 dur: (value.gourpDur / UNIT).toFixed(NUM_DIGITS), 331 parallelNum: gourp.length, 332 parallelDur: (pDur / UNIT).toFixed(NUM_DIGITS), 333 allParallel: paral.toFixed(NUM_DIGITS), 334 load: load.toFixed(NUM_DIGITS), 335 // @ts-ignore 336 tCount: value.tidArr.length, 337 children: [] 338 }; 339 // @ts-ignore 340 if (this.coreTypeMap.has(`${value.pid} ${coreKey}`)) { 341 // @ts-ignore 342 let obj = this.coreTypeMap.get(`${value.pid} ${coreKey}`); 343 // @ts-ignore 344 obj.dur += value.gourpDur; 345 // @ts-ignore 346 obj.parallelDur += pDur; 347 // @ts-ignore 348 obj.load += load; 349 // @ts-ignore 350 obj.children.push(groupObj); 351 } else { 352 // @ts-ignore 353 this.coreTypeMap.set(`${value.pid} ${coreKey}`, { 354 // @ts-ignore 355 pid: value.pid, 356 // @ts-ignore 357 tid: value.tid, 358 title: `${coreKey}`, 359 group: '', 360 // @ts-ignore 361 dur: value.gourpDur, 362 parallelNum: gourp.length, 363 parallelDur: pDur, 364 allParallel: null, 365 load: load, 366 tCount: null, 367 children: [groupObj] 368 }); 369 } 370 } 371 } 372 373 //每次stateItem计算的的结果 374 hanldMapLogic(dumpObj: unknown, value?: unknown, param?: unknown): void { 375 // @ts-ignore 376 if (dumpObj.len !== 1) { 377 // @ts-ignore 378 param += dumpObj.endTs - dumpObj.ts; 379 } 380 // @ts-ignore 381 return param; 382 } 383 //初始化cpu check状态 384 initDefaultConfig(): void { 385 if (this.isCreateCpu) { 386 if (Utils.getInstance().getWinCpuCount() === CORE_NUM) { 387 this.littleCores = [...LITTLE_CPU_NUM]; 388 this.midCores = [...MID_CPU_NUM12]; 389 this.bigCores = [...BIG_CPU_NUM12]; 390 } else { 391 this.littleCores = []; 392 this.midCores = []; 393 this.bigCores = []; 394 } 395 } 396 } 397 398 //初始化分组 399 initGroupFn(arr: unknown): void { 400 let info = [...this.bufferGroupMap.values()].reduce((acc, val) => acc.concat(val), []); 401 // @ts-ignore 402 let flag = arr.filter((item: unknown) => info.includes(item)).length > 0; 403 // @ts-ignore 404 if (arr.length && arr.length > 1 && !flag) { 405 let len = this.groupContentDiv!.childNodes.length + 1; 406 // @ts-ignore 407 let str = arr.join(','); 408 // @ts-ignore 409 this.bufferGroupMap.set(`group${len}`, arr); 410 this.groupContentDiv!.innerHTML += `<div style="border-bottom: 1px solid black;">group${len}:${str}</div>`; 411 } 412 } 413 //更新Group Setting Cpu列的展示与操作状态 414 getGroupTableLine(str?: string): void { 415 this.isCreateGroup = false; 416 this.cpuTbl!.innerHTML = ''; 417 this.creatCpuHeaderDiv(); 418 let bufferInfo = [...this.bufferGroupMap.values()].reduce((acc, val) => acc.concat(val), []); 419 let switchArr = Object.values(CORE_JSON).flat(); 420 for (let i = 0; i < Utils.getInstance().getWinCpuCount(); i++) { 421 let obj = { 422 cpu: i, 423 isCheck: Utils.getInstance().getWinCpuCount() === CORE_NUM && str !== 'cut' && this.isReset ? switchArr.includes(i) : bufferInfo.includes(i), 424 disabled: 425 Utils.getInstance().getWinCpuCount() === CORE_NUM && str !== 'cut' && this.isReset ? 426 !(switchArr.includes(i)) : 427 !([...this.littleCores, ...this.midCores, ...this.bigCores].includes(i)) 428 }; 429 this.creatGroupLineDIv(obj); 430 } 431 } 432 //创建cpu表头 433 creatCpuHeaderDiv(): void { 434 let cpuIdLine = document.createElement('div'); 435 cpuIdLine.className = 'core_line'; 436 cpuIdLine.style.fontWeight = 'bold'; 437 cpuIdLine.style.fontStyle = '12px'; 438 cpuIdLine.textContent = 'Cpu'; 439 cpuIdLine.style.textAlign = 'center'; 440 this.cpuTbl?.append(...[cpuIdLine]); 441 } 442 //Gropu容器中新增Tbl的cpu Line值 443 creatGroupLineDIv(obj: unknown): void { 444 // @ts-ignore 445 let id = `${obj.cpu}`.toString(); 446 let checkBoxId = `box${id}`; 447 // 创建一个包裹div来容纳checkbox和cpuLine 448 let wrapperDiv = document.createElement('div'); 449 wrapperDiv.className = 'check-content'; 450 wrapperDiv.id = checkBoxId; 451 // 创建checkBox实例 452 let checkBox: LitCheckBox = new LitCheckBox(); 453 // @ts-ignore 454 checkBox.checked = obj.isCheck; 455 // @ts-ignore 456 checkBox.disabled = obj.disabled; 457 checkBox.setAttribute('not-close', ''); 458 // 添加事件监听器到checkBox 459 checkBox.addEventListener('change', (e: unknown) => { 460 // @ts-ignore 461 checkBox.checked = e.detail.checked; 462 // @ts-ignore 463 this.bottomFilterEl!.canUpdateCheckList(e.detail.checked, this.addGroupArr, obj.cpu); 464 }); 465 wrapperDiv.appendChild(checkBox); 466 // 创建cpuLine div 467 let cpuLine = document.createElement('div'); 468 // @ts-ignore 469 cpuLine.textContent = obj.cpu + ''; 470 cpuLine.style.textAlign = 'center'; 471 cpuLine.style.fontWeight = 'normal'; 472 // 将cpuLine也添加到wrapperDiv 473 wrapperDiv.appendChild(cpuLine); 474 this.cpuTbl!.append(wrapperDiv); 475 } 476 //回调函数,首次插入DOM时执行的初始化回调 477 connectedCallback(): void { 478 new ResizeObserver(() => { 479 if (this.parentElement?.clientHeight !== 0) { 480 // @ts-ignore 481 this.parallelTable!.shadowRoot!.querySelector('.table')!.style.height = `${this.parentElement!.clientHeight - 31}px`; 482 this.parallelTable?.reMeauseHeight(); 483 if (this.parentElement!.clientHeight >= 0 && this.parentElement!.clientHeight <= 31) { 484 this.bottomFilterEl!.style.display = 'none'; 485 } else { 486 this.bottomFilterEl!.style.display = 'flex'; 487 } 488 } 489 }).observe(this.parentElement!); 490 } 491 initHtml(): string { 492 return MtSettingHtml; 493 } 494} 495