• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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