• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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