8215771: The jfr tool should pretty print reference chains
authoregahlin
Fri, 04 Jan 2019 14:05:16 +0100
changeset 53142 5c0ec35d0533
parent 53141 6f2d65f29de3
child 53143 6b7240a24b56
8215771: The jfr tool should pretty print reference chains Reviewed-by: mgronlun
src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java	Fri Jan 04 01:59:54 2019 -0800
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/PrettyWriter.java	Fri Jan 04 14:05:16 2019 +0100
@@ -57,9 +57,11 @@
  * This class is also used by {@link RecordedObject#toString()}
  */
 public final class PrettyWriter extends EventPrintWriter {
+    private static final String TYPE_OLD_OBJECT = Type.TYPES_PREFIX + "OldObject";
     private final static DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
     private final static Long ZERO = 0L;
     private boolean showIds;
+    private RecordedEvent currentEvent;
 
     public PrettyWriter(PrintWriter destination) {
         super(destination);
@@ -198,6 +200,7 @@
     }
 
     public void print(RecordedEvent event) {
+        currentEvent = event;
         print(event.getEventType().getName(), " ");
         println("{");
         indent();
@@ -308,7 +311,11 @@
                 println(formatMethod((RecordedMethod) value));
                 return;
             }
-            print((RecordedObject) value, postFix);
+            if (field.getTypeName().equals(TYPE_OLD_OBJECT)) {
+                printOldObject((RecordedObject) value);
+                return;
+            }
+             print((RecordedObject) value, postFix);
             return;
         }
         if (value.getClass().isArray()) {
@@ -358,6 +365,70 @@
         println(text);
     }
 
+    private void printOldObject(RecordedObject object) {
+        println(" [");
+        indent();
+        printIndent();
+        try {
+            printReferenceChain(object);
+        } catch (IllegalArgumentException iae) {
+           // Could not find a field
+           // Not possible to validate fields beforehand using RecordedObject#hasField
+           // since nested objects, for example object.referrer.array.index, requires
+           // an actual array object (which may be null).
+        }
+        retract();
+        printIndent();
+        println("]");
+    }
+
+    private void printReferenceChain(RecordedObject object) {
+        printObject(object, currentEvent.getLong("arrayElements"));
+        for (RecordedObject ref = object.getValue("referrer"); ref != null; ref = object.getValue("referrer")) {
+            long skip = ref.getLong("skip");
+            if (skip > 0) {
+                printIndent();
+                println("...");
+            }
+            String objectHolder = "";
+            long size = Long.MIN_VALUE;
+            RecordedObject array = ref.getValue("array");
+            if (array != null) {
+                long index = array.getLong("index");
+                size = array.getLong("size");
+                objectHolder = "[" + index + "]";
+            }
+            RecordedObject field = ref.getValue("field");
+            if (field != null) {
+                objectHolder = field.getString("name");
+            }
+            printIndent();
+            print(objectHolder);
+            print(" : ");
+            object = ref.getValue("object");
+            if (object != null) {
+                printObject(object, size);
+            }
+        }
+    }
+
+    void printObject(RecordedObject object, long arraySize) {
+        RecordedClass clazz = object.getClass("type");
+        if (clazz != null) {
+            String className = clazz.getName();
+            if (className!= null && className.startsWith("[")) {
+                className = decodeDescriptors(className, arraySize > 0 ? Long.toString(arraySize) : "").get(0);
+            }
+            print(className);
+            String description = object.getString("description");
+            if (description != null) {
+                print(" ");
+                print(description);
+            }
+        }
+        println();
+    }
+
     private void printClassLoader(RecordedClassLoader cl, String postFix) {
         // Purposely not printing class loader name to avoid cluttered output
         RecordedClass clazz = cl.getType();
@@ -388,7 +459,7 @@
         StringJoiner sj = new StringJoiner(", ");
         String md = m.getDescriptor().replace("/", ".");
         String parameter = md.substring(1, md.lastIndexOf(")"));
-        for (String qualifiedName : decodeDescriptors(parameter)) {
+        for (String qualifiedName : decodeDescriptors(parameter, "")) {
             String typeName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
             sj.add(typeName);
         }
@@ -409,17 +480,18 @@
         }
         String className = clazz.getName();
         if (className.startsWith("[")) {
-            className = decodeDescriptors(className).get(0);
+            className = decodeDescriptors(className, "").get(0);
         }
         println(className + " (classLoader = " + classLoaderName + ")" + postFix);
     }
 
-    List<String> decodeDescriptors(String descriptor) {
+    List<String> decodeDescriptors(String descriptor, String arraySize) {
         List<String> descriptors = new ArrayList<>();
         for (int index = 0; index < descriptor.length(); index++) {
             String arrayBrackets = "";
             while (descriptor.charAt(index) == '[') {
-                arrayBrackets += "[]";
+                arrayBrackets = arrayBrackets +  "[" + arraySize + "]" ;
+                arraySize = "";
                 index++;
             }
             char c = descriptor.charAt(index);