Merge "[572596]: Add maximum retained heap of locals to stack frame row"
diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadInfoImpl.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadInfoImpl.java
index 4d2f348..694e58e 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadInfoImpl.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadInfoImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************

- * Copyright (c) 2008, 2020 SAP AG & IBM Corporation.

+ * Copyright (c) 2008, 2021 SAP AG & 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

@@ -45,13 +45,14 @@
     // 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);

+    public static final Column COL_CLASSNAME = new Column(Messages.Column_ClassName);

+    public static final Column COL_NAME = new Column(Messages.ThreadInfoImpl_Column_Name);

+    public static final Column COL_INSTANCE = new Column(Messages.ThreadStackQuery_Column_ObjectStackFrame);

+    public static final Column COL_SHALLOW = new Column(Messages.Column_ShallowHeap, Bytes.class);

+    public static final Column COL_RETAINED = new Column(Messages.Column_RetainedHeap, Bytes.class);

+    public static final Column COL_CONTEXTCL = new Column(Messages.ThreadInfoImpl_Column_ContextClassLoader);

+    public static final Column COL_ISDAEMON = new Column(Messages.ThreadInfoImpl_Column_IsDaemon, Boolean.class);

+    public static final Column COL_MAXLOCALRETAINED = new Column(Messages.ThreadInfoImpl_Column_MaxLocalRetainedHeap, Bytes.class).noTotals();

 

     private static final List<Column> defaultColumns = Arrays.asList(new Column[] {

                     //COL_CLASSNAME, //

@@ -59,6 +60,7 @@
                     COL_NAME, //

                     COL_SHALLOW, //

                     COL_RETAINED, //

+                    COL_MAXLOCALRETAINED, //

                     COL_CONTEXTCL,

                     COL_ISDAEMON});

 

