Mercurial > hg > release > thermostat-1.6
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)); + } + }