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 { PerfSampleBoxJumpParam, SelectionParam } from '../../../../bean/BoxSelection'; 19import { perfDataQuery } from '../../../chart/PerfDataQuery'; 20import { PerfFile, PerfSample, PerfThread } from '../../../../bean/PerfProfile'; 21import { Utils } from '../../base/Utils'; 22import { log } from '../../../../../log/Log'; 23import '../../../../../base-ui/slicer/lit-slicer'; 24import { SpSystemTrace } from '../../../SpSystemTrace'; 25import { 26 queryPerfProcess, 27 queryPerfSampleCallChain, 28 queryPerfSampleChildListByTree, 29 queryPerfSampleListByTimeRange, 30} from '../../../../database/sql/Perf.sql'; 31 32@element('tabpane-perf-sample-child') 33export class TabPanePerfSampleChild extends BaseElement { 34 private param: PerfSampleBoxJumpParam | null | undefined; 35 private perfSampleTbl: LitTable | null | undefined; 36 private tblData: LitTable | null | undefined; 37 private perfSampleSource: Array<PerfSample> = []; 38 private processMap: Map<number, PerfThread> = new Map<number, PerfThread>(); 39 private sortKey: string = 'timeString'; 40 private sortType: number = 0; 41 42 set data(sampleChildParam: PerfSampleBoxJumpParam | null | undefined) { 43 if (sampleChildParam === this.param || !sampleChildParam?.isJumpPage) { 44 return; 45 } 46 this.param = sampleChildParam; 47 this.perfSampleTbl!.style.visibility = 'visible'; 48 // @ts-ignore 49 this.perfSampleTbl?.shadowRoot?.querySelector('.table')?.style?.height = `${ 50 this.parentElement!.clientHeight - 40 51 }px`; 52 this.perfSampleTbl!.recycleDataSource = []; 53 // @ts-ignore 54 this.tblData?.shadowRoot?.querySelector('.table')?.style?.height = `${this.parentElement!.clientHeight - 25}px`; 55 this.tblData!.recycleDataSource = []; 56 if (sampleChildParam) { 57 Promise.all([ 58 queryPerfProcess(), 59 queryPerfSampleChildListByTree( 60 sampleChildParam.leftNs, 61 sampleChildParam.rightNs, 62 sampleChildParam.pid, 63 sampleChildParam.tid, 64 ), 65 ]).then((results) => { 66 let processes = results[0] as Array<PerfThread>; 67 log(`queryPerfProcess size : ${processes.length}`); 68 let samples = results[1] as Array<PerfSample>; 69 log(`queryPerfSampleChildListByTree size : ${samples.length}`); 70 this.processMap.clear(); 71 for (let process of processes) { 72 this.processMap.set(process.pid, process); 73 } 74 75 this.initPerfSampleData(samples.filter(it => { 76 if (sampleChildParam.tsArr && sampleChildParam.tsArr.length > 0) { 77 return sampleChildParam.tsArr.some(ts => it.time === ts) 78 } 79 return true; 80 })); 81 }); 82 } 83 } 84 85 private initPerfSampleData(samples: PerfSample[]): void { 86 for (let sample of samples) { 87 let process = this.processMap.get(sample.pid); 88 sample.processName = 89 process === null || process === undefined 90 ? `Process(${sample.pid})` 91 : `${process!.processName || 'Process'}(${sample.pid})`; 92 sample.threadName = 93 sample.threadName === null || sample.threadName === undefined 94 ? `Thread(${sample.tid})` 95 : `${sample.threadName}(${sample.tid})`; 96 sample.coreName = `CPU ${sample.core}`; 97 sample.timeString = Utils.getTimeString(sample.time); 98 sample.backtrace = []; 99 let call = perfDataQuery.callChainMap.get(sample.sampleId); 100 if (call === undefined || call === null) { 101 sample.depth = 0; 102 sample.backtrace.push('No Effective Call Stack'); 103 } else { 104 sample.depth = call.depth; 105 if (typeof call.name === 'number') { 106 call.name = SpSystemTrace.DATA_DICT.get(call.name) || ''; 107 } 108 sample.backtrace.push(call.name); 109 sample.backtrace.push(`(${sample.depth} other frames)`); 110 } 111 } 112 this.perfSampleSource = samples; 113 this.sortPerfSampleTable(this.sortKey, this.sortType); 114 } 115 116 setRightTableData(sample: PerfSample): void { 117 queryPerfSampleCallChain(sample.sampleId).then((result) => { 118 for (let stack of result) { 119 if (stack.sourceId !== undefined) { 120 stack.sourceFile = SpSystemTrace.DATA_DICT.get(stack.sourceId) || ''; 121 } 122 if (typeof stack.symbol === 'number') { 123 stack.symbol = SpSystemTrace.DATA_DICT.get(stack.symbol) || ''; 124 } 125 if (stack.sourceFile && stack.lineNumber !== undefined) { 126 stack.symbol = `${stack.symbol}[${stack.sourceFile}(${stack.lineNumber})]`; 127 } 128 // @ts-ignore 129 let files = (perfDataQuery.filesData[stack.fileId] ?? []) as Array<PerfFile>; 130 stack.path = files[stack.symbolId]?.path || ''; 131 stack.type = stack.path.endsWith('.so.1') || stack.path.endsWith('.dll') || stack.path.endsWith('.so') ? 0 : 1; 132 } 133 this.tblData!.dataSource = result; 134 }); 135 } 136 137 initElements(): void { 138 this.perfSampleTbl = this.shadowRoot?.querySelector<LitTable>('#tb-perf-sample'); 139 this.tblData = this.shadowRoot?.querySelector<LitTable>('#tb-stack-data'); 140 //监听row的点击事件,在对应起始时间上画标记棋子,并设置调用栈数据 141 this.perfSampleTbl!.addEventListener('row-click', (e) => { 142 // @ts-ignore 143 let data = e.detail.data as PerfSample; 144 this.setRightTableData(data); 145 // @ts-ignore 146 data.isSelected = true; 147 this.perfSampleTbl!.clearAllSelection(data); 148 this.perfSampleTbl!.setCurrentSelection(data); 149 document.dispatchEvent( 150 new CustomEvent('triangle-flag', { 151 detail: { time: [data.time], type: 'triangle' }, 152 }) 153 ); 154 }); 155 this.perfSampleTbl!.addEventListener('column-click', (evt) => { 156 // @ts-ignore 157 this.sortKey = evt.detail.key; 158 // @ts-ignore 159 this.sortType = evt.detail.sort; 160 // @ts-ignore 161 this.sortPerfSampleTable(evt.detail.key, evt.detail.sort); 162 }); 163 } 164 165 connectedCallback(): void { 166 super.connectedCallback(); 167 new ResizeObserver(() => { 168 if (this.parentElement?.clientHeight !== 0) { 169 // @ts-ignore 170 this.perfSampleTbl?.shadowRoot.querySelector('.table').style.height = `${ 171 this.parentElement!.clientHeight - 40 172 }px`; 173 // @ts-ignore 174 this.tblData?.shadowRoot.querySelector('.table').style.height = `${this.parentElement.clientHeight - 25}px`; 175 this.perfSampleTbl?.reMeauseHeight(); 176 this.tblData?.reMeauseHeight(); 177 } 178 }).observe(this.parentElement!); 179 } 180 181 sortPerfSampleTable(key: string, type: number): void { 182 this.perfSampleSource.sort((perfSampleA, perfSampleB): number => { 183 if (key === 'timeString') { 184 if (type === 0) { 185 return perfSampleA.time - perfSampleB.time; 186 } else if (type === 1) { 187 return perfSampleA.time - perfSampleB.time; 188 } else { 189 return perfSampleB.time - perfSampleA.time; 190 } 191 } else { 192 if (type === 0) { 193 return perfSampleA.core - perfSampleB.core; 194 } else if (type === 1) { 195 return perfSampleA.core - perfSampleB.core; 196 } else { 197 return perfSampleB.core - perfSampleA.core; 198 } 199 } 200 }); 201 this.perfSampleTbl!.recycleDataSource = this.perfSampleSource; 202 } 203 204 initHtml(): string { 205 return `<style> 206:host{ 207 display: flex; 208 flex-direction: column; 209 padding: 10px 10px; 210} 211</style> 212<div class="perf-sample-content" style="display: flex;flex-direction: row"> 213 <lit-slicer style="width:100%"> 214 <div id="left_table" style="width: 65%"> 215 <tab-native-data-modal style="display:none;"/></tab-native-data-modal> 216 <lit-table id="tb-perf-sample" style="height: auto"> 217 <lit-table-column class="perf-sample-column" order width="1fr" title="Sample Time" 218 data-index="timeString" key="timeString" align="flex-start" ></lit-table-column> 219 <lit-table-column class="perf-sample-column" order width="70px" title="Core" 220 data-index="coreName" key="coreName" align="flex-start" ></lit-table-column> 221 <lit-table-column class="perf-sample-column" width="1fr" title="Process" 222 data-index="processName" key="processName" align="flex-start" ></lit-table-column> 223 <lit-table-column class="perf-sample-column" width="1fr" title="Thread" 224 data-index="threadName" key="threadName" align="flex-start" ></lit-table-column> 225 <lit-table-column class="perf-sample-column" width="1fr" title="State" 226 data-index="state" key="state" align="flex-start" ></lit-table-column> 227 <lit-table-column class="perf-sample-column" width="1fr" title="Backtrace" 228 data-index="backtrace" key="backtrace" align="flex-start" > 229 <template> 230 <div> 231 <span class="title-span">{{backtrace[0]}}</span> 232 <span v-if="backtrace.length > 1">⬅</span> 233 <span v-if="backtrace.length > 1" style="color: #565656"> {{backtrace[1]}}</span> 234 </div> 235 </template> 236 </lit-table-column> 237 </lit-table> 238 </div> 239 <lit-slicer-track ></lit-slicer-track> 240 <lit-table id="tb-stack-data" hideDownload no-head 241 style="height: auto;border-left: 1px solid var(--dark-border1,#e2e2e2)"> 242 <lit-table-column width="60px" title="" data-index="type" key="type" align="flex-start" > 243 <template> 244 <img src="img/library.png" size="20" v-if=" type == 1 "> 245 <img src="img/function.png" size="20" v-if=" type == 0 "> 246 </template> 247 </lit-table-column> 248 <lit-table-column width="1fr" title="" data-index="symbol" key="symbol" align="flex-start"></lit-table-column> 249 </lit-table> 250 </lit-slicer> 251</div>`; 252 } 253} 254