* Copyright (C) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Utils as TraceUtil } from './component/trace/base/Utils';
import { getThreadPoolTraceBufferCacheKey, threadPool } from './database/SqlLite';
export enum TraceMode {
NORMAL,
LONG_TRACE,
DISTRIBUTED,
}
export const applicationHtml: string = `
<style>
:host{
}
.dark{
--dark-background: #272C34;
--dark-background1: #424851;
--dark-background2: #262f3c;
--dark-background3: #292D33;
--dark-background4: #323841;
--dark-background5: #333840;
--dark-background6: rgba(82,145,255,0.2);
--dark-background7: #494d52;
--dark-background8: #5291FF;
--dark-color: rgba(255,255,255,0.6);
--dark-color1: rgba(255,255,255,0.86);
--dark-color2: rgba(255,255,255,0.9);
--dark-border: #474F59;
--dark-color3:#4694C2;
--dark-color4:#5AADA0;
--dark-border1: #454E5A;
--bark-expansion:#0076FF;
--bark-prompt:#9e9e9e;
--dark-icon:#adafb3;
--dark-img: url('img/dark_pic.png');
background: #272C34;
color: #FFFFFF;
}
.root{
display: grid;
grid-template-rows: min-content 1fr;
grid-template-columns: min-content 1fr;
grid-template-areas: 'm s'
'm b';
height: 100vh;
width: 100vw;
}
.filedrag::after {
content: 'Drop the trace file to open it';
position: fixed;
z-index: 2001;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 5px dashed var(--dark-color1,#404854);
text-align: center;
font-size: 3rem;
line-height: 100vh;
background: rgba(255, 255, 255, 0.5);
}
.menu{
grid-area: m;
/*transition: all 0.2s;*/
box-shadow: 4px 0px 20px rgba(0,0,0,0.05);
z-index: 2000;
}
.search-vessel{
z-index: 999;
position: relative;
cursor: default;
}
.progress{
bottom: 0;
position: absolute;
height: 1px;
left: 0;
right: 0;
}
:host(:not([search])) .search-vessel {
display: none;
}
:host(:not([search])) .search-vessel .search {
background-color: var(--dark-background5,#F6F6F6);
}
.search{
grid-area: s;
background-color: var(--dark-background,#FFFFFF);
height: 48px;
display: flex;
justify-content: center;
align-items: center;
}
.search .search-bg{
background-color: var(--dark-background5,#fff);
border-radius: 40px;
padding: 3px 20px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--dark-border,#c5c5c5);
}
lit-search input{
outline: none;
border: 0px;
background-color: transparent;
font-size: inherit;
color: var(--dark-color,#666666);
width: 30vw;
height: auto;
vertical-align:middle;
line-height:inherit;
height:inherit;
padding: 6px 6px 6px 6px};
max-height: inherit;
box-sizing: border-box;
}
::placeholder { /* CSS 3 標準 */
color: #b5b7ba;
font-size: 1em;
}
lit-search input::placeholder {
color: #b5b7ba;
font-size: 1em;
}
.content{
grid-area: b;
background-color: #ffffff;
height: 100%;
overflow: auto;
position:relative;
}
.sheet{
}
.sidebar-button{
position: absolute;
top: 0;
left: 0;
background-color: var(--dark-background1,#FFFFFF);
height: 100%;
border-radius: 0 5px 5px 0;
width: 48px;
display: flex;
align-content: center;
justify-content: center;
cursor: pointer;
}
:host{
font-size: inherit;
display: inline-block;
transition: .3s;
}
:host([spin]){
animation: rotate 1.75s linear infinite;
}
@keyframes rotate {
to{
transform: rotate(360deg);
}
}
.icon{
display: block;
width: 1em;
height: 1em;
margin: auto;
fill: currentColor;
overflow: hidden;
font-size: 20px;
color: #1E4EEA;
}
:host([chart_filter]) .chart-filter {
display: grid;
grid-template-rows: min-content min-content min-content max-content auto;
overflow-y: clip;
height: 99%;
visibility: visible;
position: absolute;
width: 40%;
right: 0;
z-index: 1001;
top: 0;
}
:host([custom-color]) .custom-color {
display: grid;
grid-template-rows: min-content min-content min-content max-content auto;
overflow-y: auto;
height: 100%;
visibility: visible;
position: absolute;
width: 50%;
right: 0;
z-index: 1002;
top: 0;
}
.filter-config {
opacity: 1;
visibility: hidden;
}
.filter-config:hover {
opacity: 0.7;
}
.page-button[prohibit] {
cursor: none;
}
.page-button {
background: #D8D8D8;
border-radius: 12px;
width: 24px;
height: 24px;
margin-right: 12px;
display: flex;
justify-content: center;
align-items: center;
}
#preview-button:hover {
cursor: pointer;
background: #0A59F7;
color: #FFFFFF;
opacity: 1;
}
#next-button:hover {
cursor: pointer;
background: #0A59F7;
color: #FFFFFF;
opacity: 1;
}
.pagination:hover {
cursor: pointer;
background: #0A59F7;
color: #FFFFFF;
opacity: 1;
}
.confirm-button:hover {
cursor: pointer;
background: #0A59F7;
color: #FFFFFF;
opacity: 1;
}
.pagination {
background: #D8D8D8;
color: #000000;
border-radius: 12px;
width: 24px;
height: 24px;
margin-right: 12px;
display: flex;
justify-content: center;
align-items: center;
font-family: Helvetica;
font-size: 12px;
text-align: center;
line-height: 20px;
font-weight: 400;
opacity: 0.6;
}
.pagination[selected] {
background: #0A59F7;
color: #FFFFFF;
opacity: 1;
}
.page-jump-font {
opacity: 0.6;
font-family: Helvetica;
font-size: 12px;
color: #000000;
text-align: center;
line-height: 20px;
font-weight: 400;
}
.page-input {
background: #D8D8D8;
border-radius: 10px;
width: 40px;
height: 24px;
justify-content: center;
align-items: center;
text-align: center;
margin-right: 8px;
border: none;
}
.confirm-button {
font-family: Helvetica;
font-size: 12px;
color: #0A59F7;
text-align: center;
font-weight: 400;
border: 1px solid #0A59F7;
border-radius: 10px;
width: 64px;
height: 24px;
line-height: 24px;
}
.long_trace_page {
justify-content: center;
width: -webkit-fill-available;
margin-right: 5.2em;
align-items: center;
display: none;
}
.content-center-option {
justify-content: center;
width: -webkit-fill-available;
margin-right: 5.2em;
align-items: center;
width: auto;
}
.page-number-list {
display: flex;
}
#sp-ai-analysis {
top:75px;
right:0px;
position:absolute;
z-index:9999;
min-width:430px;
max-width:75%;
width:430px;
height:740px;
box-shadow:3px 0px 14px #000;
border-radius:8px;
background-color:#fff;
padding:10px 10px 30px 5px;
box-sizing:border-box;
visibility:hidden;
}
#sp-snapshot-view {
top:75px;
right:0px;
position:absolute;
z-index:2000;
width:25%;
height:90%;
box-shadow:3px 0px 14px #000;
background-color:#fff;
box-sizing:border-box;
visibility:hidden;
}
</style>
<div class="root" style="position: relative;">
<sp-advertisement style="bottom:2px;right:2px;position: absolute;z-index: 10086" id= "sp-advertisement"></sp-advertisement>
<lit-main-menu id="main-menu" class="menu" data=''></lit-main-menu>
<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">
</sp-keyboard>
<div class="search-vessel">
<div class="search" style="position: relative;">
<div class="sidebar-button" style="width: 0">
<svg class="icon" id="icon" aria-hidden="true" viewBox="0 0 1024 1024">
<use id="use" xlink:href="./base-ui/icon.svg#icon-menu"></use>
</svg>
</div>
<div class = "content-left-option" style="text-align: left;
position: absolute;left: 5px ; cursor: pointer;top: 15px">
<div title="Import Key Path" id="import-key-path" style="display: none ;text-align: left;cursor: pointer;">
<input id="import-config" style="display: none;pointer-events: none" type="file" accept=".json" >
<label style="width: 20px;height: 20px;cursor: pointer;" for="import-config">
<lit-icon id="import-btn" name="copy-csv" style="pointer-events: none" size="20">
</lit-icon>
</label>
</div>
<lit-icon id="close-key-path" name="close" title="Close Key Path" color='#fff' size="20" style="display: none;text-align: left;cursor: pointer;">
</lit-icon>
</div>
<lit-search id="lit-search"></lit-search>
<lit-search id="lit-record-search"></lit-search>
<div class="content-center-option" style="display: ">
<div class="long_trace_page" style="display: none;">
<div class="page-button" id="preview-button">
<img title="preview" src="img/preview.png"/>
</div>
<div class="page-number-list"></div>
<div class="page-button" id="next-button" style="margin-right: 8px;">
<img title="next" src="img/next.png"/>
</div>
<div class="page-jump-font" style="margin-right: 8px;">To</div>
<input class="page-input" />
<div class="confirm-button">Confirm</div>
</div>
</div>
</div>
<div class = "content-right-option" style="display: flex;flex-flow: nowrap;text-align: right;position: absolute;right: 1.2em;cursor: pointer;top: 17px"">
<lit-icon class="export-record" title="Download Mark Trace" name="download" size="16" style="margin-left: 0.8em;"></lit-icon>
<img class="cut-trace-file" title="Cut Trace File" src="img/menu-cut.svg" style="margin-left: 0.8em;">
<img class="filter-config" title="Display Template" src="img/config_filter.png" style="margin-left: 0.8em;">
</div>
<lit-progress-bar class="progress"></lit-progress-bar>
</div>
<div id="app-content" class="content">
<sp-welcome style="visibility:visible;top:0px;left:0px;position:absolute;z-index: 100" id="sp-welcome">
</sp-welcome>
</sp-ai-analysis>
<sp-system-trace style="visibility:hidden;z-index: 101;" id="sp-system-trace">
</sp-system-trace>
<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">
</sp-record-trace>
<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>
<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">
</sp-metrics>
<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">
</sp-query-sql>
<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">
</sp-info-and-stats>
<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">
</sp-convert-trace>
<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">
</sp-help>
<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">
</sp-flags>
<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">
</sp-third-party>
<trace-row-config class="chart-filter" style="height:100%;top:0px;right:0;bottom:0px;position:absolute;z-index: 1001"></trace-row-config>
<custom-theme-color class="custom-color" style="height:100%;top:0px;right:0;bottom:0px;position:absolute;z-index: 1001"></custom-theme-color>
</div>
<sp-ai-analysis id="sp-ai-analysis" style="visibility:hidden;"></sp-ai-analysis>
<sp-snapshot-view id="sp-snapshot-view" style="visibility:hidden;"></sp-snapshot-view>
</div>
`;
export function readTraceFileBuffer(): Promise<ArrayBuffer | undefined> {
return new Promise((resolve) => {
caches.match(getThreadPoolTraceBufferCacheKey('1')).then((res) => {
if (res) {
res.arrayBuffer().then((buffer) => {
resolve(buffer);
});
} else {
resolve(undefined);
}
});
});
}
export function clearTraceFileCache(): void {
caches.keys().then((keys) => {
keys.forEach((key) => {
if (key === getThreadPoolTraceBufferCacheKey('1')) {
caches.delete(key).then();
} else if (key.includes('/') && key.includes('-')) {
let splits = key.split('/');
let keyStr = splits[splits.length - 1];
let time = keyStr.split('-')[0];
let fileDate = new Date(parseInt(time));
if (fileDate.toLocaleDateString() !== new Date().toLocaleDateString()) {
caches.delete(key).then();
}
} else {
caches.delete(key).then();
}
});
});
}
export function postLog(filename: string, fileSize: string): void {
fetch(`https://${window.location.host.split(':')[0]}:${window.location.port}${window.location.pathname}logger`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileName: filename,
fileSize: fileSize,
}),
})
.then((response) => response.json())
.then((data) => { })
.catch((error) => { });
}
export function indexedDataToBufferData(sourceData: unknown): ArrayBuffer {
let uintArrayLength = 0;
let uintDataList = sourceData.map((item: unknown) => {
let currentBufData = new Uint8Array(item.buf);
uintArrayLength += currentBufData.length;
return currentBufData;
});
let resultArrayBuffer = new ArrayBuffer(uintArrayLength);
let resultUintArray = new Uint8Array(resultArrayBuffer);
let offset = 0;
uintDataList.forEach((currentArray: Uint8Array) => {
resultUintArray.set(currentArray, offset);
offset += currentArray.length;
});
return resultArrayBuffer;
}
export function findFreeSizeAlgorithm(numbers: Array<number>, freeSize: number): Array<number> {
let closestSize = 0;
let currentSize = 0;
let finalIndex: Array<number> = [];
let currentSelectIndex: Array<number> = [];
function reBackFind(index: number): void {
if (index === numbers.length) {
const sumDifference = Math.abs(currentSize - freeSize);
if (currentSize <= freeSize && sumDifference < Math.abs(closestSize - freeSize)) {
closestSize = currentSize;
finalIndex = [...currentSelectIndex];
}
return;
}
currentSize += numbers[index];
currentSelectIndex.push(index);
reBackFind(index + 1);
currentSize -= numbers[index];
currentSelectIndex.pop();
reBackFind(index + 1);
}
reBackFind(0);
return finalIndex;
}
export function getCurrentDataTime(): string[] {
let current = new Date();
let year = '' + current.getFullYear();
let month = ('0' + (current.getMonth() + 1)).slice(-2);
let day = ('0' + current.getDate()).slice(-2);
let hours = ('0' + current.getHours()).slice(-2);
let minutes = ('0' + current.getMinutes()).slice(-2);
let seconds = ('0' + current.getSeconds()).slice(-2);
return [year, month, day, hours, minutes, seconds];
}
const ZIP_HEADER = [0x50, 0x4B, 0x03, 0x04];
const ZLIB_HEADER = [0x78, 0x9c];
* 验证Uint8Array实例并检查长度是否满足要求
* @param uint8Array 待验证的Uint8Array实例
* @param requiredLength 所需的最小长度
* @returns 如果uint8Array是一个Uint8Array实例且长度大于等于requiredLength,则返回true,否则返回false
*/
function validateUint8Array(uint8Array: Uint8Array, requiredLength: number): boolean {
return uint8Array instanceof Uint8Array && uint8Array.length >= requiredLength;
}
* 检查Uint8Array是否表示一个ZIP文件
* @param uint8Array 待检查的Uint8Array实例
* @returns 如果uint8Array表示的是一个ZIP文件,则返回true,否则返回false
*/
export function isZipFile(uint8Array: Uint8Array): boolean {
if (!validateUint8Array(uint8Array, ZIP_HEADER.length)) {
return false;
}
for (let i = 0; i < ZIP_HEADER.length; i++) {
if (uint8Array[i] !== ZIP_HEADER[i]) {
return false;
}
}
return true;
}
* 检查Uint8Array是否表示一个ZLIB文件
* @param uint8Array 待检查的Uint8Array实例
* @returns 如果uint8Array表示的是一个ZLIB文件,则返回true,否则返回false
*/
export function isZlibFile(uint8Array: Uint8Array): boolean {
if (!validateUint8Array(uint8Array, ZLIB_HEADER.length)) {
return false;
}
for (let i = 0; i < ZLIB_HEADER.length; i++) {
if (uint8Array[i] !== ZLIB_HEADER[i]) {
return false;
}
}
return true;
}
* 外部交互,向外传输db文件
*/
export function addExportDBToParentEvent() {
window.addEventListener('message', e => {
if (e.data.name == 'exportDbToParent') {
threadPool.submit('download-db', '', {}, async (reqBufferDB: ArrayBuffer) => {
if (reqBufferDB && reqBufferDB.byteLength > 0) {
window.parent.postMessage({
name: `${(TraceUtil.currentTraceName || 'trace').split('.')[0]}.db`,
data: reqBufferDB
}, '*');
} else {
window.parent.postMessage({
name: `暂无数据`,
data: null
}, '*');
}
}, 'download-db');
}
});
}
export function loadTraceCompleteEvent() {
if (window?.parent) {
window.parent.postMessage({
name:'trace_load_complete',
data: null
}, '*');
}
}