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 { SelectionParam } from '../../../bean/BoxSelection'; 17import { procedurePool } from '../../../database/Procedure'; 18import {queryNativeHookResponseTypes} from "../../../database/sql/NativeHook.sql"; 19 20export class Utils { 21 private static statusMap: Map<string, string> = new Map<string, string>(); 22 private static instance: Utils | null = null; 23 static THREAD_MAP: Map<number, string> = new Map<number, string>(); 24 static PROCESS_MAP: Map<number, string> = new Map<number, string>(); 25 static SCHED_SLICE_MAP: Map< 26 string, 27 { 28 endState: string; 29 priority: number; 30 } 31 > = new Map< 32 string, 33 { 34 endState: string; 35 priority: number; 36 } 37 >(); 38 39 constructor() { 40 Utils.statusMap.set('D', 'Uninterruptible Sleep'); 41 Utils.statusMap.set('D-NIO', 'Uninterruptible Sleep(non-IO)'); 42 Utils.statusMap.set('D-IO', 'Uninterruptible Sleep(IO)'); 43 Utils.statusMap.set('DK', 'Uninterruptible Sleep + Wake Kill'); 44 Utils.statusMap.set('DK-NIO', 'Uninterruptible Sleep(non-IO) + Wake Kill'); 45 Utils.statusMap.set('DK-IO', 'Uninterruptible Sleep(IO) + Wake Kill'); 46 Utils.statusMap.set('S', 'Sleeping'); 47 Utils.statusMap.set('R', 'Runnable'); 48 Utils.statusMap.set('Running', 'Running'); 49 Utils.statusMap.set('R+', 'Runnable (Preempted)'); 50 Utils.statusMap.set('R-B', 'Runnable (Binder)'); 51 Utils.statusMap.set('I', 'Task Dead'); 52 Utils.statusMap.set('T', 'Stopped'); 53 Utils.statusMap.set('t', 'Traced'); 54 Utils.statusMap.set('X', 'Exit (Dead)'); 55 Utils.statusMap.set('Z', 'Exit (Zombie)'); 56 Utils.statusMap.set('P', 'Parked'); 57 Utils.statusMap.set('N', 'No Load'); 58 } 59 60 public static getInstance(): Utils { 61 if (Utils.instance === null) { 62 Utils.instance = new Utils(); 63 } 64 return Utils.instance; 65 } 66 67 public static clearData() { 68 Utils.THREAD_MAP.clear(); 69 Utils.PROCESS_MAP.clear(); 70 Utils.SCHED_SLICE_MAP.clear(); 71 } 72 73 public static getEndState(state: string): string { 74 if (Utils.getInstance().getStatusMap().has(state)) { 75 return Utils.getInstance().getStatusMap().get(state) || 'Unknown State'; 76 } else { 77 if ('' === state || state === null) { 78 return ''; 79 } 80 return 'Unknown State'; 81 } 82 } 83 84 public static isBinder(data: any): boolean { 85 return ( 86 data.funName != null && 87 (data.funName.toLowerCase().startsWith('binder transaction async') || //binder transaction 88 data.funName.toLowerCase().startsWith('binder async') || 89 data.funName.toLowerCase().startsWith('binder reply')) 90 ); 91 } 92 93 public static transferPTSTitle(ptsValue: any) { 94 if (ptsValue.startsWith('S-')) { 95 return Utils.getEndState(ptsValue.replace('S-', '')); 96 } else if (ptsValue.startsWith('P-')) { 97 let pid = ptsValue.replace('P-', ''); 98 let process = Utils.PROCESS_MAP.get(parseInt(pid)) || 'Process'; 99 return `${process} [${pid}]`; 100 } else if (ptsValue.startsWith('T-')) { 101 let tid = ptsValue.replace('T-', ''); 102 let thread = Utils.THREAD_MAP.get(parseInt(tid)) || 'Thread'; 103 return `${thread} [${tid}]`; 104 } else { 105 return ''; 106 } 107 } 108 109 public static transferBinderTitle(value: any) { 110 if (value.startsWith('P-')) { 111 let pid = value.replace('P-', ''); 112 let process = Utils.PROCESS_MAP.get(parseInt(pid)) || 'Process'; 113 return `${process} [${pid}]`; 114 } else if (value.startsWith('T-')) { 115 let tid = value.replace('T-', ''); 116 let thread = Utils.THREAD_MAP.get(parseInt(tid)) || 'Thread'; 117 return `${thread} [${tid}]`; 118 } else { 119 return ''; 120 } 121 } 122 123 public static getStateColor(state: string): string { 124 if (state === 'D-NIO' || state === 'DK-NIO') { 125 return '#795548'; 126 } else if (state === 'D-IO' || state === 'DK-IO' || state === 'D' || state === 'DK') { 127 return '#f19b38'; 128 } else if (state === 'R' || state === 'R+') { 129 return '#a0b84d'; 130 } else if (state === 'R-B') { 131 return '#87CEFA'; 132 } else if (state === 'I') { 133 return '#673ab7'; 134 } else if (state === 'Running') { 135 return '#467b3b'; 136 } else if (state === 'S') { 137 return '#e0e0e0'; 138 } else { 139 return '#ff6e40'; 140 } 141 } 142 143 public static getTimeString(ns: number): string { 144 let currentTime = ns; 145 let hour1 = 3600_000_000_000; 146 let minute1 = 60_000_000_000; 147 let second1 = 1_000_000_000; 148 let millisecond1 = 1_000_000; 149 let microsecond1 = 1_000; 150 let res = ''; 151 if (currentTime >= hour1) { 152 res += Math.floor(currentTime / hour1) + 'h '; 153 currentTime = currentTime - Math.floor(currentTime / hour1) * hour1; 154 } 155 if (currentTime >= minute1) { 156 res += Math.floor(currentTime / minute1) + 'm '; 157 currentTime = currentTime - Math.floor(ns / minute1) * minute1; 158 } 159 if (currentTime >= second1) { 160 res += Math.floor(currentTime / second1) + 's '; 161 currentTime = currentTime - Math.floor(currentTime / second1) * second1; 162 } 163 if (currentTime >= millisecond1) { 164 res += Math.floor(currentTime / millisecond1) + 'ms '; 165 currentTime = currentTime - Math.floor(currentTime / millisecond1) * millisecond1; 166 } 167 if (currentTime >= microsecond1) { 168 res += Math.floor(currentTime / microsecond1) + 'μs '; 169 currentTime = currentTime - Math.floor(currentTime / microsecond1) * microsecond1; 170 } 171 if (currentTime > 0) { 172 res += currentTime + 'ns '; 173 } 174 if (res === '') { 175 res = ns + ''; 176 } 177 return res; 178 } 179 180 public static getProbablyTime(timeNs: number): string { 181 let currentNs = timeNs; 182 let probablyHour = 3600_000_000_000; 183 let probablyMinute1 = 60_000_000_000; 184 let probablySecond1 = 1_000_000_000; 185 let probablyMillisecond1 = 1_000_000; 186 let probablyMicrosecond1 = 1_000; 187 let res = ''; 188 if (currentNs >= probablyHour) { 189 res += (currentNs / probablyHour).toFixed(2) + 'h '; 190 } else if (currentNs >= probablyMinute1) { 191 res += (currentNs / probablyMinute1).toFixed(2) + 'm '; 192 } else if (currentNs >= probablySecond1) { 193 res += (currentNs / probablySecond1).toFixed(2) + 's '; 194 } else if (currentNs >= probablyMillisecond1) { 195 res += (currentNs / probablyMillisecond1).toFixed(2) + 'ms '; 196 } else if (currentNs >= probablyMicrosecond1) { 197 res += (currentNs / probablyMicrosecond1).toFixed(2) + 'μs '; 198 } else if (currentNs > 0) { 199 res += currentNs + 'ns '; 200 } else if (res === '') { 201 res = timeNs + ''; 202 } 203 return res; 204 } 205 206 public static getTimeStringHMS(ns: number): string { 207 let currentNs = ns; 208 let hour1 = 3600_000_000_000; 209 let minute1 = 60_000_000_000; 210 let second1 = 1_000_000_000; // 1 second 211 let millisecond1 = 1_000_000; // 1 millisecond 212 let microsecond1 = 1_000; // 1 microsecond 213 let res = ''; 214 if (currentNs >= hour1) { 215 res += Math.floor(currentNs / hour1) + ':'; 216 currentNs = currentNs - Math.floor(currentNs / hour1) * hour1; 217 } 218 if (currentNs >= minute1) { 219 res += Math.floor(currentNs / minute1) + ':'; 220 currentNs = currentNs - Math.floor(ns / minute1) * minute1; 221 } 222 if (currentNs >= second1) { 223 res += Math.floor(currentNs / second1) + ':'; 224 currentNs = currentNs - Math.floor(currentNs / second1) * second1; 225 } 226 if (currentNs >= millisecond1) { 227 res += Math.floor(currentNs / millisecond1) + '.'; 228 currentNs = currentNs - Math.floor(currentNs / millisecond1) * millisecond1; 229 } 230 if (currentNs >= microsecond1) { 231 res += Math.floor(currentNs / microsecond1) + '.'; 232 currentNs = currentNs - Math.floor(currentNs / microsecond1) * microsecond1; 233 } 234 if (currentNs > 0) { 235 res += currentNs + ''; 236 } 237 if (res === '') { 238 res = ns + ''; 239 } 240 return res; 241 } 242 243 public static getByteWithUnit(bytes: number): string { 244 if (bytes < 0) { 245 return '-' + this.getByteWithUnit(Math.abs(bytes)); 246 } 247 let currentByte = bytes; 248 let kb1 = 1 << 10; 249 let mb1 = (1 << 10) << 10; 250 let gb1 = ((1 << 10) << 10) << 10; // 1 gb 251 let res = ''; 252 if (currentByte > gb1) { 253 res += (currentByte / gb1).toFixed(2) + ' Gb'; 254 } else if (currentByte > mb1) { 255 res += (currentByte / mb1).toFixed(2) + ' Mb'; 256 } else if (currentByte > kb1) { 257 res += (currentByte / kb1).toFixed(2) + ' Kb'; 258 } else { 259 res += Math.round(currentByte) + ' byte'; 260 } 261 return res; 262 } 263 264 public static groupByMap(array: Array<any>, key: string) { 265 let result = new Map(); 266 array.forEach((item) => { 267 let value = item[key]; 268 if (!result.has(value)) { 269 result.set(value, []); 270 } 271 result.get(value).push(item); 272 }); 273 return result; 274 } 275 276 public static groupBy(array: Array<any>, key: string) { 277 return array.reduce((pre, current, index, arr) => { 278 (pre[current[key]] = pre[current[key]] || []).push(current); 279 return pre; 280 }, {}); 281 } 282 283 public static timeMsFormat2p(ms: number) { 284 let currentNs = ms; 285 let hour1 = 3600_000; 286 let minute1 = 60_000; 287 let second1 = 1_000; // 1 second 288 let result = ''; 289 if (currentNs >= hour1) { 290 result += Math.round(currentNs / hour1).toFixed(2) + 'h'; 291 return result; 292 } 293 if (currentNs >= minute1) { 294 result += Math.round(currentNs / minute1).toFixed(2) + 'min'; 295 return result; 296 } 297 if (currentNs >= second1) { 298 result += Math.round(currentNs / second1).toFixed(2) + 's'; 299 return result; 300 } 301 if (currentNs > 0) { 302 result += currentNs.toFixed(2) + 'ms'; 303 return result; 304 } 305 if (result === '') { 306 result = '0s'; 307 } 308 return result; 309 } 310 311 public static uuid(): string { 312 // @ts-ignore 313 return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => 314 (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) 315 ); 316 } 317 318 public static getBinaryKBWithUnit(kbytes: number): string { 319 if (kbytes === 0) { 320 return '0KB'; 321 } 322 let currentBytes = kbytes; 323 let mib1 = 1024; 324 let gib1 = 1024 * 1024; 325 let res = ''; 326 if (currentBytes >= gib1) { 327 res += (currentBytes / gib1).toFixed(2) + 'GB'; 328 } else if (currentBytes >= mib1) { 329 res += (currentBytes / mib1).toFixed(2) + 'MB'; 330 } else { 331 res += currentBytes.toFixed(2) + 'KB'; 332 } 333 return res; 334 } 335 336 public static getBinaryByteWithUnit(bytes: number): string { 337 if (bytes === 0) { 338 return '0Bytes'; 339 } 340 let currentBytes = bytes; 341 let kib1 = 1024; 342 let mib1 = 1024 * 1024; 343 let gib1 = 1024 * 1024 * 1024; 344 let res = ''; 345 if (bytes < 0) { 346 res = '-'; 347 currentBytes = Math.abs(currentBytes); 348 } 349 if (currentBytes >= gib1) { 350 res += (currentBytes / gib1).toFixed(2) + 'GB'; 351 } else if (currentBytes >= mib1) { 352 res += (currentBytes / mib1).toFixed(2) + 'MB'; 353 } else if (currentBytes >= kib1) { 354 res += (currentBytes / kib1).toFixed(2) + 'KB'; 355 } else { 356 res += currentBytes.toFixed(2) + 'Bytes'; 357 } 358 return res; 359 } 360 361 public static getTimeStampHMS(ns: number): string { 362 let currentNs = ns; 363 let hour1 = 3600_000_000_000; 364 let minute1 = 60_000_000_000; 365 let second1 = 1_000_000_000; // 1 second 366 let millisecond1 = 1_000_000; // 1 millisecond 367 let microsecond1 = 1_000; // 1 microsecond 368 let res = ''; 369 if (currentNs >= hour1) { 370 res += this.getCompletionTime(Math.floor(currentNs / hour1), 2) + ':'; 371 currentNs = currentNs - Math.floor(currentNs / hour1) * hour1; 372 } 373 if (currentNs >= minute1) { 374 res += this.getCompletionTime(Math.floor(currentNs / minute1), 2) + ':'; 375 currentNs = currentNs - Math.floor(ns / minute1) * minute1; 376 } 377 if (currentNs >= second1) { 378 res += this.getCompletionTime(Math.floor(currentNs / second1), 2) + ':'; 379 currentNs = currentNs - Math.floor(currentNs / second1) * second1; 380 } else { 381 res += '00:'; 382 } 383 if (currentNs >= millisecond1) { 384 res += this.getCompletionTime(Math.floor(currentNs / millisecond1), 3) + '.'; 385 currentNs = currentNs - Math.floor(currentNs / millisecond1) * millisecond1; 386 } else { 387 res += '000.'; 388 } 389 if (currentNs >= microsecond1) { 390 res += this.getCompletionTime(Math.floor(currentNs / microsecond1), 3) + '.'; 391 currentNs = currentNs - Math.floor(currentNs / microsecond1) * microsecond1; 392 } else { 393 res += '000'; 394 } 395 if (currentNs > 0) { 396 res += this.getCompletionTime(currentNs, 3); 397 } 398 if (res === '') { 399 res = ns + ''; 400 } 401 return res; 402 } 403 404 public static getDurString(ns: number): string { 405 let currentNs = ns; 406 let second1 = 1000000000; 407 let millisecond1 = 1000000; 408 let res = ''; 409 if (currentNs >= second1) { 410 let cu = currentNs / second1; 411 res += cu.toFixed(3) + ' s '; 412 return res; 413 } 414 if (currentNs >= millisecond1) { 415 res += Math.floor(currentNs / millisecond1) + ' ms '; 416 return res; 417 } 418 if (res === '') { 419 res = ns + ''; 420 } 421 return res; 422 } 423 424 private static getCompletionTime(time: number, maxLength: number): string { 425 if (maxLength === 2) { 426 if (time.toString().length === 2) { 427 return '' + time; 428 } else { 429 return '0' + time; 430 } 431 } else if (maxLength === 3) { 432 if (time.toString().length === 3) { 433 return time.toString(); 434 } else if (time.toString().length === 2) { 435 return '0' + time; 436 } else { 437 return '00' + time; 438 } 439 } else { 440 return '0'; 441 } 442 } 443 444 public getStatusMap(): Map<string, string> { 445 return Utils.statusMap; 446 } 447 448 public static removeDuplicates(array1: any[], array2: any[], key: string) { 449 let obj: any = {}; 450 return array1.concat(array2).reduce(function (total, item) { 451 if (!obj[`${item[key]}-${item['pid']}`]) { 452 obj[`${item[key]}-${item['pid']}`] = true; 453 total.push(item); 454 } 455 return total; 456 }, []); 457 } 458 459 static getFrequencyWithUnit = (maxFreq: number) => { 460 let maxFreqObj = { 461 maxFreqName: ' ', 462 maxFreq: 0, 463 }; 464 let units: Array<string> = ['', 'K', 'M', 'G', 'T', 'E']; 465 let sb = ' '; 466 if (maxFreq > 0) { 467 let log10: number = Math.ceil(Math.log10(maxFreq)); 468 let pow10: number = Math.pow(10, log10); 469 let afterCeil: number = Math.ceil(maxFreq / (pow10 / 4)) * (pow10 / 4); 470 maxFreqObj.maxFreq = afterCeil; 471 let unitIndex: number = Math.floor(log10 / 3); 472 sb = `${afterCeil / Math.pow(10, unitIndex * 3)}${units[unitIndex + 1]}`; 473 } 474 maxFreqObj.maxFreqName = sb.toString(); 475 return maxFreqObj; 476 }; 477 478 public static getTimeIsCross(startTime: number, endTime: number, startTime1: number, endTime1: number) { 479 return Math.max(startTime, startTime1) <= Math.min(endTime, endTime1); 480 } 481 482 initResponseTypeList(val: SelectionParam): void { 483 const isStatistic = val.nativeMemoryStatistic.length > 0; 484 const selection = isStatistic ? val.nativeMemoryStatistic : val.nativeMemory; 485 let types: Array<string | number> = []; 486 if (selection.indexOf('All Heap & Anonymous VM') != -1) { 487 if (isStatistic) { 488 types.push(0, 1); 489 } else { 490 types.push("'AllocEvent'", "'MmapEvent'"); 491 } 492 } else { 493 if (selection.indexOf('All Heap') != -1) { 494 if (isStatistic) { 495 types.push(0); 496 } else { 497 types.push("'AllocEvent'"); 498 } 499 } 500 if (selection.indexOf('All Anonymous VM') != -1) { 501 if (isStatistic) { 502 types.push(1); 503 } else { 504 types.push("'MmapEvent'"); 505 } 506 } 507 } 508 queryNativeHookResponseTypes(val.leftNs, val.rightNs, types, isStatistic).then((res) => { 509 procedurePool.submitWithName('logic0', 'native-memory-init-responseType', res, undefined, () => {}); 510 }); 511 } 512 513 setCurrentSelectIPid(ipid: number): void { 514 procedurePool.submitWithName('logic0', 'native-memory-set-current_ipid', ipid, undefined, () => {}); 515 } 516} 517