blob: 03bbba94552577b784df5993847a830c608787c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2020 SAP AG & IBM Corporation.
* 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
* IBM Corporation - optional columns
*******************************************************************************/
package org.eclipse.mat.inspections.threads;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayInt;
import org.eclipse.mat.internal.Messages;
import org.eclipse.mat.query.Bytes;
import org.eclipse.mat.query.Column;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.results.CompositeResult;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.extension.IRequestDetailsResolver;
import org.eclipse.mat.snapshot.extension.IThreadDetailsResolver;
import org.eclipse.mat.snapshot.extension.IThreadInfo;
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.snapshot.model.ThreadToLocalReference;
import org.eclipse.mat.snapshot.registry.RequestDetailResolverRegistry;
import org.eclipse.mat.snapshot.registry.ThreadDetailResolverRegistry;
import org.eclipse.mat.util.IProgressListener;
/* package */class ThreadInfoImpl implements IThreadInfo
{
// //////////////////////////////////////////////////////////////
// column meta-data
// //////////////////////////////////////////////////////////////
private static final Column COL_CLASSNAME = new Column(Messages.Column_ClassName);
private static final Column COL_NAME = new Column(Messages.ThreadInfoImpl_Column_Name);
private static final Column COL_INSTANCE = new Column(Messages.ThreadStackQuery_Column_ObjectStackFrame);
private static final Column COL_SHALLOW = new Column(Messages.Column_ShallowHeap, Bytes.class);
private static final Column COL_RETAINED = new Column(Messages.Column_RetainedHeap, Bytes.class);
private static final Column COL_CONTEXTCL = new Column(Messages.ThreadInfoImpl_Column_ContextClassLoader);
private static final Column COL_ISDAEMON = new Column(Messages.ThreadInfoImpl_Column_IsDaemon, Boolean.class);
private static final List<Column> defaultColumns = Arrays.asList(new Column[] {
//COL_CLASSNAME, //
COL_INSTANCE, //
COL_NAME, //
COL_SHALLOW, //
COL_RETAINED, //
COL_CONTEXTCL,
COL_ISDAEMON});
// //////////////////////////////////////////////////////////////
// factory methods
// //////////////////////////////////////////////////////////////
/* package */static ThreadInfoImpl build(IObject thread, boolean readFully, IProgressListener listener)
throws SnapshotException
{
ThreadInfoImpl info = new ThreadInfoImpl();
info.subject = thread;
extractGeneralAttribtes(info);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
extractFromDetailsResolver(info, readFully, listener);
if (readFully)
extractFromRequestResolver(info, listener);
return info;
}
private static void extractFromRequestResolver(ThreadInfoImpl info, IProgressListener listener)
throws SnapshotException
{
ISnapshot snapshot = info.subject.getSnapshot();
int[] localVars = getLocalVarsForThread(info.subject);
for (int localId : localVars)
{
IClass clazz = snapshot.getClassOf(localId);
while (clazz != null)
{
IRequestDetailsResolver resolver = RequestDetailResolverRegistry.instance().lookup(clazz.getName());
if (resolver != null)
{
resolver.complement(snapshot, info, localVars, localId, listener);
break;
}
clazz = clazz.getSuperClass();
}
}
}
private static int[] getLocalVarsForThread(IObject thread) throws SnapshotException
{
List<NamedReference> refs = thread.getOutboundReferences();
ArrayInt result = new ArrayInt();
for (NamedReference ref : refs)
{
if (ref instanceof ThreadToLocalReference)
result.add(ref.getObjectId());
}
return result.toArray();
}
private static void extractGeneralAttribtes(ThreadInfoImpl info) throws SnapshotException
{
info.className = info.subject.getDisplayName();
info.name = info.subject.getClassSpecificName();
info.instance = info.subject.getTechnicalName();
info.shallowHeap = new Bytes(info.subject.getUsedHeapSize());
info.retainedHeap = new Bytes(info.subject.getRetainedHeapSize());
info.isDaemon = resolveIsDaemon(info.subject);
IObject contextClassLoader = (IObject) info.subject.resolveValue("contextClassLoader"); //$NON-NLS-1$
if (contextClassLoader != null)
{
info.contextClassLoader = contextClassLoader.getClassSpecificName();
if (info.contextClassLoader == null)
info.contextClassLoader = contextClassLoader.getTechnicalName();
info.contextClassLoaderId = contextClassLoader.getObjectId();
}
}
private static Boolean resolveIsDaemon(IObject thread)
{
try
{
Object daemon = thread.resolveValue("daemon");
if (daemon == null) {
daemon = thread.resolveValue("isDaemon");
}
if (daemon != null) {
if (daemon instanceof Boolean) {
return (Boolean)daemon;
}
}
}
catch (SnapshotException e)
{
// Failing to get daemon status is not the end of the world
}
return null;
}
private static void extractFromDetailsResolver(ThreadInfoImpl info, boolean readFully, IProgressListener listener)
throws SnapshotException
{
for (IThreadDetailsResolver resolver : ThreadDetailResolverRegistry.instance().delegates())
{
if (readFully)
resolver.complementDeep(info, listener);
else
resolver.complementShallow(info, listener);
}
}
/* package */List<Column> getUsedColumns()
{
List<Column> answer = new ArrayList<Column>();
// Return copy of columns so independent and columns don't permanently retain decorators
for (Column col : defaultColumns)
{
Column col2 = new Column(col.getLabel(), col.getType());
answer.add(col2);
}
for (IThreadDetailsResolver resolver : ThreadDetailResolverRegistry.instance().delegates())
{
Column[] cols = resolver.getColumns();
if (cols != null)
for (int ii = 0; ii < cols.length; ii++)
{
if (properties.containsKey(cols[ii]))
answer.add(cols[ii]);
}
}
return answer;
}
/* package */static List<Column> getUsedColumns(List<ThreadInfoImpl> threads)
{
List<Column> answer = new ArrayList<Column>();
// Return copy of columns so independent and columns don't permanently retain decorators
for (Column col : defaultColumns)
{
Column col2 = new Column(col.getLabel(), col.getType());
answer.add(col2);
}
for (IThreadDetailsResolver resolver : ThreadDetailResolverRegistry.instance().delegates())
{
Column[] cols = resolver.getColumns();
if (cols != null)
for (int ii = 0; ii < cols.length; ii++)
{
for (ThreadInfoImpl thread : threads)
if (thread.properties.containsKey(cols[ii]))
{
answer.add(cols[ii]);
break;
}
}
}
return answer;
}
// //////////////////////////////////////////////////////////////
// instance
// //////////////////////////////////////////////////////////////
private IObject subject;
// general attributes
private String name;
private String className;
private String instance;
private Bytes shallowHeap = new Bytes(0);
private Bytes retainedHeap = new Bytes(0);
private String contextClassLoader;
private int contextClassLoaderId;
private Boolean isDaemon;
// extended properties
private Map<Column, Object> properties = new HashMap<Column, Object>();
private List<String> keywords = new ArrayList<String>();
private CompositeResult details;
private CompositeResult requests;
private ThreadInfoImpl()
{}
public IObject getThreadObject()
{
return subject;
}
public int getThreadId()
{
return subject.getObjectId();
}
public String getName()
{
return name;
}
public String getClassName()
{
return className;
}
public String getInstance()
{
return instance;
}
public Bytes getShallowHeap()
{
return shallowHeap;
}
public Bytes getRetainedHeap()
{
return retainedHeap;
}
public String getContextClassLoader()
{
return contextClassLoader;
}
public int getContextClassLoaderId()
{
return contextClassLoaderId;
}
public void addDetails(String name, IResult detail)
{
if (details == null)
details = new CompositeResult();
details.addResult(name, detail);
}
public CompositeResult getDetails()
{
return details;
}
public void addKeyword(String keyword)
{
keywords.add(keyword);
}
public Collection<String> getKeywords()
{
return keywords;
}
public void setValue(Column column, Object value)
{
properties.put(column, value);
}
public void addRequest(String summary, IResult detail)
{
if (requests == null)
requests = new CompositeResult();
requests.addResult(summary, detail);
}
public CompositeResult getRequests()
{
return requests;
}
private Object isDaemon()
{
return isDaemon;
}
public Object getValue(Column column)
{
// Rely on Column equality via just the label
if (COL_CLASSNAME.equals(column))
return getClassName();
else if (COL_NAME.equals(column))
return getName();
else if (COL_INSTANCE.equals(column))
return getInstance();
else if (COL_SHALLOW.equals(column))
return getShallowHeap();
else if (COL_RETAINED.equals(column))
return getRetainedHeap();
else if (COL_CONTEXTCL.equals(column))
return getContextClassLoader();
else if (COL_ISDAEMON.equals(column))
return isDaemon();
else
return properties.get(column);
}
}