1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.event.oldobject; 27 28 import java.lang.reflect.Array; 29 import java.util.ArrayList; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Set; 33 import java.util.concurrent.Callable; 34 35 import jdk.jfr.Recording; 36 import jdk.jfr.consumer.RecordedEvent; 37 import jdk.jfr.consumer.RecordedObject; 38 import jdk.jfr.internal.test.WhiteBox; 39 import jdk.test.lib.jfr.EventNames; 40 import jdk.test.lib.jfr.Events; 41 42 /** 43 * @test 44 * @key jfr 45 * 46 * 47 * @library /lib / 48 * 49 * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestObjectDescription 50 */ 51 public class TestObjectDescription { 52 53 private static final int OBJECT_DESCRIPTION_MAX_SIZE = 100; 54 private static final String CLASS_NAME = TestClassLoader.class.getName() + "$TestClass"; 55 public static List<?> leaks; 56 57 public final static class MyThreadGroup extends ThreadGroup { 58 public final static String NAME = "My Thread Group"; 59 60 public MyThreadGroup(String name) { 61 super(name); 62 } 63 64 // Allocate array to trigger sampling code path for interpreter / c1 65 byte[] bytes = new byte[10]; 66 } 67 68 public final static class MyThread extends Thread { 69 public final static String NAME = "My Thread"; 70 71 public MyThread() { 72 super(NAME); 73 } 74 75 // Allocate array to trigger sampling code path for interpreter / c1 76 byte[] bytes = new byte[10]; 77 } 78 79 public static void main(String[] args) throws Exception { 80 WhiteBox.setWriteAllObjectSamples(true); 81 82 testThreadGroupName(); 83 testThreadName(); 84 testClassName(); 85 testSize(); 86 testEllipsis(); 87 } 88 89 private static void testThreadName() throws Exception { 90 asseertObjectDescription(() -> { 91 List<MyThread> threads = new ArrayList<>(OldObjects.MIN_SIZE); 92 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 93 threads.add(new MyThread()); 94 } 95 return threads; 96 }, "Thread Name: " + MyThread.NAME); 97 } 98 99 private static void testThreadGroupName() throws Exception { 100 asseertObjectDescription(() -> { 101 List<MyThreadGroup> groups = new ArrayList<>(OldObjects.MIN_SIZE); 102 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 103 groups.add(new MyThreadGroup("My Thread Group")); 104 } 105 return groups; 106 }, "Thread Group: " + "My Thread Group"); 107 } 108 109 private static void testClassName() throws Exception { 110 asseertObjectDescription(() -> { 111 TestClassLoader testClassLoader = new TestClassLoader(); 112 List<Object> classObjects = new ArrayList<>(OldObjects.MIN_SIZE); 113 for (Class<?> clazz : testClassLoader.loadClasses(OldObjects.MIN_SIZE / 20)) { 114 // Allocate array to trigger sampling code path for interpreter / c1 115 for (int i = 0; i < 20; i++) { 116 Object classArray = Array.newInstance(clazz, 20); 117 Array.set(classArray, i, clazz.newInstance()); 118 classObjects.add(classArray); 119 } 120 } 121 return classObjects; 122 }, "Class Name: " + CLASS_NAME); 123 } 124 125 private static void testSize() throws Exception { 126 asseertObjectDescription(() -> { 127 List<Object> arrayLists = new ArrayList<>(OldObjects.MIN_SIZE); 128 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 129 List<Object> arrayList = new ArrayList<>(); 130 arrayList.add(new Object()); 131 arrayList.add(new Object()); 132 arrayList.add(new Object()); 133 arrayLists.add(arrayList); 134 } 135 return arrayLists; 136 }, "Size: 3"); 137 } 138 139 private static void testEllipsis() throws Exception { 140 asseertObjectDescription(() -> { 141 StringBuilder sb = new StringBuilder(); 142 for (int i = 0; i < 2 * OBJECT_DESCRIPTION_MAX_SIZE; i++) { 143 sb.append("x"); 144 } 145 String threadName = sb.toString(); 146 List<Thread> threads = new ArrayList<>(); 147 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 148 threads.add(new Thread(threadName)); 149 } 150 return threads; 151 }, "xxx..."); 152 } 153 154 private static void asseertObjectDescription(Callable<List<?>> callable, String text) throws Exception { 155 int iteration = 1; 156 while (true) { 157 try (Recording recording = new Recording()) { 158 System.out.println("Iteration: " + iteration); 159 recording.enable(EventNames.OldObjectSample).withoutStackTrace().with("cutoff", "infinity"); 160 recording.start(); 161 leaks = null; 162 System.gc(); 163 leaks = callable.call(); 164 165 recording.stop(); 166 167 List<RecordedEvent> events = Events.fromRecording(recording); 168 Set<String> objectDescriptions = extractObjectDecriptions(events); 169 for (String s : objectDescriptions) { 170 if (s.contains(text)) { 171 printDescriptions(objectDescriptions); 172 return; 173 } 174 } 175 System.out.println("Could not find object description containing text \"" + text + "\""); 176 printDescriptions(objectDescriptions); 177 System.out.println(); 178 iteration++; 179 } 180 } 181 } 182 183 private static void printDescriptions(Set<String> objectDescriptions) { 184 System.out.println("Found descriptions:"); 185 for (String t : objectDescriptions) { 186 System.out.println(t); 187 } 188 } 189 190 private static Set<String> extractObjectDecriptions(List<RecordedEvent> events) { 191 Set<String> objectDescriptions = new HashSet<>(); 192 for (RecordedEvent e : events) { 193 objectDescriptions.addAll(extractObjectDescriptions(e.getValue("object"))); 194 } 195 return objectDescriptions; 196 } 197 198 private static Set<String> extractObjectDescriptions(RecordedObject o) { 199 Set<Long> visited = new HashSet<>(); 200 Set<String> descriptions = new HashSet<>(); 201 while (o != null) { 202 Long memoryAddress = o.getValue("address"); 203 if (visited.contains(memoryAddress)) { 204 return descriptions; 205 } 206 visited.add(memoryAddress); 207 String od = o.getValue("description"); 208 if (od != null) { 209 descriptions.add(od); 210 } 211 RecordedObject referrer = o.getValue("referrer"); 212 o = referrer != null ? referrer.getValue("object") : null; 213 } 214 return descriptions; 215 } 216 }