Skip to content

Commit

Permalink
Bug 581644 Allow stack frames as pseudo-objects for HPROF snapshots
Browse files Browse the repository at this point in the history
Improve handling of ExportHPROF dumps, including those from DTFJ,
with stack frames as objects.

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=581644
Change-Id: I4987639750b59a14aee3b3371c3088e58b082df6
  • Loading branch information
ajohnson1 committed Sep 9, 2023
1 parent 21b1804 commit 9e353fd
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,9 @@ else if (bootLoaderObject == null)
try
{
newMethodName = getMethodName(jm, listener);
String newMethodMod = getModifiers(jm, listener);
if (!newMethodMod.isEmpty())
newMethodName = newMethodMod + " " + newMethodName;
}
catch (CorruptDataException e)
{
Expand All @@ -1391,6 +1394,9 @@ else if (bootLoaderObject == null)
if (oldJm != null)
{
oldMethodName = getMethodName(oldJm, listener);
String oldMethodMod = getModifiers(oldJm, listener);
if (!oldMethodMod.isEmpty())
oldMethodName = oldMethodMod + " " + oldMethodName;
}
else
{
Expand Down Expand Up @@ -2806,8 +2812,9 @@ static long getFrameAddress(JavaStackFrame jf, long prevFrameAddress, int pointe
{
frameAddress = 0;
}
if (frameAddress == 0 && prevFrameAddress != 0)
{
// Native methods sometimes have the same frame address as the caller
if (frameAddress == 0 && prevFrameAddress != 0 || frameAddress == prevFrameAddress)
{
frameAddress = prevFrameAddress + (pointerSize + 31) / 32 * 4;
}
return frameAddress;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011,2013 IBM Corporation.
* Copyright (c) 2011,2023 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
Expand All @@ -17,7 +17,7 @@
*/
public class PreferenceConstants
{
/** Whether to treat stack frames as psuedo-objects and methods as pseudo-classes */
/** Whether to treat stack frames as pseudo-objects and methods as pseudo-classes */
public static final String P_METHODS = "methodsAsClasses"; //$NON-NLS-1$
public static final String P_SUPPRESS_CLASS_NATIVE_SIZES = "suppressClassNativeSizes"; //$NON-NLS-1$
public static final String P_RELIABILITY_CHECK = "reliabilityCheck"; //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ interface DumpSegment
// The size of identifiers in the dump file
protected int idSize;
protected final HprofPreferences.HprofStrictness strictnessPreference;
/** First stack frame address */
protected long stackFrameBase = 0x100;
/** Alignment of stack frames - should not be stricter than rest of heap */
protected long stackFrameAlign = 0x100;
/*
* Names used as pseudo-class names
* Not translatable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayLong;
import org.eclipse.mat.collect.BitField;
import org.eclipse.mat.collect.SetInt;
import org.eclipse.mat.hprof.AbstractParser.Constants;
Expand Down Expand Up @@ -142,7 +143,7 @@ private RedactType(String s)
public File mapFile;

@Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "skip")
public Pattern skipPattern = Pattern.compile("java\\..*|boolean|byte|char|short|int|long|float|double|void"); //$NON-NLS-1$
public Pattern skipPattern = Pattern.compile("java\\..*|boolean|byte|char|short|int|long|float|double|void|<[a-z ]+>"); //$NON-NLS-1$

@Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "avoid")
public Pattern avoidPattern = Pattern.compile(Messages.ExportHprof_AvoidExample);
Expand Down Expand Up @@ -810,7 +811,7 @@ private void dumpThreadStacks(DataOutput os, long startTime)
throws SnapshotException, IOException
{
dummyStackTrace(os, UNKNOWN_STACK_TRACE_SERIAL, 1);
int frameid = 1;
long nextFrameid = 1;
int serialid = UNKNOWN_STACK_TRACE_SERIAL + 1;
// Find the threads
for (int i : snapshot.getGCRoots())
Expand All @@ -825,9 +826,34 @@ private void dumpThreadStacks(DataOutput os, long startTime)
if (its == null)
continue;
// Find the stack frames
int firstframeid = frameid;
ArrayLong frameIds = new ArrayLong();
IObject thread = snapshot.getObject(i);
int frameNumber = 0;
for (IStackFrame isf : its.getStackFrames())
{
long frameid = 0;
for (NamedReference ref : thread.getOutboundReferences())
{
if (ref instanceof ThreadToLocalReference)
{
ThreadToLocalReference tlr = (ThreadToLocalReference)ref;
for (GCRootInfo rootInfo : tlr.getGcRootInfo())
{
if (rootInfo.getType() == GCRootInfo.Type.JAVA_STACK_FRAME)
{
if (Integer.valueOf(frameNumber).equals(tlr.getObject().resolveValue("frameNumber")))
{
frameid = tlr.getObjectAddress();
break;
}
}
}
}
}
if (frameid == 0)
{
frameid = nextFrameid++;
}
String frametext = isf.getText();
Frame f = Frame.parse(frametext);
String classname = f.clazz;
Expand Down Expand Up @@ -858,19 +884,20 @@ private void dumpThreadStacks(DataOutput os, long startTime)
writeString(os, sourcefile);
os.writeInt(clsid);
os.writeInt(linenum);
++frameid;
frameIds.add(frameid);
++frameNumber;
}
os.writeByte(Constants.Record.STACK_TRACE);
os.writeInt((int) (System.currentTimeMillis() - startTime));
os.writeInt(3 * 4 + (frameid - firstframeid) * idsize);
os.writeInt(3 * 4 + frameIds.size() * idsize);
os.writeInt(serialid);
Integer prev = threadToStack.put(i, serialid);
//if (prev != null && prev != serialid)
// throw new IllegalStateException("thread " + i + "0x" + Long.toHexString(gr.getObjectAddress()) + " " + serialid + " != " + prev); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
++serialid;
os.writeInt(threadToSerial.get(i));
os.writeInt(frameid - firstframeid);
for (int j = firstframeid; j < frameid; ++j)
os.writeInt(frameIds.size());
for (long j : frameIds.toArray())
{
writeID(os, j);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2020 SAP AG and IBM Corporation.
* Copyright (c) 2008, 2023 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
Expand Down Expand Up @@ -114,6 +114,8 @@ public void fill(IPreliminaryIndex preliminary, IProgressListener listener) thro
boolean parallel = maxFree > memestimate;

Pass2Parser pass2 = new Pass2Parser(handler, mon, strictnessPreference, streamLength, parallel);
pass2.stackFrameAlign = pass1.stackFrameAlign;
pass2.stackFrameBase = pass1.stackFrameBase;
pass2.read(file, prefix, dumpNrToRead);

if (listener.isCanceled())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.eclipse.core.runtime.Platform;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.HashMapLongObject;
import org.eclipse.mat.collect.SetLong;
import org.eclipse.mat.hprof.ui.HprofPreferences;
import org.eclipse.mat.parser.model.ClassImpl;
import org.eclipse.mat.snapshot.MultipleSnapshotsException;
Expand All @@ -55,6 +56,8 @@ public class Pass1Parser extends AbstractParser
// New size of classes including per-instance fields
private final boolean NEWCLASSSIZE = HprofPreferences.useAdditionalClassReferences();
private final String METHODSASCLASSES = HprofPreferences.methodsAsClasses();
private final boolean READFRAMES = HprofPreferences.FRAMES_ONLY.equals(METHODSASCLASSES)
|| HprofPreferences.RUNNING_METHODS_AS_CLASSES.equals(METHODSASCLASSES);

private HashMapLongObject<String> class2name = new HashMapLongObject<String>();
private HashMapLongObject<Long> thread2id = new HashMapLongObject<Long>();
Expand All @@ -63,6 +66,7 @@ public class Pass1Parser extends AbstractParser
private HashMapLongObject<Long> classSerNum2id = new HashMapLongObject<Long>();
private HashMapLongObject<List<JavaLocal>> thread2locals = new HashMapLongObject<List<JavaLocal>>();
private HashMapLongObject<String> constantPool = new HashMapLongObject<String>();
private SetLong frameObjs;
private IHprofParserHandler handler;
private SimpleMonitor.Listener monitor;
private long previousArrayStart;
Expand All @@ -73,10 +77,10 @@ public class Pass1Parser extends AbstractParser
private final boolean verbose = Platform.inDebugMode() && HprofPlugin.getDefault().isDebugging()
&& Boolean.parseBoolean(Platform.getDebugOption("org.eclipse.mat.hprof/debug/parser")); //$NON-NLS-1$
private BufferingRafPositionInputStream in;
/** First stack frame address */
private static final long stackFrameBase = 0x100;
/** Alignment of stack frames - should not be stricter than rest of heap */
private static final long stackFrameAlign = 0x100;
/** First stack frame class address */
private long stackFrameClassBase = 0x100;
/** Alignment of stack frame classes frames - should not be stricter than rest of heap */
private long stackFrameClassAlign = 0x100;

public Pass1Parser(IHprofParserHandler handler, SimpleMonitor.Listener monitor,
HprofPreferences.HprofStrictness strictnessPreference)
Expand Down Expand Up @@ -587,12 +591,17 @@ private void readGCWithThreadContext(int gcType, boolean hasLineInfo) throws IOE
long id = in.readID(idSize);
int threadSerialNo = in.readInt();
Long tid = thread2id.get(threadSerialNo);
int lineNumber;
if (hasLineInfo)
lineNumber = in.readInt();
else
lineNumber = -1;
if (tid != null)
{
// With METHODSASCLASSES instead we add references from the stack
// frame to the local
if (!((HprofPreferences.FRAMES_ONLY.equals(METHODSASCLASSES)
|| HprofPreferences.RUNNING_METHODS_AS_CLASSES.equals(METHODSASCLASSES)) && hasLineInfo))
|| HprofPreferences.RUNNING_METHODS_AS_CLASSES.equals(METHODSASCLASSES)) && hasLineInfo && lineNumber >= 0))
handler.addGCRoot(id, tid, gcType);
}
else
Expand All @@ -602,7 +611,6 @@ private void readGCWithThreadContext(int gcType, boolean hasLineInfo) throws IOE

if (hasLineInfo)
{
int lineNumber = in.readInt();
List<JavaLocal> locals = thread2locals.get(threadSerialNo);
if (locals == null)
{
Expand Down Expand Up @@ -803,7 +811,8 @@ private void readInstanceDump(long segmentStartPos) throws IOException

checkSkipBytes(payload);

handler.reportInstanceWithClass(address, segmentStartPos, classID, payload);
if (!skipFrameObject(address))
handler.reportInstanceWithClass(address, segmentStartPos, classID, payload);
}

private void readObjectArrayDump(long segmentStartPos) throws IOException
Expand Down Expand Up @@ -903,7 +912,7 @@ private void dumpThreads() throws IOException
long frameI = stack.frameIds[i];
StackFrame frame = id2frame.get(frameI);
{
long frameAddress = stackFrameBase + frame.frameId * stackFrameAlign;
long frameAddress = frameIdToAddress(frame.frameId);
out.println(" objectId=0x" + Long.toHexString(frameAddress) + ", line=" + (i)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
Expand Down Expand Up @@ -975,7 +984,11 @@ private ClassImpl genClass(long nextAddress, String className, long superClass,
{
IClass existing = handler.lookupClassByName(className, false);
if (existing instanceof ClassImpl)
{
if (clsType instanceof ClassImpl)
((ClassImpl)existing).setClassInstance((ClassImpl) clsType);
return (ClassImpl)existing;
}
ClassImpl newClass = new ClassImpl(nextAddress, className, superClass, classLoader, flds, desc);
class2name.put(nextAddress, className);
// This will be replaced by a size calculated from the field sizes
Expand All @@ -990,24 +1003,82 @@ private long nextFreeAddr(long addr)
{
while (class2name.containsKey(addr))
{
addr += stackFrameAlign;
addr += stackFrameClassAlign;
}
return addr;
}

private void stackFramesAsObjects() throws IOException
private void initFrameToAddress()
{
final long rootLoader = 0;
// Find the biggest frameId
long maxFrameId = 0;
for (long a : id2frame.getAllKeys())
long ids[] = id2frame.getAllKeys().clone();
Arrays.sort(ids);
if (ids.length >= 1)
{
if (a > maxFrameId)
maxFrameId = a;
maxFrameId = ids[ids.length - 1];
long idBits = 0;
for (long id : ids)
{
idBits |= id;
}
if ((idBits & 0x3) != 0)
{
stackFrameAlign = 0x100;
stackFrameBase = 0x100;
}
else
{
stackFrameAlign = 1;
stackFrameBase = 0;
}
}
long next = frameIdToAddress(maxFrameId + 1);
stackFrameClassBase = next + Math.floorMod(next + stackFrameClassAlign, stackFrameClassAlign);
}

/**
* Converts a frame ID to a pseudo-object address.
* @param frameId the ID
* @return the address
*/
private long frameIdToAddress(long frameId)
{
return stackFrameBase + frameId * stackFrameAlign;
}

private boolean skipFrameObject(long addr)
{
if (frameObjs == null)
{
frameObjs = new SetLong();
if (READFRAMES)
{
initFrameToAddress();
long ids[] = id2frame.getAllKeys();
for (long id : ids)
{
frameObjs.add(frameIdToAddress(id));
}
}
}
return frameObjs.contains(addr);
}

private long systemClassLoader()
{
IClass c = handler.lookupClassByName(ClassImpl.JAVA_LANG_CLASS, false);
if (c == null)
return 0;
return c.getClassLoaderAddress();
}

private void stackFramesAsObjects() throws IOException
{
final long rootLoader = systemClassLoader();

long nextAddress = stackFrameClassBase;
// The method addresses come after the stack frames
long nextAddress = stackFrameBase + (maxFrameId + 1) * stackFrameAlign;
nextAddress = nextFreeAddr(nextAddress);

// Equivalent to java.lang.Object
Expand All @@ -1026,6 +1097,7 @@ private void stackFramesAsObjects() throws IOException

ClassImpl clazzMethod;
ClassImpl clazzStackFrame;
Field[] frameStatics = new Field[0];
if (HprofPreferences.RUNNING_METHODS_AS_CLASSES.equals(METHODSASCLASSES))
{
clazzMethodType = genClass(nextAddress, METHOD_TYPE, clazzNativeMemoryType.getObjectAddress(), rootLoader, new Field[0], new FieldDescriptor[0], clazzNativeMemoryType);
Expand All @@ -1037,7 +1109,7 @@ private void stackFramesAsObjects() throws IOException
new FieldDescriptor(FRAME_NUMBER, IObject.Type.INT),
new FieldDescriptor(STACK_DEPTH, IObject.Type.INT),
};
clazzMethod = genClass(nextAddress, METHOD, clazzNativeMemory.getObjectAddress(), rootLoader, new Field[0], fldm, clazzMethodType);
clazzMethod = genClass(nextAddress, METHOD, clazzNativeMemory.getObjectAddress(), rootLoader, frameStatics, fldm, clazzMethodType);
nextAddress = nextFreeAddr(nextAddress);
clazzStackFrame = null;
}
Expand All @@ -1054,7 +1126,7 @@ private void stackFramesAsObjects() throws IOException
new FieldDescriptor(FILE_NAME, IObject.Type.OBJECT),
new FieldDescriptor(METHOD_NAME, IObject.Type.OBJECT),
};
clazzStackFrame = genClass(nextAddress, STACK_FRAME, clazzNativeMemory.getObjectAddress(), rootLoader, new Field[0], fld, clazzNativeMemoryType);
clazzStackFrame = genClass(nextAddress, STACK_FRAME, clazzNativeMemory.getObjectAddress(), rootLoader, frameStatics, fld, clazzNativeMemoryType);
nextAddress = nextFreeAddr(nextAddress);
}

Expand All @@ -1070,7 +1142,7 @@ private void stackFramesAsObjects() throws IOException
{
long frameId = st.frameIds[linenum];
StackFrame frame = id2frame.get(frameId);
long frameAddress = stackFrameBase + frame.frameId * stackFrameAlign;
long frameAddress = frameIdToAddress(frame.frameId);
long frameType = 0;
ClassImpl frameClazz = null;
if (HprofPreferences.RUNNING_METHODS_AS_CLASSES.equals(METHODSASCLASSES))
Expand Down Expand Up @@ -1103,7 +1175,10 @@ private void stackFramesAsObjects() throws IOException
{
frameType = e.getKey();
if (cls instanceof ClassImpl)
{
frameClazz = (ClassImpl)cls;
frameClazz.setClassInstance(clazzMethodType);
}
break;
}
}
Expand Down

0 comments on commit 9e353fd

Please sign in to comment.