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 * @requires vm.gc == "null" 46 * @library /test/lib /test/jdk 47 * @modules jdk.jfr/jdk.jfr.internal.test 48 * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestObjectDescription 49 */ 50 public class TestObjectDescription { 51 52 private static final int OBJECT_DESCRIPTION_MAX_SIZE = 100; 53 private static final String CLASS_NAME = TestClassLoader.class.getName() + "$TestClass"; 54 public static List<?> leaks; 55 56 public final static class MyThreadGroup extends ThreadGroup { 57 public final static String NAME = "My Thread Group"; 58 59 public MyThreadGroup(String name) { 60 super(name); 61 } 62 63 // Allocate array to trigger sampling code path for interpreter / c1 64 byte[] bytes = new byte[10]; 65 } 66 67 public final static class MyThread extends Thread { 68 public final static String NAME = "My Thread"; 69 70 public MyThread() { 71 super(NAME); 72 } 73 74 // Allocate array to trigger sampling code path for interpreter / c1 75 byte[] bytes = new byte[10]; 76 } 77 78 public static void main(String[] args) throws Exception { 79 WhiteBox.setWriteAllObjectSamples(true); 80 81 testThreadGroupName(); 82 testThreadName(); 83 testClassName(); 84 testSize(); 85 testEllipsis(); 86 } 87 88 private static void testThreadName() throws Exception { 89 asseertObjectDescription(() -> { 90 List<MyThread> threads = new ArrayList<>(OldObjects.MIN_SIZE); 91 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 92 threads.add(new MyThread()); 93 } 94 return threads; 95 }, "Thread Name: " + MyThread.NAME); 96 } 97 98 private static void testThreadGroupName() throws Exception { 99 asseertObjectDescription(() -> { 100 List<MyThreadGroup> groups = new ArrayList<>(OldObjects.MIN_SIZE); 101 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 102 groups.add(new MyThreadGroup("My Thread Group")); 103 } 104 return groups; 105 }, "Thread Group: " + "My Thread Group"); 106 } 107 108 private static void testClassName() throws Exception { 109 asseertObjectDescription(() -> { 110 TestClassLoader testClassLoader = new TestClassLoader(); 111 List<Object> classObjects = new ArrayList<>(OldObjects.MIN_SIZE); 112 for (Class<?> clazz : testClassLoader.loadClasses(OldObjects.MIN_SIZE / 20)) { 113 // Allocate array to trigger sampling code path for interpreter / c1 114 for (int i = 0; i < 20; i++) { 115 Object classArray = Array.newInstance(clazz, 20); 116 Array.set(classArray, i, clazz.newInstance()); 117 classObjects.add(classArray); 118 } 119 } 120 return classObjects; 121 }, "Class Name: " + CLASS_NAME); 122 } 123 124 private static void testSize() throws Exception { 125 asseertObjectDescription(() -> { 126 List<Object> arrayLists = new ArrayList<>(OldObjects.MIN_SIZE); 127 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 128 List<Object> arrayList = new ArrayList<>(); 129 arrayList.add(new Object()); 130 arrayList.add(new Object()); 131 arrayList.add(new Object()); 132 arrayLists.add(arrayList); 133 } 134 return arrayLists; 135 }, "Size: 3"); 136 } 137 138 private static void testEllipsis() throws Exception { 139 asseertObjectDescription(() -> { 140 StringBuilder sb = new StringBuilder(); 141 for (int i = 0; i < 2 * OBJECT_DESCRIPTION_MAX_SIZE; i++) { 142 sb.append("x"); 143 } 144 String threadName = sb.toString(); 145 List<Thread> threads = new ArrayList<>(); 146 for (int i = 0; i < OldObjects.MIN_SIZE; i++) { 147 threads.add(new Thread(threadName)); 148 } 149 return threads; 150 }, "xxx..."); 151 } 152 153 private static void asseertObjectDescription(Callable<List<?>> callable, String text) throws Exception { 154 int iteration = 1; 155 while (true) { 156 try (Recording recording = new Recording()) { 157 System.out.println("Iteration: " + iteration); 158 recording.enable(EventNames.OldObjectSample).withoutStackTrace().with("cutoff", "infinity"); 159 recording.start(); 160 leaks = null; 161 System.gc(); 162 leaks = callable.call(); 163 164 recording.stop(); 165 166 List<RecordedEvent> events = Events.fromRecording(recording); 167 Set<String> objectDescriptions = extractObjectDecriptions(events); 168 for (String s : objectDescriptions) { 169 if (s.contains(text)) { 170 printDescriptions(objectDescriptions); 171 return; 172 } 173 } 174 System.out.println("Could not find object description containing text \"" + text + "\""); 175 printDescriptions(objectDescriptions); 176 System.out.println(); 177 iteration++; 178 } 179 } 180 } 181 182 private static void printDescriptions(Set<String> objectDescriptions) { 183 System.out.println("Found descriptions:"); 184 for (String t : objectDescriptions) { 185 System.out.println(t); 186 } 187 } 188 189 private static Set<String> extractObjectDecriptions(List<RecordedEvent> events) { 190 Set<String> objectDescriptions = new HashSet<>(); 191 for (RecordedEvent e : events) { 192 objectDescriptions.addAll(extractObjectDescriptions(e.getValue("object"))); 193 } 194 return objectDescriptions; 195 } 196 197 private static Set<String> extractObjectDescriptions(RecordedObject o) { 198 Set<Long> visited = new HashSet<>(); 199 Set<String> descriptions = new HashSet<>(); 200 while (o != null) { 201 Long memoryAddress = o.getValue("address"); 202 if (visited.contains(memoryAddress)) { 203 return descriptions; 204 } 205 visited.add(memoryAddress); 206 String od = o.getValue("description"); 207 if (od != null) { 208 descriptions.add(od); 209 } 210 RecordedObject referrer = o.getValue("referrer"); 211 o = referrer != null ? referrer.getValue("object") : null; 212 } 213 return descriptions; 214 } 215 }