mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-27 07:10:45 +00:00
8353614: JFR: jfr print --exact
Reviewed-by: mgronlun
This commit is contained in:
parent
e604bb9e94
commit
1d7138fe02
@ -59,13 +59,20 @@ import jdk.jfr.internal.util.ValueFormatter;
|
||||
*/
|
||||
public final class PrettyWriter extends EventPrintWriter {
|
||||
private static final String TYPE_OLD_OBJECT = Type.TYPES_PREFIX + "OldObject";
|
||||
private static final DateTimeFormatter TIME_FORMAT_EXACT = DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSSSSS (yyyy-MM-dd)");
|
||||
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS (yyyy-MM-dd)");
|
||||
private static final Long ZERO = 0L;
|
||||
private final boolean showExact;
|
||||
private boolean showIds;
|
||||
private RecordedEvent currentEvent;
|
||||
|
||||
public PrettyWriter(PrintWriter destination) {
|
||||
public PrettyWriter(PrintWriter destination, boolean showExact) {
|
||||
super(destination);
|
||||
this.showExact = showExact;
|
||||
}
|
||||
|
||||
public PrettyWriter(PrintWriter destination) {
|
||||
this(destination, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -508,7 +515,11 @@ public final class PrettyWriter extends EventPrintWriter {
|
||||
println("Forever");
|
||||
return true;
|
||||
}
|
||||
println(ValueFormatter.formatDuration(d));
|
||||
if (showExact) {
|
||||
println(String.format("%.9f s", (double) d.toNanos() / 1_000_000_000));
|
||||
} else {
|
||||
println(ValueFormatter.formatDuration(d));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (value instanceof OffsetDateTime odt) {
|
||||
@ -516,40 +527,34 @@ public final class PrettyWriter extends EventPrintWriter {
|
||||
println("N/A");
|
||||
return true;
|
||||
}
|
||||
println(TIME_FORMAT.format(odt));
|
||||
if (showExact) {
|
||||
println(TIME_FORMAT_EXACT.format(odt));
|
||||
} else {
|
||||
println(TIME_FORMAT.format(odt));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Percentage percentage = field.getAnnotation(Percentage.class);
|
||||
if (percentage != null) {
|
||||
if (value instanceof Number n) {
|
||||
double d = n.doubleValue();
|
||||
println(String.format("%.2f", d * 100) + "%");
|
||||
double p = 100 * n.doubleValue();
|
||||
if (showExact) {
|
||||
println(String.format("%.9f%%", p));
|
||||
} else {
|
||||
println(String.format("%.2f%%", p));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
DataAmount dataAmount = field.getAnnotation(DataAmount.class);
|
||||
if (dataAmount != null) {
|
||||
if (value instanceof Number n) {
|
||||
long amount = n.longValue();
|
||||
if (field.getAnnotation(Frequency.class) != null) {
|
||||
if (dataAmount.value().equals(DataAmount.BYTES)) {
|
||||
println(ValueFormatter.formatBytesPerSecond(amount));
|
||||
return true;
|
||||
}
|
||||
if (dataAmount.value().equals(DataAmount.BITS)) {
|
||||
println(ValueFormatter.formatBitsPerSecond(amount));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (dataAmount.value().equals(DataAmount.BYTES)) {
|
||||
println(ValueFormatter.formatBytes(amount));
|
||||
return true;
|
||||
}
|
||||
if (dataAmount.value().equals(DataAmount.BITS)) {
|
||||
println(ValueFormatter.formatBits(amount));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (dataAmount != null && value instanceof Number number) {
|
||||
boolean frequency = field.getAnnotation(Frequency.class) != null;
|
||||
String unit = dataAmount.value();
|
||||
boolean bits = unit.equals(DataAmount.BITS);
|
||||
boolean bytes = unit.equals(DataAmount.BYTES);
|
||||
if (bits || bytes) {
|
||||
formatMemory(number.longValue(), bytes, frequency);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
MemoryAddress memoryAddress = field.getAnnotation(MemoryAddress.class);
|
||||
@ -571,6 +576,35 @@ public final class PrettyWriter extends EventPrintWriter {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void formatMemory(long value, boolean bytesUnit, boolean frequency) {
|
||||
if (showExact) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(value);
|
||||
sb.append(bytesUnit ? " byte" : " bit");
|
||||
if (value > 1) {
|
||||
sb.append("s");
|
||||
}
|
||||
if (frequency) {
|
||||
sb.append("/s");
|
||||
}
|
||||
println(sb.toString());
|
||||
return;
|
||||
}
|
||||
if (frequency) {
|
||||
if (bytesUnit) {
|
||||
println(ValueFormatter.formatBytesPerSecond(value));
|
||||
} else {
|
||||
println(ValueFormatter.formatBitsPerSecond(value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (bytesUnit) {
|
||||
println(ValueFormatter.formatBytes(value));
|
||||
} else {
|
||||
println(ValueFormatter.formatBits(value));
|
||||
}
|
||||
}
|
||||
|
||||
public void setShowIds(boolean showIds) {
|
||||
this.showIds = showIds;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ final class Print extends Command {
|
||||
@Override
|
||||
public List<String> getOptionSyntax() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("[--xml|--json]");
|
||||
list.add("[--xml|--json|--exact]");
|
||||
list.add("[--categories <filter>]");
|
||||
list.add("[--events <filter>]");
|
||||
list.add("[--stack-depth <depth>]");
|
||||
@ -73,6 +73,8 @@ final class Print extends Command {
|
||||
stream.println();
|
||||
stream.println(" --json Print recording in JSON format");
|
||||
stream.println();
|
||||
stream.println(" --exact Pretty-print numbers and timestamps with full precision.");
|
||||
stream.println();
|
||||
stream.println(" --categories <filter> Select events matching a category name.");
|
||||
stream.println(" The filter is a comma-separated list of names,");
|
||||
stream.println(" simple and/or qualified, and/or quoted glob patterns");
|
||||
@ -95,7 +97,7 @@ final class Print extends Command {
|
||||
char q = quoteCharacter();
|
||||
stream.println(" jfr print --categories " + q + "GC,JVM,Java*" + q + " recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --events "+ q + "jdk.*" + q +" --stack-depth 64 recording.jfr");
|
||||
stream.println(" jfr print --exact --events "+ q + "jdk.*" + q +" --stack-depth 64 recording.jfr");
|
||||
stream.println();
|
||||
stream.println(" jfr print --json --events CPULoad recording.jfr");
|
||||
}
|
||||
@ -140,6 +142,9 @@ final class Print extends Command {
|
||||
throw new UserSyntaxException("not a valid value for --stack-depth");
|
||||
}
|
||||
}
|
||||
if (acceptFormatterOption(options, eventWriter, "--exact")) {
|
||||
eventWriter = new PrettyWriter(pw, true);;
|
||||
}
|
||||
if (acceptFormatterOption(options, eventWriter, "--json")) {
|
||||
eventWriter = new JSONWriter(pw);
|
||||
}
|
||||
@ -155,7 +160,7 @@ final class Print extends Command {
|
||||
optionCount = options.size();
|
||||
}
|
||||
if (eventWriter == null) {
|
||||
eventWriter = new PrettyWriter(pw); // default to pretty printer
|
||||
eventWriter = new PrettyWriter(pw, false); // default to pretty printer
|
||||
}
|
||||
eventWriter.setStackDepth(stackDepth);
|
||||
if (!eventFilters.isEmpty()) {
|
||||
|
||||
@ -106,7 +106,7 @@ Use `jfr print` to print the contents of a flight recording file to standard out
|
||||
|
||||
The syntax is:
|
||||
|
||||
`jfr print` \[`--xml`|`--json`\]
|
||||
`jfr print` \[`--xml`|`--json`|`--exact`\]
|
||||
\[`--categories` <*filters*>\]
|
||||
\[`--events` <*filters*>\]
|
||||
\[`--stack-depth` <*depth*>\]
|
||||
@ -120,6 +120,9 @@ where:
|
||||
<a id="print-option-json">`--json`</a>
|
||||
: Print the recording in JSON format.
|
||||
|
||||
<a id="print-option-exact">`--exact`</a>
|
||||
: Pretty-print numbers and timestamps with full precision.
|
||||
|
||||
<a id="print-option-categories">`--categories` <*filters*></a>
|
||||
: Select events matching a category name.
|
||||
The filter is a comma-separated list of names,
|
||||
|
||||
@ -24,9 +24,19 @@
|
||||
package jdk.jfr.tool;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Percentage;
|
||||
import jdk.jfr.Timestamp;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.DataAmount;
|
||||
import jdk.jfr.Frequency;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
@ -40,20 +50,105 @@ import jdk.test.lib.process.OutputAnalyzer;
|
||||
*/
|
||||
public class TestPrint {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
static class ExactEvent extends Event {
|
||||
@DataAmount(DataAmount.BITS)
|
||||
long oneBit;
|
||||
|
||||
@DataAmount(DataAmount.BITS)
|
||||
long bits;
|
||||
|
||||
@Frequency
|
||||
@DataAmount(DataAmount.BITS)
|
||||
long oneBitPerSecond;
|
||||
|
||||
@Frequency
|
||||
@DataAmount(DataAmount.BITS)
|
||||
long bitsPerSecond;
|
||||
|
||||
@DataAmount(DataAmount.BYTES)
|
||||
long oneByte;
|
||||
|
||||
@DataAmount(DataAmount.BYTES)
|
||||
long bytes;
|
||||
|
||||
@Frequency
|
||||
@DataAmount(DataAmount.BYTES)
|
||||
long oneBytePerSecond;
|
||||
|
||||
@Frequency
|
||||
@DataAmount(DataAmount.BYTES)
|
||||
long bytesPerSecond;
|
||||
|
||||
@Percentage
|
||||
double percentage;
|
||||
|
||||
@Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
|
||||
long timestamp;
|
||||
|
||||
@Timespan(Timespan.NANOSECONDS)
|
||||
long timespan;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
testNoFile();
|
||||
testMissingFile();
|
||||
testIncorrectOption();
|
||||
testExact();
|
||||
}
|
||||
|
||||
private static void testNoFile() throws Throwable {
|
||||
OutputAnalyzer output = ExecuteHelper.jfr("print");
|
||||
output.shouldContain("missing file");
|
||||
}
|
||||
|
||||
output = ExecuteHelper.jfr("print", "missing.jfr");
|
||||
private static void testMissingFile() throws Throwable {
|
||||
OutputAnalyzer output = ExecuteHelper.jfr("print", "missing.jfr");
|
||||
output.shouldContain("could not open file ");
|
||||
}
|
||||
|
||||
Path file = Utils.createTempFile("faked-print-file", ".jfr");
|
||||
private static void testIncorrectOption() throws Throwable {
|
||||
Path file = Utils.createTempFile("faked-print-file", ".jfr");
|
||||
FileWriter fw = new FileWriter(file.toFile());
|
||||
fw.write('d');
|
||||
fw.close();
|
||||
output = ExecuteHelper.jfr("print", "--wrongOption", file.toAbsolutePath().toString());
|
||||
OutputAnalyzer output = ExecuteHelper.jfr("print", "--wrongOption", file.toAbsolutePath().toString());
|
||||
output.shouldContain("unknown option");
|
||||
Files.delete(file);
|
||||
}
|
||||
|
||||
private static void testExact() throws Throwable{
|
||||
try (Recording r = new Recording()) {
|
||||
r.start();
|
||||
ExactEvent e = new ExactEvent();
|
||||
e.begin();
|
||||
e.oneBit = 1L;
|
||||
e.bits = 222_222_222L;
|
||||
e.oneBitPerSecond = 1L;
|
||||
e.bitsPerSecond = 333_333_333L;
|
||||
e.oneByte = 1L;
|
||||
e.bytes = 444_444_444L;
|
||||
e.oneBytePerSecond = 1L;
|
||||
e.bytesPerSecond = 555_555_555L;
|
||||
e.percentage = 0.666_666_666_66;
|
||||
e.timestamp = 777;
|
||||
e.timespan = 888_888_888L;
|
||||
e.commit();
|
||||
r.stop();
|
||||
Path file = Path.of("exact.jfr");
|
||||
r.dump(file);
|
||||
OutputAnalyzer output = ExecuteHelper.jfr("print", "--exact", file.toAbsolutePath().toString());
|
||||
output.shouldContain("oneBit = 1 bit");
|
||||
output.shouldContain("bits = 222222222 bits");
|
||||
output.shouldContain("oneBitPerSecond = 1 bit/s");
|
||||
output.shouldContain("bitsPerSecond = 333333333 bits/s");
|
||||
output.shouldContain("oneByte = 1 byte");
|
||||
output.shouldContain("bytes = 444444444 bytes");
|
||||
output.shouldContain("oneBytePerSecond = 1 byte/s");
|
||||
output.shouldContain("bytesPerSecond = 555555555 bytes/s");
|
||||
output.shouldContain(String.valueOf(100 * e.percentage) + "%");
|
||||
output.shouldContain("00.777000000 (19");
|
||||
output.shouldContain(String.valueOf(e.timespan) + " s");
|
||||
Files.delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user