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 { ColorUtils } from "../../component/trace/base/ColorUtils.js"; 17import { 18 BaseStruct, 19 drawFlagLine, 20 drawLines, 21 drawLoading, 22 drawSelection, PerfRender, 23 RequestMessage 24} from "./ProcedureWorkerCommon.js"; 25 26export class FileSystemRender extends PerfRender{ 27 render(req: RequestMessage, list: Array<any>, filter: Array<any>,dataList2:Array<any>){ 28 let groupBy10MS = req.scale > 20_000_000; 29 let isDiskIO :boolean = req.type!.includes("disk-io"); 30 if(isDiskIO){ 31 groupBy10MS = true; 32 } 33 if (req.lazyRefresh) { 34 fileSysChart(list, dataList2, req.type!, filter, req.startNS, req.endNS, req.totalNS, req.frame, groupBy10MS, isDiskIO,req.useCache || !req.range.refresh); 35 } else { 36 if (!req.useCache) { 37 fileSysChart(list, dataList2, req.type!, filter, req.startNS, req.endNS, req.totalNS, req.frame, groupBy10MS, isDiskIO,false); 38 } 39 } 40 let hoverStruct : FileSysChartStruct | undefined; 41 if (req.canvas) { 42 req.context.clearRect(0, 0, req.frame.width, req.frame.height); 43 let arr = filter; 44 if (arr.length > 0 && !req.range.refresh && !req.useCache && req.lazyRefresh) { 45 drawLoading(req.context, req.startNS, req.endNS, req.totalNS, req.frame, arr[0].startNS, arr[arr.length - 1].startNS + arr[arr.length - 1].dur) 46 } 47 drawLines(req.context, req.xs, req.frame.height, req.lineColor) 48 req.context.stroke(); 49 req.context.beginPath(); 50 if (req.isHover) { 51 let offset = groupBy10MS ? 0 : 3; 52 for (let re of filter) { 53 if (re.frame && req.hoverX >= re.frame.x - offset && req.hoverX <= re.frame.x + re.frame.width + offset) {//&& req.hoverY >= re.frame.y && req.hoverY <= re.frame.y + re.frame.height 54 hoverStruct = re; 55 break; 56 } 57 } 58 } 59 req.context.fillStyle = req.chartColor ?? ColorUtils.MD_PALETTE[0]; 60 req.context.strokeStyle = req.chartColor ?? ColorUtils.MD_PALETTE[0]; 61 let path = new Path2D(); 62 for (let re of filter) { 63 FileSysChartStruct.draw(req.context, path, re, groupBy10MS); 64 } 65 req.context.fill(path); 66 drawSelection(req.context, req.params); 67 req.context.closePath(); 68 drawFlagLine(req.context, req.flagMoveInfo, req.flagSelectedInfo, req.startNS, req.endNS, req.totalNS, req.frame, req.slicesTime); 69 } 70 let msg = { 71 id: req.id, 72 type: req.type, 73 results: req.canvas ? undefined : filter, 74 hover: hoverStruct 75 } 76 self.postMessage(msg); 77 } 78} 79 80export function fileSysChart(arr: Array<any>, arr2: any, type: string, res: Array<any>, startNS: number, endNS: number, totalNS: number, frame: any, groupBy10MS: boolean, isDiskIO:boolean,use: boolean) { 81 if (use && res.length > 0 ) {//&& !groupBy10MS 82 let pns = (endNS - startNS) / frame.width; 83 let y = frame.y; 84 for (let i = 0; i < res.length; i++) { 85 let it = res[i]; 86 if((it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS){ 87 if (!it.frame) { 88 it.frame = {}; 89 it.frame.y = y; 90 } 91 it.frame.height = it.height; 92 FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame); 93 }else{ 94 it.frame = null; 95 } 96 } 97 return; 98 } 99 res.length = 0; 100 if(arr){ 101 let list: Array<any> = []; 102 let pns = (endNS - startNS) / frame.width; 103 let y = frame.y; 104 if(groupBy10MS){ 105 if (arr2[type] && arr2[type].length > 0) { 106 list = arr2[type]; 107 } else { 108 list = isDiskIO ? FileSysChartStruct.groupBy10MSWithMaxLatency(arr) : FileSysChartStruct.groupBy10MSWithCount(arr); 109 arr2[type] = list; 110 } 111 list = list.filter(it => (it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS); 112 let groups = list.map(it => { 113 if (!it.frame) { 114 it.frame = {}; 115 it.frame.y = y; 116 } 117 it.frame.height = it.height; 118 FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame); 119 return it; 120 }).reduce((pre, current, index, arr) => { 121 (pre[`${current.frame.x}`] = pre[`${current.frame.x}`] || []).push(current); 122 return pre; 123 }, {}); 124 Reflect.ownKeys(groups).map((kv => { 125 // let arr = (groups[kv].sort((a: any, b: any) => b.frame.width - a.frame.width)); 126 res.push(groups[kv][0]); 127 })); 128 }else{ 129 let filter = arr.filter(it => (it.startNS || 0) + (it.dur || 0) > startNS && (it.startNS || 0) < endNS); 130 list = FileSysChartStruct.computeHeightNoGroup(filter,totalNS); 131 list.map(it => { 132 if (!it.frame) { 133 it.frame = {}; 134 it.frame.y = y; 135 } 136 it.frame.height = it.height; 137 FileSysChartStruct.setFrame(it, pns, startNS, endNS, frame); 138 res.push(it); 139 }) 140 } 141 } 142} 143 144export class FileSysChartStruct extends BaseStruct { 145 startNS: number | undefined; 146 endNS: number | undefined; 147 dur: number | undefined; 148 size: number | undefined; 149 height: number | undefined; 150 group10Ms: boolean | undefined; 151 152 static draw(ctx: CanvasRenderingContext2D, path: Path2D, data: FileSysChartStruct, groupBy10MS: boolean) { 153 if (data.frame) { 154 let width = data.frame.width; 155 path.rect(data.frame.x, 40 - (data.height || 0), width, data.height || 0) 156 } 157 } 158 159 static setFrame(node: any, pns: number, startNS: number, endNS: number, frame: any) { 160 if ((node.startNS || 0) < startNS) { 161 node.frame.x = 0; 162 } else { 163 node.frame.x = Math.floor(((node.startNS || 0) - startNS) / pns); 164 } 165 if ((node.startNS || 0) + (node.dur || 0) > endNS) { 166 node.frame.width = frame.width - node.frame.x; 167 } else { 168 node.frame.width = Math.ceil(((node.startNS || 0) + (node.dur || 0) - startNS) / pns - node.frame.x); 169 } 170 if (node.frame.width < 1) { 171 node.frame.width = 1; 172 } 173 } 174 175 static computeHeightNoGroup(array:Array<any>,totalNS:number):Array<any>{ 176 if(array.length > 0){ 177 let time :Array<{time:number,type:number}> = []; 178 array.map((item) => { 179 time.push({time:item.startNS,type:1}); 180 time.push({time:item.endNS || totalNS,type:-1}); 181 }) 182 time = time.sort((a,b) => a.time - b.time); 183 let arr:Array<any> = []; 184 let first = { 185 startNS: time[0].time ?? 0, 186 dur: 0, 187 size:1, 188 group10Ms:false, 189 height:1, 190 }; 191 arr.push(first); 192 let max = 2 193 for (let i = 1,len = time.length; i < len; i++) { 194 let heap = { 195 startNS: time[i].time, 196 dur: 0, 197 size:0, 198 group10Ms:false, 199 height: 0 200 }; 201 arr[i - 1].dur = heap.startNS - arr[i - 1].startNS; 202 if (i == len - 1) { 203 heap.dur = totalNS - heap.startNS; 204 } 205 heap.size = arr[i - 1].size + time[i].type; 206 heap.height = Math.floor(heap.size / 6 * 36); 207 max = max > heap.size ? max : heap.size; 208 arr.push(heap) 209 } 210 arr.map(it => it.height = Math.floor(it.size / max * 36)) 211 return arr; 212 }else{ 213 return []; 214 } 215 } 216 217 static groupBy10MSWithCount(array: Array<any>): Array<any> { 218 let obj = array.map(it => { 219 it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0; 220 return it; 221 }).reduce((pre, current) => { 222 (pre[current["timestamp_group"]] = pre[current["timestamp_group"]] || []).push(current); 223 return pre; 224 }, {}); 225 let arr: any[] = []; 226 let max = 1; 227 for (let aKey in obj) { 228 max = obj[aKey].length > max ? obj[aKey].length : max 229 } 230 for (let aKey in obj) { 231 let ns = parseInt(aKey); 232 let height: number = Math.floor(obj[aKey].length / max * 36); 233 arr.push({ 234 startNS: ns, 235 dur: 1_000_000_0, 236 group10Ms:true, 237 size:obj[aKey].length, 238 height: height, 239 }) 240 } 241 return arr; 242 } 243 244 static groupBy10MSWithMaxLatency(array: Array<any>): Array<any> { 245 let obj = array.map(it => { 246 it.timestamp_group = Math.trunc(it.startNS / 1_000_000_0) * 1_000_000_0; 247 return it; 248 }).reduce((pre, current) => { 249 if(pre[current["timestamp_group"]] == undefined || pre[current["timestamp_group"]] == null){ 250 pre[current["timestamp_group"]] = []; 251 } 252 if(pre[current["timestamp_group"]].length > 0){ 253 let p = pre[current["timestamp_group"]][0]; 254 if(p.dur < current.dur){ 255 pre[current["timestamp_group"]][0] = current; 256 } 257 }else{ 258 pre[current["timestamp_group"]][0] = current; 259 } 260 return pre; 261 }, {}); 262 let arr: any[] = []; 263 let max = 1; 264 for (let aKey in obj) { 265 max = obj[aKey][0].dur > max ? obj[aKey][0].dur : max 266 } 267 for (let aKey in obj) { 268 let ns = parseInt(aKey); 269 let height: number = Math.floor(obj[aKey][0].dur / max * 36); 270 arr.push({ 271 startNS: ns, 272 dur: 1_000_000_0, 273 group10Ms:true, 274 size:obj[aKey][0].dur, 275 height: height, 276 }) 277 } 278 return arr; 279 } 280}