@@ -187,13 +189,7 @@
 

     /* 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);

-        }

+        List<Column> answer = getDefaultColumnsCopy();

         for (IThreadDetailsResolver resolver : ThreadDetailResolverRegistry.instance().delegates())

         {

             Column[] cols = resolver.getColumns();

@@ -211,13 +207,7 @@
 

     /* 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);

-        }

+        List<Column> answer = getDefaultColumnsCopy();

         for (IThreadDetailsResolver resolver : ThreadDetailResolverRegistry.instance().delegates())

         {

             Column[] cols = resolver.getColumns();

@@ -239,6 +229,25 @@
         return answer;

     }

 

+    private static List<Column> getDefaultColumnsCopy()

+    {

+        List<Column> answer = new ArrayList<Column>();

+        // Return copy of columns so independent and columns don't permanently

+        // retain decorators

+        for (Column col : defaultColumns)

+        {

+            // Copy everything that's used in Column.equals

+            Column col2 = new Column(col.getLabel(), col.getType(), col.getAlign(), col.getSortDirection(),

+                            col.getFormatter(), col.getComparator());

+            if (!col.getCalculateTotals())

+            {

+                col2.noTotals();

+            }

+            answer.add(col2);

+        }

+        return answer;

+    }

+

     // //////////////////////////////////////////////////////////////

     // instance

     // //////////////////////////////////////////////////////////////

@@ -370,6 +379,8 @@
             return getContextClassLoader();

         else if (COL_ISDAEMON.equals(column))

             return isDaemon();

+        else if (COL_MAXLOCALRETAINED.equals(column))

+            return null;

         else

             return properties.get(column);

     }

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadOverviewQuery.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadOverviewQuery.java
index 3edc0bf..e322fc9 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadOverviewQuery.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/inspections/threads/ThreadOverviewQuery.java
@@ -22,6 +22,7 @@
 import java.util.Iterator;

 import java.util.List;

 import java.util.Objects;

+import java.util.logging.Logger;

 

 import org.eclipse.mat.SnapshotException;

 import org.eclipse.mat.collect.ArrayInt;

@@ -29,6 +30,7 @@
 import org.eclipse.mat.collect.SetInt;

 import org.eclipse.mat.inspections.InspectionAssert;

 import org.eclipse.mat.internal.Messages;

+import org.eclipse.mat.query.Bytes;

 import org.eclipse.mat.query.Column;

 import org.eclipse.mat.query.DetailResultProvider;

 import org.eclipse.mat.query.IContextObject;

@@ -57,6 +59,7 @@
 import org.eclipse.mat.snapshot.query.ObjectListResult;

 import org.eclipse.mat.snapshot.query.SnapshotQuery;

 import org.eclipse.mat.util.IProgressListener;

+import org.eclipse.mat.util.MessageUtil;

 

 @CommandName("thread_overview")

 @Icon("/META-INF/icons/threads.gif")

@@ -265,6 +268,8 @@
         private int depth;

         private boolean firstNonNativeFrame;

         private int objectId;

+        private boolean extraColumnsCalculated;

+        private Bytes maxLocalRetainedHeapSize;

     }

     

     private static class ThreadStackFrameLocalNode

@@ -418,13 +423,25 @@
             } else {

                 if (row instanceof ThreadStackFrameNode)

                 {

-                    switch (columnIndex)

+                    ThreadStackFrameNode info = (ThreadStackFrameNode) row;

+                    Column column = columns[columnIndex];

+                    try

                     {

-                        case 0:

-                            IStackFrame frame = ((ThreadStackFrameNode) row).stackFrame;

+                        if (ThreadInfoImpl.COL_INSTANCE.equals(column))

+                        {

+                            IStackFrame frame = info.stackFrame;

                             return frame.getText();

-                        default:

-                            break;

+                        }

+                        else if (ThreadInfoImpl.COL_MAXLOCALRETAINED.equals(column))

+                        {

+                            calculateStackFrameNodeColumnsIfNeeded(info);

+                            return info.maxLocalRetainedHeapSize;

+                        }

+                    }

+                    catch (SnapshotException se)

+                    {

+                        Logger.getLogger(getClass().getCanonicalName()).warning(MessageUtil.format(

+                                        Messages.ThreadOverviewQuery_StackFrameLocalIssue, se.getLocalizedMessage()));

                     }

                 }

                 else

@@ -443,6 +460,35 @@
             }

         }

 

+        private void calculateStackFrameNodeColumnsIfNeeded(ThreadStackFrameNode info) throws SnapshotException

+        {

+            if (!info.extraColumnsCalculated)

+            {

+                try

+                {

+                    @SuppressWarnings("unchecked")

+                    List<ThreadStackFrameLocalNode> locals = (List<ThreadStackFrameLocalNode>) getChildren(info);

+                    if (locals != null && locals.size() > 0)

+                    {

+                        long maxLocalRetainedHeapSize = 0, localRetainedHeapSize;

+                        for (ThreadStackFrameLocalNode local : locals)

+                        {

+                            localRetainedHeapSize = snapshot.getRetainedHeapSize(local.objectId);

+                            if (localRetainedHeapSize > maxLocalRetainedHeapSize)

+                            {

+                                maxLocalRetainedHeapSize = localRetainedHeapSize;

+                            }

+                        }

+                        info.maxLocalRetainedHeapSize = new Bytes(maxLocalRetainedHeapSize);

+                    }

+                }

+                finally

+                {

+                    info.extraColumnsCalculated = true;

+                }

+            }

+        }

+

         public IContextObject getContext(final Object row)

         {

             if (row instanceof ThreadOverviewNode) {

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
index c6bd61d..a1ed347 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/Messages.java
@@ -595,6 +595,7 @@
     public static String ThreadInfoImpl_Column_ContextClassLoader;

     public static String ThreadInfoImpl_Column_IsDaemon;

     public static String ThreadInfoImpl_Column_Instance;

+    public static String ThreadInfoImpl_Column_MaxLocalRetainedHeap;

     public static String ThreadInfoImpl_Column_Name;

 

     public static String ThreadInfoQuery_Requests;

@@ -604,6 +605,7 @@
     public static String ThreadInfoQuery_ThreadStack;

 

     public static String ThreadOverviewQuery_SearchingThreads;

+    public static String ThreadOverviewQuery_StackFrameLocalIssue;

     public static String ThreadOverviewQuery_ThreadDetails;

 

     public static String ThreadStackQuery_Column_ObjectStackFrame;

diff --git a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
index 906ad49..310a221 100644
--- a/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
+++ b/plugins/org.eclipse.mat.api/src/org/eclipse/mat/internal/messages.properties
@@ -526,6 +526,7 @@
 ThreadInfoImpl_Column_ContextClassLoader=Context Class Loader

 ThreadInfoImpl_Column_IsDaemon=Is Daemon

 ThreadInfoImpl_Column_Instance=Instance

+ThreadInfoImpl_Column_MaxLocalRetainedHeap=Max. Local Retained Heap

 ThreadInfoImpl_Column_Name=Name

 ThreadInfoQuery_Requests=Requests

 ThreadInfoQuery_ThreadDetails=Thread Details

@@ -533,6 +534,7 @@
 ThreadInfoQuery_ThreadProperties=Thread Properties

 ThreadInfoQuery_ThreadStack=Thread Stack

 ThreadOverviewQuery_SearchingThreads=Searching Threads...

+ThreadOverviewQuery_StackFrameLocalIssue=Could not evaluate stack frame local: {0}

 ThreadOverviewQuery_ThreadDetails=Thread Details

 ThreadStackQuery_Column_ObjectStackFrame=Object / Stack Frame

 ThreadStackQuery_Label_Local=<local>