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