8232594: Make the output of the JFR command duration more user friendly
Co-authored-by: Erik Gahlin <erik.gahlin@oracle.com> Reviewed-by: egahlin, cito
This commit is contained in:
parent
e00ded00f0
commit
558aadf608
@ -45,6 +45,7 @@ import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -78,6 +79,17 @@ public final class Utils {
|
||||
private static Boolean SAVE_GENERATED;
|
||||
|
||||
|
||||
private static final Duration MICRO_SECOND = Duration.ofNanos(1_000);
|
||||
private static final Duration SECOND = Duration.ofSeconds(1);
|
||||
private static final Duration MINUTE = Duration.ofMinutes(1);
|
||||
private static final Duration HOUR = Duration.ofHours(1);
|
||||
private static final Duration DAY = Duration.ofDays(1);
|
||||
private static final int NANO_SIGNIFICANT_FIGURES = 9;
|
||||
private static final int MILL_SIGNIFICANT_FIGURES = 3;
|
||||
private static final int DISPLAY_NANO_DIGIT = 3;
|
||||
private static final int BASE = 10;
|
||||
|
||||
|
||||
public static void checkAccessFlightRecorder() throws SecurityException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
@ -597,6 +609,90 @@ public final class Utils {
|
||||
return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr";
|
||||
}
|
||||
|
||||
public static String formatDuration(Duration d) {
|
||||
Duration roundedDuration = roundDuration(d);
|
||||
if (roundedDuration.equals(Duration.ZERO)) {
|
||||
return "0 s";
|
||||
} else if(roundedDuration.isNegative()){
|
||||
return "-" + formatPositiveDuration(roundedDuration.abs());
|
||||
} else {
|
||||
return formatPositiveDuration(roundedDuration);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatPositiveDuration(Duration d){
|
||||
if (d.compareTo(MICRO_SECOND) < 0) {
|
||||
// 0.000001 ms - 0.000999 ms
|
||||
double outputMs = (double) d.toNanosPart() / 1_000_000;
|
||||
return String.format("%.6f ms", outputMs);
|
||||
} else if (d.compareTo(SECOND) < 0) {
|
||||
// 0.001 ms - 999 ms
|
||||
int valueLength = countLength(d.toNanosPart());
|
||||
int outputDigit = NANO_SIGNIFICANT_FIGURES - valueLength;
|
||||
double outputMs = (double) d.toNanosPart() / 1_000_000;
|
||||
return String.format("%." + outputDigit + "f ms", outputMs);
|
||||
} else if (d.compareTo(MINUTE) < 0) {
|
||||
// 1.00 s - 59.9 s
|
||||
int valueLength = countLength(d.toSecondsPart());
|
||||
int outputDigit = MILL_SIGNIFICANT_FIGURES - valueLength;
|
||||
double outputSecond = d.toSecondsPart() + (double) d.toMillisPart() / 1_000;
|
||||
return String.format("%." + outputDigit + "f s", outputSecond);
|
||||
} else if (d.compareTo(HOUR) < 0) {
|
||||
// 1 m 0 s - 59 m 59 s
|
||||
return String.format("%d m %d s", d.toMinutesPart(), d.toSecondsPart());
|
||||
} else if (d.compareTo(DAY) < 0) {
|
||||
// 1 h 0 m - 23 h 59 m
|
||||
return String.format("%d h %d m", d.toHoursPart(), d.toMinutesPart());
|
||||
} else {
|
||||
// 1 d 0 h -
|
||||
return String.format("%d d %d h", d.toDaysPart(), d.toHoursPart());
|
||||
}
|
||||
}
|
||||
|
||||
private static int countLength(long value){
|
||||
return (int) Math.log10(value) + 1;
|
||||
}
|
||||
|
||||
private static Duration roundDuration(Duration d) {
|
||||
if (d.equals(Duration.ZERO)) {
|
||||
return d;
|
||||
} else if(d.isNegative()){
|
||||
Duration roundedPositiveDuration = roundPositiveDuration(d.abs());
|
||||
return roundedPositiveDuration.negated();
|
||||
} else {
|
||||
return roundPositiveDuration(d);
|
||||
}
|
||||
}
|
||||
|
||||
private static Duration roundPositiveDuration(Duration d){
|
||||
if (d.compareTo(MICRO_SECOND) < 0) {
|
||||
// No round
|
||||
return d;
|
||||
} else if (d.compareTo(SECOND) < 0) {
|
||||
// Round significant figures to three digits
|
||||
int valueLength = countLength(d.toNanosPart());
|
||||
int roundValue = (int) Math.pow(BASE, valueLength - DISPLAY_NANO_DIGIT);
|
||||
long roundedNanos = Math.round((double) d.toNanosPart() / roundValue) * roundValue;
|
||||
return d.truncatedTo(ChronoUnit.SECONDS).plusNanos(roundedNanos);
|
||||
} else if (d.compareTo(MINUTE) < 0) {
|
||||
// Round significant figures to three digits
|
||||
int valueLength = countLength(d.toSecondsPart());
|
||||
int roundValue = (int) Math.pow(BASE, valueLength);
|
||||
long roundedMills = Math.round((double) d.toMillisPart() / roundValue) * roundValue;
|
||||
return d.truncatedTo(ChronoUnit.SECONDS).plusMillis(roundedMills);
|
||||
} else if (d.compareTo(HOUR) < 0) {
|
||||
// Round for more than 500 ms or less
|
||||
return d.plusMillis(SECOND.dividedBy(2).toMillisPart()).truncatedTo(ChronoUnit.SECONDS);
|
||||
} else if (d.compareTo(DAY) < 0) {
|
||||
// Round for more than 30 seconds or less
|
||||
return d.plusSeconds(MINUTE.dividedBy(2).toSecondsPart()).truncatedTo(ChronoUnit.MINUTES);
|
||||
} else {
|
||||
// Round for more than 30 minutes or less
|
||||
return d.plusMinutes(HOUR.dividedBy(2).toMinutesPart()).truncatedTo(ChronoUnit.HOURS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void takeNap(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. 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
|
||||
@ -57,9 +57,6 @@ import jdk.jfr.internal.Utils;
|
||||
* This class is also used by {@link RecordedObject#toString()}
|
||||
*/
|
||||
public final class PrettyWriter extends EventPrintWriter {
|
||||
private static final Duration MILLSECOND = Duration.ofMillis(1);
|
||||
private static final Duration SECOND = Duration.ofSeconds(1);
|
||||
private static final Duration MINUTE = Duration.ofMinutes(1);
|
||||
private static final String TYPE_OLD_OBJECT = Type.TYPES_PREFIX + "OldObject";
|
||||
private final static DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
|
||||
private final static Long ZERO = 0L;
|
||||
@ -553,15 +550,7 @@ public final class PrettyWriter extends EventPrintWriter {
|
||||
println("N/A");
|
||||
return true;
|
||||
}
|
||||
if(d.compareTo(MILLSECOND) < 0){
|
||||
println(String.format("%.3f us", (double)d.toNanos() / 1_000));
|
||||
} else if(d.compareTo(SECOND) < 0){
|
||||
println(String.format("%.3f ms", (double)d.toNanos() / 1_000_000));
|
||||
} else if(d.compareTo(MINUTE) < 0){
|
||||
println(String.format("%.3f s", (double)d.toMillis() / 1_000));
|
||||
} else {
|
||||
println(String.format("%d s", d.toSeconds()));
|
||||
}
|
||||
println(Utils.formatDuration(d));
|
||||
return true;
|
||||
}
|
||||
if (value instanceof OffsetDateTime) {
|
||||
|
173
test/jdk/jdk/jfr/jvm/TestFormatDuration.java
Normal file
173
test/jdk/jdk/jfr/jvm/TestFormatDuration.java
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2019, Oracle and/or its affiliates. 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.jvm;
|
||||
|
||||
import jdk.jfr.internal.Utils;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib
|
||||
* @modules jdk.jfr/jdk.jfr.internal
|
||||
* @run main/othervm jdk.jfr.jvm.TestFormatDuration
|
||||
*/
|
||||
public class TestFormatDuration {
|
||||
public static void main(String[] args) throws Exception{
|
||||
Locale.setDefault(Locale.US);
|
||||
|
||||
// Nanoseconds
|
||||
assertDuration("0 ns ", "0 s");
|
||||
assertDuration("1 ns ", "0.000001 ms");
|
||||
assertDuration("10 ns ", "0.000010 ms");
|
||||
assertDuration("100 ns ", "0.000100 ms");
|
||||
assertDuration("999 ns ", "0.000999 ms");
|
||||
assertDuration("1000 ns", "0.00100 ms");
|
||||
assertDuration("1004 ns", "0.00100 ms");
|
||||
assertDuration("1005 ns", "0.00101 ms");
|
||||
|
||||
// 10 us
|
||||
assertDuration("9 us 994 ns", "0.00999 ms");
|
||||
assertDuration("9 us 995 ns", "0.0100 ms");
|
||||
assertDuration("10 us ", "0.0100 ms");
|
||||
assertDuration("10 us 49 ns", "0.0100 ms");
|
||||
assertDuration("10 us 50 ns", "0.0101 ms");
|
||||
|
||||
// 100 us
|
||||
assertDuration("99 us 949 ns ", "0.0999 ms");
|
||||
assertDuration("99 us 950 ns ", "0.100 ms");
|
||||
assertDuration("100 us ", "0.100 ms");
|
||||
assertDuration("100 us 499 ns", "0.100 ms");
|
||||
assertDuration("100 us 500 ns", "0.101 ms");
|
||||
|
||||
// 1 ms
|
||||
assertDuration("999 us 499 ns ", "0.999 ms");
|
||||
assertDuration("999 us 500 ns ", "1.00 ms");
|
||||
assertDuration("1 ms ", "1.00 ms");
|
||||
assertDuration("1 ms 4 us 999 ns ", "1.00 ms");
|
||||
assertDuration("1 ms 5 us", "1.01 ms");
|
||||
|
||||
// 10 ms
|
||||
assertDuration("9 ms 994 us 999 ns", "9.99 ms");
|
||||
assertDuration("9 ms 995 us ", "10.0 ms");
|
||||
assertDuration("10 ms ", "10.0 ms");
|
||||
assertDuration("10 ms 49 us 999 ns", "10.0 ms");
|
||||
assertDuration("10 ms 50 us 999 ns", "10.1 ms");
|
||||
|
||||
// 100 ms
|
||||
assertDuration("99 ms 949 us 999 ns ", "99.9 ms");
|
||||
assertDuration("99 ms 950 us 000 ns ", "100 ms");
|
||||
assertDuration("100 ms ", "100 ms");
|
||||
assertDuration("100 ms 499 us 999 ns", "100 ms");
|
||||
assertDuration("100 ms 500 us ", "101 ms");
|
||||
|
||||
// 1 second
|
||||
assertDuration("999 ms 499 us 999 ns ", "999 ms");
|
||||
assertDuration("999 ms 500 us ", "1.00 s");
|
||||
assertDuration("1 s ", "1.00 s");
|
||||
assertDuration("1 s 4 ms 999 us 999 ns", "1.00 s");
|
||||
assertDuration("1 s 5 ms ", "1.01 s");
|
||||
|
||||
// 10 seconds
|
||||
assertDuration("9 s 994 ms 999 us 999 ns ", "9.99 s");
|
||||
assertDuration("9 s 995 ms ", "10.0 s");
|
||||
assertDuration("10 s ", "10.0 s");
|
||||
assertDuration("10 s 049 ms 999 us 999 ns", "10.0 s");
|
||||
assertDuration("10 s 050 ms ", "10.1 s");
|
||||
|
||||
// 1 minute
|
||||
assertDuration("59 s 949 ms 999 us 999 ns", "59.9 s");
|
||||
assertDuration("59 s 950 ms ", "1 m 0 s");
|
||||
assertDuration("1 m 0 s ", "1 m 0 s");
|
||||
assertDuration("60 s 499 ms 999 us 999 ns", "1 m 0 s");
|
||||
assertDuration("60 s 500 ms ", "1 m 1 s");
|
||||
|
||||
// 10 minutes
|
||||
assertDuration("10 m 0 s", "10 m 0 s");
|
||||
|
||||
// 1 hour
|
||||
assertDuration("59 m 59 s 499 ms 999 us 999 ns", "59 m 59 s");
|
||||
assertDuration("59 m 59 s 500 ms ", "1 h 0 m");
|
||||
assertDuration("1 h 0 m ", "1 h 0 m");
|
||||
assertDuration("1 h 29 s 999 ms 999 us 999 ns ", "1 h 0 m");
|
||||
assertDuration("1 h 30 s ", "1 h 1 m");
|
||||
|
||||
// 1 day
|
||||
assertDuration("23 h 59 m 29 s 999 ms 999 us 999 ns", "23 h 59 m");
|
||||
assertDuration("23 h 59 m 30 s ", "1 d 0 h");
|
||||
assertDuration("1 d 0 h ", "1 d 0 h");
|
||||
assertDuration("1 d 29 m 59 s 999 ms 999 us 999 ns ", "1 d 0 h");
|
||||
assertDuration("1 d 30 m ", "1 d 1 h");
|
||||
|
||||
// 100 days
|
||||
assertDuration("100 d 13 h", "100 d 13 h");
|
||||
|
||||
// 1000 days
|
||||
assertDuration("1000 d 13 h", "1000 d 13 h");
|
||||
}
|
||||
|
||||
private static void assertDuration(String value, String expected) throws Exception {
|
||||
long nanos = parse(value);
|
||||
System.out.println(value + " == " + expected + " ? (" + nanos + " ns) ");
|
||||
Asserts.assertEquals(Utils.formatDuration(Duration.ofNanos(nanos)), expected);
|
||||
if (nanos != 0) {
|
||||
Asserts.assertEquals(Utils.formatDuration(Duration.ofNanos(-nanos)), "-" + expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static long parse(String duration) throws Exception {
|
||||
String[] t = duration.trim().split(" ");
|
||||
long nanos = 0;
|
||||
for (int i = 0; i < t.length - 1; i += 2) {
|
||||
nanos += Long.parseLong(t[i]) * parseUnit(t[i + 1]);
|
||||
}
|
||||
return nanos;
|
||||
}
|
||||
|
||||
private static long parseUnit(String unit) throws Exception {
|
||||
switch (unit) {
|
||||
case "ns":
|
||||
return 1L;
|
||||
case "us":
|
||||
return 1_000L;
|
||||
case "ms":
|
||||
return 1_000_000L;
|
||||
case "s":
|
||||
return 1_000_000_000L;
|
||||
case "m":
|
||||
return 60 * 1_000_000_000L;
|
||||
case "h":
|
||||
return 3600 * 1_000_000_000L;
|
||||
case "d":
|
||||
return 24 * 3600 * 1_000_000_000L;
|
||||
}
|
||||
throw new Exception("Test error. Unknown unit " + unit);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user