24 */ |
24 */ |
25 package jdk.jfr.internal.dcmd; |
25 package jdk.jfr.internal.dcmd; |
26 |
26 |
27 import java.io.IOException; |
27 import java.io.IOException; |
28 import java.nio.file.InvalidPathException; |
28 import java.nio.file.InvalidPathException; |
29 import java.util.HashMap; |
29 import java.time.Duration; |
30 import java.util.Map; |
30 import java.time.Instant; |
31 |
31 import java.time.LocalDate; |
|
32 import java.time.LocalDateTime; |
|
33 import java.time.LocalTime; |
|
34 import java.time.ZoneId; |
|
35 import java.time.ZonedDateTime; |
|
36 import java.time.format.DateTimeParseException; |
|
37 |
|
38 import jdk.jfr.FlightRecorder; |
32 import jdk.jfr.Recording; |
39 import jdk.jfr.Recording; |
|
40 import jdk.jfr.internal.LogLevel; |
|
41 import jdk.jfr.internal.LogTag; |
|
42 import jdk.jfr.internal.Logger; |
|
43 import jdk.jfr.internal.PlatformRecorder; |
33 import jdk.jfr.internal.PlatformRecording; |
44 import jdk.jfr.internal.PlatformRecording; |
34 import jdk.jfr.internal.PrivateAccess; |
45 import jdk.jfr.internal.PrivateAccess; |
35 import jdk.jfr.internal.SecuritySupport.SafePath; |
46 import jdk.jfr.internal.SecuritySupport.SafePath; |
36 import jdk.jfr.internal.Utils; |
47 import jdk.jfr.internal.Utils; |
37 import jdk.jfr.internal.WriteableUserPath; |
48 import jdk.jfr.internal.WriteableUserPath; |
38 |
49 |
39 /** |
50 /** |
40 * JFR.dump |
51 * JFR.dump |
41 * |
52 * |
42 */ |
53 */ |
43 //Instantiated by native |
54 // Instantiated by native |
44 final class DCmdDump extends AbstractDCmd { |
55 final class DCmdDump extends AbstractDCmd { |
45 /** |
56 /** |
46 * Execute JFR.dump. |
57 * Execute JFR.dump. |
47 * |
58 * |
48 * @param recordingText name or id of the recording to dump, or |
59 * @param name name or id of the recording to dump, or <code>null</code> to dump everything |
49 * <code>null</code> |
60 * |
50 * |
61 * @param filename file path where recording should be written, not null |
51 * @param textPath file path where recording should be written. |
62 * @param maxAge how far back in time to dump, may be null |
|
63 * @param maxSize how far back in size to dump data from, may be null |
|
64 * @param begin point in time to dump data from, may be null |
|
65 * @param end point in time to dump data to, may be null |
|
66 * @param pathToGcRoots if Java heap should be swept for reference chains |
52 * |
67 * |
53 * @return result output |
68 * @return result output |
54 * |
69 * |
55 * @throws DCmdException if the dump could not be completed |
70 * @throws DCmdException if the dump could not be completed |
56 */ |
71 */ |
57 public String execute(String recordingText, String textPath, Boolean pathToGcRoots) throws DCmdException { |
72 public String execute(String name, String filename, Long maxAge, Long maxSize, String begin, String end, Boolean pathToGcRoots) throws DCmdException { |
58 if (textPath == null) { |
73 if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { |
59 throw new DCmdException("Failed to dump %s, missing filename.", recordingText); |
74 Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, |
60 } |
75 "Executing DCmdDump: name=" + name + |
61 Recording recording = findRecording(recordingText); |
76 ", filename=" + filename + |
|
77 ", maxage=" + maxAge + |
|
78 ", maxsize=" + maxSize + |
|
79 ", begin=" + begin + |
|
80 ", end" + end + |
|
81 ", path-to-gc-roots=" + pathToGcRoots); |
|
82 } |
|
83 |
|
84 if (FlightRecorder.getFlightRecorder().getRecordings().isEmpty()) { |
|
85 throw new DCmdException("No recordings to dump from. Use JFR.start to start a recording."); |
|
86 } |
|
87 |
|
88 if (maxAge != null) { |
|
89 if (end != null || begin != null) { |
|
90 throw new DCmdException("Dump failed, maxage can't be combined with begin or end."); |
|
91 } |
|
92 |
|
93 if (maxAge < 0) { |
|
94 throw new DCmdException("Dump failed, maxage can't be negative."); |
|
95 } |
|
96 if (maxAge == 0) { |
|
97 maxAge = Long.MAX_VALUE / 2; // a high value that won't overflow |
|
98 } |
|
99 } |
|
100 |
|
101 if (maxSize!= null) { |
|
102 if (maxSize < 0) { |
|
103 throw new DCmdException("Dump failed, maxsize can't be negative."); |
|
104 } |
|
105 if (maxSize == 0) { |
|
106 maxSize = Long.MAX_VALUE / 2; // a high value that won't overflow |
|
107 } |
|
108 } |
|
109 |
|
110 Instant beginTime = parseTime(begin, "begin"); |
|
111 Instant endTime = parseTime(end, "end"); |
|
112 |
|
113 if (beginTime != null && endTime != null) { |
|
114 if (endTime.isBefore(beginTime)) { |
|
115 throw new DCmdException("Dump failed, begin must preceed end."); |
|
116 } |
|
117 } |
|
118 |
|
119 Duration duration = null; |
|
120 if (maxAge != null) { |
|
121 duration = Duration.ofNanos(maxAge); |
|
122 beginTime = Instant.now().minus(duration); |
|
123 } |
|
124 Recording recording = null; |
|
125 if (name != null) { |
|
126 recording = findRecording(name); |
|
127 } |
|
128 PlatformRecorder recorder = PrivateAccess.getInstance().getPlatformRecorder(); |
|
129 synchronized (recorder) { |
|
130 dump(recorder, recording, name, filename, maxSize, pathToGcRoots, beginTime, endTime); |
|
131 } |
|
132 return getResult(); |
|
133 } |
|
134 |
|
135 public void dump(PlatformRecorder recorder, Recording recording, String name, String filename, Long maxSize, Boolean pathToGcRoots, Instant beginTime, Instant endTime) throws DCmdException { |
|
136 try (PlatformRecording r = newSnapShot(recorder, recording, pathToGcRoots)) { |
|
137 r.filter(beginTime, endTime, maxSize); |
|
138 if (r.getChunks().isEmpty()) { |
|
139 throw new DCmdException("Dump failed. No data found in the specified interval."); |
|
140 } |
|
141 SafePath dumpFile = resolvePath(recording, filename); |
|
142 |
|
143 // Needed for JVM |
|
144 Utils.touch(dumpFile.toPath()); |
|
145 r.dumpStopped(new WriteableUserPath(dumpFile.toPath())); |
|
146 reportOperationComplete("Dumped", name, dumpFile); |
|
147 } catch (IOException | InvalidPathException e) { |
|
148 throw new DCmdException("Dump failed. Could not copy recording data. %s", e.getMessage()); |
|
149 } |
|
150 } |
|
151 |
|
152 private Instant parseTime(String time, String parameter) throws DCmdException { |
|
153 if (time == null) { |
|
154 return null; |
|
155 } |
62 try { |
156 try { |
63 SafePath dumpFile = resolvePath(textPath, "Failed to dump %s"); |
157 return Instant.parse(time); |
64 // create file for JVM |
158 } catch (DateTimeParseException dtp) { |
65 Utils.touch(dumpFile.toPath()); |
159 // fall through |
66 PlatformRecording r = PrivateAccess.getInstance().getPlatformRecording(recording); |
160 } |
67 WriteableUserPath wup = new WriteableUserPath(dumpFile.toPath()); |
161 try { |
68 |
162 LocalDateTime ldt = LocalDateTime.parse(time); |
69 Map<String, String> overlay = new HashMap<>(); |
163 return ZonedDateTime.of(ldt, ZoneId.systemDefault()).toInstant(); |
70 Utils.updateSettingPathToGcRoots(overlay, pathToGcRoots); |
164 } catch (DateTimeParseException dtp) { |
71 |
165 // fall through |
72 r.copyTo(wup, "Dumped by user", overlay); |
166 } |
73 reportOperationComplete("Dumped", recording, dumpFile); |
167 try { |
74 } catch (IOException | InvalidPathException e) { |
168 LocalTime lt = LocalTime.parse(time); |
75 throw new DCmdException("Failed to dump %s. Could not copy recording for dump. %s", recordingText, e.getMessage()); |
169 LocalDate ld = LocalDate.now(); |
76 } |
170 Instant instant = ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant(); |
77 return getResult(); |
171 Instant now = Instant.now(); |
|
172 if (instant.isAfter(now) && !instant.isBefore(now.plusSeconds(3600))) { |
|
173 // User must have meant previous day |
|
174 ld = ld.minusDays(1); |
|
175 } |
|
176 return ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant(); |
|
177 } catch (DateTimeParseException dtp) { |
|
178 // fall through |
|
179 } |
|
180 |
|
181 if (time.startsWith("-")) { |
|
182 try { |
|
183 long durationNanos = Utils.parseTimespan(time.substring(1)); |
|
184 Duration duration = Duration.ofNanos(durationNanos); |
|
185 return Instant.now().minus(duration); |
|
186 } catch (NumberFormatException nfe) { |
|
187 // fall through |
|
188 } |
|
189 } |
|
190 throw new DCmdException("Dump failed, not a valid %s time.", parameter); |
|
191 } |
|
192 |
|
193 private PlatformRecording newSnapShot(PlatformRecorder recorder, Recording recording, Boolean pathToGcRoots) throws DCmdException, IOException { |
|
194 if (recording == null) { |
|
195 // Operate on all recordings |
|
196 PlatformRecording snapshot = recorder.newTemporaryRecording(); |
|
197 recorder.fillWithRecordedData(snapshot, pathToGcRoots); |
|
198 return snapshot; |
|
199 } |
|
200 |
|
201 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); |
|
202 return pr.newSnapshotClone("Dumped by user", pathToGcRoots); |
78 } |
203 } |
79 |
204 |
80 } |
205 } |