changeset 1735:eeff1ae6fcc1

More readable method names in profiler results Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-March/013385.html
author Omair Majid <omajid@redhat.com>
date Mon, 30 Mar 2015 13:50:51 -0400
parents 16960ccfe1a0
children 8b6f962afffc
files common/core/src/main/java/com/redhat/thermostat/common/utils/DescriptorConverter.java common/core/src/main/java/com/redhat/thermostat/common/utils/MethodDescriptorConverter.java common/core/src/test/java/com/redhat/thermostat/common/utils/DescriptorConverterTest.java common/core/src/test/java/com/redhat/thermostat/common/utils/MethodDescriptorConverterTest.java vm-profiler/client-core/src/main/java/com/redhat/thermostat/vm/profiler/client/core/ProfilingResultParser.java
diffstat 5 files changed, 231 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/utils/DescriptorConverter.java	Tue Aug 18 11:23:58 2015 -0400
+++ b/common/core/src/main/java/com/redhat/thermostat/common/utils/DescriptorConverter.java	Mon Mar 30 13:50:51 2015 -0400
@@ -42,6 +42,8 @@
 /**
  * The JVM uses internal names for classes and fields (like "<code>[I</code>").
  * This class helps to decode them.
+ *
+ * @see MethodDescriptorConverter
  */
 public class DescriptorConverter {
 
@@ -59,6 +61,10 @@
     }
 
     public static String toJavaType(String fieldDescriptor) {
+        return toJavaType(fieldDescriptor, lookupTable);
+    }
+
+    static String toJavaType(String fieldDescriptor, Map<Character, String> lookupTable) {
         StringBuilder result = new StringBuilder();
 
         int arrayDimensions = 0;
@@ -74,7 +80,9 @@
         if (lookupTable.get(indicator) != null) {
             result.append(lookupTable.get(indicator));
         } else if (indicator == 'L') {
-            result.append(fieldDescriptor.substring(lastLocation + 1, fieldDescriptor.length() - 1));
+            String internalClassName = fieldDescriptor.substring(lastLocation + 1, fieldDescriptor.length() - 1);
+            String commonClassName = internalClassName.replace('/', '.');
+            result.append(commonClassName);
         } else {
             result.append(fieldDescriptor);
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/main/java/com/redhat/thermostat/common/utils/MethodDescriptorConverter.java	Mon Mar 30 13:50:51 2015 -0400
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012-2015 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Convert JVM-internal descriptors of methods to standard Java-style method
+ * descriptions.
+ *
+ * @see DescriptorConverter
+ */
+public class MethodDescriptorConverter {
+
+    public static final String UNKNOWN_METHOD_NAME = "???";
+
+    private static final Map<Character, String> lookupTable = new HashMap<>();
+
+    static {
+        lookupTable.put('Z', "boolean");
+        lookupTable.put('B', "byte");
+        lookupTable.put('C', "char");
+        lookupTable.put('S', "short");
+        lookupTable.put('I', "int");
+        lookupTable.put('J', "long");
+        lookupTable.put('F', "float");
+        lookupTable.put('D', "double");
+        lookupTable.put('V', "void");
+    }
+
+    public static String toJavaType(String descriptor) {
+        return toJavaType(UNKNOWN_METHOD_NAME, descriptor);
+    }
+
+    public static String toJavaType(String methodName, String descriptor) {
+        final int NOT_FOUND = -1;
+
+        int start = descriptor.indexOf('(');
+        int end = descriptor.indexOf(')');
+        if (start == NOT_FOUND || end == NOT_FOUND) {
+            throw new IllegalArgumentException("Malformed descriptor: " + descriptor);
+        }
+
+        String parameterPart = descriptor.substring(start+1, end);
+        List<String> decodedParameters = convertParameters(parameterPart);
+        String parameters = StringUtils.join(", ", decodedParameters);
+
+        String returnPart = descriptor.substring(end+1);
+        String returnType = DescriptorConverter.toJavaType(returnPart, lookupTable);
+
+        return returnType + " " + methodName.replace('/', '.') + "(" + parameters + ")";
+    }
+
+    private static List<String> convertParameters(String parameterPart) {
+        List<String> decodedParameters = new ArrayList<>();
+
+        int index = 0;
+        while (index < parameterPart.length()) {
+            int arrayDimensions = 0;
+            char code = parameterPart.charAt(index);
+            while (code == '[') {
+                arrayDimensions++;
+                index++;
+                code = parameterPart.charAt(index);
+            }
+
+            if (null != lookupTable.get(code)) {
+                decodedParameters.add(lookupTable.get(code) + StringUtils.repeat("[]", arrayDimensions));
+                index++;
+            } else if (code == 'L') {
+                int endIndex = parameterPart.indexOf(';', index+1);
+                if (endIndex == -1) {
+                    throw new IllegalArgumentException("Malformed descriptor: " + code);
+                }
+                String commonClassName = parameterPart.substring(index+1, endIndex).replace('/', '.');
+                decodedParameters.add(commonClassName + StringUtils.repeat("[]", arrayDimensions));
+                index = endIndex + 1;
+            } else {
+                throw new IllegalArgumentException("Unrecognized descriptor : " + code);
+            }
+        }
+        return decodedParameters;
+    }
+
+}
--- a/common/core/src/test/java/com/redhat/thermostat/common/utils/DescriptorConverterTest.java	Tue Aug 18 11:23:58 2015 -0400
+++ b/common/core/src/test/java/com/redhat/thermostat/common/utils/DescriptorConverterTest.java	Mon Mar 30 13:50:51 2015 -0400
@@ -55,6 +55,10 @@
         check("java.util.ArrayList", "java.util.ArrayList");
         check("java.util.HashMap$Entry", "java.util.HashMap$Entry");
 
+        // this isn't, strictly speaking, the format of an internal descriptor:
+        check("java.lang.Object[]", "[Ljava.lang.Object;");
+        // this is:
+        check("java.lang.Object[]", "[Ljava/lang/Object;");
         check("java.lang.Object[]", "[Ljava.lang.Object;");
         check("java.lang.String[]", "[Ljava.lang.String;");
         check("java.util.HashMap$Entry[]", "[Ljava.util.HashMap$Entry;");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/core/src/test/java/com/redhat/thermostat/common/utils/MethodDescriptorConverterTest.java	Mon Mar 30 13:50:51 2015 -0400
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012-2015 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.common.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class MethodDescriptorConverterTest {
+
+    @Test
+    public void verifyMethodDescriptors() {
+        check("void ???()", "()V");
+        check("int ???()", "()I");
+        check("int[] ???()", "()[I");
+        check("long ???()", "()J");
+        check("double ???()", "()D");
+        check("java.lang.Object ???()", "()Ljava/lang/Object;");
+        check("java.lang.Object[] ???()", "()[Ljava/lang/Object;");
+
+        check("void ???(java.lang.Object[])", "([Ljava/lang/Object;)V");
+
+        check("java.lang.Object ???(int, double, java.lang.Thread)", "(IDLjava/lang/Thread;)Ljava/lang/Object;");
+        check("java.lang.Object ???(java.lang.Object, java.lang.String[], java.lang.Thread)", "(Ljava.lang.Object;[Ljava.lang.String;Ljava/lang/Thread;)Ljava/lang/Object;");
+        check("java.lang.Object[] ???(int[], double, java.lang.Thread[])", "([ID[Ljava/lang/Thread;)[Ljava/lang/Object;");
+    }
+
+    private static void check(String expected, String input) {
+        String result = MethodDescriptorConverter.toJavaType(input);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void verifyMethodNameAndDescriptors() {
+        check("int[] foo()", "foo", "()[I");
+        check("java.lang.Object[] foo()", "foo", "()[Ljava/lang/Object;");
+
+        check("java.lang.Object foo(int, double, java.lang.Thread)", "foo", "(IDLjava/lang/Thread;)Ljava/lang/Object;");
+        check("java.lang.Object[] foo(int[], double, java.lang.Thread[])", "foo", "([ID[Ljava/lang/Thread;)[Ljava/lang/Object;");
+    }
+
+    private static void check(String expected, String methodName, String descriptor) {
+        String result = MethodDescriptorConverter.toJavaType(methodName, descriptor);
+        assertEquals(expected, result);
+    }
+
+}
--- a/vm-profiler/client-core/src/main/java/com/redhat/thermostat/vm/profiler/client/core/ProfilingResultParser.java	Tue Aug 18 11:23:58 2015 -0400
+++ b/vm-profiler/client-core/src/main/java/com/redhat/thermostat/vm/profiler/client/core/ProfilingResultParser.java	Mon Mar 30 13:50:51 2015 -0400
@@ -45,6 +45,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
@@ -52,6 +53,7 @@
 import java.util.logging.Logger;
 
 import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.common.utils.MethodDescriptorConverter;
 import com.redhat.thermostat.vm.profiler.client.core.ProfilingResult.MethodInfo;
 
 public class ProfilingResultParser {
@@ -89,7 +91,9 @@
         }
 
         for (Entry<String, Long> entry : results.entrySet()) {
-            MethodInfo method = new MethodInfo(entry.getKey(), entry.getValue(), (entry.getValue() * 1.0 / totalTime) * 100);
+            String methodNameAndDescriptor = entry.getKey();
+            String methodName = prettify(methodNameAndDescriptor);
+            MethodInfo method = new MethodInfo(methodName, entry.getValue(), (entry.getValue() * 1.0 / totalTime) * 100);
             info.add(method);
         }
 
@@ -103,4 +107,14 @@
         return new ProfilingResult(info);
     }
 
+    private String prettify(String name) {
+        int startDescriptor = name.indexOf('(');
+        if (startDescriptor == -1) {
+            // handle malformed method descriptor by returning it as it is
+            return name;
+        }
+        String methodClassName = name.substring(0, startDescriptor);
+        return MethodDescriptorConverter.toJavaType(methodClassName, name.substring(startDescriptor));
+    }
+
 }