Skip to content

Commit 5cef172

Browse files
committed
JFR dump to Jcmd current working directory by default
If the filename supplied is not an absolute path, Jcmd current work directory is used instead of the target VM location; Throw an error if duration option doesn't have time units. Signed-off-by: Jason Feng <[email protected]>
1 parent 6410fb4 commit 5cef172

File tree

2 files changed

+72
-21
lines changed

2 files changed

+72
-21
lines changed

jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323

2424
package openj9.internal.tools.attach.target;
2525

26+
import java.io.File;
2627
import java.io.PrintWriter;
2728
import java.io.StringWriter;
29+
import java.nio.file.InvalidPathException;
30+
import java.nio.file.Path;
31+
import java.nio.file.Paths;
2832
import java.util.Arrays;
2933
import java.util.HashMap;
3034
import java.util.Map;
@@ -144,6 +148,9 @@ public class DiagnosticUtils {
144148
private static final String DIAGNOSTICS_JFR_START = "JFR.start";
145149
private static final String DIAGNOSTICS_JFR_DUMP = "JFR.dump";
146150
private static final String DIAGNOSTICS_JFR_STOP = "JFR.stop";
151+
152+
private static final int ERROR_NO_TIME_UNIT = -1;
153+
private static final int ERROR_NO_TIME_DURATION = -2;
147154
/*[ENDIF] JFR_SUPPORT */
148155

149156
/**
@@ -209,6 +216,35 @@ public static String makeThreadPrintCommand(boolean lockedSynchronizers) {
209216
* @return formatted string
210217
*/
211218
public static String makeJcmdCommand(String[] options, int skip) {
219+
/*[IF JFR_SUPPORT]*/
220+
if (options.length >= 2) {
221+
// there is a jcmd command
222+
if (DIAGNOSTICS_JFR_START.equalsIgnoreCase(options[1])) {
223+
// search JFR.start options
224+
for (int i = 2; i < options.length; i++) {
225+
String option = options[i];
226+
IPC.logMessage("makeJcmdCommand: option = ", option);
227+
if (option.startsWith("filename=")) {
228+
String fileName = option.substring(option.indexOf("=") + 1);
229+
try {
230+
Path filePath = Paths.get(fileName);
231+
if (!filePath.isAbsolute()) {
232+
// default recording file path is jcmd current working directory
233+
String jcmdPWD = Paths.get("").toAbsolutePath().toString();
234+
fileName = jcmdPWD + File.separator + fileName;
235+
IPC.logMessage("makeJcmdCommand: absolute filename = ", fileName);
236+
// replace existing entry with an absolute jcmd pwd path for the target VM
237+
options[i] = "filename=" + fileName;
238+
}
239+
} catch (InvalidPathException ipe) {
240+
// ignore this exception and keep the original entry
241+
IPC.logMessage("makeJcmdCommand: ipe = ", ipe.getMessage());
242+
}
243+
}
244+
}
245+
}
246+
}
247+
/*[ENDIF] JFR_SUPPORT */
212248
String cmd = String.join(DIAGNOSTICS_OPTION_SEPARATOR, Arrays.asList(options).subList(skip, options.length));
213249
return cmd;
214250
}
@@ -415,13 +451,25 @@ private static long convertToMilliseconds(String timeValue) {
415451
timeInMilli = TimeUnit.DAYS.toMillis(time);
416452
break;
417453
default:
418-
// no unit or unrecognized unit, assume milliseconds
419-
timeInMilli = time;
454+
// no unit or unrecognized unit, return ERROR_NO_TIME_UNIT
455+
timeInMilli = ERROR_NO_TIME_UNIT;
420456
break;
421457
}
422458
return timeInMilli;
423459
}
424460

461+
/**
462+
* Parse a time parameter, and return the duration in milliseconds.
463+
* If the time unit is missing, ERROR_NO_TIME_UNIT is returned.
464+
* If the paramName is not in parameters, ERROR_NO_TIME_DURATION is returned.
465+
*
466+
* @param paramName the parameter name
467+
* @param parameters the parameter array
468+
*
469+
* @return the duration in milliseconds,
470+
* ERROR_NO_TIME_UNIT if no time unit,
471+
* ERROR_NO_TIME_DURATION if paramName wasn't found.
472+
*/
425473
private static long parseTimeParameter(String paramName, String[] parameters) {
426474
for (String param : parameters) {
427475
if (param.startsWith(paramName + "=")) {
@@ -431,7 +479,7 @@ private static long parseTimeParameter(String paramName, String[] parameters) {
431479
}
432480
}
433481
}
434-
return -1;
482+
return ERROR_NO_TIME_DURATION;
435483
}
436484

