Skip to content

Commit

Permalink
Bug 582305 Improve Leak Suspects report for references in paths
Browse files Browse the repository at this point in the history
Add reference to accumulation point for suspects with a group of
objects.
Add jdk. to skip pattern for queries.

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=582305
Change-Id: I6501890198a7880a058e35ebaab34fea95e025b7
  • Loading branch information
ajohnson1 committed Aug 23, 2023
1 parent 31a98ec commit 4a2bb3a
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 19 deletions.
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 @@ -50,7 +50,7 @@ public class BigDropsQuery implements IQuery, IResultTree
public ISnapshot snapshot;

@Argument(advice = Advice.CLASS_NAME_PATTERN, isMandatory = false, flag = "skip")
public Pattern pattern = Pattern.compile("java.*|com\\.sun.\\.*"); //$NON-NLS-1$
public Pattern pattern = Pattern.compile("java\\..*|javax\\..*|com\\.sun\\..*|jdk\\..*"); //$NON-NLS-1$

// @Argument(isMandatory = false, flag = "t")
// @Help("Differences in the retained sizes of the parent and the children
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2010 SAP AG.
* Copyright (c) 2008, 2023 SAP AG.
* 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 @@ -52,7 +52,7 @@ public class ImmediateDominatorsQuery implements IQuery
public IHeapObjectArgument objects;

@Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "skip")
public Pattern skipPattern = Pattern.compile("java.*|com\\.sun\\..*"); //$NON-NLS-1$
public Pattern skipPattern = Pattern.compile("java\\..*|javax\\..*|com\\.sun\\..*|jdk\\..*"); //$NON-NLS-1$

public IResult execute(IProgressListener listener) throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.IStackFrame;
import org.eclipse.mat.snapshot.model.IThreadStack;
import org.eclipse.mat.snapshot.model.NamedReference;
import org.eclipse.mat.snapshot.model.ThreadToLocalReference;
import org.eclipse.mat.snapshot.query.Icons;
import org.eclipse.mat.snapshot.query.ObjectListResult;
import org.eclipse.mat.snapshot.query.PieFactory;
Expand Down Expand Up @@ -118,7 +120,7 @@ public class LeakHunterQuery implements IQuery
public int max_paths = 10000;

@Argument(isMandatory = false, advice = Advice.CLASS_NAME_PATTERN, flag = "skip")
public Pattern skipPattern = Pattern.compile("java.*|com\\.sun\\..*"); //$NON-NLS-1$
public Pattern skipPattern = Pattern.compile("java\\..*|javax\\..*|com\\.sun\\..*|jdk\\..*"); //$NON-NLS-1$

@Argument(isMandatory = false)
public List<String> excludes = Arrays.asList( //
Expand Down Expand Up @@ -241,6 +243,28 @@ private boolean isStackFrame(int objectId) throws SnapshotException
return false;
}

/**
* Is this object a local variable from the frameId?
* @param frameId the stack frame ID, or thread ID if no stack frames as variables.
* @param objectId the object in questions
* @return true if a local variable
* @throws SnapshotException
*/
private boolean isStackFrameLocal(int frameId, int objectId) throws SnapshotException
{
IObject frame = snapshot.getObject(frameId);
List<NamedReference> refs = frame.getOutboundReferences();
for (NamedReference ref : refs)
{
if (ref instanceof ThreadToLocalReference)
{
if (ref.getObjectId() == objectId)
return true;
}
}
return false;
}

