• 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 { SpFlagHtml } from './SpFlag.html';
18const VSYNC_VAL = {
19  'VsyncGeneratior': 'H:VsyncGenerator',
20  'Vsync-rs': 'H:rs_SendVsync',
21  'Vsync-app': 'H:app_SendVsync'
22};
23
24const CAT_SORT = {
25  'Business first': 'business',
26  'Thread first': 'thread'
27};
28
29const CONFIG_STATE: unknown = {
30  'VSync': ['vsyncValue', 'VsyncGeneratior'],
31  'Start&Finish Trace Category': ['catValue', 'Business first'],
32  'Hangs': ['hangsSelect', 'Instant'],
33};
34
35@element('sp-flags')
36export class SpFlags extends BaseElement {
37  private bodyEl: HTMLElement | undefined | null;
38
39  initElements(): void {
40    let parentElement = this.parentNode as HTMLElement;
41    parentElement.style.overflow = 'hidden';
42    this.bodyEl = this.shadowRoot?.querySelector('.body');
43    this.initConfigList();
44  }
45
46  initHtml(): string {
47    return SpFlagHtml;
48  }
49
50  private createConfigDiv(): HTMLDivElement {
51    let configDiv = document.createElement('div');
52    configDiv.className = 'flag-widget';
53    return configDiv;
54  }
55  //控制按钮设置为'Disabled'时,我们需要给一个默认值
56  private createCustomDiv(config: FlagConfigItem, configDiv: HTMLDivElement): void {
57    let configHadDiv = document.createElement('div');
58    configHadDiv.className = 'flag-head-div';
59    let titleLabel = document.createElement('label');
60    titleLabel.textContent = config.title;
61    titleLabel.className = 'flag-title-label';
62    let configSelect = document.createElement('select');
63    configSelect.className = 'flag-select';
64    configSelect.setAttribute('title', config.title);
65    config.switchOptions.forEach((optionItem) => {
66      let configOption = document.createElement('option');
67      configOption.value = optionItem.option;
68      configOption.textContent = optionItem.option;
69      if (optionItem.selected) {
70        configOption.selected = true;
71      }
72      configSelect.appendChild(configOption);
73    });
74    configSelect.addEventListener('change', () => {
75      this.flagSelectListener(configSelect);
76    });
77    let description = document.createElement('div');
78    description.className = 'flag-des-div';
79    description.textContent = config.describeContent;
80    configHadDiv.appendChild(titleLabel);
81    configHadDiv.appendChild(configSelect);
82    configDiv.appendChild(configHadDiv);
83    configDiv.appendChild(description);
84  }
85  //监听flag-select的状态选择
86  private flagSelectListener(configSelect: HTMLSelectElement): void {
87    // @ts-ignore
88    let title = configSelect.getAttribute('title');
89
90    //@ts-ignore
91    let listSelect = this.shadowRoot?.querySelector(`#${CONFIG_STATE[title]?.[0]}`);
92    // @ts-ignore
93    FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value);
94    //@ts-ignore
95    if (listSelect) {
96      // @ts-ignore
97      if (configSelect.selectedOptions[0].value === 'Enabled') {
98        listSelect?.removeAttribute('disabled');
99      } else {
100        listSelect?.childNodes.forEach((child: ChildNode) => {
101          let selectEl = child as HTMLOptionElement;
102          //@ts-ignore
103          if (child.textContent === CONFIG_STATE[title]?.[1]) {
104            selectEl.selected = true;
105            //@ts-ignore
106            FlagsConfig.updateFlagsConfig(CONFIG_STATE[title]?.[0], selectEl.value);
107          } else {
108            selectEl.selected = false;
109          }
110        });
111        listSelect?.setAttribute('disabled', 'disabled');
112      }
113    }
114  }
115
116  //初始化Flag对应的内容
117  private initConfigList(): void {
118    let allConfig = FlagsConfig.getAllFlagConfig();
119    allConfig.forEach((config) => {
120      let configDiv = this.createConfigDiv();
121      this.createCustomDiv(config, configDiv);
122      if (config.title === 'AnimationAnalysis') {
123        let configFooterDiv = document.createElement('div');
124        configFooterDiv.className = 'config_footer';
125        let deviceWidthLabelEl = document.createElement('label');
126        deviceWidthLabelEl.className = 'device_label';
127        deviceWidthLabelEl.textContent = 'PhysicalWidth :';
128        let deviceWidthEl = document.createElement('input');
129        deviceWidthEl.value = <string>config.addInfo!.physicalWidth;
130        deviceWidthEl.addEventListener('keyup', () => {
131          deviceWidthEl.value = deviceWidthEl.value.replace(/\D/g, '');
132        });
133        deviceWidthEl.addEventListener('blur', () => {
134          if (deviceWidthEl.value !== '') {
135            FlagsConfig.updateFlagsConfig('physicalWidth', Number(deviceWidthEl.value));
136          }
137        });
138        deviceWidthEl.className = 'device_input';
139        let deviceHeightLabelEl = document.createElement('label');
140        deviceHeightLabelEl.textContent = 'PhysicalHeight :';
141        deviceHeightLabelEl.className = 'device_label';
142        let deviceHeightEl = document.createElement('input');
143        deviceHeightEl.className = 'device_input';
144        deviceHeightEl.value = <string>config.addInfo!.physicalHeight;
145        deviceHeightEl.addEventListener('keyup', () => {
146          deviceHeightEl.value = deviceHeightEl.value.replace(/\D/g, '');
147        });
148        deviceHeightEl.addEventListener('blur', () => {
149          if (deviceWidthEl.value !== '') {
150            FlagsConfig.updateFlagsConfig('physicalHeight', Number(deviceHeightEl.value));
151          }
152        });
153        configFooterDiv.appendChild(deviceWidthLabelEl);
154        configFooterDiv.appendChild(deviceWidthEl);
155        configFooterDiv.appendChild(deviceHeightLabelEl);
156        configFooterDiv.appendChild(deviceHeightEl);
157        configDiv.appendChild(configFooterDiv);
158      }
159
160      if (config.title === 'VSync') {
161        //@ts-ignore
162        let configKey = CONFIG_STATE[config.title]?.[0];
163        let configFooterDiv = this.createPersonOption(VSYNC_VAL, configKey, <string>config.addInfo!.vsyncValue, config.title);
164        configDiv.appendChild(configFooterDiv);
165      }
166
167      if (config.title === 'Start&Finish Trace Category') {
168        //@ts-ignore
169        let configKey = CONFIG_STATE[config.title]?.[0];
170        let configFooterDiv = this.createPersonOption(CAT_SORT, configKey, <string>config.addInfo!.catValue, config.title);
171        configDiv.appendChild(configFooterDiv);
172      }
173
174      if (config.title === 'Hangs') {
175        let configFooterDiv = this.createHangsOption();
176        configDiv.appendChild(configFooterDiv);
177      }
178
179      this.bodyEl!.appendChild(configDiv);
180    });
181  }
182
183  private createPersonOption(list: unknown, key: string, defaultKey: string, parentOption: string): HTMLDivElement {
184    let configFooterDiv = document.createElement('div');
185    configFooterDiv.className = 'config_footer';
186    let vsyncLableEl = document.createElement('lable');
187    vsyncLableEl.className = 'list_lable';
188    let vsyncTypeEl = document.createElement('select');
189    vsyncTypeEl.setAttribute('id', key);
190    vsyncTypeEl.className = 'flag-select';
191    //根据给出的list遍历添加option下来选框
192    // @ts-ignore
193    for (let k of Object.keys(list)) {
194      let option = document.createElement('option'); // VsyncGeneratior = H:VsyncGenerator
195      // @ts-ignore
196      option.value = list[k];
197      option.textContent = k;
198      // @ts-ignore
199      if (list[k] === defaultKey) {
200        option.selected = true;
201        FlagsConfig.updateFlagsConfig(key, option.value);
202      }
203      vsyncTypeEl.appendChild(option);
204    }
205    vsyncTypeEl.addEventListener('change', function () {
206      let selectValue = this.selectedOptions[0].value;
207      FlagsConfig.updateFlagsConfig(key, selectValue);
208    });
209
210    let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
211    let flagsItemJson = JSON.parse(flagsItem!);
212    let vsync = flagsItemJson[parentOption];
213    if (vsync === 'Enabled') {
214      vsyncTypeEl.removeAttribute('disabled');
215    } else {
216      vsyncTypeEl.setAttribute('disabled', 'disabled');
217      FlagsConfig.updateFlagsConfig(key, defaultKey);
218    }
219    configFooterDiv.appendChild(vsyncLableEl);
220    configFooterDiv.appendChild(vsyncTypeEl);
221    return configFooterDiv;
222  }
223
224  /// Flags新增Hangs下拉框
225  private createHangsOption(): HTMLDivElement {
226    let configFooterDiv = document.createElement('div');
227    configFooterDiv.className = 'config_footer';
228    let hangsLableEl = document.createElement('lable');
229    hangsLableEl.className = 'hangs_lable';
230    let hangsTypeEl = document.createElement('select');
231    hangsTypeEl.setAttribute('id', 'hangsSelect');
232    hangsTypeEl.className = 'flag-select';
233
234    let hangOptions: Array<HTMLElementTagNameMap['option']> = [];
235    for (const settings of [
236      { value: '33', content: 'Instant' },
237      { value: '100', content: 'Circumstantial' },
238      { value: '250', content: 'Micro' },
239      { value: '500', content: 'Severe' }
240    ]) {
241      let hangOption = document.createElement('option');
242      hangOption.value = settings.value + '000000';
243      hangOption.textContent = settings.content;
244      hangOption.selected = false;
245      hangOptions.push(hangOption);
246      hangsTypeEl.appendChild(hangOption);
247    }
248
249    FlagsConfig.updateFlagsConfig('hangValue', hangOptions[0].value);
250    hangOptions[0].selected = true;
251    hangsTypeEl.addEventListener('change', function () {
252      let selectValue = this.selectedOptions[0].value;
253      FlagsConfig.updateFlagsConfig('hangValue', selectValue);
254    });
255
256    let flagsItem = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
257    let flagsItemJson = JSON.parse(flagsItem!);
258    let hangs = flagsItemJson.Hangs;
259    if (hangs === 'Enabled') {
260      hangsTypeEl.removeAttribute('disabled');
261    } else {
262      hangsTypeEl.setAttribute('disabled', 'disabled');
263    }
264    configFooterDiv.appendChild(hangsLableEl);
265    configFooterDiv.appendChild(hangsTypeEl);
266    return configFooterDiv;
267  }
268}
269
270export type Params = {
271  [key: string]: unknown;
272};
273
274export class FlagsConfig {
275  static FLAGS_CONFIG_KEY = 'FlagsConfig';
276  static DEFAULT_CONFIG: Array<FlagConfigItem> = [
277    {
278      title: 'TaskPool',
279      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
280      describeContent: 'Analyze TaskPool templates',
281    },
282    {
283      title: 'AnimationAnalysis',
284      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
285      describeContent: 'Analyze Animation effect templates',
286      addInfo: { physicalWidth: 0, physicalHeight: 0 },
287    },
288    {
289      title: 'AppStartup',
290      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
291      describeContent: 'App Startup templates',
292    },
293    {
294      title: 'SchedulingAnalysis',
295      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
296      describeContent: 'Scheduling analysis templates',
297    },
298    {
299      title: 'BinderRunnable',
300      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
301      describeContent: 'support Cpu State Binder-Runnable',
302    },
303    {
304      title: 'FfrtConvert',
305      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
306      describeContent: 'Ffrt Convert templates',
307    },
308    {
309      title: 'HMKernel',
310      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
311      describeContent: '',
312    },
313    {
314      title: 'VSync',
315      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
316      describeContent: 'VSync Signal drawing',
317      addInfo: { vsyncValue: VSYNC_VAL.VsyncGeneratior },
318    },
319    {
320      title: 'Hangs',
321      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
322      describeContent: '',
323    },
324    {
325      title: 'LTPO',
326      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
327      describeContent: 'Lost Frame and HitchTime templates',
328    },
329    {
330      title: 'Start&Finish Trace Category',
331      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
332      describeContent: 'Asynchronous trace aggregation',
333      addInfo: { catValue: CAT_SORT['Business first'] },
334    },
335    {
336      title: 'UserPluginsRow',
337      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
338      describeContent: 'User Upload Plugin To Draw',
339    },
340    {
341      title: 'CPU by Irq',
342      switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }],
343      describeContent: 'The real CPU after being split by irq and softirq',
344    },
345    {
346      title: 'RawTraceCutStartTs',
347      switchOptions: [{ option: 'Enabled', selected: true }, { option: 'Disabled' }],
348      describeContent: 'Raw Trace Cut By StartTs, StartTs = Max(Cpu1 StartTs, Cpu2 StartTs, ..., CpuN StartTs)',
349    },
350  ];
351
352  static getAllFlagConfig(): Array<FlagConfigItem> {
353    let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
354    if (flagsConfigStr === null) {
355      let flagConfigObj: Params = {};
356      FlagsConfig.DEFAULT_CONFIG.forEach((config) => {
357        let selectedOption = config.switchOptions.filter((option) => {
358          return option.selected;
359        });
360        let value = config.switchOptions[0].option;
361        if (selectedOption[0] !== undefined) {
362          value = selectedOption[0].option;
363        }
364        flagConfigObj[config.title] = value;
365        if (config.addInfo) {
366          for (const [key, value] of Object.entries(config.addInfo)) {
367            flagConfigObj[key] = value;
368          }
369        }
370      });
371      window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj));
372      return FlagsConfig.DEFAULT_CONFIG;
373    } else {
374      let flagsConfig = JSON.parse(flagsConfigStr);
375      FlagsConfig.DEFAULT_CONFIG.forEach((config) => {
376        let cfg = flagsConfig[config.title];
377        if (cfg) {
378          config.switchOptions.forEach((option) => {
379            if (option.option === cfg) {
380              option.selected = true;
381            } else {
382              option.selected = false;
383            }
384          });
385        }
386        if (config.addInfo) {
387          for (const [key, value] of Object.entries(config.addInfo)) {
388            let cfg = flagsConfig[key];
389            if (cfg) {
390              config.addInfo[key] = cfg;
391            }
392          }
393        }
394      });
395    }
396    return FlagsConfig.DEFAULT_CONFIG;
397  }
398
399  static getSpTraceStreamParseConfig(): string {
400    let parseConfig = {};
401    FlagsConfig.getAllFlagConfig().forEach((configItem) => {
402      let selectedOption = configItem.switchOptions.filter((option) => {
403        return option.selected;
404      });
405      // @ts-ignore
406      parseConfig[configItem.title] = selectedOption[0].option === 'Enabled' ? 1 : 0;
407    });
408    return JSON.stringify({ config: parseConfig });
409  }
410
411  static getFlagsConfig(flagName: string): Params | undefined {
412    let flagConfigObj: Params = {};
413    let configItem = FlagsConfig.getAllFlagConfig().find((config) => {
414      return config.title === flagName;
415    });
416    if (configItem) {
417      let selectedOption = configItem.switchOptions.filter((option) => {
418        return option.selected;
419      });
420      let value = configItem.switchOptions[0].option;
421      if (selectedOption[0] !== undefined) {
422        value = selectedOption[0].option;
423      }
424      flagConfigObj[configItem.title] = value;
425      if (configItem.addInfo) {
426        for (const [key, value] of Object.entries(configItem.addInfo)) {
427          flagConfigObj[key] = value;
428        }
429      }
430      return flagConfigObj;
431    } else {
432      return configItem;
433    }
434  }
435
436  static getFlagsConfigEnableStatus(flagName: string): boolean {
437    let config = FlagsConfig.getFlagsConfig(flagName);
438    let enable: boolean = false;
439    if (config && config[flagName]) {
440      enable = config[flagName] === 'Enabled';
441    }
442    return enable;
443  }
444  //获取Cat的二级下拉选框所选的内容
445  static getSecondarySelectValue(value: string): string {
446    let list = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
447    let listJson = JSON.parse(list!);
448    let catSelectValue = listJson[value];
449    return catSelectValue;
450  }
451
452  static updateFlagsConfig(key: string, value: unknown): void {
453    let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY);
454    let flagConfigObj: Params = {};
455    if (flagsConfigStr !== null) {
456      flagConfigObj = JSON.parse(flagsConfigStr);
457    }
458    flagConfigObj[key] = value;
459    window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj));
460  }
461}
462
463export interface FlagConfigItem {
464  title: string;
465  switchOptions: OptionItem[];
466  describeContent: string;
467  addInfo?: Params;
468}
469
470export interface OptionItem {
471  option: string;
472  selected?: boolean;
473}
474