23 * questions. |
23 * questions. |
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.Files; |
28 import java.nio.file.InvalidPathException; |
29 import java.nio.file.InvalidPathException; |
|
30 import java.nio.file.Path; |
29 import java.nio.file.Paths; |
31 import java.nio.file.Paths; |
30 import java.text.ParseException; |
32 import java.text.ParseException; |
31 import java.time.Duration; |
33 import java.time.Duration; |
|
34 import java.util.Arrays; |
32 import java.util.HashMap; |
35 import java.util.HashMap; |
33 import java.util.Map; |
36 import java.util.Map; |
34 |
37 |
35 import jdk.jfr.FlightRecorder; |
38 import jdk.jfr.FlightRecorder; |
36 import jdk.jfr.Recording; |
39 import jdk.jfr.Recording; |
37 import jdk.jfr.internal.JVM; |
40 import jdk.jfr.internal.JVM; |
|
41 import jdk.jfr.internal.LogLevel; |
|
42 import jdk.jfr.internal.LogTag; |
|
43 import jdk.jfr.internal.Logger; |
|
44 import jdk.jfr.internal.OldObjectSample; |
|
45 import jdk.jfr.internal.PrivateAccess; |
38 import jdk.jfr.internal.SecuritySupport.SafePath; |
46 import jdk.jfr.internal.SecuritySupport.SafePath; |
39 import jdk.jfr.internal.Type; |
47 import jdk.jfr.internal.Type; |
40 import jdk.jfr.internal.Utils; |
|
41 import jdk.jfr.internal.jfc.JFC; |
48 import jdk.jfr.internal.jfc.JFC; |
42 |
49 |
43 /** |
50 /** |
44 * JFR.start |
51 * JFR.start |
45 * |
52 * |
49 |
56 |
50 /** |
57 /** |
51 * Execute JFR.start. |
58 * Execute JFR.start. |
52 * |
59 * |
53 * @param name optional name that can be used to identify recording. |
60 * @param name optional name that can be used to identify recording. |
54 * @param configurations names of settings files to use, i.e. "default" or |
61 * @param settings names of settings files to use, i.e. "default" or |
55 * "default.jfc". |
62 * "default.jfc". |
56 * @param delay delay before recording is started, in nanoseconds. Must be |
63 * @param delay delay before recording is started, in nanoseconds. Must be |
57 * at least 1 second. |
64 * at least 1 second. |
58 * @param duration duration of the recording, in nanoseconds. Must be at |
65 * @param duration duration of the recording, in nanoseconds. Must be at |
59 * least 1 second. |
66 * least 1 second. |
71 * @return result output |
78 * @return result output |
72 * |
79 * |
73 * @throws DCmdException if recording could not be started |
80 * @throws DCmdException if recording could not be started |
74 */ |
81 */ |
75 @SuppressWarnings("resource") |
82 @SuppressWarnings("resource") |
76 public String execute(String name, String[] configurations, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException { |
83 public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException { |
|
84 if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { |
|
85 Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + |
|
86 ", settings=" + Arrays.asList(settings) + |
|
87 ", delay=" + delay + |
|
88 ", duration=" + duration + |
|
89 ", disk=" + disk+ |
|
90 ", filename=" + path + |
|
91 ", maxage=" + maxAge + |
|
92 ", maxsize=" + maxSize + |
|
93 ", dumponexit =" + dumpOnExit + |
|
94 ", path-to-gc-roots=" + pathToGcRoots); |
|
95 } |
77 if (name != null) { |
96 if (name != null) { |
78 try { |
97 try { |
79 Integer.parseInt(name); |
98 Integer.parseInt(name); |
80 throw new DCmdException("Name of recording can't be numeric"); |
99 throw new DCmdException("Name of recording can't be numeric"); |
81 } catch (NumberFormatException nfe) { |
100 } catch (NumberFormatException nfe) { |
84 } |
103 } |
85 |
104 |
86 if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) { |
105 if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) { |
87 throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename."); |
106 throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename."); |
88 } |
107 } |
89 if (dumpOnExit == null && path != null) { |
108 |
90 dumpOnExit = Boolean.TRUE; |
|
91 } |
|
92 |
109 |
93 Map<String, String> s = new HashMap<>(); |
110 Map<String, String> s = new HashMap<>(); |
94 |
111 |
95 if (configurations == null || configurations.length == 0) { |
112 if (settings == null || settings.length == 0) { |
96 configurations = new String[] { "default" }; |
113 settings = new String[] { "default" }; |
97 } |
114 } |
98 |
115 |
99 for (String configName : configurations) { |
116 for (String configName : settings) { |
100 try { |
117 try { |
101 s.putAll(JFC.createKnown(configName).getSettings()); |
118 s.putAll(JFC.createKnown(configName).getSettings()); |
102 } catch (IOException | ParseException e) { |
119 } catch (IOException | ParseException e) { |
103 throw new DCmdException("Could not parse setting " + configurations[0], e); |
120 throw new DCmdException("Could not parse setting " + settings[0], e); |
104 } |
121 } |
105 } |
122 } |
106 |
123 |
107 Utils.updateSettingPathToGcRoots(s, pathToGcRoots); |
124 OldObjectSample.updateSettingPathToGcRoots(s, pathToGcRoots); |
108 |
125 |
109 if (duration != null) { |
126 if (duration != null) { |
110 if (duration < 1000L * 1000L * 1000L) { |
127 if (duration < 1000L * 1000L * 1000L) { |
111 // to avoid typo, duration below 1s makes no sense |
128 // to avoid typo, duration below 1s makes no sense |
112 throw new DCmdException("Could not start recording, duration must be at least 1 second."); |
129 throw new DCmdException("Could not start recording, duration must be at least 1 second."); |
131 |
148 |
132 if (disk != null) { |
149 if (disk != null) { |
133 recording.setToDisk(disk.booleanValue()); |
150 recording.setToDisk(disk.booleanValue()); |
134 } |
151 } |
135 recording.setSettings(s); |
152 recording.setSettings(s); |
|
153 SafePath safePath = null; |
136 |
154 |
137 if (path != null) { |
155 if (path != null) { |
138 try { |
156 try { |
139 recording.setDestination(Paths.get(path)); |
157 if (dumpOnExit == null) { |
|
158 // default to dumponexit=true if user specified filename |
|
159 dumpOnExit = Boolean.TRUE; |
|
160 } |
|
161 Path p = Paths.get(path); |
|
162 if (Files.isDirectory(p) && Boolean.TRUE.equals(dumpOnExit)) { |
|
163 // Decide destination filename at dump time |
|
164 // Purposely avoid generating filename in Recording#setDestination due to |
|
165 // security concerns |
|
166 PrivateAccess.getInstance().getPlatformRecording(recording).setDumpOnExitDirectory(new SafePath(p)); |
|
167 } else { |
|
168 safePath = resolvePath(recording, path); |
|
169 recording.setDestination(safePath.toPath()); |
|
170 } |
140 } catch (IOException | InvalidPathException e) { |
171 } catch (IOException | InvalidPathException e) { |
141 recording.close(); |
172 recording.close(); |
142 throw new DCmdException("Could not start recording, not able to write to file %s. %s ", path, e.getMessage()); |
173 throw new DCmdException("Could not start recording, not able to write to file %s. %s ", path, e.getMessage()); |
143 } |
174 } |
144 } |
175 } |
173 if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) { |
204 if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) { |
174 print(" No limit specified, using maxsize=250MB as default."); |
205 print(" No limit specified, using maxsize=250MB as default."); |
175 recording.setMaxSize(250*1024L*1024L); |
206 recording.setMaxSize(250*1024L*1024L); |
176 } |
207 } |
177 |
208 |
178 if (path != null && duration != null) { |
209 if (safePath != null && duration != null) { |
179 println(" The result will be written to:"); |
210 println(" The result will be written to:"); |
180 println(); |
211 println(); |
181 printPath(new SafePath(path)); |
212 printPath(safePath); |
182 } else { |
213 } else { |
183 println(); |
214 println(); |
184 println(); |
215 println(); |
185 String cmd = duration == null ? "dump" : "stop"; |
216 String cmd = duration == null ? "dump" : "stop"; |
186 String fileOption = path == null ? "filename=FILEPATH " : ""; |
217 String fileOption = path == null ? "filename=FILEPATH " : ""; |