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