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 { HeapLoader } from './logic/HeapLoader'; 17import { 18 AllocationFunction, 19 ConstructorComparison, 20 ConstructorItem, 21 ConstructorType, 22 FileInfo, 23} from './model/UiStruct'; 24import { HeapNodeToConstructorItem } from './utils/Utils'; 25import { FileStruct, HeapSample, HeapTraceFunctionInfo } from './model/DatabaseStruct'; 26 27export interface ParseListener { 28 parseDone(fileModule: Array<FileInfo>): void; 29} 30 31export class HeapDataInterface { 32 private static instance: HeapDataInterface; 33 private isParseDone = false; 34 private parseListener!: ParseListener; 35 private fileStructs!: Array<FileStruct>; 36 private baseFileStruct!: FileStruct | null; 37 38 public static getInstance() { 39 if (!this.instance) { 40 this.instance = new HeapDataInterface(); 41 } 42 return this.instance; 43 } 44 45 private getFileStructById(id: number): FileStruct | null { 46 for (let fileStruct of this.fileStructs) { 47 if (fileStruct.id == id) { 48 return fileStruct; 49 } 50 } 51 return null; 52 } 53 54 /** 55 * tell interface current file to provider file interface 56 * @param fileId file id 57 */ 58 public setFileId(fileId: number) { 59 this.baseFileStruct = this.getFileStructById(fileId); 60 } 61 62 /** 63 * set ParseListener to callback when node_files table in database is parse done 64 * @param listener callback 65 */ 66 public setPraseListener(listener: ParseListener) { 67 this.parseListener = listener; 68 } 69 70 /** 71 * obtain the list of Constructor classes 72 * @returns Array<ConstructorItem> 73 */ 74 public getClassesListForSummary(fileId: number, minNodeId?: number, maxNodeId?: number): Array<ConstructorItem> { 75 let constructorMap; 76 let constructorList = new Array<ConstructorItem>(); 77 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(fileId); 78 if (this.isParseDone && filStruct) { 79 constructorMap = filStruct.heapLoader.getClassesForSummary(minNodeId, maxNodeId); 80 constructorMap.forEach((construct, _) => { 81 constructorList.push(construct); 82 }); 83 constructorList.sort(function (a, b) { 84 return b.retainedSize - a.retainedSize; 85 }); 86 } 87 return constructorList; 88 } 89 90 /** 91 * compare base file and target file, calculate delta size and count to target class 92 * @param baseFileId current file id 93 * @param targetFileId select id which file is to compare 94 * @returns diff class list 95 */ 96 public getClassListForComparison(baseFileId: number, targetFileId: number) { 97 let baseFileStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(baseFileId); 98 let targetFileStruct = this.getFileStructById(targetFileId); 99 if (!baseFileStruct || !targetFileStruct) { 100 return []; 101 } 102 let diffClassList = new Array<ConstructorComparison>(); 103 let diffClassMap = baseFileStruct.heapLoader.getClassesForComparison( 104 targetFileId, 105 targetFileStruct.heapLoader.getClassesForSummary() 106 ); 107 if (!diffClassMap || diffClassMap.size === 0) { 108 return []; 109 } 110 111 for (let diffClass of diffClassMap.values()) { 112 diffClassList.push(diffClass); 113 } 114 diffClassList.sort((a, b) => b.addedSize - a.addedSize); 115 return diffClassList; 116 } 117 118 /** 119 * get sample data for timeline 120 * @param fileId timeline file id 121 * @returns time stamp with size 122 */ 123 public getSamples(fileId: number): Array<HeapSample> { 124 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(fileId); 125 if (!filStruct) { 126 return []; 127 } 128 let samples = filStruct.snapshotStruct.samples; 129 return samples; 130 } 131 132 /** 133 * get the functions which call the node 134 * @param node current select node 135 * @returns node.parent 136 */ 137 public getParentFunction(node: AllocationFunction) { 138 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(node.fileId); 139 if (!filStruct) { 140 return; 141 } 142 filStruct.heapLoader.loadAllocationParent(node); 143 } 144 145 /** 146 * get select node children while node type is class 147 * @param node current select node 148 * @returns node.children 149 */ 150 public getNextForConstructor(node: ConstructorItem): Array<ConstructorItem> { 151 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(node.fileId); 152 let children = new Array<ConstructorItem>(); 153 switch (node.type) { 154 case ConstructorType.ClassType: 155 children = node.classChildren; 156 break; 157 case ConstructorType.InstanceType: 158 case ConstructorType.FiledType: 159 children = filStruct!.heapLoader.getNextNode(node); 160 break; 161 } 162 return children; 163 } 164 165 public getNextForComparison(comparisonNode: ConstructorComparison): Array<ConstructorItem> { 166 let baseFileStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(comparisonNode.fileId); 167 let targetFileStruct = this.getFileStructById(comparisonNode.targetFileId); 168 if (!baseFileStruct || !targetFileStruct) { 169 return []; 170 } 171 let children = new Array<ConstructorItem>(); 172 if (comparisonNode.type === ConstructorType.ComparisonType) { 173 for (let idx of comparisonNode.addedIndx) { 174 let node = baseFileStruct.heapLoader.getNodes()[idx]; 175 let compareNode = HeapNodeToConstructorItem(node); 176 compareNode.type = ConstructorType.InstanceType; 177 compareNode.addedSize = compareNode.shallowSize; 178 compareNode.isAdd = true; 179 compareNode.hasNext = node.edgeCount > 0; 180 children.push(compareNode); 181 } 182 183 for (let idx of comparisonNode.deletedIdx) { 184 let node = targetFileStruct.heapLoader.getNodes()[idx]; 185 let compareNode = HeapNodeToConstructorItem(node); 186 compareNode.type = ConstructorType.InstanceType; 187 compareNode.removedSize = compareNode.shallowSize; 188 compareNode.isAdd = false; 189 compareNode.hasNext = node.edgeCount > 0; 190 children.push(compareNode); 191 } 192 } else { 193 children = this.getNextForConstructor(comparisonNode); 194 } 195 return children; 196 } 197 198 /** 199 * get nodes which referenced this node 200 * @param constructor current node 201 * @returns reference nodes 202 */ 203 public getRetains(constructor: ConstructorItem) { 204 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(constructor.fileId); 205 if (!filStruct) { 206 return []; 207 } 208 return filStruct?.heapLoader.getRetains(constructor); 209 } 210 211 /** 212 * get AllocationStack page data 213 * @param node the row of data clicked 214 * @returns AllocationStackFrame[] 215 */ 216 public getAllocationStackData(node: ConstructorItem): Array<HeapTraceFunctionInfo> { 217 let functions = new Array<HeapTraceFunctionInfo>(); 218 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(node.fileId); 219 if (!filStruct && (node.type == ConstructorType.ClassType || node.type == ConstructorType.RetainersType)) { 220 return functions; 221 } else { 222 functions = filStruct!.heapLoader.getAllocationStack(node.traceNodeId); 223 } 224 return functions; 225 } 226 227 /** 228 * obtain the minimum id of the node 229 * @param fileId current file id 230 * @returns minNodeId 231 */ 232 public getMinNodeId(fileId: number): number | undefined { 233 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(fileId); 234 if (!filStruct) { 235 return undefined; 236 } 237 return filStruct!.heapLoader.getMinAndMaxNodeId().minNodeId; 238 } 239 240 /** 241 * obtain the maximum id of the node 242 * @param fileId current file id 243 * @returns maxNodeId 244 */ 245 public getMaxNodeId(fileId: number): number | undefined { 246 let filStruct = this.baseFileStruct ? this.baseFileStruct : this.getFileStructById(fileId); 247 if (!filStruct) { 248 return undefined; 249 } 250 return filStruct!.heapLoader.getMinAndMaxNodeId().maxNodeId; 251 } 252 253 async parseData(fileModule: Array<FileStruct>) { 254 this.fileStructs = fileModule; 255 this.isParseDone = false; 256 let percent: number; 257 for (let fileStruct of fileModule) { 258 let heapLoader = new HeapLoader(fileStruct); 259 fileStruct.heapLoader = heapLoader; 260 percent = 50 + Math.floor(50 / fileModule.length) * (fileModule.indexOf(fileStruct) + 1); 261 } 262 this.isParseDone = true; 263 if (this.parseListener) { 264 this.parseListener.parseDone(fileModule); 265 } 266 } 267 268 /** 269 * get all file struct in database 270 * @returns all fileInfo 271 */ 272 public getFileStructs(): Array<FileInfo> { 273 return this.fileStructs; 274 } 275 276 /** 277 * clear Cache 278 */ 279 public clearData() { 280 if (!this.fileStructs) { 281 return; 282 } 283 for (let file of this.fileStructs) { 284 file.snapshotStruct.clear(); 285 file.heapLoader.clear(); 286 } 287 this.fileStructs.length = 0; 288 } 289} 290