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 { BaseElement, element } from '../../../../../base-ui/BaseElement'; 17import { LitTable } from '../../../../../base-ui/table/lit-table'; 18import { SelectionData, SelectionParam, SliceBoxJumpParam } from '../../../../bean/BoxSelection'; 19import { Utils } from '../../base/Utils'; 20import { resizeObserver } from '../SheetUtils'; 21import { getTabDetails, getGhDetails, getSfDetails, getParentDetail, getFuncChildren } from '../../../../database/sql/Func.sql'; 22 23@element('box-slice-child') 24export class TabPaneSliceChild extends BaseElement { 25 private sliceChildTbl: LitTable | null | undefined; 26 private boxChildSource: Array<unknown> = []; 27 private sliceChildParam: { param: SliceBoxJumpParam, selection: SelectionParam } | null | undefined; 28 29 set data(boxChildValue: { param: SliceBoxJumpParam, selection: SelectionParam | null | undefined }) { 30 //切换Tab页 保持childTab数据不变 除非重新点击跳转 31 if (boxChildValue === this.sliceChildParam || !boxChildValue.param.isJumpPage) { 32 return; 33 } 34 // @ts-ignore 35 this.sliceChildParam = boxChildValue; 36 this.sliceChildTbl!.recycleDataSource = []; 37 //合并SF异步信息,相同pid和tid的name 38 let sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }> = new Map(); 39 let filterSfAsyncFuncName = boxChildValue.selection!.funAsync; 40 if (boxChildValue.param.name) { 41 filterSfAsyncFuncName = filterSfAsyncFuncName.filter((item) => 42 boxChildValue.param.name?.some((boxItem) => item.name === boxItem) 43 ); 44 } 45 filterSfAsyncFuncName.forEach((it: { name: string; pid: number, tid: number | undefined }) => { 46 if (sfAsyncFuncMap.has(`${it.pid}-${it.tid}`)) { 47 let item = sfAsyncFuncMap.get(`${it.pid}-${it.tid}`); 48 item?.name.push(it.name); 49 } else { 50 sfAsyncFuncMap.set(`${it.pid}-${it.tid}`, { 51 name: [it.name], 52 pid: it.pid, 53 tid: it.tid 54 }); 55 } 56 }); 57 //@ts-ignore 58 this.getDataByDB(boxChildValue, sfAsyncFuncMap, boxChildValue.selection!.funCatAsync); 59 } 60 61 initElements(): void { 62 this.sliceChildTbl = this.shadowRoot?.querySelector<LitTable>('#tb-slice-child'); 63 this.sliceChildTbl!.addEventListener('column-click', (evt): void => { 64 // @ts-ignore 65 this.sortByColumn(evt.detail); 66 }); 67 //监听row的点击事件,在对应起始时间上画标记棋子 68 this.sliceChildTbl!.addEventListener('row-click', (evt): void => { 69 //@ts-ignore 70 let param = evt.detail.data; 71 param.isSelected = true; 72 this.sliceChildTbl!.clearAllSelection(param); 73 this.sliceChildTbl!.setCurrentSelection(param); 74 document.dispatchEvent( 75 new CustomEvent('triangle-flag', { 76 detail: { time: [param.startNs], type: 'triangle' }, 77 }) 78 ); 79 }); 80 } 81 82 connectedCallback(): void { 83 super.connectedCallback(); 84 resizeObserver(this.parentElement!, this.sliceChildTbl!, 25); 85 } 86 87 getDataByDB( 88 val: { param: SliceBoxJumpParam, selection: SelectionParam }, 89 sfAsyncFuncMap: Map<string, { name: string[]; pid: number, tid: number | undefined }>, 90 ghAsyncFunc: { threadName: string; pid: number }[]): void { 91 //获取点击跳转,SF异步Func数据 92 let result1 = (): Array<unknown> => { 93 let promises: unknown[] = []; 94 sfAsyncFuncMap.forEach(async (item: { name: string[]; pid: number, tid: number | undefined }) => { 95 let res = await getSfDetails(item.name, item.pid, item.tid, val.param.leftNs, val.param.rightNs); 96 if (res !== undefined && res.length > 0) { 97 promises.push(...res); 98 } 99 }); 100 return promises; 101 }; 102 103 //获取点击跳转,GH异步Func数据 104 let result2 = (): unknown => { 105 let promises: unknown[] = []; 106 ghAsyncFunc.forEach(async (item: { pid: number; threadName: string }) => { 107 let res = await getGhDetails(val.param.name!, item.threadName, item.pid, val.param.leftNs, val.param.rightNs); 108 if (res !== undefined && res.length > 0) { 109 promises.push(...res); 110 } 111 }); 112 return promises; 113 }; 114 115 //获取同步Func数据,同步Func数据 116 let result3 = async (): Promise<unknown> => { 117 let promises: unknown[] = []; 118 let res = await getTabDetails(val.param.name!, val.param.processId, val.param.threadId, val.param.leftNs, val.param.rightNs); 119 if (res !== undefined && res.length > 0) { 120 promises.push(...res); 121 } 122 return promises; 123 }; 124 this.sliceChildTbl!.loading = true; 125 Promise.all([result1(), result2(), result3()]).then(async res => { 126 this.sliceChildTbl!.loading = false; 127 let result: unknown = (res[0] || []).concat(res[1] || []).concat(res[2] || []); 128 this.sliceChildTbl!.loading = false; 129 // @ts-ignore 130 if (result.length !== null && result.length > 0) { 131 let funcIdArr: Array<number> = []; 132 let minStartTS = Infinity; 133 let maxEndTS = -Infinity; 134 // @ts-ignore 135 let parentDetail: [{ 136 startTS: number, 137 endTS: number, 138 depth: number, 139 id: number, 140 name: string 141 }] = await getParentDetail( 142 val.param.processId, 143 val.param.threadId, 144 val.param.leftNs, 145 val.param.rightNs 146 ); 147 // @ts-ignore 148 parentDetail.forEach(item => { 149 funcIdArr.push(item.id); 150 if (item.depth === 0) { 151 if (item.startTS < minStartTS) { 152 minStartTS = item.startTS; 153 } 154 if (item.endTS > maxEndTS) { 155 maxEndTS = item.endTS; 156 } 157 } 158 }); 159 160 let FuncChildrenList = await getFuncChildren(funcIdArr, val.param.processId, val.param.threadId, minStartTS, maxEndTS, true); 161 let childDurMap = new Map<number, number>(); 162 FuncChildrenList.forEach((it: unknown) => {// @ts-ignore 163 if (!childDurMap.has(it.parentId)) {// @ts-ignore 164 childDurMap.set(it.parentId, it.duration); 165 } else {// @ts-ignore 166 let dur = childDurMap.get(it.parentId); // @ts-ignore 167 dur += it.duration; // @ts-ignore 168 childDurMap.set(it.parentId, dur!); 169 } 170 }); 171 // @ts-ignore 172 result.map((e: unknown) => { 173 // @ts-ignore 174 e.selfTime = childDurMap.has(e.id) ? (e.duration - childDurMap.get(e.id)) / 1000000 : e.duration / 1000000; 175 // @ts-ignore 176 e.startTime = Utils.getTimeString(e.startNs); 177 // @ts-ignore 178 e.absoluteTime = ((window as unknown).recordStartNS + e.startNs) / 1000000000; 179 // @ts-ignore 180 e.duration = e.duration / 1000000; 181 // @ts-ignore 182 e.state = Utils.getEndState(e.state)!; 183 // @ts-ignore 184 e.processName = `${e.process === undefined || e.process === null ? 'process' : e.process}[${e.processId}]`; 185 // @ts-ignore 186 e.threadName = `${e.thread === undefined || e.thread === null ? 'thread' : e.thread}[${e.threadId}]`; 187 }); 188 // @ts-ignore 189 this.boxChildSource = result; 190 if (this.sliceChildTbl) { 191 // @ts-ignore 192 this.sliceChildTbl.recycleDataSource = result; 193 } 194 } else { 195 this.boxChildSource = []; 196 if (this.sliceChildTbl) { 197 // @ts-ignore 198 this.sliceChildTbl.recycleDataSource = []; 199 }; 200 } 201 }); 202 } 203 204 initHtml(): string { 205 return ` 206 <style> 207 :host{ 208 padding: 10px 10px; 209 display: flex; 210 flex-direction: column; 211 } 212 </style> 213 <lit-table id="tb-slice-child" style="height: auto"> 214 <lit-table-column order title="StartTime(Relative)" width="15%" data-index="startTime" key="startTime" align="flex-start" > 215 </lit-table-column> 216 <lit-table-column order title="StartTime(Absolute)" width="15%" data-index="absoluteTime" key="absoluteTime" align="flex-start" > 217 </lit-table-column> 218 <lit-table-column order width="15%" data-index="processName" key="processName" title="Process" align="flex-start" > 219 </lit-table-column> 220 <lit-table-column order width="15%" data-index="threadName" key="threadName" align="flex-start" title="Thread" > 221 </lit-table-column> 222 <lit-table-column order width="1fr" data-index="name" key="name" align="flex-start" title="Name"> 223 </lit-table-column> 224 <lit-table-column order width="1fr" data-index="duration" key="duration" title="duration(ms)" align="flex-start"> 225 </lit-table-column> 226 <lit-table-column order width="1fr" data-index="selfTime" key="selfTime" title="selfTime(ms)" align="flex-start"> 227 </lit-table-column> 228 </lit-table> 229 `; 230 } 231 232 sortByColumn(detail: unknown): void { 233 // @ts-ignore 234 function compare(property, sort, type) { 235 return function (boxChildLeftData: SelectionData, boxChildRightData: SelectionData): number { 236 if (type === 'number') { 237 return sort === 2 // @ts-ignore 238 ? parseFloat(boxChildRightData[property]) - parseFloat(boxChildLeftData[property]) // @ts-ignore 239 : parseFloat(boxChildLeftData[property]) - parseFloat(boxChildRightData[property]); 240 } else { 241 // @ts-ignore 242 if (boxChildRightData[property] > boxChildLeftData[property]) { 243 return sort === 2 ? 1 : -1; 244 } else { 245 // @ts-ignore 246 if (boxChildRightData[property] === boxChildLeftData[property]) { 247 return 0; 248 } else { 249 return sort === 2 ? -1 : 1; 250 } 251 } 252 } 253 }; 254 } 255 //@ts-ignore 256 if (detail.key === 'startTime' || detail.key === 'processName' || detail.key === 'threadName' ||//@ts-ignore 257 detail.key === 'name') { 258 // @ts-ignore 259 this.boxChildSource.sort(compare(detail.key, detail.sort, 'string'));// @ts-ignore 260 } else if (detail.key === 'absoluteTime' || detail.key === 'duration') {// @ts-ignore 261 this.boxChildSource.sort(compare(detail.key, detail.sort, 'number')); 262 } 263 // @ts-ignore 264 this.boxChildSource.sort(compare(detail.key, detail.sort, 'string')); 265 this.sliceChildTbl!.recycleDataSource = this.boxChildSource; 266 } 267} 268