diff --git a/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/HeapDumpAnalyzer.java b/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/HeapDumpAnalyzer.java index 5ad49148..49d668ea 100644 --- a/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/HeapDumpAnalyzer.java +++ b/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/HeapDumpAnalyzer.java @@ -54,6 +54,8 @@ public interface HeapDumpAnalyzer { @ApiMeta(aliases = "inspector.value") String getObjectValue(int objectId); + @ApiMeta(aliases = "vmBoard.summary") + VMBoard.Summary getSummaryOfVMBoard(); @ApiMeta(aliases = "classLoaderExplorer.summary") ClassLoader.Summary getSummaryOfClassLoaders(); @@ -71,10 +73,13 @@ PageView getChildrenOfClassLoader(int classLoaderId, PageView getUnreachableObjects(int page, int pageSize); @ApiMeta(aliases = "directByteBuffer.summary") - DirectByteBuffer.Summary getSummaryOfDirectByteBuffers(); + DirectByteBuffer.Summary getSummaryOfDirectByteBuffers(String mode); @ApiMeta(aliases = "directByteBuffer.records") - PageView getDirectByteBuffers(int page, int pageSize); + PageView getDirectByteBuffers(String mode, int page, int pageSize); + + @ApiMeta(aliases = "listObjects") + PageView getListObjects(String objectLabel, int objectId, int type, int page, int pageSize); @ApiMeta(aliases = "outbounds") PageView getOutboundOfObject(int objectId, int page, int pageSize); diff --git a/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/Model.java b/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/Model.java index 63bcfb19..7b28dc10 100644 --- a/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/Model.java +++ b/analysis/heap-dump/api/src/main/java/org/eclipse/jifa/hda/api/Model.java @@ -537,6 +537,15 @@ public TreeResult(PageView pv) { } + interface VMBoard { + @Data + class Summary{ + public String requestId; + public List vmOptions; + public List gc; + } + } + interface GCRootPath { List EXCLUDES = Arrays.asList("java.lang.ref.WeakReference:referent", diff --git a/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/AnalysisContext.java b/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/AnalysisContext.java index 08484510..720bc568 100644 --- a/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/AnalysisContext.java +++ b/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/AnalysisContext.java @@ -20,10 +20,7 @@ import org.eclipse.mat.snapshot.ISnapshot; import java.lang.ref.SoftReference; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; public class AnalysisContext { @@ -54,14 +51,23 @@ static class ClassLoaderExplorerData { } static class DirectByteBufferData { - static final String OQL = - "SELECT s.@displayName as label, s.position as position, s.limit as limit, s.capacity as " + - "capacity FROM java.nio.DirectByteBuffer s where s.cleaner != null"; + static final String JDK_MANAGED_BUFFER_OQL = + "SELECT s.@displayName as label, s.position as position, s.limit as limit, s.capacity as " + + "capacity FROM java.nio.DirectByteBuffer s where s.cleaner != null"; + static final String JNI_ALLOCATED_BUFFER_OQL = + "SELECT s.@displayName as label, s.position as position, s.limit as limit, s.capacity as " + + "capacity FROM java.nio.DirectByteBuffer s where s.cleaner = null and s.att = null"; + static final String ALL_BUFFER_OQL = + "SELECT s.@displayName as label, s.position as position, s.limit as limit, s.capacity as " + + "capacity FROM java.nio.DirectByteBuffer s"; + + static final Map JDK_MANAGED_BUFFER_ARGS = + Collections.singletonMap("queryString", JDK_MANAGED_BUFFER_OQL); + static final Map JNI_ALLOC_BUFFER_ARGS = + Collections.singletonMap("queryString", JNI_ALLOCATED_BUFFER_OQL); + static final Map ALL_BUFFER_ARGS = + Collections.singletonMap("queryString", ALL_BUFFER_OQL); - static final Map ARGS = new HashMap<>(1); - static { - ARGS.put("queryString", OQL); - } RefinedTable resultContext; diff --git a/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/HeapDumpAnalyzerImpl.java b/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/HeapDumpAnalyzerImpl.java index c59a2e16..404b1178 100644 --- a/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/HeapDumpAnalyzerImpl.java +++ b/analysis/heap-dump/impl/src/main/java/org/eclipse/jifa/hda/impl/HeapDumpAnalyzerImpl.java @@ -496,6 +496,40 @@ private ClassLoaderExplorerData queryClassLoader(AnalysisContext context) throws } } + @Override + public Model.VMBoard.Summary getSummaryOfVMBoard() { + Map queryVMOptions = new HashMap<>(); + Map queryGc = new HashMap<>(); + queryVMOptions.put("queryString", "SELECT toString(x) AS VMOptions FROM OBJECTS (SELECT OBJECTS s.vmArgs.list.a[0:-1] FROM sun.management.VMManagementImpl s) x"); + queryGc.put("queryString", "SELECT toString(g.name) AS GC FROM sun.management.GarbageCollectorImpl g"); + return $(() -> { + List options = new ArrayList<>(); + List gc = new ArrayList<>(); + IResultTable result = null; + IResult r = queryByCommand(context, "oql", queryVMOptions); + if (r instanceof IResultTable) { + // In case we get IResultText, i.e. empty result + result = (IResultTable) r; + for (int i = 0; i < result.getRowCount(); i++) { + Object row = result.getRow(i); + options.add((String) result.getColumnValue(row, 0)); + } + } + r = queryByCommand(context, "oql", queryGc); + if (r instanceof IResultTable) { + result = (IResultTable) r; + for (int i = 0; i < result.getRowCount(); i++) { + Object row = result.getRow(i); + gc.add((String) result.getColumnValue(row, 0)); + } + } + Model.VMBoard.Summary vmBoard = new Model.VMBoard.Summary(); + vmBoard.setVmOptions(options); + vmBoard.setGc(gc); + return vmBoard; + }); + } + @Override public Model.ClassLoader.Summary getSummaryOfClassLoaders() { return $(() -> { @@ -603,56 +637,55 @@ public PageView getUnreachableObjects(int page, int page } private DirectByteBufferData queryDirectByteBufferData( - AnalysisContext context) throws SnapshotException { - DirectByteBufferData data = context.directByteBufferData.get(); - if (data != null) { - return data; + AnalysisContext context, String mode) throws SnapshotException { + DirectByteBufferData data = new DirectByteBufferData(); + final Map queryArg; + switch (mode) { + case "jniAlloc": + queryArg = DirectByteBufferData.JNI_ALLOC_BUFFER_ARGS; + break; + case "jvmManaged": + queryArg = DirectByteBufferData.JDK_MANAGED_BUFFER_ARGS; + break; + case "all": + default: + queryArg = DirectByteBufferData.ALL_BUFFER_ARGS; + break; } - - //noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (context) { - data = context.directByteBufferData.get(); - if (data != null) { - return data; + IResult result = queryByCommand(context, "oql", queryArg); + IResultTable table; + if (result instanceof IResultTable) { + table = (IResultTable) result; + + RefinedResultBuilder builder = + new RefinedResultBuilder(new SnapshotQueryContext(context.snapshot), table); + builder.setSortOrder(3, Column.SortDirection.DESC); + data.resultContext = (RefinedTable) builder.build(); + DirectByteBuffer.Summary summary = new DirectByteBuffer.Summary(); + summary.totalSize = data.resultContext.getRowCount(); + + for (int i = 0; i < summary.totalSize; i++) { + Object row = data.resultContext.getRow(i); + summary.position += data.position(row); + summary.limit += data.limit(row); + summary.capacity += data.capacity(row); } - - data = new DirectByteBufferData(); - IResult result = queryByCommand(context, "oql", DirectByteBufferData.ARGS); - IResultTable table; - if (result instanceof IResultTable) { - table = (IResultTable) result; - - RefinedResultBuilder builder = - new RefinedResultBuilder(new SnapshotQueryContext(context.snapshot), table); - builder.setSortOrder(3, Column.SortDirection.DESC); - data.resultContext = (RefinedTable) builder.build(); - DirectByteBuffer.Summary summary = new DirectByteBuffer.Summary(); - summary.totalSize = data.resultContext.getRowCount(); - - for (int i = 0; i < summary.totalSize; i++) { - Object row = data.resultContext.getRow(i); - summary.position += data.position(row); - summary.limit += data.limit(row); - summary.capacity += data.capacity(row); - } - data.summary = summary; - } else { - data.summary = new DirectByteBuffer.Summary(); - } - context.directByteBufferData = new SoftReference<>(data); - return data; + data.summary = summary; + } else { + data.summary = new DirectByteBuffer.Summary(); } + return data; } @Override - public DirectByteBuffer.Summary getSummaryOfDirectByteBuffers() { - return $(() -> queryDirectByteBufferData(context).summary); + public DirectByteBuffer.Summary getSummaryOfDirectByteBuffers(String mode) { + return $(() -> queryDirectByteBufferData(context, mode).summary); } @Override - public PageView getDirectByteBuffers(int page, int pageSize) { + public PageView getDirectByteBuffers(String mode, int page, int pageSize) { return $(() -> { - DirectByteBufferData data = queryDirectByteBufferData(context); + DirectByteBufferData data = queryDirectByteBufferData(context, mode); RefinedTable resultContext = data.resultContext; return PageViewBuilder.build(new PageViewBuilder.Callback() { @Override @@ -702,6 +735,69 @@ private PageView queryIOBoundsOfObject(AnalysisContext context, int }); } + @Override + public PageView getListObjects(String objectLabel, int objectId, int type, int page, int pageSize) { + Map args = new HashMap<>(); + if (type == JavaObject.ARRAY_TYPE || type == JavaObject.NORMAL_TYPE) { + objectLabel = String.valueOf(objectId); + } + String query = "list_objects " + objectLabel; + if (type == Model.Histogram.ItemType.SUPER_CLASS) { + query += " -include_subclasses"; + } + if (type == Model.Histogram.ItemType.CLASS_LOADER) { + query = "oql"; + String oql = "SELECT * FROM (SELECT * FROM java.lang.Class c WHERE c implements org.eclipse.mat.snapshot.model.IClass and c.@classLoaderId = " + objectId + ")"; + args.put("queryString", oql); + } + String finalQuery = query; + + return $(() -> { + if(type == Model.Histogram.ItemType.PACKAGE){ + IResult result = queryByCommand(context, "histogram -groupBy BY_PACKAGE"); + Histogram.PackageTree pt = (Histogram.PackageTree) result; + Object targetParentNode = new ExoticTreeFinder(pt) + .setGetChildrenCallback(node -> { + Map subPackages = ReflectionUtil.getFieldValueOrNull(node, "subPackages"); + if (subPackages != null) { + return new ArrayList<>(subPackages.values()); + } else { + return null; + } + }) + .setPredicate((theTree, theNode) -> { + if (!(theNode instanceof XClassHistogramRecord)) { + try { + java.lang.reflect.Field + field = theNode.getClass().getSuperclass().getDeclaredField("label"); + field.setAccessible(true); + String labelName = (String) field.get(theNode); + return labelName.hashCode(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return null; + }) + .findTargetParentNode(objectId); + IContextObject c = pt.getContext(targetParentNode); + if (c instanceof IContextObjectSet) { + int[] objectIds = ((IContextObjectSet) c).getObjectIds(); + return PageViewBuilder.build(objectIds, new PagingRequest(page, pageSize), this::getObjectInfo); + } + return PageView.empty(); + }else{ + IResultTree tree = queryByCommand(context, finalQuery, args); + List objectIds = tree.getElements(); + + return PageViewBuilder.build(objectIds, new PagingRequest(page, pageSize), node -> { + int id = tree.getContext(node).getObjectId(); + return getObjectInfo(id); + }); + } + }); + } + @Override public PageView getOutboundOfObject(int objectId, int page, int pageSize) { return $(() -> queryIOBoundsOfObject(context, objectId, page, pageSize, true)); diff --git a/analysis/heap-dump/impl/src/test/java/org/eclipse/jifa/hda/impl/TestHeapDumpAnalyzerImpl.java b/analysis/heap-dump/impl/src/test/java/org/eclipse/jifa/hda/impl/TestHeapDumpAnalyzerImpl.java index 3244c94e..9af1e52c 100644 --- a/analysis/heap-dump/impl/src/test/java/org/eclipse/jifa/hda/impl/TestHeapDumpAnalyzerImpl.java +++ b/analysis/heap-dump/impl/src/test/java/org/eclipse/jifa/hda/impl/TestHeapDumpAnalyzerImpl.java @@ -134,12 +134,12 @@ public void testGetUnreachableObjects() { @Test public void testGetSummaryOfDirectByteBuffers() { - ANALYZER.getSummaryOfDirectByteBuffers(); + ANALYZER.getSummaryOfDirectByteBuffers("all"); } @Test public void testGetDirectByteBuffers() { - ANALYZER.getDirectByteBuffers(1, 10); + ANALYZER.getDirectByteBuffers("all", 1, 10); } @Test