private CompositeResult getLeakSuspectDescription(SuspectRecord suspect, IProgressListener listener)
throws SnapshotException
{
Expand Down Expand Up @@ -329,18 +353,20 @@ else if (snapshot.isClass(suspectId))
if (snapshot.isClassLoader(referrerId))
{
referrerClassloader = referrer;
objectsForTroubleTicketInfo.add(suspectClassloader);
objectsForTroubleTicketInfo.add(referrerClassloader);
String referrerClassloaderName = getClassLoaderName(referrerClassloader, keywords);
overview.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedBy,
referrerClassloaderName));
}
else if (snapshot.isClass(referrerId))
{
className = ((IClass)referrer).getName();
keywords.add(className);
referrerClassloader = snapshot.getObject(((IClass) referrer).getClassLoaderId());
// involvedClassloaders.add(suspectClassloader);
objectsForTroubleTicketInfo.add(referrer);
String referrerClassloaderName = getClassLoaderName(referrerClassloader, keywords);
overview.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedByClass, className,
overview.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedByClass, HTMLUtils.escapeText(className),
referrerClassloaderName));
}
else
Expand All @@ -352,6 +378,8 @@ else if (snapshot.isClass(referrerId))
IObject suspectObject = snapshot.getObject(suspectId);
suspect = new SuspectRecord(suspectObject, suspectObject.getRetainedHeapSize(), suspect.getAccumulationPoint());
}
className = referrer.getClazz().getName();
keywords.add(className);
referrerClassloader = snapshot.getObject(referrer.getClazz().getClassLoaderId());
// involvedClassloaders.add(suspectClassloader);
objectsForTroubleTicketInfo.add(referrer);
Expand Down Expand Up @@ -452,7 +480,7 @@ else if (snapshot.isClass(accumulationPointId))
if (isThread(r[0]))
{
isThreadRelated = true;
int accObjId = (r[2] >= 0 && isStackFrame(r[1])) ? r[2] : r[1];
int accObjId = (r[2] >= 0 && isStackFrame(r[1]) && isStackFrameLocal(r[1], r[2])) ? r[2] : r[1];
AccumulationPoint ap = accObjId >= 0 ? new AccumulationPoint(snapshot.getObject(accObjId)) : null;
SuspectRecord suspect2 = new SuspectRecord(snapshot.getObject(r[0]), totalHeap, ap);
objectsForTroubleTicketInfo.add(suspect2.getSuspect());
Expand Down Expand Up @@ -660,7 +688,7 @@ else if (snapshot.isClass(accumulationPointId))
objectsForTroubleTicketInfo.add(suspect.getAccumulationPoint().getObject());
classloaderName = getClassLoaderName(accPointClassloader, keywords);

builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedFromClass, className,
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedFromClass, HTMLUtils.escapeText(className),
classloaderName, formatRetainedHeap(suspect.getAccumulationPoint().getRetainedHeapSize(), totalHeap)));
}
else
Expand All @@ -675,8 +703,69 @@ else if (snapshot.isClass(accumulationPointId))

classloaderName = getClassLoaderName(accPointClassloader, keywords);

builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedFromInstance, className,
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedFromInstance, HTMLUtils.escapeText(className),
classloaderName, formatRetainedHeap(suspect.getAccumulationPoint().getRetainedHeapSize(), totalHeap)));

boolean isThreadRelated = isThread(suspect.getAccumulationPoint().getObject().getObjectId());
/*
* if the class name matches the skip pattern, try to find the first
* referrer which does not match the pattern
*/
if (skipPattern.matcher(className).matches() && !isThreadRelated)
{
int referrerId = findReferrer(accumulationPointId);
if (referrerId != -1)
{
IObject referrer = snapshot.getObject(referrerId);
IObject referrerClassloader = null;
if (snapshot.isClassLoader(referrerId))
{
referrerClassloader = referrer;
objectsForTroubleTicketInfo.add(referrerClassloader);
String referrerClassloaderName = getClassLoaderName(referrerClassloader, keywords);
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedBy,
referrerClassloaderName));
}
else if (snapshot.isClass(referrerId))
{
className = ((IClass)referrer).getName();
keywords.add(className);
referrerClassloader = snapshot.getObject(((IClass) referrer).getClassLoaderId());
// involvedClassloaders.add(referrerClassloader);
objectsForTroubleTicketInfo.add(referrer);
String referrerClassloaderName = getClassLoaderName(referrerClassloader, keywords);
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedByClass, HTMLUtils.escapeText(className),
referrerClassloaderName));
}
else
{
SuspectRecord suspect2 = null;
if (isThread(referrerId))
{
isThreadRelated = true;
int suspectId = referrerId;
IObject suspectObject = snapshot.getObject(suspectId);
suspect2 = new SuspectRecord(suspectObject, suspectObject.getRetainedHeapSize(), suspect.getAccumulationPoint());
}
className = referrer.getClazz().getName();
keywords.add(className);
referrerClassloader = snapshot.getObject(referrer.getClazz().getClassLoaderId());
// involvedClassloaders.add(suspectClassloader);
objectsForTroubleTicketInfo.add(referrer);
String referrerClassloaderName = getClassLoaderName(referrerClassloader, keywords);
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_ReferencedByInstance, HTMLUtils.escapeText(referrer
.getDisplayName()), referrerClassloaderName));
if (isThreadRelated)
{
builder.append("<p>"); //$NON-NLS-1$
builder.append(MessageUtil.format(Messages.LeakHunterQuery_Msg_Thread, //
HTMLUtils.escapeText(suspect2.getSuspect().getDisplayName()), //
formatRetainedHeap(suspect2.getSuspectRetained(), totalHeap)));
builder.append("</p>"); //$NON-NLS-1$
}
}
}
}
}
builder.append("</p>"); //$NON-NLS-1$
}
Expand Down Expand Up @@ -818,7 +907,7 @@ else if (suspect.getCommonPath().length > 0)
if (isThread(path[0]))
{
AccumulationPoint ap;
if (path.length > 2 && isStackFrame(path[1]))
if (path.length > 2 && isStackFrame(path[1]) && isStackFrameLocal(path[1], path[2]))
{
ap = new AccumulationPoint(snapshot.getObject(path[2]));
}
Expand Down Expand Up @@ -895,7 +984,7 @@ else if (path.length > 1)
if (io instanceof IClass)
{
String cn = OQLclassName((IClass)io);
qs.setCommand("merge_shortest_paths SELECT * FROM " + cn + " s WHERE dominatorof(s) = null; -groupby FROM_GC_ROOTS_BY_CLASS"); //$NON-NLS-1$ //$NON-NLS-2$
qs.setCommand("merge_shortest_paths SELECT * FROM " + cn + " s WHERE dominatorof(s) = null; -groupby FROM_GC_ROOTS_BY_CLASS -excludes ;"); //$NON-NLS-1$ //$NON-NLS-2$
}
composite.addResult(qs);
}
Expand Down Expand Up @@ -1693,10 +1782,12 @@ private List<CompositeResult> findCommonPathForSuspects(HashMap<Integer, List<In
}

