All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.mat.parser.internal.snapshot.MultiplePathsFromGCRootsComputerImpl Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2008 SAP AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    SAP AG - initial API and implementation
 *******************************************************************************/
package org.eclipse.mat.parser.internal.snapshot;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayInt;
import org.eclipse.mat.collect.BitField;
import org.eclipse.mat.collect.QueueInt;
import org.eclipse.mat.parser.index.IIndexReader;
import org.eclipse.mat.parser.internal.Messages;
import org.eclipse.mat.parser.internal.SnapshotImpl;
import org.eclipse.mat.snapshot.IMultiplePathsFromGCRootsComputer;
import org.eclipse.mat.snapshot.MultiplePathsFromGCRootsClassRecord;
import org.eclipse.mat.snapshot.MultiplePathsFromGCRootsRecord;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.NamedReference;
import org.eclipse.mat.util.IProgressListener;

public class MultiplePathsFromGCRootsComputerImpl implements IMultiplePathsFromGCRootsComputer {

    int[] objectIds; // the initial objects
    Object[] paths; // paths for each of the objects

    SnapshotImpl snapshot; // snapshot
    IIndexReader.IOne2ManyIndex outboundIndex; // outbound references index

    private BitField excludeInstances;
    private Map> excludeMap;

    private boolean pathsCalculated;

    private static final int NOT_VISITED = -2;
    private static final int NO_PARENT = -1;

    public MultiplePathsFromGCRootsComputerImpl(int[] objectIds, Map> excludeMap, SnapshotImpl snapshot) throws SnapshotException {
        this.snapshot = snapshot;
        this.objectIds = objectIds;
        this.excludeMap = excludeMap;
        outboundIndex = snapshot.getIndexManager().outbound;

        if (excludeMap != null) {
            initExcludeInstances();
        }
    }

    private void initExcludeInstances() throws SnapshotException {
        excludeInstances = new BitField(snapshot.getIndexManager().o2address().size());
        for (IClass clazz : excludeMap.keySet()) {
            int[] objects = clazz.getObjectIds();
            for (int objId : objects) {
                excludeInstances.set(objId);
            }
        }
    }

    private void computePaths(IProgressListener progressListener) throws SnapshotException {
        ArrayList pathsList = new ArrayList();

        // make a breadth first search for the objects, starting from the roots
        int[] parent = bfs(progressListener);

        // then get the shortest path per object
        for (int i = 0; i < objectIds.length; i++) {
            int[] path = getPathFromBFS(objectIds[i], parent);

            /*
             * if there is an exclude filter, for some objects there could be no
             * path, i.e. null is expected then
             */
            if (path != null) {
                pathsList.add(path);
            }
        }

        pathsCalculated = true;
        paths = pathsList.toArray();
    }

    public MultiplePathsFromGCRootsRecord[] getPathsByGCRoot(IProgressListener progressListener) throws SnapshotException {
        if (!pathsCalculated) {
            computePaths(progressListener);
        }

        MultiplePathsFromGCRootsRecord dummy = new MultiplePathsFromGCRootsRecord(-1, -1, snapshot);
        for (int i = 0; i < paths.length; i++) {
            dummy.addPath((int[]) paths[i]);
        }

        return dummy.nextLevel();
    }

    public Object[] getAllPaths(IProgressListener progressListener) throws SnapshotException {
        if (!pathsCalculated) {
            computePaths(progressListener);
        }
        return paths;
    }

    public MultiplePathsFromGCRootsClassRecord[] getPathsGroupedByClass(boolean startFromTheGCRoots, IProgressListener progressListener)
            throws SnapshotException {
        if (!pathsCalculated) {
            computePaths(progressListener);
        }

        MultiplePathsFromGCRootsClassRecord dummy = new MultiplePathsFromGCRootsClassRecord(null, -1, startFromTheGCRoots, snapshot);
        for (int i = 0; i < paths.length; i++) {
            dummy.addPath((int[]) paths[i]);
        }

        return dummy.nextLevel();
    }

    private boolean refersOnlyThroughExcluded(int referrerId, int referentId) throws SnapshotException {
        if (!excludeInstances.get(referrerId)) return false;

        IObject referrerObject = snapshot.getObject(referrerId);
        Set excludeFields = excludeMap.get(referrerObject.getClazz());
        if (excludeFields == null) return true; // treat null as all fields

        long referentAddr = snapshot.mapIdToAddress(referentId);

        List refs = referrerObject.getOutboundReferences();
        for (NamedReference reference : refs) {
            if (referentAddr == reference.getObjectAddress() && !excludeFields.contains(reference.getName())) {
                return false;
            }
        }
        return true;
    }

    private int[] bfs(IProgressListener progressListener) throws SnapshotException {
        // number objects in the heap
        final int numObjects = snapshot.getSnapshotInfo().getNumberOfObjects();
        final boolean skipReferences = excludeMap != null; // should some paths
        // be excluded?

        // used to store the parent of each object during the BFS
        int[] parent = new int[numObjects];
        Arrays.fill(parent, NOT_VISITED);

        // use boolean[numObjects] instead of SetInt, as it is faster to check
        boolean[] toBeChecked = new boolean[numObjects];

        int count = 0; // the number of distinct objects whose paths should be
        // calculated
        for (int i : objectIds) {
            if (!toBeChecked[i]) count++;
            toBeChecked[i] = true;
        }

        // use first-in-first-out to get the shortest paths
        QueueInt fifo = new QueueInt(numObjects / 8);

        // initially queue all GC roots
        int[] gcRoots = snapshot.getGCRoots();
        for (int root : gcRoots) {
            fifo.put(root);
            parent[root] = NO_PARENT;
        }

        // used for the progress listener
        int countVisitedObjects = 0;
        int reportFrequency = Math.max(10, numObjects / 100);

        progressListener.beginTask(Messages.MultiplePathsFromGCRootsComputerImpl_FindingPaths, 100);

        // loop until the queue is empty, or all necessary paths are found
        while (fifo.size() > 0 && count > 0) {
            int objectId = fifo.get();

            // was some of the objects of interest reached?
            if (toBeChecked[objectId]) {
                count--; // reduce the remaining work
            }

            // queue any unprocessed referenced object
            int[] outbound = outboundIndex.get(objectId);
            for (int child : outbound) {
                if (parent[child] == NOT_VISITED) {
                    if (skipReferences) {
                        if (refersOnlyThroughExcluded(objectId, child)) continue;
                    }
                    parent[child] = objectId;
                    fifo.put(child);
                }
            }

            countVisitedObjects++;
            if (countVisitedObjects % reportFrequency == 0) {
                if (progressListener.isCanceled())
                    throw new IProgressListener.OperationCanceledException();
                progressListener.worked(1);
            }
        }
        progressListener.done();
        return parent;
    }

    /*
     * Returns the shortest path to an object, using the stored parent of every
     * needed object calculated during a BFS
     *
     * @param int objectId the object to which a path should be calculated
     *
     * @param int[] parent an array, result of a BSF, which keeps a parent[i] is
     * the parent of the object with index i, as calculated during the BFS
     *
     * @return int[] the shortest path from a GC root. The object of interest is
     * at index 0, the GC root at index length-1
     */
    private int[] getPathFromBFS(int objectId, int[] parent) {
        // check if the object wasn't reached at all. This may happen if some
        // paths are excluded
        if (parent[objectId] == NOT_VISITED) return null;

        ArrayInt path = new ArrayInt();
        while (objectId != NO_PARENT) {
            path.add(objectId);
            objectId = parent[objectId];
        }

        return path.toArray();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy