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 '../../base-ui/popover/LitPopover';
18import '../../base-ui/button/LitButton';
19import { LitMainMenuGroup } from '../../base-ui/menu/LitMainMenuGroup';
20import { LitMainMenuItem } from '../../base-ui/menu/LitMainMenuItem';
21import { SpRecordSetting } from './setting/SpRecordSetting';
22import { LitMainMenu, MenuGroup, MenuItem } from '../../base-ui/menu/LitMainMenu';
23import { SpProbesConfig } from './setting/SpProbesConfig';
24import { SpTraceCommand } from './setting/SpTraceCommand';
25import { HdcStream } from '../../hdc/hdcclient/HdcStream';
26import { FlagsConfig } from './SpFlags';
27import LitSwitch from '../../base-ui/switch/lit-switch';
28import { LitSlider } from '../../base-ui/slider/LitSlider';
29
30import { CreateSessionRequest } from './setting/bean/ProfilerServiceTypes';
31import { PluginConvertUtils } from './setting/utils/PluginConvertUtils';
32import { SpAllocations } from './setting/SpAllocations';
33import { SpRecordPerf } from './setting/SpRecordPerf';
34import { HdcDeviceManager } from '../../hdc/HdcDeviceManager';
35import { LitButton } from '../../base-ui/button/LitButton';
36import { SpApplication } from '../SpApplication';
37import { LitSearch } from './trace/search/Search';
38import { LitProgressBar } from '../../base-ui/progress-bar/LitProgressBar';
39import { log } from '../../log/Log';
40import { CmdConstant } from '../../command/CmdConstant';
41import { Cmd } from '../../command/Cmd';
42import { SpFileSystem } from './setting/SpFileSystem';
43import { SpSdkConfig } from './setting/SpSdkConfig';
44import { SpVmTracker } from './setting/SpVmTracker';
45import { SpHisysEvent } from './setting/SpHisysEvent';
46import { SpRecordTemplate } from './setting/SpRecordTemplate';
47import { SpStatisticsHttpUtil } from '../../statistics/util/SpStatisticsHttpUtil';
48import { SpArkTs } from './setting/SpArkTs';
49import { SpWebHdcShell } from './setting/SpWebHdcShell';
50import { SpHilogRecord } from './setting/SpHilogRecord';
51import { SpXPowerRecord } from './setting/SpXPowerRecord';
52import { LongTraceDBUtils } from '../database/LongTraceDBUtils';
53import {
54  createFpsPluginConfig,
55  createHTracePluginConfig,
56  createHiPerfConfig,
57  createMemoryPluginConfig,
58  createMonitorPlugin,
59  createNativePluginConfig,
60  createSessionRequest,
61  createSystemConfig,
62  createSdkConfig,
63  createHiSystemEventPluginConfig,
64  createArkTsConfig,
65  createHiLogConfig, createFFRTPluginConfig,
66  createXPowerConfig,
67} from './SpRecordConfigModel';
68import { SpRecordTraceHtml } from './SpRecordTrace.html';
69import { SpFFRTConfig } from './setting/SpFFRTConfig';
70import { shadowRootInput } from '../../trace/component/trace/base/shadowRootInput';
71
72const DEVICE_NOT_CONNECT =
73  '<div>1.请确认抓取设备上是否已勾选并确认总是允许smartPerf-Host调试的弹窗</div>' +
74  '<div>2.请关闭DevEco Studio,DevEco Testing等会占用hdc端口的应用</div>' +
75  '<div>3.请使用系统管理员权限打开cmd窗口,并执行hdc kill,确保PC端任务管理器中没有hdc进程</div>' +
76  '<div>4.若没有效果,请重新插拔一下手机。紧急情况可拷贝trace命令,在cmd窗口离线抓取</div>';
77
78@element('sp-record-trace')
79export class SpRecordTrace extends BaseElement {
80  public static serialNumber: string = '';
81  public static selectVersion: string | null;
82  public static isVscode = false;
83  public static cancelRecord = false;
84  static supportVersions = ['3.2', '4.0+', '5.0+'];
85  public deviceSelect: HTMLSelectElement | undefined;
86  public deviceVersion: HTMLSelectElement | undefined;
87  private _menuItems: Array<MenuItem> | undefined;
88  private recordButtonText: HTMLSpanElement | undefined;
89  private devicePrompt: HTMLSpanElement | undefined;
90  private recordButton: LitButton | undefined;
91  private cancelButton: LitButton | undefined;
92  private sp: SpApplication | undefined;
93  private progressEL: LitProgressBar | undefined;
94  private litSearch: LitSearch | undefined;
95  private addButton: LitButton | undefined | null;
96  private disconnectButton: LitButton | undefined | null;
97  private recordSetting: SpRecordSetting | undefined;
98  private probesConfig: SpProbesConfig | undefined;
99  private traceCommand: SpTraceCommand | undefined;
100  private spAllocations: SpAllocations | undefined;
101  private spRecordPerf: SpRecordPerf | undefined;
102  private spFileSystem: SpFileSystem | undefined;
103  private spSdkConfig: SpSdkConfig | undefined;
104  private spVmTracker: SpVmTracker | undefined;
105  private spHiSysEvent: SpHisysEvent | undefined;
106  private spRecordTemplate: SpRecordTemplate | undefined;
107  private spArkTs: SpArkTs | undefined;
108  private spHiLog: SpHilogRecord | undefined;
109  private spXPower: SpXPowerRecord | undefined;
110  private spFFRTConfig: SpFFRTConfig | undefined;
111  private ftraceSlider: LitSlider | undefined | null;
112  private spWebShell: SpWebHdcShell | undefined;
113  private menuGroup: LitMainMenuGroup | undefined | null;
114  private appContent: HTMLElement | undefined | null;
115  private record = 'Record';
116  private stop = 'StopRecord';
117  private nowChildItem: HTMLElement | undefined;
118  private longTraceList: Array<string> = [];
119  private refreshDeviceTimer: number | undefined;
120  private hintEl: HTMLSpanElement | undefined;
121  private selectedTemplate: Map<string, number> = new Map();
122  private hintTimeOut: number = -1;
123  private MenuItemArkts: MenuItem | undefined | null;
124  private MenuItemArktsHtml: LitMainMenuItem | undefined | null;
125  private MenuItemEbpf: MenuItem | undefined | null;
126  private MenuItemEbpfHtml: LitMainMenuItem | undefined | null;
127  private hdcList: Array<unknown> = [];
128
129  set record_template(re: boolean) {
130    if (re) {
131      this.setAttribute('record_template', '');
132    } else {
133      this.removeAttribute('record_template');
134    }
135    if (this.recordSetting) {
136      this.recordSetting.isRecordTemplate = re;
137    }
138  }
139
140  get record_template(): boolean {
141    return this.hasAttribute('record_template');
142  }
143
144  set vs(vs: boolean) {
145    if (vs) {
146      SpRecordTrace.isVscode = true;
147      this.setAttribute('vs', '');
148    } else {
149      SpRecordTrace.isVscode = false;
150      this.removeAttribute('vs');
151    }
152  }
153
154  get vs(): boolean {
155    return this.hasAttribute('vs');
156  }
157
158  private compareArray(devs: Array<string>): boolean {
159    let clearFlag: boolean = false;
160    if (devs.length !== this.deviceSelect!.options.length) {
161      clearFlag = true;
162    } else {
163      let optionArray: string[] = [];
164      for (let index = 0; index < this.deviceSelect!.options.length; index++) {
165        optionArray.push(this.deviceSelect!.options[index].value);
166      }
167      devs.forEach((value): void => {
168        if (optionArray.indexOf(value) === -1) {
169          clearFlag = true;
170        }
171      });
172    }
173    return clearFlag;
174  }
175
176  private async refreshDeviceList(sn?: unknown): Promise<void> {
177    if (this.vs) {
178      this.refreshDeviceListByVs();
179    } else {
180      this.deviceSelect!.innerHTML = '';
181      // @ts-ignore
182      HdcDeviceManager.getDevices().then(async (devs: USBDevice[]) => {
183        if (devs.length === 0) {
184          this.recordButton!.hidden = true;
185          this.disconnectButton!.hidden = true;
186          this.devicePrompt!.innerText = 'Device not connected';
187          this.hintEl!.innerHTML = DEVICE_NOT_CONNECT;
188          if (!this.showHint) {
189            this.showHint = true;
190          }
191        }
192        this.hdcList = devs;
193        let optionNum = 0;
194        for (let len = 0; len < devs.length; len++) {
195          let dev = devs[len];
196          let option = document.createElement('option');
197          option.className = 'select';
198          if (typeof dev.serialNumber === 'string') {
199              optionNum++;
200              option.value = dev.serialNumber;
201              option.textContent = dev!.serialNumber ? dev!.serialNumber!.toString() : 'hdc Device';
202              this.deviceSelect!.appendChild(option);
203            if (dev.serialNumber === sn) {
204              option.selected = true;
205              this.recordButton!.hidden = false;
206              this.disconnectButton!.hidden = false;
207              this.showHint = false;
208              this.devicePrompt!.innerText = '';
209              this.hintEl!.textContent = '';
210              SpRecordTrace.serialNumber = option.value;
211              this.refreshDeviceVersion(option);
212            }
213          }
214        }
215        if (!optionNum) {
216          this.deviceSelect!.style!.border = '2px solid red';
217          setTimeout(() => {
218            this.deviceSelect!.style!.border = '1px solid #4D4D4D';
219          }, 3000);
220          this.recordButton!.hidden = true;
221          this.disconnectButton!.hidden = true;
222          this.devicePrompt!.innerText = 'Device not connected';
223          this.hintEl!.innerHTML = DEVICE_NOT_CONNECT;
224          if (!this.showHint) {
225            this.showHint = true;
226          }
227        }
228      });
229    }
230  }
231  private refreshDeviceVersion(option: HTMLOptionElement): void {
232    HdcDeviceManager.connect(option.value).then(async (result) => {
233      if (result) {
234        if (this.MenuItemArkts && this.MenuItemArktsHtml) {//连接成功后,arkts开关置灰不能点击
235          this.MenuItemArktsHtml.style.color = 'gray';
236          this.MenuItemArktsHtml.disabled = true;
237          if (this.MenuItemArkts.clickHandler) {
238            this.MenuItemArkts.clickHandler = undefined;
239          }
240        }
241        try {
242          let kernelInfo = await HdcDeviceManager.shellResultAsString(CmdConstant.CMD_UNAME, false);
243          if (kernelInfo.includes('HongMeng')) {
244            if (this.MenuItemEbpf && this.MenuItemEbpfHtml) {//如果为鸿蒙内核,ebpf开关置灰不能点击
245              this.MenuItemEbpfHtml.style.color = 'gray';
246              this.MenuItemEbpfHtml.disabled = true;
247              if (this.MenuItemEbpf.clickHandler) {
248                this.MenuItemEbpf.clickHandler = undefined;
249              }
250            }
251          }
252        } catch (error) {
253          console.error('Failed to get kernel info:', error);
254        }
255        HdcDeviceManager.shellResultAsString(CmdConstant.CMD_GET_VERSION, false).then((version) => {
256          SpRecordTrace.selectVersion = this.getDeviceVersion(version);
257          this.setDeviceVersionSelect(SpRecordTrace.selectVersion);
258          this.nativeMemoryHideBySelectVersion();
259          this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
260            PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
261            this.recordSetting!.output,
262            this.recordSetting!.maxDur
263          );
264          if (this.nowChildItem === this.spWebShell) {
265            window.publish(window.SmartEvent.UI.DeviceConnect, option.value);
266          }
267        });
268      } else {
269        SpRecordTrace.selectVersion = SpRecordTrace.supportVersions[0];
270        this.setDeviceVersionSelect(SpRecordTrace.selectVersion);
271        this.nativeMemoryHideBySelectVersion();
272        let cmdTxt = PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false);
273        this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
274          cmdTxt,
275          this.recordSetting!.output,
276          this.recordSetting!.maxDur
277        );
278      }
279    });
280  }
281  private refreshDeviceListByVs(): void {
282    Cmd.execHdcCmd(CmdConstant.CMD_HDC_DEVICES, (res: string) => {
283      let devs: string[] = res.trim().replace(/\r\n/g, '\r').replace(/\n/g, '\r').split(/\r/);
284      if (devs.length === 1 && devs[0].indexOf('Empty') !== -1) {
285        this.deviceSelect!.innerHTML = '';
286        return;
287      }
288      let clearFlag = this.compareArray(devs);
289      if (clearFlag) {
290        this.deviceSelect!.innerHTML = '';
291        if (devs.length === 0) {
292          this.recordButton!.hidden = true;
293          this.disconnectButton!.hidden = true;
294          this.devicePrompt!.innerText = 'Device not connected';
295        }
296        for (let i = 0; i < devs.length; i++) {
297          let dev = devs[i];
298          let option = document.createElement('option');
299          option.className = 'select';
300          option.textContent = dev;
301          this.deviceSelect!.appendChild(option);
302          if (i === 0) {
303            option.selected = true;
304            this.recordButton!.hidden = false;
305            this.disconnectButton!.hidden = false;
306            SpRecordTrace.serialNumber = option.value;
307            this.devicePrompt!.innerText = '';
308          }
309        }
310      }
311    });
312  }
313
314  private getDeviceVersion(version: string): string {
315    if (version.indexOf('3.2') !== -1) {
316      return '3.2';
317    } else if (version.indexOf('4.') !== -1) {
318      return '4.0+';
319    } else if (version.indexOf('5.') !== -1) {
320      return '5.0+';
321    }
322    return '3.2';
323  }
324
325  private freshMenuDisable(disable: boolean): void {
326    let mainMenu = this.sp!.shadowRoot?.querySelector('#main-menu') as LitMainMenu;
327    mainMenu.menus?.forEach((men): void => {
328      // @ts-ignore
329      men.children.forEach((child: HTMLElement): void => {
330        // @ts-ignore
331        child.disabled = disable;
332      });
333    });
334    mainMenu.menus = mainMenu.menus;
335  }
336
337  refreshConfig(isTraceConfig: boolean): void {
338    let recordSettingEl = this.shadowRoot?.querySelector('record-setting') as SpRecordSetting;
339    if (recordSettingEl) {
340      if (isTraceConfig) {
341        recordSettingEl.setAttribute('trace_config', '');
342      } else {
343        if (recordSettingEl.hasAttribute('trace_config')) {
344          recordSettingEl.removeAttribute('trace_config');
345        }
346      }
347    }
348  }
349
350  refreshHint(): void {
351    let flags = FlagsConfig.getAllFlagConfig();
352    let showHint = false;
353    for (let i = 0; i < flags.length; i++) {
354      let flag = flags[i];
355      if (this.selectedTemplate.has(flag.title)) {
356        let selectedOption = flag.switchOptions.filter((option) => {
357          return option.selected;
358        });
359        if (selectedOption[0].option === 'Disabled') {
360          showHint = true;
361          break;
362        }
363      }
364    }
365    this.showHint = showHint;
366  }
367
368  get showHint(): boolean {
369    return this.hasAttribute('show_hint');
370  }
371
372  set showHint(bool: boolean) {
373    if (bool) {
374      if (this.hasAttribute('show_hint')) {
375        this.removeAttribute('show_hint');
376        this.hintTimeOut = window.setTimeout(() => {
377          this.setAttribute('show_hint', '');
378        }, timeOut);
379      } else {
380        this.setAttribute('show_hint', '');
381      }
382    } else {
383      if (this.hintTimeOut !== -1) {
384        window.clearTimeout(this.hintTimeOut);
385        this.hintTimeOut = -1;
386      }
387      this.removeAttribute('show_hint');
388    }
389  }
390
391  initElements(): void {
392    let parentElement = this.parentNode as HTMLElement;
393    if (parentElement) {
394      parentElement.style.overflow = 'hidden';
395    }
396    this.sp = document.querySelector('sp-application') as SpApplication;
397    if (!this.shadowRoot || !this.sp) {
398      return;
399    }
400    this.initConfigPage();
401    this.hintEl = this.shadowRoot.querySelector('#hint') as HTMLSpanElement;
402    this.deviceSelect = this.shadowRoot.querySelector('#device-select') as HTMLSelectElement;
403    this.deviceVersion = this.shadowRoot.querySelector('#device-version') as HTMLSelectElement;
404    this.devicePrompt = this.shadowRoot.querySelector('.prompt') as HTMLSpanElement;
405    this.disconnectButton = this.shadowRoot.querySelector<LitButton>('.disconnect');
406    this.recordButton = this.shadowRoot.querySelector('.record') as LitButton;
407    this.recordButtonText = this.shadowRoot.querySelector('.record_text') as HTMLSpanElement;
408    this.cancelButton = this.shadowRoot.querySelector('.cancel') as LitButton;
409    this.progressEL = this.sp.shadowRoot?.querySelector('.progress') as LitProgressBar;
410    this.litSearch = this.sp.shadowRoot?.querySelector('#lit-record-search') as LitSearch;
411    this.menuGroup = this.shadowRoot.querySelector('#menu-group') as LitMainMenuGroup;
412    this.addButton = this.shadowRoot.querySelector<LitButton>('.add');
413    if (this.record_template) {
414      this.buildTemplateTraceItem();
415    } else {
416      this.buildNormalTraceItem();
417    }
418    this.initMenuItems();
419    this.appendDeviceVersion();
420    if (this.deviceSelect.options && this.deviceSelect.options.length > 0) {
421      this.disconnectButton!.hidden = false;
422      this.recordButton.hidden = false;
423      this.devicePrompt.innerText = '';
424    } else {
425      this.disconnectButton!.hidden = true;
426      this.recordButton.hidden = true;
427      this.devicePrompt.innerText = 'Device not connected';
428    }
429  }
430
431  connectedCallback(): void {
432    super.connectedCallback();
433    this.addButton!.addEventListener('click', this.addButtonClickEvent);
434    this.deviceSelect!.addEventListener('mousedown', this.deviceSelectMouseDownEvent);
435    this.deviceSelect!.addEventListener('change', this.deviceSelectChangeEvent);
436    this.deviceVersion!.addEventListener('change', this.deviceVersionChangeEvent);
437    this.disconnectButton?.addEventListener('click', this.disconnectButtonClickEvent);
438    this.recordButton?.addEventListener('mousedown', this.recordButtonMouseDownEvent);
439    this.cancelButton?.addEventListener('click', this.cancelRecordListener);
440    this.spRecordPerf?.addEventListener('addProbe', this.recordAddProbeEvent);
441    this.spAllocations?.addEventListener('addProbe', this.recordAddProbeEvent);
442    this.probesConfig?.addEventListener('addProbe', this.recordAddProbeEvent);
443    this.spRecordTemplate?.addEventListener('addProbe', this.recordTempAddProbe);
444    this.spRecordTemplate?.addEventListener('delProbe', this.recordTempDelProbe);
445  }
446
447  disconnectedCallback(): void {
448    super.disconnectedCallback();
449    this.addButton!.removeEventListener('click', this.addButtonClickEvent);
450    this.deviceSelect!.removeEventListener('mousedown', this.deviceSelectMouseDownEvent);
451    this.deviceSelect!.removeEventListener('change', this.deviceSelectChangeEvent);
452    this.deviceVersion!.removeEventListener('change', this.deviceVersionChangeEvent);
453    this.disconnectButton?.removeEventListener('click', this.disconnectButtonClickEvent);
454    this.recordButton?.removeEventListener('mousedown', this.recordButtonMouseDownEvent);
455    this.cancelButton?.removeEventListener('click', this.cancelRecordListener);
456    this.spRecordPerf?.removeEventListener('addProbe', this.recordAddProbeEvent);
457    this.spAllocations?.removeEventListener('addProbe', this.recordAddProbeEvent);
458    this.probesConfig?.removeEventListener('addProbe', this.recordAddProbeEvent);
459    this.spRecordTemplate?.removeEventListener('addProbe', this.recordTempAddProbe);
460    this.spRecordTemplate?.removeEventListener('delProbe', this.recordTempDelProbe);
461  }
462
463  recordTempAddProbe = (ev: CustomEventInit<{ elementId: string }>): void => {
464    if (
465      FlagsConfig.DEFAULT_CONFIG.find((flagItem) => {
466        return flagItem.title === ev.detail!.elementId;
467      })
468    ) {
469      this.selectedTemplate.set(ev.detail!.elementId, 1);
470      let flagConfig = FlagsConfig.getFlagsConfig(ev.detail!.elementId);
471      if (flagConfig![ev.detail!.elementId] !== 'Enabled') {
472        this.hintEl!.textContent = 'Please open the corresponding Flags tag when parsing';
473        if (!this.showHint) {
474          this.showHint = true;
475        }
476      }
477    }
478  };
479
480  recordTempDelProbe = (ev: CustomEventInit<{ elementId: string }>): void => {
481    if (
482      FlagsConfig.DEFAULT_CONFIG.find((flagItem): boolean => {
483        return flagItem.title === ev.detail!.elementId;
484      })
485    ) {
486      this.selectedTemplate.delete(ev.detail!.elementId);
487      if (this.selectedTemplate.size === 0) {
488        this.showHint = false;
489      }
490    }
491  };
492
493  recordAddProbeEvent = (): void => {
494    this.showHint = false;
495  };
496
497  addButtonClickEvent = (event: MouseEvent): void => {
498    if (this.vs) {
499      this.refreshDeviceList();
500    } else {
501      // @ts-ignore
502      HdcDeviceManager.findDevice().then((usbDevices): void => {
503        log(usbDevices);
504        this.refreshDeviceList(usbDevices.serialNumber);
505      });
506    }
507  };
508
509  deviceSelectMouseDownEvent = (evt: MouseEvent): void => {
510    if (this.deviceSelect!.options.length === 0) {
511      evt.preventDefault();
512    }
513  };
514
515  deviceSelectChangeEvent = (): void => {
516    if (this.deviceSelect!.options.length > 0) {
517      this.recordButton!.hidden = false;
518      this.disconnectButton!.hidden = false;
519      this.devicePrompt!.innerText = '';
520    } else {
521      this.recordButton!.hidden = true;
522      this.disconnectButton!.hidden = true;
523      this.devicePrompt!.innerText = 'Device not connected';
524    }
525    let deviceItem = this.deviceSelect!.options[this.deviceSelect!.selectedIndex];
526    let value = deviceItem.value;
527    SpRecordTrace.serialNumber = value;
528    if (this.vs) {
529      let cmd = Cmd.formatString(CmdConstant.CMD_GET_VERSION_DEVICES, [SpRecordTrace.serialNumber]);
530      Cmd.execHdcCmd(cmd, (deviceVersion: string) => {
531        this.selectedDevice(deviceVersion);
532        this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
533          PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
534          this.recordSetting!.output,
535          this.recordSetting!.maxDur
536        );
537      });
538    } else {
539      HdcDeviceManager.connect(value).then((result): void => {
540        if (result) {
541          HdcDeviceManager.shellResultAsString(CmdConstant.CMD_GET_VERSION, true).then((deviceVersion) => {
542            this.selectedDevice(deviceVersion);
543            this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
544              PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
545              this.recordSetting!.output,
546              this.recordSetting!.maxDur
547            );
548            if (this.nowChildItem === this.spWebShell) {
549              window.publish(window.SmartEvent.UI.DeviceConnect, value);
550            }
551          });
552        } else {
553          SpRecordTrace.selectVersion = SpRecordTrace.supportVersions[0];
554          this.setDeviceVersionSelect(SpRecordTrace.selectVersion);
555          this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
556            PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
557            this.recordSetting!.output,
558            this.recordSetting!.maxDur
559          );
560        }
561      });
562    }
563  };
564
565  deviceVersionChangeEvent = (): void => {
566    let versionItem = this.deviceVersion!.options[this.deviceVersion!.selectedIndex];
567    SpRecordTrace.selectVersion = versionItem.getAttribute('device-version');
568    this.spAllocations!.startup_mode = false;
569    this.spAllocations!.recordJsStack = false;
570    this.nativeMemoryHideBySelectVersion();
571    this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
572      PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
573      this.recordSetting!.output,
574      this.recordSetting!.maxDur
575    );
576  };
577
578  disconnectButtonClickEvent = (): void => {
579    // --------------我修改的
580    let index = this.deviceSelect!.selectedIndex;
581    if (index !== -1 && this.deviceSelect!.options.length > 0) {
582      for (let i = 0; i < this.deviceSelect!.options.length; i++) {
583        let selectOption = this.deviceSelect!.options[i];
584        let value = selectOption.value;
585        HdcDeviceManager.disConnect(value).then((): void => {
586          this.deviceSelect!.removeChild(selectOption);
587          if (this.nowChildItem === this.spWebShell) {
588            window.publish(window.SmartEvent.UI.DeviceDisConnect, value);
589          }
590          let options = this.deviceSelect!.options;
591          if (options.length <= 0) {
592            this.recordButton!.hidden = true;
593            this.disconnectButton!.hidden = true;
594            this.devicePrompt!.innerText = 'Device not connected';
595            this.sp!.search = false;
596            SpRecordTrace.serialNumber = '';
597          }
598        });
599      }
600    }
601  };
602
603  recordButtonMouseDownEvent = (event: MouseEvent): void => {
604    if (event.button === 0) {
605      if (this.recordButtonText!.textContent === this.record) {
606        this.recordButtonListener();
607      } else {
608        this.stopRecordListener();
609      }
610    }
611  };
612
613  private initConfigPage(): void {
614    this.recordSetting = new SpRecordSetting();
615    this.probesConfig = new SpProbesConfig();
616    this.traceCommand = new SpTraceCommand();
617    this.spAllocations = new SpAllocations();
618    this.spRecordPerf = new SpRecordPerf();
619    this.spFileSystem = new SpFileSystem();
620    this.spSdkConfig = new SpSdkConfig();
621    this.spVmTracker = new SpVmTracker();
622    this.spHiSysEvent = new SpHisysEvent();
623    this.spArkTs = new SpArkTs();
624    this.spHiLog = new SpHilogRecord();
625    this.spXPower = new SpXPowerRecord();
626    this.spFFRTConfig = new SpFFRTConfig();
627    this.spWebShell = new SpWebHdcShell();
628    this.spRecordTemplate = new SpRecordTemplate(this);
629    this.appContent = this.shadowRoot?.querySelector('#app-content') as HTMLElement;
630    if (this.record_template) {
631      this.appContent.append(this.spRecordTemplate);
632    } else {
633      this.appContent.append(this.recordSetting);
634    }
635    // @ts-ignore
636    if (navigator.usb) {
637      // @ts-ignore
638      navigator.usb.addEventListener(
639        'disconnect',
640        // @ts-ignore
641        (ev: USBConnectionEvent) => {
642          this.usbDisConnectionListener(ev);
643        }
644      );
645    }
646  }
647
648  private nativeMemoryHideBySelectVersion(): void {
649    let divConfigs = this.spAllocations?.shadowRoot?.querySelectorAll<HTMLDivElement>('.version-controller');
650    if (divConfigs) {
651      if (SpRecordTrace.selectVersion !== '3.2') {
652        for (let divConfig of divConfigs) {
653          divConfig!.style.zIndex = '1';
654        }
655      } else {
656        for (let divConfig of divConfigs) {
657          divConfig!.style.zIndex = '-1';
658        }
659      }
660    }
661  }
662
663  private selectedDevice(deviceVersion: string): void {
664    SpRecordTrace.selectVersion = this.getDeviceVersion(deviceVersion);
665    this.setDeviceVersionSelect(SpRecordTrace.selectVersion);
666  }
667
668  private appendDeviceVersion(): void {
669    SpRecordTrace.supportVersions.forEach((supportVersion) => {
670      let option = document.createElement('option');
671      option.className = 'select';
672      option.selected = supportVersion === '4.0+';
673      option.textContent = `OpenHarmony-${supportVersion}`;
674      option.setAttribute('device-version', supportVersion);
675      this.deviceVersion!.append(option);
676      SpRecordTrace.selectVersion = '4.0+';
677      this.nativeMemoryHideBySelectVersion();
678    });
679  }
680
681  private setDeviceVersionSelect(selected: string): void {
682    let children = this.deviceVersion!.children;
683    for (let i = 0; i < children.length; i++) {
684      let child = children[i] as HTMLOptionElement;
685      if (child.getAttribute('device-version') === selected) {
686        child.selected = true;
687        break;
688      }
689    }
690  }
691
692  stopRecordListener(): void {
693    this.recordButtonText!.textContent = this.record;
694    this.recordButtonDisable(true);
695    this.cancelButtonShow(false);
696    if (this.vs) {
697      let cmd = Cmd.formatString(CmdConstant.CMS_HDC_STOP, [SpRecordTrace.serialNumber]);
698      Cmd.execHdcCmd(cmd, (): void => { });
699    } else {
700      let selectedOption = this.deviceSelect!.options[this.deviceSelect!.selectedIndex] as HTMLOptionElement;
701      HdcDeviceManager.connect(selectedOption.value).then((result) => {
702        if (result) {
703          try {
704            HdcDeviceManager.shellResultAsString(CmdConstant.CMS_STOP, true).then((): void => { });
705          } catch (exception) {
706            this.recordButtonDisable(false);
707            log(exception);
708          }
709        }
710      });
711    }
712  }
713
714  cancelRecordListener = (): void => {
715    this.recordButtonText!.textContent = this.record;
716    this.cancelButtonShow(false);
717    if (this.vs) {
718      let cmd = Cmd.formatString(CmdConstant.CMS_HDC_CANCEL, [SpRecordTrace.serialNumber]);
719      Cmd.execHdcCmd(cmd, () => {
720        this.freshMenuDisable(false);
721        this.freshConfigMenuDisable(false);
722        this.progressEL!.loading = false;
723        this.sp!.search = false;
724        this.litSearch!.clear();
725        this.addButton!.style.pointerEvents = 'auto';
726        this.deviceSelect!.style.pointerEvents = 'auto';
727        this.disconnectButton!.style.pointerEvents = 'auto';
728        this.deviceVersion!.style.pointerEvents = 'auto';
729      });
730    } else {
731      let selectedOption = this.deviceSelect!.options[this.deviceSelect!.selectedIndex] as HTMLOptionElement;
732      HdcDeviceManager.connect(selectedOption.value).then((result) => {
733        if (result) {
734          this.freshMenuDisable(false);
735          this.freshConfigMenuDisable(false);
736          try {
737            this.progressEL!.loading = false;
738            this.sp!.search = false;
739            this.litSearch!.clear();
740            this.disconnectButton!.style.pointerEvents = 'auto';
741            this.addButton!.style.pointerEvents = 'auto';
742            this.deviceSelect!.style.pointerEvents = 'auto';
743            this.deviceVersion!.style.pointerEvents = 'auto';
744            SpRecordTrace.cancelRecord = true;
745            HdcDeviceManager.stopHiprofiler(CmdConstant.CMS_CANCEL).then((): void => { });
746          } catch (exception) {
747            log(exception);
748          }
749        }
750      });
751    }
752  };
753
754  private cancelButtonShow(show: boolean): void {
755    if (show) {
756      this.cancelButton!.style.visibility = 'visible';
757    } else {
758      this.cancelButton!.style.visibility = 'hidden';
759    }
760  }
761
762  private traceCommandClickHandler(recordTrace: SpRecordTrace): void {
763    recordTrace.appContent!.innerHTML = '';
764    recordTrace.appContent!.append(recordTrace.traceCommand!);
765    recordTrace.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
766      PluginConvertUtils.BeanToCmdTxt(recordTrace.makeRequest(), false),
767      recordTrace.recordSetting!.output,
768      recordTrace.recordSetting!.maxDur
769    );
770    recordTrace.freshMenuItemsStatus('Trace command');
771  }
772
773  private initMenuItems(): void {
774    this._menuItems?.forEach((item): void => {
775      let th = new LitMainMenuItem();
776      th.setAttribute('icon', item.icon || '');
777      th.setAttribute('title', item.title || '');
778      th.style.height = '60px';
779      th.style.fontFamily = 'Helvetica-Bold';
780      th.style.fontSize = '16px';
781      th.style.lineHeight = '28px';
782      th.style.fontWeight = '700';
783      th.removeAttribute('file');
784      th.addEventListener('click', (): void => {
785        if (item.clickHandler) {
786          item.clickHandler(item);
787        }
788      });
789      if (item.title === 'Ark Ts') {
790        this.MenuItemArkts = item;
791        this.MenuItemArktsHtml = th;
792      } else if (item.title === 'eBPF Config') {
793        this.MenuItemEbpf = item;
794        this.MenuItemEbpfHtml = th;
795      }
796      this.menuGroup!.appendChild(th);
797      if (item.title === 'Ark Ts') { 
798        this.menuGroup!.removeChild(th);
799      }
800    });
801  }
802
803  private recordCommandClickHandler(recordTrace: SpRecordTrace): void {
804    let request = recordTrace.makeRequest();
805    recordTrace.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
806      PluginConvertUtils.BeanToCmdTxt(request, false),
807      recordTrace.recordSetting!.output,
808      recordTrace.recordSetting!.maxDur
809    );
810  }
811
812  private hdcShellClickHandler(recordTrace: SpRecordTrace): void {
813    recordTrace.spWebShell!.shellDiv!.scrollTop = recordTrace.spWebShell!.currentScreenRemain;
814    setTimeout(() => {
815      recordTrace.spWebShell!.hdcShellFocus();
816    }, 100);
817    recordTrace.nowChildItem = recordTrace.spWebShell!;
818  }
819
820  private nativeMemoryClickHandler(recordTrace: SpRecordTrace): void {
821    let startNativeSwitch = recordTrace.spAllocations?.shadowRoot?.getElementById('switch-disabled') as LitSwitch;
822    let recordModeSwitch = recordTrace.probesConfig?.shadowRoot?.querySelector('lit-switch') as LitSwitch;
823    let checkDesBoxDis = recordTrace.probesConfig?.shadowRoot?.querySelectorAll('check-des-box');
824    let litCheckBoxDis = recordTrace.probesConfig?.shadowRoot?.querySelectorAll('lit-check-box');
825    recordTrace.ftraceSlider =
826      recordTrace.probesConfig?.shadowRoot?.querySelector<LitSlider>('#ftrace-buff-size-slider');
827    startNativeSwitch.addEventListener('change', (event: unknown): void => {
828      //@ts-ignore
829      let detail = event.detail;
830      if (detail!.checked) {
831        recordModeSwitch.removeAttribute('checked');
832        checkDesBoxDis?.forEach((item: unknown): void => {
833          //@ts-ignore
834          item.setAttribute('disabled', '');
835          //@ts-ignore
836          item.checked = false;
837        });
838        litCheckBoxDis?.forEach((item: unknown): void => {
839          //@ts-ignore
840          item.setAttribute('disabled', '');
841          //@ts-ignore
842          item.checked = false;
843        });
844        recordTrace.ftraceSlider!.setAttribute('disabled', '');
845      }
846    });
847    let divConfigs = recordTrace.spAllocations?.shadowRoot?.querySelectorAll<HTMLDivElement>('.version-controller');
848    if ((!SpRecordTrace.selectVersion || SpRecordTrace.selectVersion === '3.2') && divConfigs) {
849      for (let divConfig of divConfigs) {
850        divConfig!.style.zIndex = '-1';
851      }
852    }
853  }
854
855  private eBPFConfigClickHandler(recordTrace: SpRecordTrace): void {
856    recordTrace.spFileSystem!.setAttribute('long_trace', '');
857  }
858
859  private buildMenuItem(
860    title: string,
861    icon: string,
862    configPage: BaseElement,
863    clickHandlerFun?: Function,
864    fileChoose: boolean = false
865  ): MenuItem {
866    return {
867      title: title,
868      icon: icon,
869      fileChoose: fileChoose,
870      clickHandler: (): void => {
871        this.appContent!.innerHTML = '';
872        this.appContent!.append(configPage);
873        shadowRootInput.preventBubbling(configPage);
874        this.freshMenuItemsStatus(title);
875        if (clickHandlerFun) {
876          clickHandlerFun(this);
877        }
878      },
879    };
880  }
881
882  private buildTemplateTraceItem(): void {
883    this._menuItems = [
884      this.buildMenuItem('Record setting', 'properties', this.recordSetting!),
885      this.buildMenuItem('Trace template', 'realIntentionBulb', this.spRecordTemplate!),
886      this.buildMenuItem('Trace command', 'dbsetbreakpoint', this.spRecordTemplate!, this.traceCommandClickHandler),
887    ];
888  }
889
890  private buildNormalTraceItem(): void {
891    this._menuItems = [
892      this.buildMenuItem('Record setting', 'properties', this.recordSetting!),
893      this.buildMenuItem('Trace command', 'dbsetbreakpoint', this.traceCommand!, this.recordCommandClickHandler),
894      this.buildMenuItem('Hdc Shell', 'file-config', this.spWebShell!, this.hdcShellClickHandler),
895      this.buildMenuItem('Probes config', 'realIntentionBulb', this.probesConfig!),
896      this.buildMenuItem('Native Memory', 'externaltools', this.spAllocations!, this.nativeMemoryClickHandler),
897      this.buildMenuItem('Hiperf', 'realIntentionBulb', this.spRecordPerf!),
898      this.buildMenuItem('eBPF Config', 'file-config', this.spFileSystem!, this.eBPFConfigClickHandler),
899      this.buildMenuItem('VM Tracker', 'vm-tracker', this.spVmTracker!),
900      this.buildMenuItem('HiSystemEvent', 'externaltools', this.spHiSysEvent!),
901      this.buildMenuItem('Ark Ts', 'file-config', this.spArkTs!),
902      this.buildMenuItem('FFRT', 'file-config', this.spFFRTConfig!),
903      this.buildMenuItem('Hilog', 'realIntentionBulb', this.spHiLog!),
904      this.buildMenuItem('Xpower', 'externaltools', this.spXPower!), 
905    ];
906  }
907
908  // @ts-ignore
909  usbDisConnectionListener(event: USBConnectionEvent): void {
910    // @ts-ignore
911    let disConnectDevice: USBDevice = event.device;
912    for (let index = 0; index < this.deviceSelect!.children.length; index++) {
913      let option = this.deviceSelect!.children[index] as HTMLOptionElement;
914      if (option.value === disConnectDevice.serialNumber) {
915        let optValue = option.value;
916        HdcDeviceManager.disConnect(optValue).then(() => { });
917        this.deviceSelect!.removeChild(option);
918        if (SpRecordTrace.serialNumber === optValue) {
919          if (this.nowChildItem === this.spWebShell) {
920            window.publish(window.SmartEvent.UI.DeviceDisConnect, optValue);
921          }
922          let options = this.deviceSelect!.options;
923          if (options.length > 0) {
924            let selectedOpt = options[this.deviceSelect!.selectedIndex];
925            SpRecordTrace.serialNumber = selectedOpt.value;
926          } else {
927            this.recordButton!.hidden = true;
928            this.disconnectButton!.hidden = true;
929            this.devicePrompt!.innerText = 'Device not connected';
930            SpRecordTrace.serialNumber = '';
931          }
932        }
933      }
934    }
935  }
936
937  private vsCodeRecordCmd(traceCommandStr: string): void {
938    Cmd.execHdcCmd(Cmd.formatString(CmdConstant.CMS_HDC_STOP, [SpRecordTrace.serialNumber]), (stopRes: string) => {
939      let cmd = Cmd.formatString(CmdConstant.CMD_MOUNT_DEVICES, [SpRecordTrace.serialNumber]);
940      Cmd.execHdcCmd(cmd, (res: string) => {
941        this.sp!.search = true;
942        this.progressEL!.loading = true;
943        this.litSearch!.clear();
944        this.litSearch!.setPercent(`tracing  ${this.recordSetting!.maxDur * 1000}ms`, -1);
945        this.initRecordUIState();
946        this.recordButtonText!.textContent = this.stop;
947        this.cancelButtonShow(true);
948        Cmd.execHdcTraceCmd(traceCommandStr, SpRecordTrace.serialNumber, (traceResult: string): void => {
949          if (traceResult.indexOf('DestroySession done') !== -1) {
950            this.litSearch!.setPercent('tracing htrace down', -1);
951            let cmd = Cmd.formatString(CmdConstant.CMD_FIEL_RECV_DEVICES, [
952              SpRecordTrace.serialNumber,
953              this.recordSetting!.output,
954            ]);
955            Cmd.execFileRecv(cmd, this.recordSetting!.output, (rt: ArrayBuffer): void => {
956              this.litSearch!.setPercent('downloading Hitrace file ', 101);
957              let fileName = this.recordSetting!.output.substring(this.recordSetting!.output.lastIndexOf('/') + 1);
958              let file = new File([rt], fileName);
959              let main = this!.parentNode!.parentNode!.querySelector('lit-main-menu') as LitMainMenu;
960              let children = main.menus as Array<MenuGroup>;
961              let child = children[0].children as Array<MenuItem>;
962              let fileHandler = child[0].fileHandler;
963              if (fileHandler && !SpRecordTrace.cancelRecord) {
964                this.recordButtonText!.textContent = this.record;
965                this.cancelButtonShow(false);
966                this.freshMenuDisable(false);
967                this.freshConfigMenuDisable(false);
968                fileHandler({ detail: file });
969              } else {
970                SpRecordTrace.cancelRecord = false;
971              }
972            });
973          } else {
974            this.litSearch!.setPercent('tracing htrace failed, please check your config ', -2);
975            this.recordButtonText!.textContent = this.record;
976            this.freshMenuDisable(false);
977            this.freshConfigMenuDisable(false);
978            this.progressEL!.loading = false;
979          }
980          this.buttonDisable(false);
981        });
982      });
983    });
984  }
985
986  private initRecordCmdStatus(): void {
987    this.appContent!.innerHTML = '';
988    this.appContent!.append(this.traceCommand!);
989    let config = this.makeRequest();
990    this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
991      PluginConvertUtils.BeanToCmdTxt(config, false),
992      this.recordSetting!.output,
993      this.recordSetting!.maxDur
994    );
995    this.freshMenuItemsStatus('Trace command');
996  }
997
998  private webRecordCmd(traceCommandStr: string, selectedOption: HTMLOptionElement): void {
999    HdcDeviceManager.connect(selectedOption.value).then((result) => {
1000      log(`result is ${result}`);
1001      if (result) {
1002        this.initRecordCmdStatus();
1003        try {
1004          HdcDeviceManager.stopHiprofiler(CmdConstant.CMS_CANCEL).then(() => {
1005            HdcDeviceManager.shellResultAsString(CmdConstant.CMD_MOUNT, true).then(() => {
1006              this.sp!.search = true;
1007              this.progressEL!.loading = true;
1008              this.litSearch!.clear();
1009              this.litSearch!.setPercent(`tracing  ${this.recordSetting!.maxDur * 1000}ms`, -1);
1010              this.buttonDisable(true);
1011              this.freshMenuDisable(true);
1012              this.freshConfigMenuDisable(true);
1013              if (SpApplication.isLongTrace) {
1014                HdcDeviceManager.shellResultAsString(
1015                  `${CmdConstant.CMD_CLEAR_LONG_FOLD + this.recordSetting!.longOutPath}*`,
1016                  false
1017                ).then(() => {
1018                  HdcDeviceManager.shellResultAsString(
1019                    CmdConstant.CMD_MKDIR_LONG_FOLD + this.recordSetting!.longOutPath,
1020                    false
1021                  ).then(() => {
1022                    HdcDeviceManager.shellResultAsString(
1023                      CmdConstant.CMD_SET_FOLD_AUTHORITY + this.recordSetting!.longOutPath,
1024                      false
1025                    ).then(() => {
1026                      this.recordLongTraceCmd(traceCommandStr);
1027                    });
1028                  });
1029                });
1030              } else {
1031                this.recordTraceCmd(traceCommandStr);
1032              }
1033            });
1034          });
1035        } catch (e) {
1036          this.freshMenuDisable(false);
1037          this.freshConfigMenuDisable(false);
1038          this.buttonDisable(false);
1039        }
1040      } else {
1041        this.sp!.search = true;
1042        this.litSearch!.clear();
1043        this.litSearch!.setPercent('please kill other hdc-server !', -2);
1044      }
1045    });
1046  }
1047
1048  recordButtonListener(): void {
1049    SpRecordTrace.cancelRecord = false;
1050    let request = this.makeRequest();
1051    this.showHint = true;
1052    if (request.pluginConfigs.length === 0) {
1053      this.hintEl!.textContent = "It looks like you didn't add any probes. Please add at least one";
1054      return;
1055    }
1056    this.showHint = false;
1057    let traceCommandStr = PluginConvertUtils.createHdcCmd(
1058      PluginConvertUtils.BeanToCmdTxt(request, false),
1059      this.recordSetting!.output,
1060      this.recordSetting!.maxDur
1061    );
1062    let pluginList: Array<string> = [];
1063    request.pluginConfigs.forEach((pluginConfig) => {
1064      pluginList.push(pluginConfig.pluginName);
1065    });
1066    SpStatisticsHttpUtil.addOrdinaryVisitAction({
1067      action: 'config_page',
1068      event: 'online_record',
1069      eventData: {
1070        plugin: pluginList,
1071      },
1072    });
1073    let selectedOption = this.deviceSelect!.options[this.deviceSelect!.selectedIndex] as HTMLOptionElement;
1074    if (selectedOption) {
1075      SpRecordTrace.serialNumber = selectedOption.value;
1076    } else {
1077      this.sp!.search = true;
1078      this.litSearch!.clear();
1079      this.progressEL!.loading = false;
1080      this.litSearch!.setPercent('please connect device', -2);
1081    }
1082    if (this.vs) {
1083      this.appContent!.innerHTML = '';
1084      this.appContent!.append(this.traceCommand!);
1085      this.traceCommand!.hdcCommon = PluginConvertUtils.createHdcCmd(
1086        PluginConvertUtils.BeanToCmdTxt(this.makeRequest(), false),
1087        this.recordSetting!.output,
1088        this.recordSetting!.maxDur
1089      );
1090      this.freshMenuItemsStatus('Trace command');
1091      this.vsCodeRecordCmd(traceCommandStr);
1092    } else {
1093      this.webRecordCmd(traceCommandStr, selectedOption);
1094    }
1095  }
1096
1097  private recordTraceCmd(traceCommandStr: string): void {
1098    let executeCmdCallBack = (cmdStateResult: string): void => {
1099      if (cmdStateResult.includes('tracing ')) {
1100        this.litSearch!.setPercent('Start to record...', -1);
1101      }
1102    };
1103    this.litSearch!.setPercent('Waiting to record...', -1);
1104    HdcDeviceManager.shellResultAsString(CmdConstant.CMD_SHELL + traceCommandStr, false, executeCmdCallBack).then((traceResult) => {
1105      let re = this.isSuccess(traceResult);
1106      if (re === 0) {
1107        this.litSearch!.setPercent('Tracing htrace down', -1);
1108        HdcDeviceManager.shellResultAsString(CmdConstant.CMD_TRACE_FILE_SIZE + this.recordSetting!.output, false).then(
1109          (traceFileSize) => {
1110            this.litSearch!.setPercent(`TraceFileSize is ${traceFileSize}`, -1);
1111            if (traceFileSize.indexOf('No such') !== -1) {
1112              this.refreshDisableStyle(false, true, 'No such file or directory', -2);
1113            } else if (Number(traceFileSize) <= MaxFileSize) {
1114              HdcDeviceManager.fileRecv(this.recordSetting!.output, (perNumber: number) => {
1115                this.litSearch!.setPercent('Downloading Hitrace file ', perNumber);
1116              }).then((pullRes) => {
1117                this.litSearch!.setPercent('Downloading Hitrace file ', 101);
1118                pullRes.arrayBuffer().then((buffer) => {
1119                  let fileName = this.recordSetting!.output.substring(this.recordSetting!.output.lastIndexOf('/') + 1);
1120                  let file = new File([buffer], fileName);
1121                  let main = this!.parentNode!.parentNode!.querySelector('lit-main-menu') as LitMainMenu;
1122                  let children = main.menus as Array<MenuGroup>;
1123                  let child = children[0].children as Array<MenuItem>;
1124                  let fileHandler = child[0].fileHandler;
1125                  if (fileHandler && !SpRecordTrace.cancelRecord) {
1126                    this.refreshDisableStyle(false, false);
1127                    fileHandler({
1128                      detail: file,
1129                    });
1130                  } else {
1131                    SpRecordTrace.cancelRecord = false;
1132                  }
1133                });
1134              });
1135            } else {
1136              this.recordButtonText!.textContent = this.record;
1137              this.refreshDisableStyle(false, true, 'Htrace file is too big', -2);
1138            }
1139          }
1140        );
1141      } else if (re === 2) {
1142        this.refreshDisableStyle(false, true, 'Stop tracing htrace ', -1);
1143      } else if (re === -1) {
1144        this.refreshDisableStyle(false, true, 'The device is abnormal', -2);
1145        this.progressEL!.loading = false;
1146      } else {
1147        this.refreshDisableStyle(false, true, 'Tracing htrace failed, please check your config ', -2);
1148      }
1149    });
1150  }
1151
1152  private recordLongTraceCmd(traceCommandStr: string): void {
1153    HdcDeviceManager.shellResultAsString(CmdConstant.CMD_SHELL + traceCommandStr, false).then((traceResult) => {
1154      let re = this.isSuccess(traceResult);
1155      if (re === 0) {
1156        this.litSearch!.setPercent('tracing htrace down', -1);
1157        HdcDeviceManager.shellResultAsString(
1158          CmdConstant.CMD_TRACE_FILE_SIZE + this.recordSetting!.longOutPath,
1159          false
1160        ).then((traceFileSize) => {
1161          this.litSearch!.setPercent(`traceFileSize is ${traceFileSize}`, -1);
1162          if (traceFileSize.indexOf('No such') !== -1) {
1163            this.litSearch!.setPercent('No such file or directory', -2);
1164            this.buttonDisable(false);
1165            this.freshConfigMenuDisable(false);
1166            this.freshMenuDisable(false);
1167          } else {
1168            this.recordLongTrace();
1169          }
1170        });
1171      } else if (re === 2) {
1172        this.refreshDisableStyle(false, true, 'stop tracing htrace ', -1);
1173      } else if (re === -1) {
1174        this.refreshDisableStyle(false, true, 'The device is abnormal', -2);
1175        this.progressEL!.loading = false;
1176      } else {
1177        this.refreshDisableStyle(false, true, 'tracing htrace failed, please check your config ', -2);
1178      }
1179    });
1180  }
1181
1182  private refreshDisableStyle(
1183    disable: boolean,
1184    isFreshSearch: boolean,
1185    percentName?: string,
1186    percentValue?: number
1187  ): void {
1188    if (isFreshSearch) {
1189      this.litSearch!.setPercent(percentName, percentValue!);
1190    }
1191    this.recordButtonDisable(disable);
1192    this.freshConfigMenuDisable(disable);
1193    this.freshMenuDisable(disable);
1194    this.buttonDisable(disable);
1195  }
1196
1197  private getLongTraceTypePage(): Array<number> {
1198    let traceTypePage: Array<number> = [];
1199    for (let fileIndex = 0; fileIndex < this.longTraceList.length; fileIndex++) {
1200      let traceFileName = this.longTraceList[fileIndex];
1201      if (this.sp!.fileTypeList.some((fileType) => traceFileName.toLowerCase().includes(fileType))) {
1202        continue;
1203      }
1204      let firstLastIndexOf = traceFileName.lastIndexOf('.');
1205      let firstText = traceFileName.slice(0, firstLastIndexOf);
1206      let resultLastIndexOf = firstText.lastIndexOf('_');
1207      traceTypePage.push(Number(firstText.slice(resultLastIndexOf + 1, firstText.length)) - 1);
1208    }
1209    traceTypePage.sort((leftNum: number, rightNum: number) => leftNum - rightNum);
1210    return traceTypePage;
1211  }
1212
1213  private loadLongTraceFile(timStamp: number): Promise<unknown> {
1214    return new Promise(async (resolve): Promise<void> => {
1215      let traceTypePage = this.getLongTraceTypePage();
1216      for (let fileIndex = 0; fileIndex < this.longTraceList.length; fileIndex++) {
1217        if (this.longTraceList[fileIndex] !== '') {
1218          let types = this.sp!.fileTypeList.filter((type) =>
1219            this.longTraceList[fileIndex].toLowerCase().includes(type.toLowerCase())
1220          );
1221          let pageNumber = 0;
1222          let fileType = types[0];
1223          if (types.length === 0) {
1224            fileType = 'trace';
1225            let searchNumber =
1226              Number(
1227                this.longTraceList[fileIndex].substring(
1228                  this.longTraceList[fileIndex].lastIndexOf('_') + 1,
1229                  this.longTraceList[fileIndex].lastIndexOf('.')
1230                )
1231              ) - 1;
1232            pageNumber = traceTypePage.lastIndexOf(searchNumber);
1233          }
1234          let pullRes = await HdcDeviceManager.fileRecv(
1235            this.recordSetting!.longOutPath + this.longTraceList[fileIndex],
1236            (perNumber: number) => {
1237              this.litSearch!.setPercent(`downloading ${fileType} file `, perNumber);
1238            }
1239          );
1240          this.litSearch!.setPercent(`downloading ${fileType} file `, 101);
1241          await this.saveIndexDBByLongTrace(pullRes, fileType, pageNumber, timStamp);
1242        }
1243      }
1244      resolve(1);
1245    });
1246  }
1247
1248  private async saveIndexDBByLongTrace(
1249    pullRes: Blob,
1250    fileType: string,
1251    pageNumber: number,
1252    timStamp: number
1253  ): Promise<void> {
1254    let buffer = await pullRes.arrayBuffer();
1255    let chunks = Math.ceil(buffer.byteLength / indexDBMaxSize);
1256    let offset = 0;
1257    let sliceLen = 0;
1258    let message = { fileType: '', startIndex: 0, endIndex: 0, size: 0 };
1259    for (let chunkIndex = 0; chunkIndex < chunks; chunkIndex++) {
1260      let start = chunkIndex * indexDBMaxSize;
1261      let end = Math.min(start + indexDBMaxSize, buffer.byteLength);
1262      let chunk = buffer.slice(start, end);
1263      if (chunkIndex === 0) {
1264        message.fileType = fileType;
1265        message.startIndex = chunkIndex;
1266      }
1267      sliceLen = Math.min(buffer.byteLength - offset, indexDBMaxSize);
1268      if (chunkIndex === 0 && fileType === 'trace') {
1269        this.sp!.longTraceHeadMessageList.push({ pageNum: pageNumber, data: buffer.slice(offset, kbSize) });
1270      }
1271      this.sp!.longTraceDataList.push({
1272        index: chunkIndex,
1273        fileType: fileType,
1274        pageNum: pageNumber,
1275        startOffsetSize: offset,
1276        endOffsetSize: offset + sliceLen,
1277      });
1278      await LongTraceDBUtils.getInstance().indexedDBHelp.add(LongTraceDBUtils.getInstance().tableName, {
1279        buf: chunk,
1280        id: `${fileType}_${timStamp}_${pageNumber}_${chunkIndex}`,
1281        fileType: fileType,
1282        pageNum: pageNumber,
1283        startOffset: offset,
1284        endOffset: offset + sliceLen,
1285        index: chunkIndex,
1286        timStamp: timStamp,
1287      });
1288      offset += sliceLen;
1289      if (offset >= buffer.byteLength) {
1290        message.endIndex = chunkIndex;
1291        message.size = buffer.byteLength;
1292        this.longTraceFileMapHandler(pageNumber, message);
1293      }
1294    }
1295  }
1296
1297  private longTraceFileMapHandler(
1298    pageNumber: number,
1299    message: {
1300      fileType: string;
1301      startIndex: number;
1302      endIndex: number;
1303      size: number;
1304    }
1305  ): void {
1306    if (this.sp!.longTraceTypeMessageMap) {
1307      if (this.sp!.longTraceTypeMessageMap?.has(pageNumber)) {
1308        let oldTypeList = this.sp!.longTraceTypeMessageMap?.get(pageNumber);
1309        oldTypeList?.push(message);
1310        this.sp!.longTraceTypeMessageMap?.set(pageNumber, oldTypeList!);
1311      } else {
1312        this.sp!.longTraceTypeMessageMap?.set(pageNumber, [message]);
1313      }
1314    } else {
1315      this.sp!.longTraceTypeMessageMap = new Map();
1316      this.sp!.longTraceTypeMessageMap.set(pageNumber, [message]);
1317    }
1318  }
1319
1320  private recordLongTrace(): void {
1321    let querySelector = this.sp!.shadowRoot?.querySelector('.long_trace_page') as HTMLDivElement;
1322    if (querySelector) {
1323      querySelector.style.display = 'none';
1324    }
1325    HdcDeviceManager.shellResultAsString(CmdConstant.CMD_GET_LONG_FILES + this.recordSetting!.longOutPath, false).then(
1326      (result) => {
1327        this.longTraceList = result.split('\n').filter((fileName) => Boolean(fileName));
1328        if (this.longTraceList.length > 0) {
1329          this.sp!.longTraceHeadMessageList = [];
1330          this.sp!.longTraceDataList = [];
1331          this.sp!.longTraceTypeMessageMap = undefined;
1332          let timStamp = new Date().getTime();
1333          this.loadLongTraceFile(timStamp).then(() => {
1334            let main = this!.parentNode!.parentNode!.querySelector('lit-main-menu') as LitMainMenu;
1335            let children = main.menus as Array<MenuGroup>;
1336            let child = children[1].children as Array<MenuItem>;
1337            let fileHandler = child[0].clickHandler;
1338            if (fileHandler && !SpRecordTrace.cancelRecord) {
1339              this.freshConfigMenuDisable(false);
1340              this.freshMenuDisable(false);
1341              this.buttonDisable(false);
1342              this.recordButtonDisable(false);
1343              fileHandler(
1344                {
1345                  detail: {
1346                    timeStamp: timStamp,
1347                  },
1348                },
1349                true
1350              );
1351            } else {
1352              SpRecordTrace.cancelRecord = false;
1353            }
1354          });
1355        }
1356      }
1357    );
1358  }
1359
1360  private initRecordUIState(): void {
1361    this.buttonDisable(true);
1362    this.freshMenuDisable(true);
1363    this.freshConfigMenuDisable(true);
1364  }
1365
1366  private isSuccess(traceResult: string): number {
1367    if (traceResult.indexOf('CreateSession FAIL') !== -1 || traceResult.indexOf('failed') !== -1) {
1368      return 1;
1369    } else if (traceResult.indexOf('Signal') !== -1) {
1370      return 2;
1371    } else if (traceResult.indexOf('signal(2)') !== -1) {
1372      return 0;
1373    } else if (traceResult.indexOf('The device is abnormal') !== -1) {
1374      return -1;
1375    } else {
1376      return 0;
1377    }
1378  }
1379
1380  private makeRequest = (): CreateSessionRequest => {
1381    let request = createSessionRequest(this.recordSetting!);
1382    if (this.record_template) {
1383      let templateConfigs = this.spRecordTemplate?.getTemplateConfig();
1384      templateConfigs?.forEach((config) => {
1385        request.pluginConfigs.push(config);
1386      });
1387    } else {
1388      if (SpApplication.isLongTrace && request.sessionConfig) {
1389        request.sessionConfig.splitFile = true;
1390        request.sessionConfig!.splitFileMaxSizeMb = this.recordSetting!.longTraceSingleFileMaxSize;
1391        request.sessionConfig!.splitFileMaxNum = 20;
1392      }
1393      let reportingFrequency: number = 5;
1394      if (this.recordSetting!.maxDur <= 20) {
1395        reportingFrequency = 2;
1396      }
1397      createHTracePluginConfig(this.probesConfig!, request);
1398      createFpsPluginConfig(this.probesConfig!, request);
1399      createMonitorPlugin(this.probesConfig!, request);
1400      createMemoryPluginConfig(reportingFrequency, this.spVmTracker!, this.probesConfig!, request);
1401      createNativePluginConfig(reportingFrequency, this.spAllocations!, SpRecordTrace.selectVersion, request);
1402      createHiPerfConfig(reportingFrequency, this.spRecordPerf!, this.recordSetting!, request);
1403      createSystemConfig(this.spFileSystem!, this.recordSetting!, request);
1404      createSdkConfig(this.spSdkConfig!, request);
1405      createHiSystemEventPluginConfig(this.spHiSysEvent!, request);
1406      createArkTsConfig(this.spArkTs!, this.recordSetting!, request);
1407      createHiLogConfig(reportingFrequency, this.spHiLog!, request);
1408      createFFRTPluginConfig(this.spFFRTConfig!, SpRecordTrace.selectVersion, request);
1409      createXPowerConfig(this.spXPower!, request);
1410    }
1411    return request;
1412  };
1413
1414  initHtml(): string {
1415    return SpRecordTraceHtml;
1416  }
1417
1418  private freshConfigMenuDisable(disable: boolean): void {
1419    let querySelectors = this.shadowRoot?.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1420    querySelectors!.forEach((item) => {
1421      if (item.title !== 'Hdc Shell') {
1422        if (disable) {
1423          item.style.pointerEvents = 'none';
1424        } else {
1425          item.style.pointerEvents = 'auto';
1426        }
1427        item.disabled = disable;
1428      }
1429    });
1430  }
1431
1432  public startRefreshDeviceList(): void {
1433    if (this.refreshDeviceTimer === undefined) {
1434      this.refreshDeviceTimer = window.setInterval((): void => {
1435        this.refreshDeviceList();
1436      }, 5000);
1437    }
1438  }
1439
1440  private recordButtonDisable(disable: boolean): void {
1441    this.recordButton!.style.pointerEvents = disable ? 'none' : 'auto';
1442  }
1443
1444  private buttonDisable(disable: boolean): void {
1445    let pointerEventValue = 'auto';
1446    this.recordButtonText!.textContent = this.record;
1447    if (disable) {
1448      pointerEventValue = 'none';
1449      this.recordButtonText!.textContent = this.stop;
1450    }
1451    this.cancelButtonShow(disable);
1452    this.disconnectButton!.style.pointerEvents = pointerEventValue;
1453    this.addButton!.style.pointerEvents = pointerEventValue;
1454    this.deviceSelect!.style.pointerEvents = pointerEventValue;
1455    this.deviceVersion!.style.pointerEvents = pointerEventValue;
1456  }
1457
1458  private freshMenuItemsStatus(currentValue: string): void {
1459    let litMainMenuGroup = this.shadowRoot?.querySelector<LitMainMenuGroup>('lit-main-menu-group');
1460    let litMainMenuItemNodeListOf = litMainMenuGroup!.querySelectorAll<LitMainMenuItem>('lit-main-menu-item');
1461    litMainMenuItemNodeListOf.forEach((item) => {
1462      item.back = item.title === currentValue;
1463    });
1464  }
1465
1466  synchronizeDeviceList(): void {
1467    this.deviceSelect!.innerHTML = '';
1468    if (SpRecordTrace.serialNumber !== '') {
1469      for (let i = 0; i < this.hdcList.length; i++) {
1470        let dev = this.hdcList[i];
1471        let option = document.createElement('option');
1472        option.className = 'select';
1473        //@ts-ignore
1474        option.selected = dev.serialNumber === SpRecordTrace.serialNumber;
1475        //@ts-ignore
1476        option.value = dev.serialNumber;
1477        //@ts-ignore
1478        option.textContent = dev.serialNumber;
1479        this.deviceSelect!.appendChild(option);
1480        this.recordButton!.hidden = false;
1481        this.disconnectButton!.hidden = false;
1482        this.devicePrompt!.innerText = '';
1483      }
1484      if (SpRecordTrace.selectVersion && SpRecordTrace.selectVersion !== '') {
1485        this.setDeviceVersionSelect(SpRecordTrace.selectVersion);
1486      }
1487    }
1488  }
1489}
1490
1491const kbSize = 1024;
1492const timeOut = 200;
1493const unitSize = 48;
1494const indexDBMaxSize = unitSize * kbSize * kbSize;
1495export const MaxFileSize: number = kbSize * kbSize * kbSize;
1496