24 */ |
24 */ |
25 |
25 |
26 package jdk.jfr.jcmd; |
26 package jdk.jfr.jcmd; |
27 |
27 |
28 import java.io.File; |
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; |
29 |
33 |
30 import jdk.test.lib.jfr.FileHelper; |
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; |
31 import jdk.test.lib.process.OutputAnalyzer; |
39 import jdk.test.lib.process.OutputAnalyzer; |
32 |
40 |
33 /* |
41 /* |
34 * @test |
42 * @test |
35 * @summary The test verifies JFR.dump command |
43 * @summary The test verifies JFR.dump command |
36 * @key jfr |
44 * @key jfr |
37 * @library /test/lib /test/jdk |
45 * @library /test/lib /test/jdk |
38 * @run main/othervm jdk.jfr.jcmd.TestJcmdDump |
46 * @run main/othervm -XX:FlightRecorderOptions:maxchunksize=1M jdk.jfr.jcmd.TestJcmdDump |
39 */ |
47 */ |
40 public class TestJcmdDump { |
48 public class TestJcmdDump { |
41 |
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 |
42 public static void main(String[] args) throws Exception { |
58 public static void main(String[] args) throws Exception { |
43 String name = "TestJcmdDump"; |
|
44 File recording = new File(name + ".jfr"); |
|
45 |
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(); |
46 |
66 |
47 OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name); |
67 // The implementation of JFR.dump touch code that can't be executed using the |
48 JcmdAsserts.assertRecordingHasStarted(output); |
68 // Java API. It is therefore important to try all combinations. The |
49 JcmdHelper.waitUntilRunning(name); |
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(); |
50 |
84 |
51 output = JcmdHelper.jcmd("JFR.dump", |
85 // Expect no path to GC roots |
52 "name=" + name, |
86 jfrDump(Boolean.FALSE, name, disk, rootCount -> rootCount == 0); |
53 "filename=" + recording.getAbsolutePath()); |
87 // Expect path to GC roots |
54 JcmdAsserts.assertRecordingDumpedToFile(output, name, recording); |
88 jfrDump(null, name, disk, rootCount -> rootCount == 0); |
55 JcmdHelper.stopAndCheck(name); |
89 // Expect at least one path to a GC root |
56 FileHelper.verifyRecording(recording); |
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]); |
57 } |
170 } |
58 } |
171 } |