blob: cd764e9b4a77894558095854829513f416ee9548 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2013 SAP AG and IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP AG - initial API and implementation
* IBM Corporation - allow larger resize of arrays
*******************************************************************************/
package org.eclipse.mat.parser.internal;
import java.io.IOException;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayUtils;
import org.eclipse.mat.collect.BitField;
import org.eclipse.mat.collect.IteratorInt;
import org.eclipse.mat.parser.index.IIndexReader;
import org.eclipse.mat.parser.index.IndexManager;
import org.eclipse.mat.parser.index.IndexWriter;
import org.eclipse.mat.parser.index.IndexManager.Index;
import org.eclipse.mat.parser.internal.util.IntStack;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.SimpleMonitor;
public class DominatorTree
{
public static void calculate(SnapshotImpl snapshot, IProgressListener listener) throws SnapshotException,
IOException
{
new Calculator(snapshot, listener).compute();
}
static class Calculator
{
SnapshotImpl snapshot;
SimpleMonitor monitor;
IIndexReader.IOne2ManyIndex inboundIndex;
IIndexReader.IOne2ManyIndex outboundIndex;
int[] gcRootsArray;
private BitField gcRootsSet;
int[] bucket;
private int r, n;
private int[] dom;
private int[] parent;
private int[] anchestor;
private int[] vertex;
private int[] label;
private int[] semi;
private static int ROOT_VALUE = -1;
private static int[] ROOT_VALUE_ARR = new int[] { ROOT_VALUE };
public Calculator(SnapshotImpl snapshot, IProgressListener listener) throws SnapshotException
{
this.snapshot = snapshot;
inboundIndex = snapshot.getIndexManager().inbound();
outboundIndex = snapshot.getIndexManager().outbound();
this.monitor = new SimpleMonitor(Messages.DominatorTree_CalculatingDominatorTree, listener, new int[] {
300, 300, 200, 200, 200 });
gcRootsArray = snapshot.getGCRoots();
gcRootsSet = new BitField(snapshot.getSnapshotInfo().getNumberOfObjects());
for (int id : gcRootsArray)
{
gcRootsSet.set(id);
}
IndexManager manager = this.snapshot.getIndexManager();
try
{
manager.a2size().unload();
manager.o2address().unload();
manager.o2class().unload();
}
catch (IOException e)
{
throw new SnapshotException(e);
}
n = snapshot.getSnapshotInfo().getNumberOfObjects() + 1;
r = 1;
parent = new int[n + 1];
anchestor = new int[n + 1];
vertex = new int[n + 1];
label = new int[n + 1];
semi = new int[n + 1];
/*
* Allocate these up front, to check for early OOM, but then free
* so that dfs() can use the space for outbound index caching.
*/
dom = new int[n + 1];
bucket = new int[n + 1];
dom = null;
bucket = null;
}
public void compute() throws IOException, SnapshotException, IProgressListener.OperationCanceledException
{
IProgressListener progressListener0 = this.monitor.nextMonitor();
progressListener0.beginTask(Messages.DominatorTree_DominatorTreeCalculation, 3);
n = 0;
dfs(r);
outboundIndex.unload();
IProgressListener progressListener = this.monitor.nextMonitor();
progressListener.beginTask(Messages.DominatorTree_ComputingDominators, n / 1000);
/*
* Reallocate just before use.
*/
dom = new int[snapshot.getSnapshotInfo().getNumberOfObjects() + 2];
bucket = new int[snapshot.getSnapshotInfo().getNumberOfObjects() + 2];
Arrays.fill(bucket, -1);
for (int i = n; i >= 2; i--)
{
int w = vertex[i];
for (int v : getPredecessors(w))
{
v += 2;
if (v < 0)
continue;
int u = eval(v);
if (semi[u] < semi[w])
{
semi[w] = semi[u];
}
}
// add w to bucket(vertex(semi(w)))
// create the bucket if needed
bucket[w] = bucket[vertex[semi[w]]]; // serves as next(w)
bucket[vertex[semi[w]]] = w; // serves as
// first(vertex[semi[w]])
link(parent[w], w);
int v = bucket[parent[w]];
while (v != -1)
{
int u = eval(v);
if (semi[u] < semi[v])
{
dom[v] = u;
}
else
{
dom[v] = parent[w];
}
v = bucket[v]; // here bucket serves as next[]
}
bucket[parent[w]] = -1;
// }
if (i % 1000 == 0)
{
if (progressListener.isCanceled())
throw new IProgressListener.OperationCanceledException();
progressListener.worked(1);
}
}
for (int i = 2; i <= n; i++)
{
int w = vertex[i];
if (dom[w] != vertex[semi[w]])
{
dom[w] = dom[dom[w]];
}
}
dom[r] = 0;
progressListener.done();
parent = anchestor = vertex = label = semi = bucket = null;
inboundIndex.unload();
if (progressListener0.isCanceled())
throw new IProgressListener.OperationCanceledException();
// pre-condition for index writing:
// retainedSetIdx is still sorted by object id
snapshot.getIndexManager().setReader(
IndexManager.Index.DOMINATOR,
new IndexWriter.IntIndexStreamer().writeTo(IndexManager.Index.DOMINATOR.getFile(snapshot
.getSnapshotInfo().getPrefix()), new IteratorInt()
{
int nextIndex = 2;
public boolean hasNext()
{
return nextIndex < dom.length;
}
public int next()
{
return dom[nextIndex++];
}
}));
int[] objectIds = new int[snapshot.getSnapshotInfo().getNumberOfObjects() + 2];
for (int i = 0; i < objectIds.length; i++)
objectIds[i] = i - 2;
objectIds[0] = -2;
objectIds[1] = ROOT_VALUE;
progressListener0.worked(1);
ArrayUtils.sort(dom, objectIds, 2, dom.length - 2);
progressListener0.worked(1);
FlatDominatorTree tree = new FlatDominatorTree(snapshot, dom, objectIds, ROOT_VALUE);
if (progressListener0.isCanceled())
throw new IProgressListener.OperationCanceledException();
writeIndexFiles(tree);
progressListener0.done();
}
private void dfs(int root) throws UnsupportedOperationException
{
IProgressListener progressListener = this.monitor.nextMonitor();
progressListener.beginTask(Messages.DominatorTree_DepthFirstSearch, snapshot.getSnapshotInfo()
.getNumberOfObjects() >> 16);
// a stack for each parameter - stack code is inlined for
// performance
// currentElementStack - for v, successorsStack - for the successors
// array,
// currentSuccessorStack - for the index in the array
int capacity = 2047; // capacity for the arrays - allows resize up to 2047<<20
int size = 0; // one size for all arrays
int[] currentElementStack = new int[capacity];
int[] currentSuccessorStack = new int[capacity];
Object[] successorsStack = new Object[capacity];
int v = root;
int[] successors = gcRootsArray;
int currentSuccessor = 0;
// push the initial values
currentElementStack[size] = root;
successorsStack[size] = successors;
currentSuccessorStack[size] = currentSuccessor;
size++;
while (size > 0)
{
v = currentElementStack[size - 1];
successors = (int[]) successorsStack[size - 1];
currentSuccessor = currentSuccessorStack[size - 1];
if (semi[v] == 0)
{
n = n + 1;
semi[v] = n;
vertex[n] = v;
label[v] = v;
anchestor[v] = 0;
}
if (currentSuccessor < successors.length)
{
int w = successors[currentSuccessor++] + 2;
currentSuccessorStack[size - 1] = currentSuccessor; // update
// the top
// value
// push the next unvisited successor
if (semi[w] == 0)
{
parent[w] = v;
successors = outboundIndex.get(w - 2); // get the
// successors of w
/* start push() */
// is expanding needed?
if (size == capacity)
{
int newCapacity = capacity << 1;
// resize currentElementStack
int[] newArr = new int[newCapacity];
System.arraycopy(currentElementStack, 0, newArr, 0, capacity);
currentElementStack = newArr;
// resize currentSuccessorStack
newArr = new int[newCapacity];
System.arraycopy(currentSuccessorStack, 0, newArr, 0, capacity);
currentSuccessorStack = newArr;
// resize successorsStack
Object[] newSuccessorsArr = new Object[newCapacity];
System.arraycopy(successorsStack, 0, newSuccessorsArr, 0, capacity);
successorsStack = newSuccessorsArr;
capacity = newCapacity;
}
currentElementStack[size] = w;
successorsStack[size] = successors;
currentSuccessorStack[size] = 0;
size++;
/* end push() */
// report progress
if ((n & 0xffff) == 0)
{
if (progressListener.isCanceled())
throw new IProgressListener.OperationCanceledException();
progressListener.worked(1);
}
}
}
else
{
// this one acts as a pop() for all tree stacks
size--;
}
}
progressListener.done();
}
// gets retained set idx and returns the real indexes
private int[] getPredecessors(int v)
{
v -= 2;
// for the GC roots return the artificial root
if (gcRootsSet.get(v))
{
return ROOT_VALUE_ARR;
}
else
{
return inboundIndex.get(v);
}
}
private void compress(int v)
{
IntStack stack = new IntStack();
while (anchestor[anchestor[v]] != 0) // is ancestor[v] a root in
// the
// forest?
{
stack.push(v);
v = anchestor[v];
}
while (stack.size() > 0)
{
v = stack.pop();
if (semi[label[anchestor[v]]] < semi[label[v]])
{
label[v] = label[anchestor[v]];
}
anchestor[v] = anchestor[anchestor[v]];
}
}
private int eval(int v)
{
if (anchestor[v] == 0)
{
return v;
}
else
{
compress(v);
return label[v];
}
}
private void link(int v, int w)
{
anchestor[w] = v;
}
private void writeIndexFiles(FlatDominatorTree tree) throws IOException
{
IndexWriter.IntArray1NWriter writer = new IndexWriter.IntArray1NWriter(dom.length - 1,
IndexManager.Index.DOMINATED.getFile(snapshot.getSnapshotInfo().getPrefix()));
int numberOfObjects = snapshot.getSnapshotInfo().getNumberOfObjects();
IProgressListener progressListener = this.monitor.nextMonitor();
progressListener.beginTask(Messages.DominatorTree_CreateDominatorsIndexFile, numberOfObjects / 1000);
for (int i = -1; i < numberOfObjects; i++)
{
int[] successors = tree.getSuccessorsArr(i);
tree.sortByTotalSize(successors);
writer.log(i + 1, successors);
if (i % 1000 == 0)
{
if (progressListener.isCanceled())
throw new IProgressListener.OperationCanceledException();
progressListener.worked(1);
}
}
snapshot.getIndexManager().setReader(IndexManager.Index.DOMINATED, writer.flush());
progressListener.done();
}
public class FlatDominatorTree
{
private static final int TEMP_ARR_LENGTH = 1000000;
int[] dom;
int[] elements;
long[] ts;
SnapshotImpl dump;
// temp arrays to pass for the radix sort
long[] tempLongArray = new long[TEMP_ARR_LENGTH];
int[] tempIntArray = new int[TEMP_ARR_LENGTH];
FlatDominatorTree(SnapshotImpl dump, int[] dom, int[] elements, int root) throws SnapshotException,
IOException
{
this.dump = dump;
this.dom = dom;
this.elements = elements;
this.ts = new long[dom.length];
calculateTotalSizesIterative(root);
}
public SuccessorsEnum getSuccessorsEnum(int i)
{
return new SuccessorsEnum(i);
}
public int[] getSuccessorsArr(int parentId)
{
parentId += 2;
// find the first child
int j = Arrays.binarySearch(dom, parentId);
if (j < 0)
return new int[0];
int i = j;
while ((i > 1) && (dom[i - 1] == parentId))
i--;
// find length
while (j < dom.length && dom[j] == parentId)
j++;
int length = j - i;
int[] result = new int[length];
System.arraycopy(elements, i, result, 0, length);
return result;
}
public void sortByTotalSize(int[] objectIds)
{
int length = objectIds.length;
// collect the total sizes of the objects
long[] totalSizes = new long[length];
for (int i = 0; i < length; i++)
{
totalSizes[i] = ts[objectIds[i] + 2];
}
// sort both arrays according to the total sizes
if (totalSizes.length > 1)
if (totalSizes.length > TEMP_ARR_LENGTH)
{
ArrayUtils.sortDesc(totalSizes, objectIds);
}
else
{
ArrayUtils.sortDesc(totalSizes, objectIds, tempLongArray, tempIntArray);
}
}
class SuccessorsEnum
{
int parent;
int nextIndex;
SuccessorsEnum(int parent)
{
this.parent = parent;
nextIndex = findFirstChildIndex(parent + 2);
}
public boolean hasMoreElements()
{
return nextIndex > 0;
}
public int nextElement()
{
if (nextIndex < 0)
throw new NoSuchElementException();
int res = elements[nextIndex++];
if (nextIndex >= dom.length || dom[nextIndex] != parent + 2)
nextIndex = -1;
return res;
}
int findFirstChildIndex(int el)
{
int i = Arrays.binarySearch(dom, el);
while ((i > 1) && (dom[i - 1] == el))
i--;
return i;
}
}
public void calculateTotalSizesIterative(int e) throws SnapshotException, IOException
{
IndexWriter.LongIndexCollector retained = new IndexWriter.LongIndexCollector(dump.getSnapshotInfo()
.getNumberOfObjects(), IndexWriter.mostSignificantBit(dump.getSnapshotInfo()
.getUsedHeapSize()));
int capacity = 2047; // capacity for the arrays - allows resize up to 2047<<20
int size = 0;
int[] stack = new int[capacity];
SuccessorsEnum[] succStack = new SuccessorsEnum[capacity];
int currentEntry = e;
SuccessorsEnum currentSucc = getSuccessorsEnum(currentEntry);
stack[size] = currentEntry;
succStack[size] = currentSucc;
size++;
IProgressListener progressListener = Calculator.this.monitor.nextMonitor();
progressListener.beginTask(Messages.DominatorTree_CalculateRetainedSizes, dump.getSnapshotInfo()
.getNumberOfObjects() / 1000);
int counter = 0;
while (size > 0)
{
currentEntry = stack[size - 1];
currentSucc = succStack[size - 1];
if (currentSucc.hasMoreElements())
{
int nextChild = currentSucc.nextElement();
currentSucc = getSuccessorsEnum(nextChild);
ts[nextChild + 2] = nextChild < 0 ? 0 : snapshot.getHeapSize(nextChild);
if (size == capacity)
{
int newCapacity = capacity << 1;
int[] newArr = new int[newCapacity];
System.arraycopy(stack, 0, newArr, 0, capacity);
stack = newArr;
// resize successorsStack
SuccessorsEnum[] newSuccessorsArr = new SuccessorsEnum[newCapacity];
System.arraycopy(succStack, 0, newSuccessorsArr, 0, capacity);
succStack = newSuccessorsArr;
capacity = newCapacity;
}
stack[size] = nextChild;
succStack[size] = currentSucc;
size++;
}
else
{
size--;
if (size > 0)
ts[stack[size - 1] + 2] += ts[currentEntry + 2];
if (currentEntry >= 0)
{
retained.set(currentEntry, ts[currentEntry + 2]);
if (++counter % 1000 == 0)
{
if (progressListener.isCanceled())
throw new IProgressListener.OperationCanceledException();
progressListener.worked(1);
}
}
}
}
dump.getIndexManager().setReader(
Index.O2RETAINED,
retained.writeTo(IndexManager.Index.O2RETAINED.getFile(dump.getSnapshotInfo()
.getPrefix())));
retained = null;
progressListener.done();
}
}
}
}