1/* 2 * Copyright (C) 2022 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 { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionData, SelectionParam } from '../../../../bean/BoxSelection'; 19import '../../../StackBar'; 20import { log } from '../../../../../log/Log'; 21import { getThreadUsageProbablyTime } from '../../../../database/logic-worker/ProcedureLogicWorkerCommon'; 22import { Utils } from '../../base/Utils'; 23import { CpuStruct } from '../../../../database/ui-worker/cpu/ProcedureWorkerCPU'; 24import { resizeObserver } from '../SheetUtils'; 25import { getTabRunningPersent, getTabThreadStatesCpu } from '../../../../database/sql/ProcessThread.sql'; 26 27@element('tabpane-thread-usage') 28export class TabPaneThreadUsage extends BaseElement { 29 private threadUsageTbl: LitTable | null | undefined; 30 private range: HTMLLabelElement | null | undefined; 31 private threadUsageSource: Array<SelectionData> = []; 32 private cpuCount = 0; 33 private currentSelectionParam: SelectionParam | undefined; 34 private pubColumns = ` 35 <lit-table-column width="200px" title="Process" data-index="process" key="process" 36 align="flex-start" order> 37 </lit-table-column> 38 <lit-table-column width="100px" title="PID" data-index="pid" key="pid" 39 align="flex-start" order > 40 </lit-table-column> 41 <lit-table-column width="200px" title="Thread" data-index="thread" key="thread" 42 align="flex-start" order > 43 </lit-table-column> 44 <lit-table-column width="100px" title="TID" data-index="tid" key="tid" 45 align="flex-start" order > 46 </lit-table-column> 47 <lit-table-column width="160px" title="Wall duration(μs)" data-index="wallDurationTimeStr" 48 key="wallDurationTimeStr" align="flex-start" order > 49 </lit-table-column> 50 `; 51 52 set data(threadUsageParam: SelectionParam | unknown) { 53 if (this.currentSelectionParam === threadUsageParam) { 54 return; 55 } 56 // @ts-ignore 57 this.currentSelectionParam = threadUsageParam; 58 // @ts-ignore 59 let traceId = threadUsageParam.traceId; 60 if (this.cpuCount !== Utils.getInstance().getCpuCount(traceId)) { 61 this.cpuCount = Utils.getInstance().getCpuCount(traceId); // @ts-ignore 62 this.threadUsageTbl!.innerHTML = this.getTableColumns(traceId); 63 } 64 //@ts-ignore 65 this.threadUsageTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${ 66 this.parentElement!.clientHeight - 45 67 }px`; 68 // 框选区域内running的时间 69 // @ts-ignore 70 getTabRunningPersent(threadUsageParam.threadIds, threadUsageParam.leftNs, threadUsageParam.rightNs).then( 71 (result) => { 72 // 数组套对象 73 // 开始的时间leftStartNs 74 // @ts-ignore 75 let leftStartNs = threadUsageParam.leftNs + threadUsageParam.recordStartNs; 76 // 结束的时间rightEndNs 77 // @ts-ignore 78 let rightEndNs = threadUsageParam.rightNs + threadUsageParam.recordStartNs; 79 let sum = rightEndNs - leftStartNs; 80 this.range!.textContent = `Selected range: ${(sum / 1000000.0).toFixed(5)} ms`; 81 } 82 ); 83 this.threadUsageTbl!.loading = true; 84 // @ts-ignore 85 getTabThreadStatesCpu(threadUsageParam.threadIds, threadUsageParam.leftNs, threadUsageParam.rightNs).then( 86 (result) => { 87 this.threadUsageTbl!.loading = false; 88 this.threadStatesCpuDataHandler(result, threadUsageParam); 89 } 90 ); 91 } 92 93 private threadStatesCpuDataHandler(result: unknown[], threadUsageParam: SelectionParam | unknown): void { 94 if (result !== null && result.length > 0) { 95 log(`getTabThreadStates result size : ${result.length}`); 96 // @ts-ignore 97 let filterArr = result.filter((it) => threadUsageParam.processIds.includes(it.pid)); 98 let totalDurtion = 0; 99 filterArr.forEach((item) => { 100 // @ts-ignore 101 totalDurtion = totalDurtion + item.wallDuration; 102 }); 103 let map: Map<number, unknown> = new Map<number, unknown>(); 104 for (let resultEl of filterArr) { 105 // @ts-ignore 106 if (threadUsageParam.processIds.includes(resultEl.pid)) { 107 // @ts-ignore 108 if (map.has(resultEl.tid)) { 109 // @ts-ignore 110 map.get(resultEl.tid)[`cpu${resultEl.cpu}`] = resultEl.wallDuration || 0; 111 // @ts-ignore 112 map.get(resultEl.tid)[`cpu${resultEl.cpu}TimeStr`] = getThreadUsageProbablyTime(resultEl.wallDuration || 0); 113 // @ts-ignore 114 map.get(resultEl.tid).wallDuration = map.get(resultEl.tid).wallDuration + (resultEl.wallDuration || 0); 115 // @ts-ignore 116 map.get(resultEl.tid).wallDurationTimeStr = getThreadUsageProbablyTime(map.get(resultEl.tid).wallDuration); 117 } else { 118 // @ts-ignore 119 let process = Utils.getInstance().getProcessMap(threadUsageParam.traceId).get(resultEl.pid); 120 // @ts-ignore 121 let thread = Utils.getInstance().getThreadMap(threadUsageParam.traceId).get(resultEl.tid); 122 let threadStatesStruct: unknown = { 123 // @ts-ignore 124 tid: resultEl.tid, 125 // @ts-ignore 126 pid: resultEl.pid, 127 thread: thread || 'null', 128 process: process || 'null', 129 // @ts-ignore 130 wallDuration: resultEl.wallDuration || 0, 131 // @ts-ignore 132 wallDurationTimeStr: getThreadUsageProbablyTime(resultEl.wallDuration || 0), 133 }; 134 for (let i = 0; i < this.cpuCount; i++) { 135 // @ts-ignore 136 threadStatesStruct[`cpu${i}`] = 0; 137 // @ts-ignore 138 threadStatesStruct[`cpu${i}TimeStr`] = '0'; 139 // @ts-ignore 140 threadStatesStruct[`cpu${i}Ratio`] = '0'; 141 } 142 // @ts-ignore 143 threadStatesStruct[`cpu${resultEl.cpu}`] = resultEl.wallDuration || 0; 144 // @ts-ignore 145 threadStatesStruct[`cpu${resultEl.cpu}TimeStr`] = getThreadUsageProbablyTime(resultEl.wallDuration || 0); 146 // @ts-ignore 147 map.set(resultEl.tid, threadStatesStruct); 148 } 149 } 150 } 151 map.forEach((val) => { 152 for (let i = 0; i < this.cpuCount; i++) { 153 // @ts-ignore 154 val[`cpu${i}Ratio`] = ((100.0 * val[`cpu${i}`]) / val.wallDuration).toFixed(2); 155 } 156 }); 157 // @ts-ignore 158 this.threadUsageSource = Array.from(map.values()); 159 this.threadUsageTbl!.recycleDataSource = this.threadUsageSource; 160 } else { 161 this.threadUsageSource = []; 162 this.threadUsageTbl!.recycleDataSource = []; 163 } 164 } 165 166 getTableColumns(traceId?: string | null): string { 167 let threadUsageHtml = `${this.pubColumns}`; 168 let cpuCount = Utils.getInstance().getCpuCount(traceId); 169 for (let index = 0; index < cpuCount; index++) { 170 threadUsageHtml = `${threadUsageHtml} 171 <lit-table-column width="100px" title="cpu${index}(μs)" data-index="cpu${index}TimeStr" 172 key="cpu${index}TimeStr" align="flex-start" order> 173 </lit-table-column> 174 <lit-table-column width="100px" title="%" data-index="cpu${index}Ratio" 175 key="cpu${index}Ratio" align="flex-start" order> 176 </lit-table-column> 177 `; 178 } 179 return threadUsageHtml; 180 } 181 182 initElements(): void { 183 this.threadUsageTbl = this.shadowRoot?.querySelector<LitTable>('#tb-thread-states'); 184 this.range = this.shadowRoot?.querySelector('#thread-usage-time-range'); 185 this.threadUsageTbl!.addEventListener('column-click', (evt: unknown) => { 186 // @ts-ignore 187 this.sortByColumn(evt.detail); 188 }); 189 } 190 191 connectedCallback(): void { 192 super.connectedCallback(); 193 resizeObserver(this.parentElement!, this.threadUsageTbl!); 194 } 195 196 initHtml(): string { 197 return ` 198 <style> 199 .thread-usage-table{ 200 flex-direction: row; 201 margin-bottom: 5px; 202 } 203 :host{ 204 display: flex; 205 flex-direction: column; 206 padding: 10px 10px; 207 } 208 </style> 209 <div class="thread-usage-table" style="display: flex;height: 20px;align-items: center; 210 flex-direction: row;margin-bottom: 5px"> 211 <stack-bar id="thread-usage-stack-bar" style="flex: 1"></stack-bar> 212 <label id="thread-usage-time-range" style="width: auto;text-align: end; 213 font-size: 10pt;">Selected range:0.0 ms</label> 214 </div> 215 <div style="overflow: auto"> 216 <lit-table id="tb-thread-states" style="height: auto"></lit-table> 217 </div> 218 `; 219 } 220 221 sortByColumn(treadUsageDetail: unknown): void { 222 function compare(property: unknown, treadUsageSort: unknown, type: unknown) { 223 return function (threadUsageLeftData: SelectionData | unknown, threadUsageRightData: SelectionData | unknown) { 224 // @ts-ignore 225 if (threadUsageLeftData.process === ' ' || threadUsageRightData.process === ' ') { 226 return 0; 227 } 228 if (type === 'number') { 229 return treadUsageSort === 2 230 ? // @ts-ignore 231 parseFloat(threadUsageRightData[property]) - parseFloat(threadUsageLeftData[property]) 232 : // @ts-ignore 233 parseFloat(threadUsageLeftData[property]) - parseFloat(threadUsageRightData[property]); 234 } else { 235 // @ts-ignore 236 if (threadUsageRightData[property] > threadUsageLeftData[property]) { 237 return treadUsageSort === 2 ? 1 : -1; 238 // @ts-ignore 239 } else if (threadUsageRightData[property] === threadUsageLeftData[property]) { 240 return 0; 241 } else { 242 return treadUsageSort === 2 ? -1 : 1; 243 } 244 } 245 }; 246 } 247 248 if ( 249 // @ts-ignore 250 treadUsageDetail.key === 'process' || 251 // @ts-ignore 252 treadUsageDetail.key === 'thread' || 253 // @ts-ignore 254 (treadUsageDetail.key as string).includes('Ratio') 255 ) { 256 // @ts-ignore 257 this.threadUsageSource.sort(compare(treadUsageDetail.key, treadUsageDetail.sort, 'string')); 258 } else { 259 this.threadUsageSource.sort( 260 // @ts-ignore 261 compare((treadUsageDetail.key as string).replace('TimeStr', ''), treadUsageDetail.sort, 'number') 262 ); 263 } 264 this.threadUsageTbl!.recycleDataSource = this.threadUsageSource; 265 } 266} 267 268export function judgement(result: Array<unknown>, leftStart: unknown, rightEnd: unknown): number { 269 let sum = 0; 270 if (result !== null && result.length > 0) { 271 log(`getTabRunningTime result size : ${result.length}`); 272 let rightEndNs = rightEnd; 273 let leftStartNs = leftStart; 274 // 尾部running的结束时间 275 // @ts-ignore 276 let RunningEnds = result[result.length - 1].dur - (rightEndNs - result[result.length - 1].ts) + rightEndNs; 277 // 如果截取了开头和结尾的长度 278 // @ts-ignore 279 let beigin = result[0].dur - (leftStartNs - result[0].ts); 280 // @ts-ignore 281 let end = rightEndNs - result[result.length - 1].ts; 282 // 用来存储数据的新数组 283 let arr = []; 284 // 如果开头和结尾都截取了 285 // @ts-ignore 286 if (leftStartNs > result[0].ts && rightEndNs < RunningEnds) { 287 // 首尾的running长度 288 let beginAndEnd = beigin + end; 289 290 // 截取的除了开头和结尾的数据 291 arr = result.slice(1, result.length - 1); 292 let res = arr.reduce((total, item) => { 293 // @ts-ignore 294 return total + item.dur; 295 }, 0); 296 // @ts-ignore 297 sum = beginAndEnd + res; 298 // @ts-ignore 299 } else if (leftStartNs > result[0].ts) { 300 // 如果只是截取了开头 301 arr = result.slice(1); 302 let res = arr.reduce((total, item) => { 303 // @ts-ignore 304 return total + item.dur; 305 }, 0); 306 // @ts-ignore 307 sum = beigin + res; 308 // @ts-ignore 309 } else if (rightEndNs < RunningEnds) { 310 // 如果只是截取了结尾 311 arr = result.slice(0, result.length - 1); 312 let res = arr.reduce((total, item) => { 313 // @ts-ignore 314 return total + item.dur; 315 }, 0); 316 // @ts-ignore 317 sum = end + res; 318 } else { 319 // 如果都没截取 320 for (let i of result) { 321 // @ts-ignore 322 sum += i.dur; 323 } 324 } 325 } 326 return sum; 327} 328