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