Skip to content
Permalink
Browse files
8261441: JFR: Filename expansion
Reviewed-by: jbachorik, egahlin
  • Loading branch information
Denghui Dong authored and Yi Yang committed Aug 6, 2021
1 parent e38e365 commit adb0ae56ab9efec12526433927c15308902535f7
@@ -1747,6 +1747,9 @@ written when the recording is stopped, for example:
\f[CB]/home/user/recordings/recording.jfr\f[R]
.IP \[bu] 2
\f[CB]c:\\recordings\\recording.jfr\f[R]
.PP
If \f[CB]%p\f[R] and/or \f[CB]%t\f[R] is specified in the filename, it expands to the JVM\[aq]s
PID and the current timestamp, respectively.
.RE
.TP
.B \f[CB]name=\f[R]\f[I]identifier\f[R]
@@ -441,6 +441,8 @@ If no filename is given, a filename is generated from the PID and the
current date.
The filename may also be a directory in which case, the filename is
generated from the PID and the current date in the specified directory.
If \f[CB]%p\f[R] and/or \f[CB]%t\f[R] is specified in the filename, it
expands to the JVM\[aq]s PID and the current timestamp, respectively.
(STRING, no default value)
.IP \[bu] 2
\f[CB]maxage\f[R]: (Optional) Length of time for dumping the flight
@@ -509,6 +511,8 @@ current date and is placed in the directory where the process was
started.
The filename may also be a directory in which case, the filename is
generated from the PID and the current date in the specified directory.
If \f[CB]%p\f[R] and/or \f[CB]%t\f[R] is specified in the filename, it
expands to the JVM\[aq]s PID and the current timestamp, respectively.
(STRING, no default value)
.IP \[bu] 2
\f[CB]maxage\f[R]: (Optional) Maximum time to keep the recorded data on
@@ -600,6 +604,8 @@ If no parameters are entered, then no recording is stopped.
.IP \[bu] 2
\f[CB]filename\f[R]: (Optional) Name of the file to which the recording is
written when the recording is stopped.
If \f[CB]%p\f[R] and/or \f[CB]%t\f[R] is specified in the filename, it
expands to the JVM\[aq]s PID and the current timestamp, respectively.
If no path is provided, the data from the recording is discarded.
(STRING, no default value)
.IP \[bu] 2
@@ -30,6 +30,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -191,7 +192,7 @@ protected final void print(String s) {
}

protected final void print(String s, Object... args) {
currentLine.append(String.format(s, args));
currentLine.append(args.length > 0 ? String.format(s, args) : s);
}

protected final void println(String s, Object... args) {
@@ -269,4 +270,41 @@ protected final String exampleDirectory() {
return "/directory/recordings";
}
}

