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