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 entry?: HeapSnapshotStruct 73): Promise<unknown> { 74 return new Promise((resolve, reject) => { 75 if (clickRowType === TraceRow.ROW_TYPE_HEAP_SNAPSHOT) { 76 if (row.findHoverStruct) { 77 row.findHoverStruct(); 78 } else { 79 HeapSnapshotStruct.hoverSnapshotStruct = HeapSnapshotStruct.hoverSnapshotStruct || row.getHoverStruct(); 80 } 81 if (HeapSnapshotStruct.hoverSnapshotStruct || entry) { 82 HeapSnapshotStruct.selectSnapshotStruct = entry || HeapSnapshotStruct.hoverSnapshotStruct; 83 sp.traceSheetEL?.displaySnapshotData( 84 HeapSnapshotStruct.selectSnapshotStruct!, 85 row!.dataListCache, 86 //@ts-ignore 87 snapshotClickHandler 88 ); 89 } 90 reject(new Error()); 91 } else { 92 resolve(null); 93 } 94 }); 95} 96export class HeapSnapshotStruct extends BaseStruct { 97 startTs: number = 0; 98 endTs: number = 0; 99 id: number = 0; 100 pid: number = 0; 101 name: string | undefined; 102 textWidth: number | undefined; 103 size: number = 0; 104 static hoverSnapshotStruct: HeapSnapshotStruct | undefined; 105 static selectSnapshotStruct: HeapSnapshotStruct | undefined; 106 107 static setFrame(node: HeapSnapshotStruct, startNS: number, endNS: number, totalNS: number, frame: Rect): void { 108 node.frame = undefined; 109 if ((node.startTs - startNS || node.startTs - startNS === 0) && node.endTs - node.startTs) { 110 let rectangle: Rect = new Rect( 111 Math.floor(((node.startTs - startNS) / totalNS) * frame.width), 112 0, 113 Math.ceil(((node.endTs - node.startTs) / totalNS) * frame.width), 114 40 115 ); 116 node.frame = rectangle; 117 } 118 } 119 120 static draw(ctx: CanvasRenderingContext2D, data: HeapSnapshotStruct): void { 121 if (data.frame) { 122 ctx.fillStyle = 'rgb(86,192,197)'; 123 ctx!.fillRect(data.frame!.x, data.frame!.y + padding, data.frame!.width, data.frame!.height - padding * 2); 124 if (data.frame!.width > 7) { 125 ctx.globalAlpha = 1.0; 126 ctx.lineWidth = 1; 127 ctx.fillStyle = '#fff'; 128 ctx.textBaseline = 'middle'; 129 ctx.font = '12px sans-serif'; 130 HeapSnapshotStruct.drawString(ctx, data.name || '', 3, data.frame!, data, 4); 131 HeapSnapshotStruct.drawString(ctx, Utils.getBinaryByteWithUnit(data.size) || '', 9, data.frame!, data, 2); 132 } 133 if ( 134 HeapSnapshotStruct.selectSnapshotStruct && 135 HeapSnapshotStruct.equals(HeapSnapshotStruct.selectSnapshotStruct, data) 136 ) { 137 ctx.strokeStyle = '#232c5d'; 138 ctx.lineWidth = 2; 139 ctx.strokeRect(data.frame!.x, data.frame!.y + padding, data.frame!.width - 2, data.frame!.height - padding * 2); 140 } 141 } 142 } 143 144 /** 145 * 146 * @param ctx current context 147 * @param str text 148 * @param textPadding padding 149 * @param frame rectangle 150 * @param data HeapSnapshotStruct 151 * @param location the position of the string, the bigger the numerical value, the higher the position on the canvas 152 */ 153 static drawString( 154 ctx: CanvasRenderingContext2D, 155 str: string, 156 textPadding: number, 157 frame: Rect, 158 HeapSnapshotdata: HeapSnapshotStruct, 159 location: number 160 ): void { 161 if (HeapSnapshotdata.textWidth === undefined) { 162 HeapSnapshotdata.textWidth = ctx.measureText(str).width; 163 } 164 let textWidth = Math.round(HeapSnapshotdata.textWidth / str.length); 165 let maxTextWidth = frame.width - textPadding * 2; 166 if (HeapSnapshotdata.textWidth < maxTextWidth) { 167 let x = Math.floor(frame.width / 2 - HeapSnapshotdata.textWidth / 2 + frame.x + textPadding); 168 ctx.fillText(str, x, Math.floor(frame.y + frame.height / location + textPadding), maxTextWidth); 169 } else { 170 if (maxTextWidth >= textWidth) { 171 let characterNum = maxTextWidth / textWidth; 172 let x = frame.x + textPadding; 173 if (characterNum < 2) { 174 ctx.fillText( 175 str.substring(0, 1), 176 x, 177 Math.floor(frame.y + frame.height / location + textPadding), 178 maxTextWidth 179 ); 180 } else { 181 ctx.fillText( 182 str.substring(0, characterNum - 1) + '...', 183 x, 184 Math.floor(frame.y + frame.height / location + textPadding), 185 maxTextWidth 186 ); 187 } 188 } 189 } 190 } 191 192 static equals(d1: HeapSnapshotStruct, d2: HeapSnapshotStruct): boolean { 193 return ( 194 d1 && 195 d2 && 196 d1.name === d2.name && 197 d1.id === d2.id && 198 d1.pid === d2.pid && 199 d1.startTs === d2.startTs && 200 d1.endTs === d2.endTs 201 ); 202 } 203} 204