/*
* 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 long[] 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 long numBytes;
private ObjectsInfo baseline;
public ObjectsInfo(AhatHeap heap, AhatClassObj classObj, long numInstances, long numBytes) {
this.heap = heap;
this.classObj = classObj;
this.numInstances = numInstances;
this.numBytes = numBytes;
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 long[1];
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) {
long[] newSizes = new long[heap.getIndex() + 1];
for (int i = 0; i < site.mSizesByHeap.length; i++) {
newSizes[i] = site.mSizesByHeap[i];
}
site.mSizesByHeap = newSizes;
}
site.mSizesByHeap[heap.getIndex()] += inst.getSize();
info.numInstances++;
info.numBytes += 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 long getSize(AhatHeap heap) {
int index = heap.getIndex();
return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : 0;
}
/**
* 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, 0, 0);
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 long getTotalSize() {
long total = 0;
for (int i = 0; i < mSizesByHeap.length; i++) {
total += 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);
}
}
}