• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 The Android Open Source Project
2//
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
15import {CallsiteInfo} from './state';
16
17export const SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY = 'SPACE';
18export const ALLOC_SPACE_MEMORY_ALLOCATED_KEY = 'ALLOC_SPACE';
19export const OBJECTS_ALLOCATED_NOT_FREED_KEY = 'OBJECTS';
20export const OBJECTS_ALLOCATED_KEY = 'ALLOC_OBJECTS';
21export const PERF_SAMPLES_KEY = 'PERF_SAMPLES';
22
23export const DEFAULT_VIEWING_OPTION = SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY;
24
25export function expandCallsites(
26    data: CallsiteInfo[], clickedCallsiteIndex: number): CallsiteInfo[] {
27  if (clickedCallsiteIndex === -1) return data;
28  const expandedCallsites: CallsiteInfo[] = [];
29  if (clickedCallsiteIndex >= data.length || clickedCallsiteIndex < -1) {
30    return expandedCallsites;
31  }
32  const clickedCallsite = data[clickedCallsiteIndex];
33  expandedCallsites.unshift(clickedCallsite);
34  // Adding parents
35  let parentId = clickedCallsite.parentId;
36  while (parentId > -1) {
37    expandedCallsites.unshift(data[parentId]);
38    parentId = data[parentId].parentId;
39  }
40  // Adding children
41  const parents: number[] = [];
42  parents.push(clickedCallsiteIndex);
43  for (let i = clickedCallsiteIndex + 1; i < data.length; i++) {
44    const element = data[i];
45    if (parents.includes(element.parentId)) {
46      expandedCallsites.push(element);
47      parents.push(element.id);
48    }
49  }
50  return expandedCallsites;
51}
52
53// Merge callsites that have approximately width less than
54// MIN_PIXEL_DISPLAYED. All small callsites in the same depth and with same
55// parent will be merged to one with total size of all merged callsites.
56export function mergeCallsites(data: CallsiteInfo[], minSizeDisplayed: number) {
57  const mergedData: CallsiteInfo[] = [];
58  const mergedCallsites: Map<number, number> = new Map();
59  for (let i = 0; i < data.length; i++) {
60    // When a small callsite is found, it will be merged with other small
61    // callsites of the same depth. So if the current callsite has already been
62    // merged we can skip it.
63    if (mergedCallsites.has(data[i].id)) {
64      continue;
65    }
66    const copiedCallsite = copyCallsite(data[i]);
67    copiedCallsite.parentId =
68        getCallsitesParentHash(copiedCallsite, mergedCallsites);
69
70    let mergedAny = false;
71    // If current callsite is small, find other small callsites with same depth
72    // and parent and merge them into the current one, marking them as merged.
73    if (copiedCallsite.totalSize <= minSizeDisplayed && i + 1 < data.length) {
74      let j = i + 1;
75      let nextCallsite = data[j];
76      while (j < data.length && copiedCallsite.depth === nextCallsite.depth) {
77        if (copiedCallsite.parentId ===
78                getCallsitesParentHash(nextCallsite, mergedCallsites) &&
79            nextCallsite.totalSize <= minSizeDisplayed) {
80          copiedCallsite.totalSize += nextCallsite.totalSize;
81          mergedCallsites.set(nextCallsite.id, copiedCallsite.id);
82          mergedAny = true;
83        }
84        j++;
85        nextCallsite = data[j];
86      }
87      if (mergedAny) {
88        copiedCallsite.name = '[merged]';
89        copiedCallsite.merged = true;
90      }
91    }
92    mergedData.push(copiedCallsite);
93  }
94  return mergedData;
95}
96
97function copyCallsite(callsite: CallsiteInfo): CallsiteInfo {
98  return {
99    id: callsite.id,
100    parentId: callsite.parentId,
101    depth: callsite.depth,
102    name: callsite.name,
103    totalSize: callsite.totalSize,
104    mapping: callsite.mapping,
105    selfSize: callsite.selfSize,
106    merged: callsite.merged,
107    highlighted: callsite.highlighted,
108    location: callsite.location,
109  };
110}
111
112function getCallsitesParentHash(
113    callsite: CallsiteInfo, map: Map<number, number>): number {
114  return map.has(callsite.parentId) ? +map.get(callsite.parentId)! :
115                                      callsite.parentId;
116}
117export function findRootSize(data: CallsiteInfo[]) {
118  let totalSize = 0;
119  let i = 0;
120  while (i < data.length && data[i].depth === 0) {
121    totalSize += data[i].totalSize;
122    i++;
123  }
124  return totalSize;
125}
126