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 { Utils } from '../../base/Utils'; 21import { StackBar } from '../../../StackBar'; 22import { log } from '../../../../../log/Log'; 23import { resizeObserver } from '../SheetUtils'; 24import { sliceChildBoxSender } from '../../../../database/data-trafic/SliceSender'; 25 26@element('tabpane-thread-states') 27export class TabPaneThreadStates extends BaseElement { 28 private threadStatesTbl: LitTable | null | undefined; 29 private range: HTMLLabelElement | null | undefined; 30 private stackBar: StackBar | null | undefined; 31 private threadStatesTblSource: Array<SelectionData> = []; 32 private currentSelectionParam: SelectionParam | undefined; 33 34 set data(threadStatesParam: SelectionParam | unknown) { 35 if (this.currentSelectionParam === threadStatesParam) { 36 return; 37 } 38 // @ts-ignore 39 this.currentSelectionParam = threadStatesParam; 40 //@ts-ignore 41 this.threadStatesTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${ 42 this.parentElement!.clientHeight - 45 43 }px`; 44 // // @ts-ignore 45 this.range!.textContent = `Selected range: ${// @ts-ignore 46 ((threadStatesParam.rightNs - threadStatesParam.leftNs) / 1000000.0).toFixed(5)} ms`; 47 this.threadStatesTbl!.loading = true; 48 this.initThreadStates(threadStatesParam); 49 } 50 51 async initThreadStates(threadStatesParam: SelectionParam | unknown): Promise<void> { 52 let threadStatesDetail = await sliceChildBoxSender( 53 'state-box', 54 // @ts-ignore 55 threadStatesParam.leftNs, 56 // @ts-ignore 57 threadStatesParam.rightNs, 58 // @ts-ignore 59 threadStatesParam.threadIds 60 ); 61 // @ts-ignore 62 let targetListTemp = this.updateThreadStates(threadStatesDetail, threadStatesParam.leftNs, threadStatesParam.rightNs); 63 64 let compare = (threadState1: SelectionData, threadState2: SelectionData): number => { 65 let wallDuration1 = threadState1.wallDuration; 66 let wallDuration2 = threadState2.wallDuration; 67 if (wallDuration1 < wallDuration2) { 68 return 1; 69 } else if (wallDuration1 > wallDuration2) { 70 return -1; 71 } else { 72 return 0; 73 } 74 }; 75 targetListTemp.sort(compare); 76 this.addSumLine(threadStatesParam, targetListTemp); 77 } 78 79 updateThreadStates(threadStatDetail: Array<unknown>, leftNs: number, rightNs: number): Array<SelectionData> { 80 let targetListTemp: unknown[] = []; 81 if (threadStatDetail.length > 0) { 82 let source: Map<string, unknown> = new Map<string, unknown>(); 83 let target = threadStatDetail.reduce((map, current) => { 84 // @ts-ignore 85 let mapKey = `${current.pid}-${current.tid}`; 86 // @ts-ignore 87 let key = `${current.state}-${mapKey}`; 88 // @ts-ignore 89 if (current.startTime < leftNs && (current.startTime + current.dur) < rightNs) { 90 // @ts-ignore 91 current.dur = current.dur - (leftNs - current.startTime);// @ts-ignore 92 } else if (current.startTime + current.dur > rightNs && current.startTime > leftNs) { 93 // @ts-ignore 94 current.dur = current.dur - (current.startTime + current.dur - rightNs); 95 // @ts-ignore 96 } else if (current.startTime < leftNs && (current.startTime + current.dur) > rightNs ) { 97 // @ts-ignore 98 current.dur = rightNs - leftNs; 99 } 100 // @ts-ignore 101 if (map.has(key)) { 102 // @ts-ignore 103 map.get(key).wallDuration += current.dur; 104 // @ts-ignore 105 map.get(key).occurrences += 1; 106 } else { 107 // @ts-ignore 108 map.set(key, { 109 // @ts-ignore 110 pid: current.pid, 111 // @ts-ignore 112 tid: current.tid, 113 // @ts-ignore 114 state: current.state, 115 // @ts-ignore 116 wallDuration: current.dur || 0, 117 avgDuration: 0, 118 occurrences: 1, 119 }); 120 } 121 return map; 122 }, source); 123 //@ts-ignore 124 targetListTemp = Array.from(target.values()); 125 } 126 // @ts-ignore 127 return targetListTemp; 128 } 129 130 addSumLine(threadStatesParam: SelectionParam | unknown, targetListTemp: Array<unknown>): void { 131 log(targetListTemp); 132 133 if (targetListTemp !== null && targetListTemp.length > 0) { 134 log('getTabThreadStates result size : ' + targetListTemp.length); 135 let sumWall = 0.0; 136 let sumOcc = 0; 137 let targetList = []; 138 // @ts-ignore 139 let traceId = threadStatesParam.traceId; 140 for (let e of targetListTemp) { 141 // @ts-ignore 142 if (threadStatesParam.processIds.includes(e.pid)) { 143 // @ts-ignore 144 let process = Utils.getInstance().getProcessMap(traceId).get(e.pid); 145 // @ts-ignore 146 let thread = Utils.getInstance().getThreadMap(traceId).get(e.tid); 147 // @ts-ignore 148 e.process = process || '[NULL]'; 149 // @ts-ignore 150 e.thread = thread || '[NULL]'; 151 // @ts-ignore 152 e.stateJX = Utils.getEndState(e.state); 153 // @ts-ignore 154 e.tabTitle = `${e.process}[${e.pid}]`; 155 // @ts-ignore 156 e.avgDuration = parseFloat((e.wallDuration / e.occurrences / 1000000.0).toFixed(5)); 157 // @ts-ignore 158 e.wallDuration = parseFloat((e.wallDuration / 1000000.0).toFixed(5)); 159 // @ts-ignore 160 sumWall += e.wallDuration; 161 // @ts-ignore 162 sumOcc += e.occurrences; 163 targetList.push(e); 164 } 165 } 166 if (targetList.length > 0) { 167 let count: unknown = {}; 168 // @ts-ignore 169 count.process = ' '; 170 // @ts-ignore 171 count.stateJX = ' '; 172 // @ts-ignore 173 count.wallDuration = parseFloat(sumWall.toFixed(5)); 174 // @ts-ignore 175 count.occurrences = sumOcc; 176 //@ts-ignore 177 count.summary = true; 178 // @ts-ignore 179 count.tabTitle = 'Summary'; 180 targetList.splice(0, 0, count); 181 } 182 // @ts-ignore 183 this.threadStatesTblSource = targetList; 184 this.threadStatesTbl!.recycleDataSource = targetList; 185 // @ts-ignore 186 this.stackBar!.data = targetList; 187 } else { 188 this.threadStatesTblSource = []; 189 this.stackBar!.data = []; 190 this.threadStatesTbl!.recycleDataSource = []; 191 } 192 this.threadStatesTbl!.loading = false; 193 } 194 195 initElements(): void { 196 this.threadStatesTbl = this.shadowRoot?.querySelector<LitTable>('#tb-thread-states'); 197 this.range = this.shadowRoot?.querySelector('#thread-states-time-range'); 198 this.stackBar = this.shadowRoot?.querySelector('#thread-states-stack-bar'); 199 this.threadStatesTbl!.addEventListener('column-click', (evt: unknown) => { 200 // @ts-ignore 201 this.sortByColumn(evt.detail); 202 }); 203 } 204 205 connectedCallback(): void { 206 super.connectedCallback(); 207 resizeObserver(this.parentElement!, this.threadStatesTbl!); 208 } 209 210 initHtml(): string { 211 return ` 212 <style> 213 .tread-states-table{ 214 display: flex; 215 height: 20px; 216 } 217 :host{ 218 display: flex; 219 flex-direction: column; 220 padding: 10px 10px; 221 } 222 </style> 223 <div class="tread-states-table" style="display: flex;height: 20px;align-items: center; 224 flex-direction: row;margin-bottom: 5px;justify-content: space-between"> 225 <stack-bar id="thread-states-stack-bar" style="width: calc(100vw - 520px)"></stack-bar> 226 <label id="thread-states-time-range" style="width: 250px;text-align: end; 227 font-size: 10pt;">Selected range:0.0 ms</label> 228 </div> 229 <lit-table id="tb-thread-states" style="height: auto;overflow-x: auto;width: 100%"> 230 <lit-table-column class="tread-states-column" width="240px" title="Process" 231 data-index="process" key="process" align="flex-start" order> 232 </lit-table-column> 233 <lit-table-column class="tread-states-column" width="120px" title="PID" 234 data-index="pid" key="pid" align="flex-start" order > 235 </lit-table-column> 236 <lit-table-column class="tread-states-column" width="240px" title="Thread" 237 data-index="thread" key="thread" align="flex-start" order > 238 </lit-table-column> 239 <lit-table-column class="tread-states-column" width="120px" title="TID" 240 data-index="tid" key="tid" align="flex-start" order > 241 </lit-table-column> 242 <lit-table-column class="tread-states-column" width="240px" title="State" 243 data-index="stateJX" key="stateJX" align="flex-start" order > 244 </lit-table-column> 245 <lit-table-column class="tread-states-column" width="120px" title="Wall duration(ms)" 246 data-index="wallDuration" key="wallDuration" align="flex-start" order > 247 </lit-table-column> 248 <lit-table-column class="tread-states-column" width="120px" title="Avg Wall duration(ms)" 249 data-index="avgDuration" key="avgDuration" align="flex-start" order > 250 </lit-table-column> 251 <lit-table-column class="tread-states-column" width="120px" title="Occurrences" 252 data-index="occurrences" key="occurrences" align="flex-start" order tdJump> 253 </lit-table-column> 254 </lit-table> 255 `; 256 } 257 258 sortByColumn(treadStatesDetail: unknown): void { 259 function compare(property: unknown, treadStatesSort: unknown, type: unknown) { 260 return function (threadStatesLeftData: SelectionData | unknown, threadStatesRightData: SelectionData | unknown): number { 261 // @ts-ignore 262 if (threadStatesLeftData.process === ' ' || threadStatesRightData.process === ' ') { 263 return 0; 264 } 265 if (type === 'number') { 266 return treadStatesSort === 2 267 ? // @ts-ignore 268 parseFloat(threadStatesRightData[property]) - parseFloat(threadStatesLeftData[property]) 269 : // @ts-ignore 270 parseFloat(threadStatesLeftData[property]) - parseFloat(threadStatesRightData[property]); 271 } else { 272 // @ts-ignore 273 if (threadStatesRightData[property] > threadStatesLeftData[property]) { 274 return treadStatesSort === 2 ? 1 : -1; 275 // @ts-ignore 276 } else if (threadStatesRightData[property] === threadStatesLeftData[property]) { 277 return 0; 278 } else { 279 return treadStatesSort === 2 ? -1 : 1; 280 } 281 } 282 }; 283 } 284 285 // @ts-ignore 286 if (treadStatesDetail.key === 'name' || treadStatesDetail.key === 'thread' || treadStatesDetail.key === 'stateJX') { 287 // @ts-ignore 288 this.threadStatesTblSource.sort(compare(treadStatesDetail.key, treadStatesDetail.sort, 'string')); 289 } else { 290 // @ts-ignore 291 this.threadStatesTblSource.sort(compare(treadStatesDetail.key, treadStatesDetail.sort, 'number')); 292 } 293 this.threadStatesTbl!.recycleDataSource = this.threadStatesTblSource; 294 } 295} 296