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