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