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 { Utils as TraceUtil } from './component/trace/base/Utils'; 17import { getThreadPoolTraceBufferCacheKey, threadPool } from './database/SqlLite'; 18 19export enum TraceMode { 20 NORMAL, 21 LONG_TRACE, 22 DISTRIBUTED, 23} 24 25export const applicationHtml: string = ` 26 <style> 27 :host{ 28 29 } 30 .dark{ 31 --dark-background: #272C34; 32 --dark-background1: #424851; 33 --dark-background2: #262f3c; 34 --dark-background3: #292D33; 35 --dark-background4: #323841; 36 --dark-background5: #333840; 37 --dark-background6: rgba(82,145,255,0.2); 38 --dark-background7: #494d52; 39 --dark-background8: #5291FF; 40 --dark-color: rgba(255,255,255,0.6); 41 --dark-color1: rgba(255,255,255,0.86); 42 --dark-color2: rgba(255,255,255,0.9); 43 --dark-border: #474F59; 44 --dark-color3:#4694C2; 45 --dark-color4:#5AADA0; 46 --dark-border1: #454E5A; 47 --bark-expansion:#0076FF; 48 --bark-prompt:#9e9e9e; 49 --dark-icon:#adafb3; 50 --dark-img: url('img/dark_pic.png'); 51 background: #272C34; 52 color: #FFFFFF; 53 } 54 .root{ 55 display: grid; 56 grid-template-rows: min-content 1fr; 57 grid-template-columns: min-content 1fr; 58 grid-template-areas: 'm s' 59 'm b'; 60 height: 100vh; 61 width: 100vw; 62 } 63 .filedrag::after { 64 content: 'Drop the trace file to open it'; 65 position: fixed; 66 z-index: 2001; 67 top: 0; 68 left: 0; 69 right: 0; 70 bottom: 0; 71 border: 5px dashed var(--dark-color1,#404854); 72 text-align: center; 73 font-size: 3rem; 74 line-height: 100vh; 75 background: rgba(255, 255, 255, 0.5); 76 } 77 .menu{ 78 grid-area: m; 79 /*transition: all 0.2s;*/ 80 box-shadow: 4px 0px 20px rgba(0,0,0,0.05); 81 z-index: 2000; 82 } 83 .search-vessel{ 84 z-index: 999; 85 position: relative; 86 cursor: default; 87 } 88 .progress{ 89 bottom: 0; 90 position: absolute; 91 height: 1px; 92 left: 0; 93 right: 0; 94 } 95 96 :host(:not([search])) .search-vessel { 97 display: none; 98 } 99 :host(:not([search])) .search-vessel .search { 100 background-color: var(--dark-background5,#F6F6F6); 101 } 102 .search{ 103 grid-area: s; 104 background-color: var(--dark-background,#FFFFFF); 105 height: 48px; 106 display: flex; 107 justify-content: center; 108 align-items: center; 109 110 } 111 .search .search-bg{ 112 background-color: var(--dark-background5,#fff); 113 border-radius: 40px; 114 padding: 3px 20px; 115 display: flex; 116 justify-content: center; 117 align-items: center; 118 border: 1px solid var(--dark-border,#c5c5c5); 119 } 120 lit-search input{ 121 outline: none; 122 border: 0px; 123 background-color: transparent; 124 font-size: inherit; 125 color: var(--dark-color,#666666); 126 width: 30vw; 127 height: auto; 128 vertical-align:middle; 129 line-height:inherit; 130 height:inherit; 131 padding: 6px 6px 6px 6px}; 132 max-height: inherit; 133 box-sizing: border-box; 134 135 } 136 ::placeholder { /* CSS 3 標準 */ 137 color: #b5b7ba; 138 font-size: 1em; 139 } 140 lit-search input::placeholder { 141 color: #b5b7ba; 142 font-size: 1em; 143 } 144 .content{ 145 grid-area: b; 146 background-color: #ffffff; 147 height: 100%; 148 overflow: auto; 149 position:relative; 150 } 151 .sheet{ 152 153 } 154 .sidebar-button{ 155 position: absolute; 156 top: 0; 157 left: 0; 158 background-color: var(--dark-background1,#FFFFFF); 159 height: 100%; 160 border-radius: 0 5px 5px 0; 161 width: 48px; 162 display: flex; 163 align-content: center; 164 justify-content: center; 165 cursor: pointer; 166 } 167 :host{ 168 font-size: inherit; 169 display: inline-block; 170 transition: .3s; 171 } 172 :host([spin]){ 173 animation: rotate 1.75s linear infinite; 174 } 175 @keyframes rotate { 176 to{ 177 transform: rotate(360deg); 178 } 179 } 180 .icon{ 181 display: block; 182 width: 1em; 183 height: 1em; 184 margin: auto; 185 fill: currentColor; 186 overflow: hidden; 187 font-size: 20px; 188 color: #1E4EEA; 189 } 190 :host([chart_filter]) .chart-filter { 191 display: grid; 192 grid-template-rows: min-content min-content min-content max-content auto; 193 overflow-y: clip; 194 height: 99%; 195 visibility: visible; 196 position: absolute; 197 width: 40%; 198 right: 0; 199 z-index: 1001; 200 top: 0; 201 } 202 :host([custom-color]) .custom-color { 203 display: grid; 204 grid-template-rows: min-content min-content min-content max-content auto; 205 overflow-y: auto; 206 height: 100%; 207 visibility: visible; 208 position: absolute; 209 width: 50%; 210 right: 0; 211 z-index: 1002; 212 top: 0; 213 } 214 .filter-config { 215 opacity: 1; 216 visibility: hidden; 217 } 218 .filter-config:hover { 219 opacity: 0.7; 220 } 221 .page-button[prohibit] { 222 cursor: none; 223 } 224 .page-button { 225 background: #D8D8D8; 226 border-radius: 12px; 227 width: 24px; 228 height: 24px; 229 margin-right: 12px; 230 display: flex; 231 justify-content: center; 232 align-items: center; 233 } 234 #preview-button:hover { 235 cursor: pointer; 236 background: #0A59F7; 237 color: #FFFFFF; 238 opacity: 1; 239 } 240 #next-button:hover { 241 cursor: pointer; 242 background: #0A59F7; 243 color: #FFFFFF; 244 opacity: 1; 245 } 246 .pagination:hover { 247 cursor: pointer; 248 background: #0A59F7; 249 color: #FFFFFF; 250 opacity: 1; 251 } 252 .confirm-button:hover { 253 cursor: pointer; 254 background: #0A59F7; 255 color: #FFFFFF; 256 opacity: 1; 257 } 258 .pagination { 259 background: #D8D8D8; 260 color: #000000; 261 border-radius: 12px; 262 width: 24px; 263 height: 24px; 264 margin-right: 12px; 265 display: flex; 266 justify-content: center; 267 align-items: center; 268 font-family: Helvetica; 269 font-size: 12px; 270 text-align: center; 271 line-height: 20px; 272 font-weight: 400; 273 opacity: 0.6; 274 } 275 .pagination[selected] { 276 background: #0A59F7; 277 color: #FFFFFF; 278 opacity: 1; 279 } 280 .page-jump-font { 281 opacity: 0.6; 282 font-family: Helvetica; 283 font-size: 12px; 284 color: #000000; 285 text-align: center; 286 line-height: 20px; 287 font-weight: 400; 288 } 289 .page-input { 290 background: #D8D8D8; 291 border-radius: 10px; 292 width: 40px; 293 height: 24px; 294 justify-content: center; 295 align-items: center; 296 text-align: center; 297 margin-right: 8px; 298 border: none; 299 } 300 .confirm-button { 301 font-family: Helvetica; 302 font-size: 12px; 303 color: #0A59F7; 304 text-align: center; 305 font-weight: 400; 306 border: 1px solid #0A59F7; 307 border-radius: 10px; 308 width: 64px; 309 height: 24px; 310 line-height: 24px; 311 } 312 .long_trace_page { 313 justify-content: center; 314 width: -webkit-fill-available; 315 margin-right: 5.2em; 316 align-items: center; 317 display: none; 318 } 319 .content-center-option { 320 justify-content: center; 321 width: -webkit-fill-available; 322 margin-right: 5.2em; 323 align-items: center; 324 width: auto; 325 } 326 .page-number-list { 327 display: flex; 328 } 329 330 #sp-ai-analysis { 331 top:75px; 332 right:0px; 333 position:absolute; 334 z-index:9999; 335 min-width:430px; 336 max-width:75%; 337 width:430px; 338 height:740px; 339 box-shadow:3px 0px 14px #000; 340 border-radius:8px; 341 background-color:#fff; 342 padding:10px 10px 30px 5px; 343 box-sizing:border-box; 344 visibility:hidden; 345 } 346 #sp-snapshot-view { 347 top:75px; 348 right:0px; 349 position:absolute; 350 z-index:2000; 351 width:25%; 352 height:90%; 353 box-shadow:3px 0px 14px #000; 354 background-color:#fff; 355 box-sizing:border-box; 356 visibility:hidden; 357 } 358 </style> 359 <div class="root" style="position: relative;"> 360 <sp-bubble-ai style="visibility: visible; top:50%;right:2px;position: absolute;z-index: 10000" id="sp-bubbles" draggable ="true"></sp-bubble-ai> 361 <sp-advertisement style="bottom:2px;right:2px;position: absolute;z-index: 10086" id= "sp-advertisement"></sp-advertisement> 362 <lit-main-menu id="main-menu" class="menu" data=''></lit-main-menu> 363 <sp-keyboard style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 8888" id="sp-keyboard"> 364 </sp-keyboard> 365 <div class="search-vessel"> 366 <div class="search" style="position: relative;"> 367 <div class="sidebar-button" style="width: 0"> 368 <svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 1024 1024"> 369 <use id="use" xlink:href="./base-ui/icon.svg#icon-menu"></use> 370 </svg> 371 </div> 372 <div class = "content-left-option" style="text-align: left; 373 position: absolute;left: 5px ; cursor: pointer;top: 15px"> 374 <div title="Import Key Path" id="import-key-path" style="display: none ;text-align: left;cursor: pointer;"> 375 <input id="import-config" style="display: none;pointer-events: none" type="file" accept=".json" > 376 <label style="width: 20px;height: 20px;cursor: pointer;" for="import-config"> 377 <lit-icon id="import-btn" name="copy-csv" style="pointer-events: none" size="20"> 378 </lit-icon> 379 </label> 380 </div> 381 <lit-icon id="close-key-path" name="close" title="Close Key Path" color='#fff' size="20" style="display: none;text-align: left;cursor: pointer;"> 382 </lit-icon> 383 </div> 384 <lit-search id="lit-search"></lit-search> 385 <lit-search id="lit-record-search"></lit-search> 386 <div class="content-center-option" style="display: "> 387 <div class="long_trace_page" style="display: none;"> 388 <div class="page-button" id="preview-button"> 389 <img title="preview" src="img/preview.png"/> 390 </div> 391 <div class="page-number-list"></div> 392 <div class="page-button" id="next-button" style="margin-right: 8px;"> 393 <img title="next" src="img/next.png"/> 394 </div> 395 <div class="page-jump-font" style="margin-right: 8px;">To</div> 396 <input class="page-input" /> 397 <div class="confirm-button">Confirm</div> 398 </div> 399 </div> 400 </div> 401 <div class = "content-right-option" style="display: flex;flex-flow: nowrap;text-align: right;position: absolute;right: 1.2em;cursor: pointer;top: 17px""> 402 <lit-icon class="export-record" title="Download Mark Trace" name="download" size="16" style="margin-left: 0.8em;"></lit-icon> 403 <img class="cut-trace-file" title="Cut Trace File" src="img/menu-cut.svg" style="margin-left: 0.8em;"> 404 <img class="filter-config" title="Display Template" src="img/config_filter.png" style="margin-left: 0.8em;"> 405 </div> 406 <lit-progress-bar class="progress"></lit-progress-bar> 407 </div> 408 <div id="app-content" class="content"> 409 <sp-welcome style="visibility:visible;top:0px;left:0px;position:absolute;z-index: 100" id="sp-welcome"> 410 </sp-welcome> 411 </sp-ai-analysis> 412 <sp-system-trace style="visibility:hidden;z-index: 101;" id="sp-system-trace"> 413 </sp-system-trace> 414 <sp-record-trace record_template='false' style="overflow:auto;width:100%;height:100%;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 102" id="sp-record-trace"> 415 </sp-record-trace> 416 <sp-scheduling-analysis style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0;left:0;right:0;bottom:0;position:absolute;" id="sp-scheduling-analysis"></sp-scheduling-analysis> 417 <sp-metrics style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0;left:0;right:0;bottom:0;position:absolute;z-index: 105" id="sp-metrics"> 418 </sp-metrics> 419 <sp-query-sql style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0;left:0;right:0;bottom:0;position:absolute;z-index: 106" id="sp-query-sql"> 420 </sp-query-sql> 421 <sp-info-and-stats style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0;left:0;right:0;bottom:0;position:absolute;z-index: 107" id="sp-info-and-stats"> 422 </sp-info-and-stats> 423 <sp-convert-trace style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0;left:0;right:0;bottom:0;position:absolute;z-index: 107" id="sp-convert-trace"> 424 </sp-convert-trace> 425 <sp-help style="width:100%;height:100%;overflow:hidden;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 103" id="sp-help"> 426 </sp-help> 427 <sp-flags style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 104" id="sp-flags"> 428 </sp-flags> 429 <sp-third-party style="width:100%;height:100%;overflow:auto;visibility:hidden;top:0px;left:0px;right:0;bottom:0px;position:absolute;z-index: 104" id="sp-third-party"> 430 </sp-third-party> 431 <trace-row-config class="chart-filter" style="height:100%;top:0px;right:0;bottom:0px;position:absolute;z-index: 1001"></trace-row-config> 432 <custom-theme-color class="custom-color" style="height:100%;top:0px;right:0;bottom:0px;position:absolute;z-index: 1001"></custom-theme-color> 433 </div> 434 <sp-ai-analysis id="sp-ai-analysis" style="visibility:hidden;"></sp-ai-analysis> 435 <sp-snapshot-view id="sp-snapshot-view" style="visibility:hidden;"></sp-snapshot-view> 436 </div> 437 `; 438 439export function readTraceFileBuffer(): Promise<ArrayBuffer | undefined> { 440 return new Promise((resolve) => { 441 caches.match(getThreadPoolTraceBufferCacheKey('1')).then((res) => { 442 if (res) { 443 res.arrayBuffer().then((buffer) => { 444 resolve(buffer); 445 }); 446 } else { 447 resolve(undefined); 448 } 449 }); 450 }); 451} 452 453export function clearTraceFileCache(): void { 454 caches.keys().then((keys) => { 455 keys.forEach((key) => { 456 if (key === getThreadPoolTraceBufferCacheKey('1')) { 457 caches.delete(key).then(); 458 } else if (key.includes('/') && key.includes('-')) { 459 let splits = key.split('/'); 460 let keyStr = splits[splits.length - 1]; 461 let time = keyStr.split('-')[0]; 462 let fileDate = new Date(parseInt(time)); 463 if (fileDate.toLocaleDateString() !== new Date().toLocaleDateString()) { 464 //如果不是当天的缓存则删去缓存文件 465 caches.delete(key).then(); 466 } 467 } else { 468 caches.delete(key).then(); 469 } 470 }); 471 }); 472} 473 474export function postLog(filename: string, fileSize: string): void { 475 fetch(`https://${window.location.host.split(':')[0]}:${window.location.port}${window.location.pathname}logger`, { 476 method: 'POST', 477 headers: { 478 'Content-Type': 'application/json', 479 }, 480 body: JSON.stringify({ 481 fileName: filename, 482 fileSize: fileSize, 483 }), 484 }) 485 .then((response) => response.json()) 486 .then((data) => { }) 487 .catch((error) => { }); 488} 489 490export function indexedDataToBufferData(sourceData: unknown): ArrayBuffer { 491 let uintArrayLength = 0; 492 //@ts-ignore 493 let uintDataList = sourceData.map((item: unknown) => { 494 //@ts-ignore 495 let currentBufData = new Uint8Array(item.buf); 496 uintArrayLength += currentBufData.length; 497 return currentBufData; 498 }); 499 let resultArrayBuffer = new ArrayBuffer(uintArrayLength); 500 let resultUintArray = new Uint8Array(resultArrayBuffer); 501 let offset = 0; 502 uintDataList.forEach((currentArray: Uint8Array) => { 503 resultUintArray.set(currentArray, offset); 504 offset += currentArray.length; 505 }); 506 return resultArrayBuffer; 507} 508 509export function findFreeSizeAlgorithm(numbers: Array<number>, freeSize: number): Array<number> { 510 let closestSize = 0; 511 let currentSize = 0; 512 let finalIndex: Array<number> = []; 513 let currentSelectIndex: Array<number> = []; 514 515 function reBackFind(index: number): void { 516 if (index === numbers.length) { 517 const sumDifference = Math.abs(currentSize - freeSize); 518 if (currentSize <= freeSize && sumDifference < Math.abs(closestSize - freeSize)) { 519 closestSize = currentSize; 520 finalIndex = [...currentSelectIndex]; 521 } 522 return; 523 } 524 currentSize += numbers[index]; 525 currentSelectIndex.push(index); 526 reBackFind(index + 1); 527 currentSize -= numbers[index]; 528 currentSelectIndex.pop(); 529 reBackFind(index + 1); 530 } 531 532 reBackFind(0); 533 return finalIndex; 534} 535 536export function getCurrentDataTime(): string[] { 537 let current = new Date(); 538 let year = '' + current.getFullYear(); 539 let month = ('0' + (current.getMonth() + 1)).slice(-2); 540 let day = ('0' + current.getDate()).slice(-2); 541 let hours = ('0' + current.getHours()).slice(-2); 542 let minutes = ('0' + current.getMinutes()).slice(-2); 543 let seconds = ('0' + current.getSeconds()).slice(-2); 544 return [year, month, day, hours, minutes, seconds]; 545} 546 547// 定义ZIP文件的文件头常量 548const ZIP_HEADER = [0x50, 0x4B, 0x03, 0x04]; 549// 定义ZLIB文件的文件头常量 550const ZLIB_HEADER = [0x78, 0x9c]; 551 552/** 553 * 验证Uint8Array实例并检查长度是否满足要求 554 * @param uint8Array 待验证的Uint8Array实例 555 * @param requiredLength 所需的最小长度 556 * @returns 如果uint8Array是一个Uint8Array实例且长度大于等于requiredLength,则返回true,否则返回false 557 */ 558function validateUint8Array(uint8Array: Uint8Array, requiredLength: number): boolean { 559 return uint8Array instanceof Uint8Array && uint8Array.length >= requiredLength; 560} 561 562/** 563 * 检查Uint8Array是否表示一个ZIP文件 564 * @param uint8Array 待检查的Uint8Array实例 565 * @returns 如果uint8Array表示的是一个ZIP文件,则返回true,否则返回false 566 */ 567export function isZipFile(uint8Array: Uint8Array): boolean { 568 if (!validateUint8Array(uint8Array, ZIP_HEADER.length)) { 569 return false; 570 } 571 for (let i = 0; i < ZIP_HEADER.length; i++) { 572 if (uint8Array[i] !== ZIP_HEADER[i]) { 573 return false; 574 } 575 } 576 return true; 577} 578 579/** 580 * 检查Uint8Array是否表示一个ZLIB文件 581 * @param uint8Array 待检查的Uint8Array实例 582 * @returns 如果uint8Array表示的是一个ZLIB文件,则返回true,否则返回false 583 */ 584export function isZlibFile(uint8Array: Uint8Array): boolean { 585 if (!validateUint8Array(uint8Array, ZLIB_HEADER.length)) { 586 return false; 587 } 588 for (let i = 0; i < ZLIB_HEADER.length; i++) { 589 if (uint8Array[i] !== ZLIB_HEADER[i]) { 590 return false; 591 } 592 } 593 return true; 594} 595 596/** 597 * 外部交互,向外传输db文件 598 */ 599export function addExportDBToParentEvent() { 600 window.addEventListener('message', e => { 601 if (e.data.name == 'exportDbToParent') { 602 threadPool.submit('download-db', '', {}, async (reqBufferDB: ArrayBuffer) => { 603 if (reqBufferDB && reqBufferDB.byteLength > 0) { 604 window.parent.postMessage({ 605 name: `${(TraceUtil.currentTraceName || 'trace').split('.')[0]}.db`, 606 data: reqBufferDB 607 }, '*'); 608 } else { 609 window.parent.postMessage({ 610 name: `暂无数据`, 611 data: null 612 }, '*'); 613 } 614 }, 'download-db'); 615 } 616 }); 617} 618 619export function loadTraceCompleteEvent() { 620 if (window?.parent) { 621 window.parent.postMessage({ 622 name:'trace_load_complete', 623 data: null 624 }, '*'); 625 } 626}