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 LitSwitch, { LitSwitchChangeEvent } from '../../../base-ui/switch/lit-switch'; 18import { HiperfPluginConfig, ProfilerPluginConfig, TracePluginConfig, NativePluginConfig } from './bean/ProfilerServiceTypes'; 19import { SpRecordTemplateHtml } from './SpRecordTemplate.html'; 20import { Cmd } from '../../../command/Cmd'; 21import { LitSelectV } from '../../../base-ui/select/LitSelectV'; 22 23@element('sp-record-template') 24export class SpRecordTemplate extends BaseElement { 25 static SCHEDULING_ANALYSIS_EVENT = [ 26 'sched/sched_wakeup', 27 'sched/sched_switch', 28 'sched/sched_wakeup_new', 29 'sched/sched_waking', 30 'sched/sched_process_exit', 31 'sched/sched_process_free', 32 'task/task_newtask', 33 'task/task_rename', 34 'power/cpu_frequency', 35 'power/cpu_idle', 36 'irq/irq_handler_entry', 37 'irq/irq_handler_exit', 38 'irq/softirq_entry', 39 'irq/softirq_exit', 40 'irq/softirq_raise', 41 ]; 42 static FRAME_TIMELINE_EVENTS = [ 43 'sched/sched_switch', 44 'sched/sched_wakeup', 45 'sched/sched_wakeup_new', 46 'sched/sched_waking', 47 'sched/sched_process_exit', 48 'sched/sched_process_free', 49 'sched/sched_process_free', 50 'task/task_rename', 51 'power/cpu_frequency', 52 'power/cpu_idle', 53 'power/suspend_resume', 54 ]; 55 static FRAME_TIMELINE_CATEGORIES_EVENT = [ 56 'ability', 57 'ace', 58 'app', 59 'ark', 60 'binder', 61 'disk', 62 'freq', 63 'graphic', 64 'idle', 65 'irq', 66 'memreclaim', 67 'mmc', 68 'multimodalinput', 69 'ohos', 70 'pagecache', 71 'rpc', 72 'sched', 73 'sync', 74 'window', 75 'workq', 76 'zaudio', 77 'zcamera', 78 'zimage', 79 'zmedia', 80 ]; 81 static HIPERF_DEFAULT_RECORD_ARGS = 82 '-f 1000 -a --cpu-limit 100 -e hw-cpu-cycles,sched:sched_waking' + 83 ' --call-stack dwarf --clockid monotonic --offcpu -m 256'; 84 static HIPERF_NAPI_RECORD_ARGS_BEFORE = ''; 85 static HIPERF_NAPI_RECORD_ARGS_APP = '-f 1000 --app'; 86 static HIPERF_NAPI_RECORD_ARGS_PID = '-f 1000 -p'; 87 static HIPERF_NAPI_RECORD_ARGS_AFTER = '--cpu-limit 100 -e hw-cpu-cycles --call-stack dwarf --clockid monotonic -m 256'; 88 private frameTimeline: LitSwitch | undefined | null; 89 private schedulingAnalysis: LitSwitch | undefined | null; 90 private appStartup: LitSwitch | undefined | null; 91 private taskPoolEl: LitSwitch | undefined | null; 92 private dynamicEffectEl: LitSwitch | undefined | null; 93 private packageName: LitSelectV | null | undefined; 94 private napiEl: LitSwitch | undefined | null; 95 private isNum:boolean = false; 96 97 initElements(): void { 98 this.frameTimeline = this.shadowRoot?.querySelector<LitSwitch>('#frame_timeline'); 99 this.schedulingAnalysis = this.shadowRoot?.querySelector<LitSwitch>('#scheduling_analysis'); 100 this.appStartup = this.shadowRoot?.querySelector<LitSwitch>('#app_startup'); 101 this.taskPoolEl = this.shadowRoot?.querySelector<LitSwitch>('#task_pool'); 102 this.dynamicEffectEl = this.shadowRoot?.querySelector<LitSwitch>('#dynamic_effect'); 103 this.napiEl = this.shadowRoot?.querySelector<LitSwitch>('#Napi'); 104 this.packageName = this.shadowRoot?.getElementById('napi_packageName') as LitSelectV; 105 this.addProbeListener( 106 this.frameTimeline!, 107 this.schedulingAnalysis!, 108 this.appStartup!, 109 this.taskPoolEl!, 110 this.dynamicEffectEl! 111 ); 112 this.initNapiSwitchOption(); 113 } 114 115 addProbeListener(...elements: HTMLElement[]): void { 116 elements.forEach((element) => { 117 element.addEventListener('change', (event: CustomEventInit<LitSwitchChangeEvent>) => { 118 let detail = event.detail; 119 if (detail!.checked) { 120 this.dispatchEvent(new CustomEvent('addProbe', { detail: { elementId: element.getAttribute('name') } })); 121 } else { 122 this.dispatchEvent(new CustomEvent('delProbe', { detail: { elementId: element.getAttribute('name') } })); 123 } 124 }); 125 }); 126 } 127 128 getTemplateConfig(): Array<ProfilerPluginConfig<{}>> { 129 let config: Array<ProfilerPluginConfig<{}>> = []; 130 let traceEventSet: string[] = []; 131 let hiTraceCategories: string[] = []; 132 let useFtracePlugin: boolean = false; 133 if (this.frameTimeline?.checked || this.appStartup?.checked || this.dynamicEffectEl?.checked) { 134 useFtracePlugin = true; 135 SpRecordTemplate.FRAME_TIMELINE_CATEGORIES_EVENT.forEach((categories) => { 136 if (hiTraceCategories.indexOf(categories) === -1) { 137 hiTraceCategories.push(categories); 138 } 139 }); 140 if (this.appStartup?.checked) { 141 hiTraceCategories.push('musl'); 142 config.push(this.createHiperfDefaultConfig(true)); 143 } 144 SpRecordTemplate.FRAME_TIMELINE_EVENTS.forEach((ev) => { 145 if (traceEventSet.indexOf(ev) === -1) { 146 traceEventSet.push(ev); 147 } 148 }); 149 } 150 if (this.napiEl?.checked && this.packageName?.value!) { 151 useFtracePlugin = true; 152 hiTraceCategories.push('ace'); 153 if (!this.appStartup?.checked) { 154 config.push(this.createHiperfDefaultConfig(false)); 155 } 156 config.push(this.createNativeConfig()); 157 } 158 useFtracePlugin = this.schedulingAnalysisConfig(useFtracePlugin, traceEventSet); 159 useFtracePlugin = this.taskPoolElConfig(useFtracePlugin, hiTraceCategories); 160 if (useFtracePlugin) { 161 let tracePluginConfig: TracePluginConfig = { 162 ftraceEvents: traceEventSet, 163 hitraceCategories: hiTraceCategories, 164 flushIntervalMs: 1000, 165 hitraceApps: [], 166 bufferSizeKb: 20480, 167 debugOn: false, 168 flushThresholdKb: 4096, 169 clock: 'boot', 170 tracePeriodMs: 200, 171 parseKsyms: true, 172 rawDataPrefix: '', 173 traceDurationMs: 0, 174 }; 175 let htraceProfilerPluginConfig: ProfilerPluginConfig<TracePluginConfig> = { 176 pluginName: 'ftrace-plugin', 177 sampleInterval: 1000, 178 configData: tracePluginConfig, 179 }; 180 config.push(htraceProfilerPluginConfig); 181 } 182 return config; 183 } 184 185 private schedulingAnalysisConfig(useFtracePlugin: boolean, traceEventSet: string[]): boolean { 186 if (this.schedulingAnalysis?.checked) { 187 useFtracePlugin = true; 188 SpRecordTemplate.SCHEDULING_ANALYSIS_EVENT.forEach((event) => { 189 if (traceEventSet.indexOf(event) < 0) { 190 traceEventSet.push(event); 191 } 192 }); 193 } 194 return useFtracePlugin; 195 } 196 197 private taskPoolElConfig(useFtracePlugin: boolean, hitraceCategories: string[]): boolean { 198 if (this.taskPoolEl!.checked) { 199 useFtracePlugin = true; 200 hitraceCategories.push('commonlibrary'); 201 } 202 return useFtracePlugin; 203 } 204 205 private createHiperfDefaultConfig(isAll: boolean): ProfilerPluginConfig<HiperfPluginConfig> { 206 let newVal = this.getDesiredString(this.packageName?.value!); 207 SpRecordTemplate.HIPERF_NAPI_RECORD_ARGS_BEFORE = this.isNum ? SpRecordTemplate.HIPERF_NAPI_RECORD_ARGS_PID : SpRecordTemplate.HIPERF_NAPI_RECORD_ARGS_APP; 208 let hiPerf: HiperfPluginConfig = { 209 isRoot: false, 210 outfileName: '/data/local/tmp/perf.data', 211 recordArgs: isAll ? SpRecordTemplate.HIPERF_DEFAULT_RECORD_ARGS : `${SpRecordTemplate.HIPERF_NAPI_RECORD_ARGS_BEFORE} ${newVal} ${SpRecordTemplate.HIPERF_NAPI_RECORD_ARGS_AFTER}`, 212 }; 213 return { 214 pluginName: 'hiperf-plugin', 215 sampleInterval: 5000, 216 configData: hiPerf, 217 }; 218 } 219 220 private getDesiredString(str: string): string { 221 if (/^\d+$/.test(str)) { 222 this.isNum = true; 223 return str; 224 } 225 if (str.includes(',')) { 226 this.isNum = /^\d+$/.test(str.split(',')[0]) ? true : false; 227 return str.split(',')[0]; 228 } 229 this.isNum = false; 230 return str; 231} 232 233private initNapiSwitchOption(): void { 234 let litSwitch = this.shadowRoot?.querySelector('#Napi') as LitSwitch; 235 let packageInput = this.packageName!.shadowRoot?.querySelector('input') as HTMLInputElement; 236 this.packageName!.setAttribute('disabled', ''); 237 litSwitch.addEventListener('change', (event: Event): void => { 238 // @ts-ignore 239 let detail = event.detail; 240 if (detail.checked) { 241 this.packageName!.removeAttribute('disabled'); 242 packageInput.addEventListener('mousedown', (): void => { 243 packageInput.readOnly = false; 244 this.packageMouseDownHandler(); 245 }); 246 } else { 247 packageInput.readOnly = true; 248 this.packageName!.setAttribute('disabled', ''); 249 return; 250 } 251 }); 252} 253 254private packageMouseDownHandler(): void { 255 this.packageName!.dataSource([], ''); 256 Cmd.getPackage().then((packageList: string[]): void => { 257 let finalDataList = packageList.map(str => str.replace(/\t/g, '')); 258 if (finalDataList.length > 0) { 259 this.packageName!.dataSource(finalDataList, '', true); 260 } else { 261 this.packageName!.dataSource([], ''); 262 } 263 }).catch(error => { 264 console.error('Error fetching package list:', error); 265 this.packageName!.dataSource([], ''); 266 }); 267} 268 269private createNativeConfig(): ProfilerPluginConfig<NativePluginConfig> { 270 let newVal = this.getDesiredString(this.packageName?.value!); 271 let native: NativePluginConfig = { 272 pid: undefined, 273 process_name: undefined, 274 save_file: false, 275 smb_pages: 16384, 276 max_stack_depth: 20, 277 string_compressed: true, 278 fp_unwind: false, 279 blocked: true, 280 callframe_compress: true, 281 record_accurately: true, 282 offline_symbolization: true, 283 startup_mode: false, 284 js_stack_report: 2, 285 max_js_stack_depth: 5, 286 filter_napi_name: 'info', 287 memtrace_enable: true, 288 malloc_disable: true, 289 }; 290 if (this.isNum === true) { 291 native.pid = Number(newVal); 292 delete native.process_name; 293 } else { 294 native.process_name = newVal; 295 delete native.pid; 296 } 297 298 return { 299 pluginName: 'nativehook', 300 sampleInterval: 5000, 301 configData: native, 302 is_protobuf_serialize: false 303 }; 304} 305 306 initHtml(): string { 307 return SpRecordTemplateHtml; 308 } 309} 310