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