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 } from './bean/ProfilerServiceTypes';
19import { SpRecordTemplateHtml } from './SpRecordTemplate.html';
20
21@element('sp-record-template')
22export class SpRecordTemplate extends BaseElement {
23  static SCHEDULING_ANALYSIS_EVENT = [
24    'sched/sched_wakeup',
25    'sched/sched_switch',
26    'sched/sched_wakeup_new',
27    'sched/sched_waking',
28    'sched/sched_process_exit',
29    'sched/sched_process_free',
30    'task/task_newtask',
31    'task/task_rename',
32    'power/cpu_frequency',
33    'power/cpu_idle',
34    'irq/irq_handler_entry',
35    'irq/irq_handler_exit',
36    'irq/softirq_entry',
37    'irq/softirq_exit',
38    'irq/softirq_raise',
39  ];
40  static FRAME_TIMELINE_EVENTS = [
41    'sched/sched_switch',
42    'sched/sched_wakeup',
43    'sched/sched_wakeup_new',
44    'sched/sched_waking',
45    'sched/sched_process_exit',
46    'sched/sched_process_free',
47    'sched/sched_process_free',
48    'task/task_rename',
49    'power/cpu_frequency',
50    'power/cpu_idle',
51    'power/suspend_resume',
52  ];
53  static FRAME_TIMELINE_CATEGORIES_EVENT = [
54    'ability',
55    'ace',
56    'app',
57    'ark',
58    'binder',
59    'disk',
60    'freq',
61    'graphic',
62    'idle',
63    'irq',
64    'memreclaim',
65    'mmc',
66    'multimodalinput',
67    'ohos',
68    'pagecache',
69    'rpc',
70    'sched',
71    'sync',
72    'window',
73    'workq',
74    'zaudio',
75    'zcamera',
76    'zimage',
77    'zmedia',
78  ];
79  static HIPERF_DEFAULT_RECORD_ARGS =
80    '-f 1000 -a  --cpu-limit 100 -e hw-cpu-cycles,sched:sched_waking' +
81    ' --call-stack dwarf --clockid monotonic --offcpu -m 256';
82  private frameTimeline: LitSwitch | undefined | null;
83  private schedulingAnalysis: LitSwitch | undefined | null;
84  private appStartup: LitSwitch | undefined | null;
85  private taskPoolEl: LitSwitch | undefined | null;
86  private dynamicEffectEl: LitSwitch | undefined | null;
87
88  initElements(): void {
89    this.frameTimeline = this.shadowRoot?.querySelector<LitSwitch>('#frame_timeline');
90    this.schedulingAnalysis = this.shadowRoot?.querySelector<LitSwitch>('#scheduling_analysis');
91    this.appStartup = this.shadowRoot?.querySelector<LitSwitch>('#app_startup');
92    this.taskPoolEl = this.shadowRoot?.querySelector<LitSwitch>('#task_pool');
93    this.dynamicEffectEl = this.shadowRoot?.querySelector<LitSwitch>('#dynamic_effect');
94    this.addProbeListener(
95      this.frameTimeline!,
96      this.schedulingAnalysis!,
97      this.appStartup!,
98      this.taskPoolEl!,
99      this.dynamicEffectEl!
100    );
101  }
102
103  addProbeListener(...elements: HTMLElement[]): void {
104    elements.forEach((element) => {
105      element.addEventListener('change', (event: CustomEventInit<LitSwitchChangeEvent>) => {
106        let detail = event.detail;
107        if (detail!.checked) {
108          this.dispatchEvent(new CustomEvent('addProbe', { detail: { elementId: element.getAttribute('name') } }));
109        } else {
110          this.dispatchEvent(new CustomEvent('delProbe', { detail: { elementId: element.getAttribute('name') } }));
111        }
112      });
113    });
114  }
115
116  getTemplateConfig(): Array<ProfilerPluginConfig<{}>> {
117    let config: Array<ProfilerPluginConfig<{}>> = [];
118    let traceEventSet: string[] = [];
119    let hiTraceCategories: string[] = [];
120    let useFtracePlugin: boolean = false;
121    if (this.frameTimeline?.checked || this.appStartup?.checked || this.dynamicEffectEl?.checked) {
122      useFtracePlugin = true;
123      SpRecordTemplate.FRAME_TIMELINE_CATEGORIES_EVENT.forEach((categories) => {
124        if (hiTraceCategories.indexOf(categories) === -1) {
125          hiTraceCategories.push(categories);
126        }
127      });
128      if (this.appStartup?.checked) {
129        hiTraceCategories.push('musl');
130        config.push(this.createHiperfDefaultConfig());
131      }
132      SpRecordTemplate.FRAME_TIMELINE_EVENTS.forEach((ev) => {
133        if (traceEventSet.indexOf(ev) === -1) {
134          traceEventSet.push(ev);
135        }
136      });
137    }
138    useFtracePlugin = this.schedulingAnalysisConfig(useFtracePlugin, traceEventSet);
139    useFtracePlugin = this.taskPoolElConfig(useFtracePlugin, hiTraceCategories);
140    if (useFtracePlugin) {
141      let tracePluginConfig: TracePluginConfig = {
142        ftraceEvents: traceEventSet,
143        hitraceCategories: hiTraceCategories,
144        flushIntervalMs: 1000,
145        hitraceApps: [],
146        bufferSizeKb: 20480,
147        debugOn: false,
148        flushThresholdKb: 4096,
149        clock: 'boot',
150        tracePeriodMs: 200,
151        parseKsyms: true,
152        rawDataPrefix: '',
153        traceDurationMs: 0,
154      };
155      let htraceProfilerPluginConfig: ProfilerPluginConfig<TracePluginConfig> = {
156        pluginName: 'ftrace-plugin',
157        sampleInterval: 1000,
158        configData: tracePluginConfig,
159      };
160      config.push(htraceProfilerPluginConfig);
161    }
162    return config;
163  }
164
165  private schedulingAnalysisConfig(useFtracePlugin: boolean, traceEventSet: string[]): boolean {
166    if (this.schedulingAnalysis?.checked) {
167      useFtracePlugin = true;
168      SpRecordTemplate.SCHEDULING_ANALYSIS_EVENT.forEach((event) => {
169        if (traceEventSet.indexOf(event) < 0) {
170          traceEventSet.push(event);
171        }
172      });
173    }
174    return useFtracePlugin;
175  }
176
177  private taskPoolElConfig(useFtracePlugin: boolean, hitraceCategories: string[]): boolean {
178    if (this.taskPoolEl!.checked) {
179      useFtracePlugin = true;
180      hitraceCategories.push('commonlibrary');
181    }
182    return useFtracePlugin;
183  }
184
185  private createHiperfDefaultConfig(): ProfilerPluginConfig<HiperfPluginConfig> {
186    let hiPerf: HiperfPluginConfig = {
187      isRoot: false,
188      outfileName: '/data/local/tmp/perf.data',
189      recordArgs: SpRecordTemplate.HIPERF_DEFAULT_RECORD_ARGS,
190    };
191    return {
192      pluginName: 'hiperf-plugin',
193      sampleInterval: 5000,
194      configData: hiPerf,
195    };
196  }
197
198  initHtml(): string {
199    return SpRecordTemplateHtml;
200  }
201}
202