| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ahat.heapdump; |
| |
| import com.android.tools.perflib.heap.StackFrame; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class Site implements Diffable<Site> { |
| // The site that this site was directly called from. |
| // mParent is null for the root site. |
| private Site mParent; |
| |
| private String mMethodName; |
| private String mSignature; |
| private String mFilename; |
| private int mLineNumber; |
| |
| // To identify this site, we pick a stack trace that includes the site. |
| // mId is the id of an object allocated at that stack trace, and mDepth |
| // is the number of calls between this site and the innermost site of |
| // allocation of the object with mId. |
| // For the root site, mId is 0 and mDepth is 0. |
| private long mId; |
| private int mDepth; |
| |
| // The total size of objects allocated in this site (including child sites), |
| // organized by heap index. Heap indices outside the range of mSizesByHeap |
| // implicitly have size 0. |
| private Size[] mSizesByHeap; |
| |
| // List of child sites. |
| private List<Site> mChildren; |
| |
| // List of all objects allocated in this site (including child sites). |
| private List<AhatInstance> mObjects; |
| private List<ObjectsInfo> mObjectsInfos; |
| private Map<AhatHeap, Map<AhatClassObj, ObjectsInfo>> mObjectsInfoMap; |
| |
| private Site mBaseline; |
| |
| public static class ObjectsInfo implements Diffable<ObjectsInfo> { |
| public AhatHeap heap; |
| public AhatClassObj classObj; // May be null. |
| public long numInstances; |
| public Size numBytes; |
| private ObjectsInfo baseline; |
| |
| /** |
| * Construct a new, empty objects info for the given heap and class |
| * combination. |
| */ |
| public ObjectsInfo(AhatHeap heap, AhatClassObj classObj) { |
| this.heap = heap; |
| this.classObj = classObj; |
| this.numInstances = 0; |
| this.numBytes = Size.ZERO; |
| this.baseline = this; |
| } |
| |
| /** |
| * Returns the name of the class this ObjectsInfo is associated with. |
| */ |
| public String getClassName() { |
| return classObj == null ? "???" : classObj.getName(); |
| } |
| |
| public void setBaseline(ObjectsInfo baseline) { |
| this.baseline = baseline; |
| } |
| |
| @Override public ObjectsInfo getBaseline() { |
| return baseline; |
| } |
| |
| @Override public boolean isPlaceHolder() { |
| return false; |
| } |
| } |
| |
| /** |
| * Construct a root site. |
| */ |
| public Site(String name) { |
| this(null, name, "", "", 0, 0, 0); |
| } |
| |
| public Site(Site parent, String method, String signature, String file, |
| int line, long id, int depth) { |
| mParent = parent; |
| mMethodName = method; |
| mSignature = signature; |
| mFilename = file; |
| mLineNumber = line; |
| mId = id; |
| mDepth = depth; |
| mSizesByHeap = new Size[0]; |
| mChildren = new ArrayList<Site>(); |
| mObjects = new ArrayList<AhatInstance>(); |
| mObjectsInfos = new ArrayList<ObjectsInfo>(); |
| mObjectsInfoMap = new HashMap<AhatHeap, Map<AhatClassObj, ObjectsInfo>>(); |
| mBaseline = this; |
| } |
| |
| /** |
| * Add an instance to this site. |
| * Returns the site at which the instance was allocated. |
| * @param frames - The list of frames in the stack trace, starting with the inner-most frame. |
| * @param depth - The number of frames remaining before the inner-most frame is reached. |
| */ |
| Site add(StackFrame[] frames, int depth, AhatInstance inst) { |
| return add(this, frames, depth, inst); |
| } |
| |
| private static Site add(Site site, StackFrame[] frames, int depth, AhatInstance inst) { |
| while (true) { |
| site.mObjects.add(inst); |
| |
| ObjectsInfo info = site.getObjectsInfo(inst.getHeap(), inst.getClassObj()); |
| if (inst.isReachable()) { |
| AhatHeap heap = inst.getHeap(); |
| if (heap.getIndex() >= site.mSizesByHeap.length) { |
| Size[] newSizes = new Size[heap.getIndex() + 1]; |
| for (int i = 0; i < site.mSizesByHeap.length; i++) { |
| newSizes[i] = site.mSizesByHeap[i]; |
| } |
| for (int i = site.mSizesByHeap.length; i < heap.getIndex() + 1; i++) { |
| newSizes[i] = Size.ZERO; |
| } |
| site.mSizesByHeap = newSizes; |
| } |
| site.mSizesByHeap[heap.getIndex()] |
| = site.mSizesByHeap[heap.getIndex()].plus(inst.getSize()); |
| |
| info.numInstances++; |
| info.numBytes = info.numBytes.plus(inst.getSize()); |
| } |
| |
| if (depth > 0) { |
| StackFrame next = frames[depth - 1]; |
| Site child = null; |
| for (int i = 0; i < site.mChildren.size(); i++) { |
| Site curr = site.mChildren.get(i); |
| if (curr.mLineNumber == next.getLineNumber() |
| && curr.mMethodName.equals(next.getMethodName()) |
| && curr.mSignature.equals(next.getSignature()) |
| && curr.mFilename.equals(next.getFilename())) { |
| child = curr; |
| break; |
| } |
| } |
| if (child == null) { |
| child = new Site(site, next.getMethodName(), next.getSignature(), |
| next.getFilename(), next.getLineNumber(), inst.getId(), depth - 1); |
| site.mChildren.add(child); |
| } |
| depth = depth - 1; |
| site = child; |
| } else { |
| return site; |
| } |
| } |
| } |
| |
| // Get the size of a site for a specific heap. |
| public Size getSize(AhatHeap heap) { |
| int index = heap.getIndex(); |
| return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : Size.ZERO; |
| } |
| |
| /** |
| * Get the list of objects allocated under this site. Includes objects |
| * allocated in children sites. |
| */ |
| public Collection<AhatInstance> getObjects() { |
| return mObjects; |
| } |
| |
| /** |
| * Returns the ObjectsInfo at this site for the given heap and class |
| * objects. Creates a new empty ObjectsInfo if none existed before. |
| */ |
| ObjectsInfo getObjectsInfo(AhatHeap heap, AhatClassObj classObj) { |
| Map<AhatClassObj, ObjectsInfo> classToObjectsInfo = mObjectsInfoMap.get(heap); |
| if (classToObjectsInfo == null) { |
| classToObjectsInfo = new HashMap<AhatClassObj, ObjectsInfo>(); |
| mObjectsInfoMap.put(heap, classToObjectsInfo); |
| } |
| |
| ObjectsInfo info = classToObjectsInfo.get(classObj); |
| if (info == null) { |
| info = new ObjectsInfo(heap, classObj); |
| mObjectsInfos.add(info); |
| classToObjectsInfo.put(classObj, info); |
| } |
| return info; |
| } |
| |
| public List<ObjectsInfo> getObjectsInfos() { |
| return mObjectsInfos; |
| } |
| |
| // Get the combined size of the site for all heaps. |
| public Size getTotalSize() { |
| Size total = Size.ZERO; |
| for (int i = 0; i < mSizesByHeap.length; i++) { |
| total = total.plus(mSizesByHeap[i]); |
| } |
| return total; |
| } |
| |
| /** |
| * Return the site this site was called from. |
| * Returns null for the root site. |
| */ |
| public Site getParent() { |
| return mParent; |
| } |
| |
| public String getMethodName() { |
| return mMethodName; |
| } |
| |
| public String getSignature() { |
| return mSignature; |
| } |
| |
| public String getFilename() { |
| return mFilename; |
| } |
| |
| public int getLineNumber() { |
| return mLineNumber; |
| } |
| |
| /** |
| * Returns the id of some object allocated in this site. |
| */ |
| public long getId() { |
| return mId; |
| } |
| |
| /** |
| * Returns the number of frames between this site and the site where the |
| * object with id getId() was allocated. |
| */ |
| public int getDepth() { |
| return mDepth; |
| } |
| |
| public List<Site> getChildren() { |
| return mChildren; |
| } |
| |
| void setBaseline(Site baseline) { |
| mBaseline = baseline; |
| } |
| |
| @Override public Site getBaseline() { |
| return mBaseline; |
| } |
| |
| @Override public boolean isPlaceHolder() { |
| return false; |
| } |
| |
| /** |
| * Adds a place holder instance to this site and all parent sites. |
| */ |
| void addPlaceHolderInstance(AhatInstance placeholder) { |
| for (Site site = this; site != null; site = site.mParent) { |
| site.mObjects.add(placeholder); |
| } |
| } |
| } |