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