static String expandFilename(String filename) {
if (filename == null || filename.indexOf('%') == -1) {
return filename;
}

String pid = null;
String time = null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < filename.length(); i++) {
char c = filename.charAt(i);
if (c == '%' && i < filename.length() - 1) {
char nc = filename.charAt(i + 1);
if (nc == '%') { // %% ==> %
sb.append('%');
i++;
} else if (nc == 'p') {
if (pid == null) {
pid = JVM.getJVM().getPid();
}
sb.append(pid);
i++;
} else if (nc == 't') {
if (time == null) {
time = Utils.formatDateTime(LocalDateTime.now());
}
sb.append(time);
i++;
} else {
sb.append('%');
}
} else {
sb.append(c);
}
}
return sb.toString();
}
}
@@ -55,7 +55,7 @@
public void execute(ArgumentParser parser) throws DCmdException {
parser.checkUnknownArguments();
String name = parser.getOption("name");
String filename = parser.getOption("filename");
String filename = expandFilename(parser.getOption("filename"));
Long maxAge = parser.getOption("maxage");
Long maxSize = parser.getOption("maxsize");
String begin = parser.getOption("begin");
@@ -232,6 +232,10 @@ private PlatformRecording newSnapShot(PlatformRecorder recorder, Recording recor
case, the filename is generated from the PID and the current date in
the specified directory. (STRING, no default value)
Note: If a filename is given, '%%p' in the filename will be
replaced by the PID, and '%%t' will be replaced by the time in
'yyyy_MM_dd_HH_mm_ss' format.
maxage (Optional) Length of time for dumping the flight recording data to a
file. (INTEGER followed by 's' for seconds 'm' for minutes or 'h' for
hours, no default value)
@@ -76,7 +76,7 @@ public void execute(ArgumentParser parser) throws DCmdException {
Long delay = parser.getOption("delay");
Long duration = parser.getOption("duration");
Boolean disk = parser.getOption("disk");
String path = parser.getOption("filename");
String path = expandFilename(parser.getOption("filename"));
Long maxAge = parser.getOption("maxage");
Long maxSize = parser.getOption("maxsize");
Long flush = parser.getOption("flush-interval");
@@ -339,6 +339,10 @@ Virtual Machine (JVM) shuts down. If set to 'true' and no value
generated from the PID and the current date in the specified
directory. (STRING, no default value)
Note: If a filename is given, '%%p' in the filename will be
replaced by the PID, and '%%t' will be replaced by the time in
'yyyy_MM_dd_HH_mm_ss' format.
maxage (Optional) Maximum time to keep the recorded data on disk. This
parameter is valid only when the disk parameter is set to true.
Note 0s means forever. (INTEGER followed by 's' for seconds 'm'
@@ -44,7 +44,7 @@
protected void execute(ArgumentParser parser) throws DCmdException {
parser.checkUnknownArguments();
String name = parser.getOption("name");
String filename = parser.getOption("filename");
String filename = expandFilename(parser.getOption("filename"));
try {
Recording recording = findRecording(name);
WriteableUserPath path = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination();
@@ -82,6 +82,9 @@ protected void execute(ArgumentParser parser) throws DCmdException {
recording is stopped. If no path is provided, the data from the recording
is discarded. (STRING, no default value)
Note: If a path is given, '%%p' in the path will be replaced by the PID,
and '%%t' will be replaced by the time in 'yyyy_MM_dd_HH_mm_ss' format.
name Name of the recording (STRING, no default value)
Options must be specified using the <key> or <key>=<value> syntax.
@@ -25,6 +25,7 @@

import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Collectors;

import jdk.test.lib.Asserts;
@@ -118,4 +119,16 @@ public static OutputAnalyzer jcmd(String... args) {
public static OutputAnalyzer jcmdCheck(String recordingName, boolean verbose) {
return jcmd("JFR.check", "name=" + recordingName, "verbose=" + verbose);
}

public static String readFilename(OutputAnalyzer output) throws Exception {
Iterator<String> it = output.asLines().iterator();
while (it.hasNext()) {
String line = it.next();
if (line.contains("written to")) {
line = it.next(); // blank line
return it.next();
}
}
throw new Exception("Could not find filename of dumped recording.");
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package jdk.jfr.jcmd;

import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.FileHelper;
import jdk.test.lib.process.OutputAnalyzer;

/**
* @test
* @summary The test verifies JFR.start/dump/stop commands
* @key jfr
* @requires vm.hasJFR
* @library /test/lib /test/jdk
* @run main/othervm jdk.jfr.jcmd.TestFilenameExpansion
*/
public class TestFilenameExpansion {

public static void main(String[] args) throws Exception {
String pid = Long.toString(ProcessHandle.current().pid());
String name = "output_%p_%t_%%.jfr";
String pattern = "output_" + pid + "_" + "\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}" + "_%\\.jfr";

JcmdHelper.jcmd("JFR.start name=test");
String filename = JcmdHelper.readFilename(JcmdHelper.jcmd("JFR.dump name=test filename=" + name));
File file = new File(filename);
Asserts.assertTrue(file.exists(), file.getAbsolutePath() + " does not exist");
Asserts.assertTrue(file.isFile(), file.getAbsolutePath() + " is not a file");
Asserts.assertTrue(Pattern.compile(pattern).matcher(filename).find());
}
}
@@ -26,7 +26,6 @@
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;

import jdk.jfr.Configuration;
import jdk.jfr.Recording;
@@ -60,26 +59,26 @@ public static void main(String[] args) throws Exception {

private static void testDumpFilename() throws Exception {
OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump");
verifyFile(readFilename(output), null);
verifyFile(JcmdHelper.readFilename(output), null);
}

private static void testDumpFilename(Recording r) throws Exception {
OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "name=" + r.getId());
verifyFile(readFilename(output), r.getId());
verifyFile(JcmdHelper.readFilename(output), r.getId());
}

private static void testDumpDiectory() throws Exception {
Path directory = Paths.get(".").toAbsolutePath().normalize();
OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + directory);
String filename = readFilename(output);
String filename = JcmdHelper.readFilename(output);
verifyFile(filename, null);
verifyDirectory(filename, directory);
}

private static void testDumpDiectory(Recording r) throws Exception {
Path directory = Paths.get(".").toAbsolutePath().normalize();
OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "name=" + r.getId(), "filename=" + directory);
String filename = readFilename(output);
String filename = JcmdHelper.readFilename(output);
verifyFile(filename, r.getId());
verifyDirectory(filename, directory);
}
@@ -98,16 +97,4 @@ private static void verifyFile(String filename, Long id) throws Exception {
}
FileHelper.verifyRecording(new File(filename));
}

private static String readFilename(OutputAnalyzer output) throws Exception {
Iterator<String> it = output.asLines().iterator();
while (it.hasNext()) {
String line = it.next();
if (line.contains("written to")) {
line = it.next(); // blank line
return it.next();
}
}
throw new Exception("Could not find filename of dumped recording.");
}
}

1 comment on commit adb0ae5

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on adb0ae5 Aug 6, 2021

Please sign in to comment.