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 { BaseStruct, Rect, Render, drawLoadingFrame, isFrameContainPoint } from './ProcedureWorkerCommon'; 17import { TraceRow } from '../../component/trace/base/TraceRow'; 18import { Utils } from '../../component/trace/base/Utils'; 19 20import { SpSystemTrace } from '../../component/SpSystemTrace'; 21export class HeapSnapshotRender extends Render { 22 renderMainThread( 23 req: { 24 context: CanvasRenderingContext2D; 25 useCache: boolean; 26 type: string; 27 }, 28 row: TraceRow<HeapSnapshotStruct> 29 ): void { 30 let filter = row.dataListCache; 31 HeapSnapshot( 32 filter, 33 TraceRow.range?.startNS ?? 0, 34 TraceRow.range?.endNS ?? 0, 35 (TraceRow.range?.endNS ?? 0) - (TraceRow.range?.startNS! ?? 0), // @ts-ignore 36 row.frame 37 ); 38 drawLoadingFrame(req.context, filter, row); 39 req.context!.beginPath(); 40 for (let re of filter) { 41 HeapSnapshotStruct.draw(req.context, re); 42 } 43 for (let re of filter) { 44 if (re.frame && !isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 45 HeapSnapshotStruct.hoverSnapshotStruct = undefined; 46 } 47 if (re.frame && isFrameContainPoint(re.frame, row.hoverX, row.hoverY)) { 48 HeapSnapshotStruct.hoverSnapshotStruct = re; 49 break; 50 } 51 } 52 req.context!.closePath(); 53 } 54} 55export function HeapSnapshot( 56 list: Array<HeapSnapshotStruct>, 57 startNS: number, 58 endNS: number, 59 totalNS: number, 60 frame: Rect 61): void { 62 for (let file of list) { 63 HeapSnapshotStruct.setFrame(file, startNS || 0, endNS || 0, totalNS || 0, frame); 64 } 65} 66const padding = 3; 67export function HeapSnapshotStructOnClick( 68 clickRowType: string, 69 sp: SpSystemTrace, 70 row: TraceRow<HeapSnapshotStruct>, 71 snapshotClickHandler: unknown 72): Promise<unknown> { 73 return new Promise((resolve, reject) => { 74 if (clickRowType === TraceRow.ROW_TYPE_HEAP_SNAPSHOT) { 75 if (row.findHoverStruct) { 76 row.findHoverStruct(); 77 } else { 78 HeapSnapshotStruct.hoverSnapshotStruct = HeapSnapshotStruct.hoverSnapshotStruct || row.getHoverStruct(); 79 } 80 if (HeapSnapshotStruct.hoverSnapshotStruct) { 81 HeapSnapshotStruct.selectSnapshotStruct = HeapSnapshotStruct.hoverSnapshotStruct; 82 sp.traceSheetEL?.displaySnapshotData( 83 HeapSnapshotStruct.selectSnapshotStruct!, 84 row!.dataListCache, 85 //@ts-ignore 86 snapshotClickHandler 87 ); 88 } 89 reject(new Error()); 90 } else { 91 resolve(null); 92 } 93 }); 94} 95export class HeapSnapshotStruct extends BaseStruct { 96 startTs: number = 0; 97 endTs: number = 0; 98 id: number = 0; 99 pid: number = 0; 100 name: string | undefined; 101 textWidth: number | undefined; 102 size: number = 0; 103 static hoverSnapshotStruct: HeapSnapshotStruct | undefined; 104 static selectSnapshotStruct: HeapSnapshotStruct | undefined; 105 106 static setFrame(node: HeapSnapshotStruct, startNS: number, endNS: number, totalNS: number, frame: Rect): void { 107 node.frame = undefined; 108 if ((node.startTs - startNS || node.startTs - startNS === 0) && node.endTs - node.startTs) { 109 let rectangle: Rect = new Rect( 110 Math.floor(((node.startTs - startNS) / totalNS) * frame.width), 111 0, 112 Math.ceil(((node.endTs - node.startTs) / totalNS) * frame.width), 113 40 114 ); 115 node.frame = rectangle; 116 } 117 } 118 119 static draw(ctx: CanvasRenderingContext2D, data: HeapSnapshotStruct): void { 120 if (data.frame) { 121 ctx.fillStyle = 'rgb(86,192,197)'; 122 ctx!.fillRect(data.frame!.x, data.frame!.y + padding, data.frame!.width, data.frame!.height - padding * 2); 123 if (data.frame!.width > 7) { 124 ctx.globalAlpha = 1.0; 125 ctx.lineWidth = 1; 126 ctx.fillStyle = '#fff'; 127 ctx.textBaseline = 'middle'; 128 ctx.font = '12px sans-serif'; 129 HeapSnapshotStruct.drawString(ctx, data.name || '', 3, data.frame!, data, 4); 130 HeapSnapshotStruct.drawString(ctx, Utils.getBinaryByteWithUnit(data.size) || '', 9, data.frame!, data, 2); 131 } 132 if ( 133 HeapSnapshotStruct.selectSnapshotStruct && 134 HeapSnapshotStruct.equals(HeapSnapshotStruct.selectSnapshotStruct, data) 135 ) { 136 ctx.strokeStyle = '#232c5d'; 137 ctx.lineWidth = 2; 138 ctx.strokeRect(data.frame!.x, data.frame!.y + padding, data.frame!.width - 2, data.frame!.height - padding * 2); 139 } 140 } 141 } 142 143 /** 144 * 145 * @param ctx current context 146 * @param str text 147 * @param textPadding padding 148 * @param frame rectangle 149 * @param data HeapSnapshotStruct 150 * @param location the position of the string, the bigger the numerical value, the higher the position on the canvas 151 */ 152 static drawString( 153 ctx: CanvasRenderingContext2D, 154 str: string, 155 textPadding: number, 156 frame: Rect, 157 HeapSnapshotdata: HeapSnapshotStruct, 158 location: number 159 ): void { 160 if (HeapSnapshotdata.textWidth === undefined) { 161 HeapSnapshotdata.textWidth = ctx.measureText(str).width; 162 } 163 let textWidth = Math.round(HeapSnapshotdata.textWidth / str.length); 164 let maxTextWidth = frame.width - textPadding * 2; 165 if (HeapSnapshotdata.textWidth < maxTextWidth) { 166 let x = Math.floor(frame.width / 2 - HeapSnapshotdata.textWidth / 2 + frame.x + textPadding); 167 ctx.fillText(str, x, Math.floor(frame.y + frame.height / location + textPadding), maxTextWidth); 168 } else { 169 if (maxTextWidth >= textWidth) { 170 let characterNum = maxTextWidth / textWidth; 171 let x = frame.x + textPadding; 172 if (characterNum < 2) { 173 ctx.fillText( 174 str.substring(0, 1), 175 x, 176 Math.floor(frame.y + frame.height / location + textPadding), 177 maxTextWidth 178 ); 179 } else { 180 ctx.fillText( 181 str.substring(0, characterNum - 1) + '...', 182 x, 183 Math.floor(frame.y + frame.height / location + textPadding), 184 maxTextWidth 185 ); 186 } 187 } 188 } 189 } 190 191 static equals(d1: HeapSnapshotStruct, d2: HeapSnapshotStruct): boolean { 192 return ( 193 d1 && 194 d2 && 195 d1.name === d2.name && 196 d1.id === d2.id && 197 d1.pid === d2.pid && 198 d1.startTs === d2.startTs && 199 d1.endTs === d2.endTs 200 ); 201 } 202} 203