437485
private static String parseStringParameter(String paramName, String[] parameters, String defaultValue) {
@@ -448,35 +496,33 @@ private static String parseStringParameter(String paramName, String[] parameters
448496

449497
private static DiagnosticProperties doJFR(String diagnosticCommand) {
450498
DiagnosticProperties result = null;
451-
452499
// split the command and arguments
453500
String[] parts = diagnosticCommand.split(DIAGNOSTICS_OPTION_SEPARATOR);
454501
IPC.logMessage("doJFR: ", diagnosticCommand);
455-
456502
// ensure there's at least one part for the command
457503
if (parts.length == 0) {
458504
return DiagnosticProperties.makeErrorProperties("Error: No JFR command specified");
459505
}
460-
461506
String command = parts[0].trim();
462507
String[] parameters = Arrays.copyOfRange(parts, 1, parts.length);
463-
464508
String fileName = parseStringParameter("filename", parameters, null);
465509
IPC.logMessage("doJFR: filename = ", fileName);
466-
boolean setFileName = (fileName != null) && !fileName.isEmpty();
467-
if (setFileName) {
468-
if (!VM.setJFRRecordingFileName(fileName)) {
469-
return DiagnosticProperties.makeErrorProperties("setJFRRecordingFileName failed");
470-
} else {
471-
jfrRecordingFileName = fileName;
472-
}
473-
}
474510

475511
try {
476512
if (command.equalsIgnoreCase(DIAGNOSTICS_JFR_START)) {
477513
if (VM.isJFRRecordingStarted()) {
478514
result = DiagnosticProperties.makeErrorProperties("One JFR recording is in progress [" + jfrRecordingFileName + "], only one recording is allowed at a time.");
479515
} else {
516+
// only JFR.start command is allowed to change the recording filename
517+
boolean setFileName = (fileName != null) && !fileName.isEmpty();
518+
if (setFileName) {
519+
// the recording filename should be set before VM.startJFR() which invokes JFRWriter:openJFRFile()
520+
if (!VM.setJFRRecordingFileName(fileName)) {
521+
return DiagnosticProperties.makeErrorProperties("setJFRRecordingFileName() failed");
522+
} else {
523+
jfrRecordingFileName = fileName;
524+
}
525+
}
480526
VM.startJFR();
481527
long duration = parseTimeParameter("duration", parameters);
482528
IPC.logMessage("doJFR: duration = " + duration);
@@ -488,6 +534,8 @@ public void run() {
488534
}
489535
};
490536
timer.schedule(jfrDumpTask, duration);
537+
} if (duration == ERROR_NO_TIME_UNIT) {
538+
return DiagnosticProperties.makeErrorProperties("The duration doesn't have a time unit.");
491539
} else {
492540
// the recording is on until JFR.stop
493541
}

runtime/vm/jfr.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,12 +1127,15 @@ jfrSamplingThreadProc(void *entryArg)
11271127
jboolean
11281128
setJFRRecordingFileName(J9JavaVM *vm, char *fileName)
11291129
{
1130-
UDATA defaultFileNameLen = strlen(DEFAULT_JFR_FILE_NAME);
1131-
if ((defaultFileNameLen != strlen(vm->jfrState.jfrFileName))
1132-
|| (0 != strncmp(DEFAULT_JFR_FILE_NAME, vm->jfrState.jfrFileName, defaultFileNameLen))
1133-
) {
1134-
PORT_ACCESS_FROM_JAVAVM(vm);
1135-
j9mem_free_memory(vm->jfrState.jfrFileName);
1130+
if (NULL != vm->jfrState.jfrFileName) {
1131+
UDATA defaultFileNameLen = strlen(DEFAULT_JFR_FILE_NAME);
1132+
const char *jfrFileName = vm->jfrState.jfrFileName;
1133+
if ((defaultFileNameLen != strlen(jfrFileName))
1134+
|| (0 != strncmp(DEFAULT_JFR_FILE_NAME, jfrFileName, defaultFileNameLen))
1135+
) {
1136+
PORT_ACCESS_FROM_JAVAVM(vm);
1137+
j9mem_free_memory(vm->jfrState.jfrFileName);
1138+
}
11361139
}
11371140
vm->jfrState.jfrFileName = fileName;
11381141
VM_JFRWriter::closeJFRFile(vm);

0 commit comments

Comments
 (0)