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.js'; 17 18import { 19 querySelectTraceStats, querySystemCalls, 20 queryTraceMemory, 21 queryTraceMemoryTop, 22 queryTraceMemoryUnAgg, 23 queryTraceMetaData, 24 queryTraceTaskName, 25} from '../database/SqlLite.js'; 26 27import '../../base-ui/table/lit-table.js'; 28import { initMemoryAggStrategy } from './metrics/MemAggStrategy.js'; 29import { initMemoryStrategy } from './metrics/MemStrategy.js'; 30import { initTraceStateStrategy } from './metrics/TraceStatsStrategy.js'; 31import { initTraceTaskStrategy } from './metrics/TraceTaskStrategy.js'; 32import { initMetaDataStrategy } from './metrics/MetaDataStrategy.js'; 33import { PluginConvertUtils } from './setting/utils/PluginConvertUtils.js'; 34import { info } from '../../log/Log.js'; 35import { LitProgressBar } from '../../base-ui/progress-bar/LitProgressBar.js'; 36import { SpStatisticsHttpUtil } from '../../statistics/util/SpStatisticsHttpUtil.js'; 37import { initSysCallsStrategy } from './metrics/SysCallsStrategy.js'; 38 39@element('sp-metrics') 40export class SpMetrics extends BaseElement { 41 private _metric?: string; 42 private _metricResult?: string; 43 private selectMetricEl: HTMLSelectElement | undefined; 44 private runButtonEl: HTMLButtonElement | undefined | null; 45 private responseJson: HTMLPreElement | undefined | null; 46 private metricOptionalSelects: Array<MetricQueryItem> | undefined; 47 private metricProgressLoad: LitProgressBar | undefined; 48 49 static get observedAttributes(): string[] { 50 return ['metric', 'metricResult']; 51 } 52 53 get metric(): string { 54 return this.getAttribute('metric') || ''; 55 } 56 57 set metric(value: string) { 58 this._metric = value; 59 } 60 61 get metricResult(): string { 62 return this.getAttribute('metricResult') || ''; 63 } 64 65 set metricResult(value: string) { 66 this._metricResult = value; 67 this.setAttribute('metricResult', value); 68 } 69 70 reset(): void { 71 this.selectMetricEl!.selectedIndex = 0; 72 this.responseJson!.textContent = ''; 73 } 74 75 initElements(): void { 76 this.metricProgressLoad = this.shadowRoot?.querySelector('.sp-load-metric') as LitProgressBar; 77 this.selectMetricEl = this.shadowRoot?.querySelector('.sql-select') as HTMLSelectElement; 78 this.runButtonEl = this.shadowRoot?.querySelector('.sql-select-button') as HTMLButtonElement; 79 this.responseJson = this.shadowRoot?.querySelector('.response-json') as HTMLPreElement; 80 if (this.selectMetricEl) { 81 this.selectMetricEl.addEventListener('selectionchange', () => { 82 if (this.selectMetricEl) { 83 this.selectMetricEl.textContent = ''; 84 } 85 }); 86 } 87 this.initMetricDataHandle(); 88 this.initMetricSelectOption(); 89 } 90 91 initMetric(queryItem: MetricQueryItem): void { 92 this.initMetricData(queryItem).then(() => { 93 this.metricProgressLoad!.loading = false; 94 }); 95 } 96 97 async initMetricData(queryItem: MetricQueryItem): Promise<void> { 98 let spacesNumber = 4; 99 let metricQuery = queryItem.metricQuery; 100 let queryList = await metricQuery(); 101 info('current Metric Data size is: ', queryList!.length); 102 let metric = queryItem.metricResultHandle; 103 let resultData = metric(queryList); 104 this.responseJson!.textContent = PluginConvertUtils.BeanToCmdTxtWithObjName(resultData, true, 105 queryItem.metricName, spacesNumber); 106 } 107 108 attributeChangedCallback(name: string, oldValue: string, newValue: string): void { 109 switch (name) { 110 case 'metric': 111 if (this.selectMetricEl) { 112 this.selectMetricEl.textContent = newValue; 113 } 114 break; 115 case 'metricResult': 116 if (this.selectMetricEl) { 117 this.selectMetricEl.textContent = newValue; 118 } 119 break; 120 } 121 } 122 123 runClickListener = (): void => { 124 SpStatisticsHttpUtil.addOrdinaryVisitAction({ 125 event: 'metrics', 126 action: 'metrics', 127 }); 128 this.metricProgressLoad!.loading = true; 129 let selectedIndex = this.selectMetricEl!.selectedIndex; 130 let value = this.selectMetricEl!.options[selectedIndex].value; 131 let resultQuery = this.metricOptionalSelects?.filter((item) => { 132 return item.metricName === value; 133 }); 134 if (!resultQuery || resultQuery.length === 0) { 135 return; 136 } 137 this.initMetric(resultQuery[0]); 138 }; 139 140 connectedCallback(): void { 141 // Run metric button to add listener 142 this.runButtonEl?.addEventListener('click', this.runClickListener); 143 } 144 145 disconnectedCallback(): void { 146 this.runButtonEl?.removeEventListener('click', this.runClickListener); 147 } 148 149 initMetricSelectOption(): void { 150 for (let index = 0 ; index < this.metricOptionalSelects!.length ; index++) { 151 let htmlElement = document.createElement('option'); 152 if (this.metricOptionalSelects) { 153 htmlElement.textContent = this.metricOptionalSelects[index].metricName; 154 this.selectMetricEl?.appendChild(htmlElement); 155 } 156 } 157 } 158 159 initMetricDataHandle(): void { 160 this.metricOptionalSelects = [ 161 { 162 metricName: 'trace_mem', 163 metricQuery: queryTraceMemory, 164 metricResultHandle: initMemoryStrategy, 165 }, 166 { 167 metricName: 'trace_mem_top10', 168 metricQuery: queryTraceMemoryTop, 169 metricResultHandle: initMemoryStrategy, 170 }, 171 { 172 metricName: 'trace_mem_unagg', 173 metricQuery: queryTraceMemoryUnAgg, 174 metricResultHandle: initMemoryAggStrategy, 175 }, 176 { 177 metricName: 'trace_task_names', 178 metricQuery: queryTraceTaskName, 179 metricResultHandle: initTraceTaskStrategy, 180 }, 181 { 182 metricName: 'trace_stats', 183 metricQuery: querySelectTraceStats, 184 metricResultHandle: initTraceStateStrategy, 185 }, 186 { 187 metricName: 'trace_metadata', 188 metricQuery: queryTraceMetaData, 189 metricResultHandle: initMetaDataStrategy, 190 }, 191 { 192 metricName: 'sys_calls', 193 metricQuery: querySystemCalls, 194 metricResultHandle: initSysCallsStrategy, 195 }, 196 ]; 197 } 198 199 initHtml(): string { 200 return ` 201 <style> 202 :host{ 203 width: 100%; 204 height: 100%; 205 background-color: var(--dark-background5,#F6F6F6); 206 margin: 0; 207 padding: 0; 208 font-size:16px; 209 } 210 .metric{ 211 display: flex; 212 flex-direction: column; 213 position: absolute; 214 top: 0; 215 bottom: 0; 216 left: 0; 217 right: 0; 218 background-color: var(--dark-background5,#F6F6F6); 219 } 220 .metric-select{ 221 color: #121212; 222 border-radius: 16px; 223 background-color: var(--dark-background3,#FFFFFF); 224 padding: 1% 2%; 225 margin: 2% 2.5% 0 2.5%; 226 grid-row-gap: 30px; 227 } 228 .request{ 229 min-height: 15vh; 230 overflow: auto; 231 position: relative; 232 } 233 .sql-select{ 234 font-family: Helvetica,serif; 235 color: var(--dark-color1,#212121); 236 font-size:0.875em; 237 line-height: 16px; 238 font-weight: 400; 239 text-align: left; 240 width: 50%; 241 height: 32px; 242 flex-wrap: wrap; 243 margin-top: 1%; 244 border: 1px solid var(--dark-color1,#4D4D4D); 245 border-radius: 16px; 246 padding: 5px 10px 5px 10px; 247 -webkit-appearance: none; 248 background: url('img/down.png') no-repeat 98% center var(--dark-background3,#FFFFFF); 249 } 250 button{ 251 border-radius: 16px; 252 flex-grow: 1; 253 background-color: #0A59F7; 254 height: 32px; 255 width: 96px; 256 font-size: 0.875em; 257 color: var(--dark-background3,#FFFFFF); 258 text-align: center; 259 line-height: 20px; 260 font-weight: 400; 261 border:0 solid; 262 margin-left: 2%; 263 opacity: 0.6; 264 cursor:pointer; 265 } 266 .response{ 267 flex-grow: 1; 268 margin-bottom: 1%; 269 } 270 .response-json{ 271 background-color: var(--dark-background3,#FFFFFF); 272 border-radius: 16px; 273 display: table-cell; 274 font-family: Helvetica,serif; 275 color: var(--dark-color1,#212121); 276 font-size:0.875em; 277 line-height: 20px; 278 font-weight: 400; 279 text-align: left; 280 height: 90%; 281 width: 100%; 282 border: none; 283 outline:none; 284 resize:none; 285 } 286 p{ 287 display: table-cell; 288 padding: 20% 0; 289 color: #999999; 290 font-size:0.875em; 291 line-height: 20px; 292 font-weight: 400; 293 text-align: left; 294 width: 100%; 295 } 296 ::-webkit-scrollbar-thumb{ 297 background-color: var(--dark-background7,rgba(0,0,0,0.1)); 298 border-radius: 6px; 299 } 300 .sp-load-metric{ 301 width: 95%; 302 bottom: 0; 303 } 304 ::-webkit-scrollbar{ 305 background-color: var(--dark-background3,#FFFFFF); 306 width: 8px; 307 } 308 </style> 309 <div class="metric"> 310 <div class="metric-select request"> 311 <p>Select a metric</p> 312 <select class="sql-select"> 313 </select> 314 <button class="sql-select-button"> Run </button> 315 <lit-progress-bar class="sp-load-metric"></lit-progress-bar> 316 </div> 317 <div class="metric-select response"> 318 <textarea class="response-json" readonly> 319 </textarea> 320 </div> 321 </div> 322 `; 323 } 324} 325 326export interface MetricQueryItem { 327 metricName: string; 328 metricQuery: Function; 329 metricResultHandle: Function; 330} 331