// provide the common path as details
// Only show the paths for the objects in common
IMultiplePathsFromGCRootsComputer comp2 = snapshot.getMultiplePathsFromGCRoots(referencedAccumulationPoints, excludeMap);
QuerySpec qs = new QuerySpec(Messages.LeakHunterQuery_CommonPath, //
MultiplePath2GCRootsQuery.create(snapshot, comp, commonPath.toArray(), listener));
MultiplePath2GCRootsQuery.create(snapshot, comp2, commonPath.toArray(), listener));
StringBuilder sb = new StringBuilder("merge_shortest_paths"); //$NON-NLS-1$
for (int objId : objectIds)
for (int objId : referencedAccumulationPoints)
{
long addr = snapshot.mapIdToAddress(objId);
sb.append(" 0x").append(Long.toHexString(addr)); //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,12 @@ LeakHunterQuery_Msg_Class=The class <strong><q>{0}</q></strong>, loaded by <stro
LeakHunterQuery_Msg_ClassLoader=The classloader/component <strong><q>{0}</q></strong> occupies <strong>{1}</strong> bytes.
LeakHunterQuery_Msg_Instance=One instance of <strong><q>{0}</q></strong> loaded by <strong><q>{1}</q></strong> occupies <strong>{2}</strong> bytes.
LeakHunterQuery_Msg_InstancesOccupy={0} instances of <strong><q>{1}</q></strong>, loaded by <strong><q>{2}</q></strong> occupy <strong>{3}</strong> bytes.
LeakHunterQuery_Msg_ReferencedBy=The instance is referenced by classloader/component. <strong><q>{0}</q></strong>.
LeakHunterQuery_Msg_ReferencedBy=The instance is referenced by classloader/component <strong><q>{0}</q></strong>.
LeakHunterQuery_Msg_ReferencedByClass=The instance is referenced by class <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>.
LeakHunterQuery_Msg_ReferencedByInstance=The instance is referenced by <strong>{0}</strong>&nbsp;, loaded by <strong><q>{1}</q></strong>.
LeakHunterQuery_Msg_ReferencedFromClass=These instances are referenced from the class <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>, which occupies <strong>{2}</strong> bytes.
LeakHunterQuery_Msg_ReferencedFromClassLoader=These instances are referenced from classloader/component <strong><q>{0}</q></strong> which occupies <strong>{1}</strong> bytes.
LeakHunterQuery_Msg_ReferencedFromInstance=These instances are referenced from one instance of <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>, which occupies <strong>{2}</strong> bytes.
LeakHunterQuery_Msg_ReferencedByInstance=The instance is referenced by <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>.
LeakHunterQuery_Msg_ReferencedFromClass=Most of these instances are referenced from the class <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>, which occupies <strong>{2}</strong> bytes.
LeakHunterQuery_Msg_ReferencedFromClassLoader=Most of these instances are referenced from classloader/component <strong><q>{0}</q></strong> which occupies <strong>{1}</strong> bytes.
LeakHunterQuery_Msg_ReferencedFromInstance=Most of these instances are referenced from one instance of <strong><q>{0}</q></strong>, loaded by <strong><q>{1}</q></strong>, which occupies <strong>{2}</strong> bytes.
LeakHunterQuery_Msg_SuspectsRelated=The problem suspects {0} and {1} may be related, because the reference chains to them have a common beginning.
LeakHunterQuery_Msg_Thread=The thread <strong>{0}</strong> keeps local variables with total size <strong>{1}</strong> bytes.
LeakHunterQuery_NothingFound=No leak suspect was found
Expand Down

0 comments on commit 4a2bb3a

Please sign in to comment.