com.android.ahat.AhatSnapshot.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ahat.AhatSnapshot.java

Source

/*
 * 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;

import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Heap;
import com.android.tools.perflib.heap.Instance;
import com.android.tools.perflib.heap.RootObj;
import com.android.tools.perflib.heap.RootType;
import com.android.tools.perflib.heap.Snapshot;
import com.android.tools.perflib.heap.StackFrame;
import com.android.tools.perflib.heap.StackTrace;

import com.google.common.collect.Lists;

import gnu.trove.TObjectProcedure;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * A wrapper over the perflib snapshot that provides the behavior we use in
 * ahat.
 */
class AhatSnapshot {
    private final Snapshot mSnapshot;
    private final List<Heap> mHeaps;

    // Map from Instance to the list of Instances it immediately dominates.
    private final Map<Instance, List<Instance>> mDominated = new HashMap<Instance, List<Instance>>();

    // Collection of objects whose immediate dominator is the SENTINEL_ROOT.
    private final List<Instance> mRooted = new ArrayList<Instance>();

    // Map from roots to their types.
    // Instances are only included if they are roots, and the collection of root
    // types is guaranteed to be non-empty.
    private final Map<Instance, Collection<RootType>> mRoots = new HashMap<Instance, Collection<RootType>>();

    private final Site mRootSite = new Site("ROOT");
    private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();

    private final List<InstanceUtils.NativeAllocation> mNativeAllocations = new ArrayList<InstanceUtils.NativeAllocation>();

    /**
     * Create an AhatSnapshot from an hprof file.
     */
    public static AhatSnapshot fromHprof(File hprof) throws IOException {
        Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprof));
        snapshot.computeDominators();
        return new AhatSnapshot(snapshot);
    }

    /**
     * Construct an AhatSnapshot for the given perflib snapshot.
     * Ther user is responsible for calling snapshot.computeDominators before
     * calling this AhatSnapshot constructor.
     */
    private AhatSnapshot(Snapshot snapshot) {
        mSnapshot = snapshot;
        mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());

        final ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
        for (Heap heap : mHeaps) {
            // Use a single element array for the total to act as a reference to a
            // long.
            final long[] total = new long[] { 0 };
            TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
                @Override
                public boolean execute(Instance inst) {
                    Instance dominator = inst.getImmediateDominator();
                    if (dominator != null) {
                        total[0] += inst.getSize();

                        if (dominator == Snapshot.SENTINEL_ROOT) {
                            mRooted.add(inst);
                        }

                        // Properly label the class of a class object.
                        if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
                            inst.setClassId(javaLangClass.getId());
                        }

                        // Update dominated instances.
                        List<Instance> instances = mDominated.get(dominator);
                        if (instances == null) {
                            instances = new ArrayList<Instance>();
                            mDominated.put(dominator, instances);
                        }
                        instances.add(inst);

                        // Update sites.
                        List<StackFrame> path = Collections.emptyList();
                        StackTrace stack = getStack(inst);
                        int stackId = getStackTraceSerialNumber(stack);
                        if (stack != null) {
                            StackFrame[] frames = getStackFrames(stack);
                            if (frames != null && frames.length > 0) {
                                path = Lists.reverse(Arrays.asList(frames));
                            }
                        }
                        mRootSite.add(stackId, 0, path.iterator(), inst);

                        // Update native allocations.
                        InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
                        if (alloc != null) {
                            mNativeAllocations.add(alloc);
                        }
                    }
                    return true;
                }
            };
            for (Instance instance : heap.getClasses()) {
                processInstance.execute(instance);
            }
            heap.forEachInstance(processInstance);
            mHeapSizes.put(heap, total[0]);
        }

        // Record the roots and their types.
        for (RootObj root : snapshot.getGCRoots()) {
            Instance inst = root.getReferredInstance();
            Collection<RootType> types = mRoots.get(inst);
            if (types == null) {
                types = new HashSet<RootType>();
                mRoots.put(inst, types);
            }
            types.add(root.getRootType());
        }
    }

    // Note: This method is exposed for testing purposes.
    public ClassObj findClass(String name) {
        return mSnapshot.findClass(name);
    }

    public Instance findInstance(long id) {
        return mSnapshot.findInstance(id);
    }

    public int getHeapIndex(Heap heap) {
        return mSnapshot.getHeapIndex(heap);
    }

    public Heap getHeap(String name) {
        return mSnapshot.getHeap(name);
    }

    /**
     * Returns a collection of instances whose immediate dominator is the
     * SENTINEL_ROOT.
     */
    public List<Instance> getRooted() {
        return mRooted;
    }

    /**
     * Returns true if the given instance is a root.
     */
    public boolean isRoot(Instance inst) {
        return mRoots.containsKey(inst);
    }

    /**
     * Returns the list of root types for the given instance, or null if the
     * instance is not a root.
     */
    public Collection<RootType> getRootTypes(Instance inst) {
        return mRoots.get(inst);
    }

    public List<Heap> getHeaps() {
        return mHeaps;
    }

    public Site getRootSite() {
        return mRootSite;
    }

    /**
     * Look up the site at which the given object was allocated.
     */
    public Site getSiteForInstance(Instance inst) {
        Site site = mRootSite;
        StackTrace stack = getStack(inst);
        if (stack != null) {
            StackFrame[] frames = getStackFrames(stack);
            if (frames != null) {
                List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
                site = mRootSite.getChild(path.iterator());
            }
        }
        return site;
    }

    /**
     * Return a list of those objects immediately dominated by the given
     * instance.
     */
    public List<Instance> getDominated(Instance inst) {
        return mDominated.get(inst);
    }

    /**
     * Return the total size of reachable objects allocated on the given heap.
     */
    public long getHeapSize(Heap heap) {
        return mHeapSizes.get(heap);
    }

    /**
     * Return the class name for the given class object.
     * classObj may be null, in which case "(class unknown)" is returned.
     */
    public static String getClassName(ClassObj classObj) {
        if (classObj == null) {
            return "(class unknown)";
        }
        return classObj.getClassName();
    }

    // Return the stack where the given instance was allocated.
    private static StackTrace getStack(Instance inst) {
        return inst.getStack();
    }

    // Return the list of stack frames for a stack trace.
    private static StackFrame[] getStackFrames(StackTrace stack) {
        return stack.getFrames();
    }

    // Return the serial number of the given stack trace.
    private static int getStackTraceSerialNumber(StackTrace stack) {
        return stack.getSerialNumber();
    }

    // Get the site associated with the given stack id and depth.
    // Returns the root site if no such site found.
    // depth of -1 means the full stack.
    public Site getSite(int stackId, int depth) {
        Site site = mRootSite;
        StackTrace stack = mSnapshot.getStackTrace(stackId);
        if (stack != null) {
            StackFrame[] frames = getStackFrames(stack);
            if (frames != null) {
                List<StackFrame> path = Lists.reverse(Arrays.asList(frames));
                if (depth >= 0) {
                    path = path.subList(0, depth);
                }
                site = mRootSite.getChild(path.iterator());
            }
        }
        return site;
    }

    // Return a list of known native allocations in the snapshot.
    public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
        return mNativeAllocations;
    }
}