1 /* 2 * Copyright (c) 2015, 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.jcmd; 27 28 import java.io.File; 29 import java.nio.file.Files; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.function.Predicate; 33 34 import jdk.jfr.Event; 35 import jdk.jfr.Recording; 36 import jdk.jfr.consumer.RecordedEvent; 37 import jdk.jfr.consumer.RecordingFile; 38 import jdk.test.lib.jfr.EventNames; 39 import jdk.test.lib.process.OutputAnalyzer; 40 41 /* 42 * @test 43 * @summary The test verifies JFR.dump command 44 * @key jfr 45 * @library /test/lib /test/jdk 46 * @run main/othervm -XX:FlightRecorderOptions:maxchunksize=1M jdk.jfr.jcmd.TestJcmdDump 47 */ 48 public class TestJcmdDump { 49 50 static class StoppedEvent extends Event { 51 } 52 static class RunningEvent extends Event { 53 } 54 55 private static final String[] names = { null, "r1" }; 56 private static final boolean booleanValues[] = { true, false }; 57 58 public static void main(String[] args) throws Exception { 59 60 // Create a stopped recording in the repository to complicate things 61 Recording r = new Recording(); 62 r.start(); 63 StoppedEvent de = new StoppedEvent(); 64 de.commit(); 65 r.stop(); 66 67 // The implementation of JFR.dump touch code that can't be executed using the 68 // Java API. It is therefore important to try all combinations. The 69 // implementation is non-trivial and depends on the combination 70 for (String name : names) { 71 for (boolean disk : booleanValues) { 72 try (Recording r1 = new Recording(); Recording r2 = new Recording()) { 73 System.out.println(); 74 System.out.println(); 75 System.out.println("Starting recordings with disk=" + disk); 76 r1.setToDisk(disk); 77 // To complicate things, only enable OldObjectSample for one recording 78 r1.enable(EventNames.OldObjectSample).withoutStackTrace(); 79 r1.setName("r1"); 80 r2.setToDisk(disk); 81 r2.setName("r2"); 82 r1.start(); 83 r2.start(); 84 85 // Expect no path to GC roots 86 jfrDump(Boolean.FALSE, name, disk, rootCount -> rootCount == 0); 87 // Expect path to GC roots 88 jfrDump(null, name, disk, rootCount -> rootCount == 0); 89 // Expect at least one path to a GC root 90 jfrDump(Boolean.TRUE, name, disk, rootCount -> rootCount > 0); 91 } 92 } 93 } 94 r.close(); // release recording data from the stopped recording 95 } 96 97 private static void jfrDump(Boolean pathToGCRoots, String name, boolean disk, Predicate<Integer> successPredicate) throws Exception { 98 List<Object> leakList = new ArrayList<>(); 99 leakList.add(new Object[1000_0000]); 100 System.gc(); 101 while (true) { 102 RunningEvent re = new RunningEvent(); 103 re.commit(); 104 leakList.add(new Object[1000_0000]); 105 leakList.add(new Object[1000_0000]); 106 leakList.add(new Object[1000_0000]); 107 System.gc(); // This will shorten time for object to be emitted. 108 File recording = new File("TestJCMdDump.jfr"); 109 String[] params = buildParameters(pathToGCRoots, name, recording); 110 OutputAnalyzer output = JcmdHelper.jcmd(params); 111 JcmdAsserts.assertRecordingDumpedToFile(output, recording); 112 int rootCount = 0; 113 int oldObjectCount = 0; 114 int stoppedEventCount = 0; 115 int runningEventCount = 0; 116 for (RecordedEvent e : RecordingFile.readAllEvents(recording.toPath())) { 117 if (e.getEventType().getName().equals(EventNames.OldObjectSample)) { 118 if (e.getValue("root") != null) { 119 rootCount++; 120 } 121 oldObjectCount++; 122 } 123 if (e.getEventType().getName().equals(StoppedEvent.class.getName())) { 124 stoppedEventCount++; 125 } 126 if (e.getEventType().getName().equals(RunningEvent.class.getName())) { 127 runningEventCount++; 128 } 129 } 130 System.out.println("Name: " + name); 131 System.out.println("Disk: " + disk); 132 System.out.println("Path to GC roots: " + pathToGCRoots); 133 System.out.println("Old Objects: " + oldObjectCount); 134 System.out.println("Root objects: "+ rootCount); 135 System.out.println("Stopped events: "+ stoppedEventCount); 136 System.out.println("Running events: "+ runningEventCount); 137 138 System.out.println(); 139 if (runningEventCount == 0) { 140 throw new Exception("Missing event from running recording"); 141 } 142 if (name == null && stoppedEventCount == 0) { 143 throw new Exception("Missing event from stopped recording"); 144 } 145 if (name != null && stoppedEventCount > 0) { 146 throw new Exception("Stopped event should not be part of dump"); 147 } 148 if (oldObjectCount != 0 && successPredicate.test(rootCount)) { 149 return; 150 } 151 System.out.println(); 152 System.out.println(); 153 System.out.println(); 154 System.out.println("************* Retrying! **************"); 155 Files.delete(recording.toPath()); 156 } 157 } 158 159 private static String[] buildParameters(Boolean pathToGCRoots, String name, File recording) { 160 List<String> params = new ArrayList<>(); 161 params.add("JFR.dump"); 162 params.add("filename=" + recording.getAbsolutePath()); 163 if (pathToGCRoots != null) { // if path-to-gc-roots is omitted, default is used (disabled). 164 params.add("path-to-gc-roots=" + pathToGCRoots); 165 } 166 if (name != null) { // if name is omitted, all recordings will be dumped 167 params.add("name=" + name); 168 } 169 return params.toArray(new String[0]); 170 } 171 }