From d10d40a5b2dc4bb491daaac2838cd637302e2313 Mon Sep 17 00:00:00 2001 From: Erik Gahlin Date: Wed, 1 Mar 2023 22:54:03 +0000 Subject: [PATCH] 8303077: JFR: Add example usage to jdk.jfr Reviewed-by: mgronlun --- .../share/classes/jdk/jfr/BooleanFlag.java | 12 +- .../share/classes/jdk/jfr/Configuration.java | 7 +- .../share/classes/jdk/jfr/DataAmount.java | 7 +- .../share/classes/jdk/jfr/Enabled.java | 16 +- .../share/classes/jdk/jfr/EventType.java | 5 + src/jdk.jfr/share/classes/jdk/jfr/Period.java | 7 +- .../classes/jdk/jfr/ValueDescriptor.java | 5 + .../jdk/jfr/snippet-files/Snippets.java | 265 ++++++++++++++++-- 8 files changed, 299 insertions(+), 25 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/BooleanFlag.java b/src/jdk.jfr/share/classes/jdk/jfr/BooleanFlag.java index 6fc92ceab34..10aedc19bb6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/BooleanFlag.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/BooleanFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -31,8 +31,14 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Event field annotation, specifies that the value is a boolean flag, a {@code true} or - * {@code false} value. + * Event field annotation, specifies that the value is a boolean flag, a + * {@code true} or {@code false} value. + *

+ * The following example shows how the {@code BooleanFlag} annotation can be + * used to describe that a setting is a boolean value. This information can be + * used by a graphical user interface to display the setting as a checkbox. + * + * {@snippet class = "Snippets" region = "BooleanFlagOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java index 5afd5e1a7da..8be9d2ee23d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -40,6 +40,11 @@ import jdk.jfr.internal.jfc.JFC; /** * A collection of settings and metadata describing the configuration. + *

+ * The following example shows how the {@code Configuration} class can be used + * to list available configurations and how to pass a configuration object to a + * {@code Recording}. + * {@snippet class = "Snippets" region = "ConfigurationxsOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/DataAmount.java b/src/jdk.jfr/share/classes/jdk/jfr/DataAmount.java index 5f2650a9e64..c2442ad2c92 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/DataAmount.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/DataAmount.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -32,6 +32,11 @@ import java.lang.annotation.Target; /** * Event field annotation, specifies that a value represents an amount of data (for example, bytes). + *

+ * The following example shows how the {@code DataAmount} annotation can be used to + * set the units {@code BITS} and {@code BYTES} to event fields. + * + * {@snippet class="Snippets" region="DataAmountOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java index d8e697e871e..328826356a1 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Enabled.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -34,7 +34,19 @@ import java.lang.annotation.Target; /** * Event annotation, determines if an event should be enabled by default. *

- * If an event doesn't have the annotation, then by default the event is enabled. + * If an event doesn't have the annotation, then by default the event is + * enabled. + *

+ * The following example shows how the {@code Enabled} annotation can be used to + * create a disabled event. A disabled event will at most have the overhead of + * an allocation, or none if the runtime JIT compiler is able to eliminate it. + * + * {@snippet class = "Snippets" region = "EnabledOverview"} + * + * The event can be enabled programmatically, or on command line when needed, + * for example: + * + * {@snippet class = "Snippets" region = "EnabledOverviewCommandLine"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java index 8ced73b2ab9..c8ea51a50dc 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/EventType.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/EventType.java @@ -40,6 +40,11 @@ import jdk.jfr.internal.Utils; /** * Describes an event, its fields, settings and annotations. + *

+ * The following example shows how the {@code EventType} class can + * be used to print metadata about an event. + * + * {@snippet class="Snippets" region="EventTypeOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/Period.java b/src/jdk.jfr/share/classes/jdk/jfr/Period.java index fba49b8d6f4..90c4a9699d8 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/Period.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/Period.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2023, 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 @@ -33,6 +33,11 @@ import java.lang.annotation.Target; /** * Event annotation, specifies the default setting value for a periodic event. + *

+ * The following example shows how the {@code Period} annotation can be used + * to emit events at different intervals. + * + * {@snippet class = "Snippets" region = "PeriodOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/ValueDescriptor.java b/src/jdk.jfr/share/classes/jdk/jfr/ValueDescriptor.java index 75ed52371ec..1cd01d22398 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/ValueDescriptor.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/ValueDescriptor.java @@ -36,6 +36,11 @@ import jdk.jfr.internal.Utils; /** * Describes the event fields and annotation elements. + *

+ * The following example shows how the {@code ValueDescriptor} class can + * be used to list field information of all types. + * + * {@snippet class="Snippets" region="ValueDescriptorOverview"} * * @since 9 */ diff --git a/src/jdk.jfr/share/classes/jdk/jfr/snippet-files/Snippets.java b/src/jdk.jfr/share/classes/jdk/jfr/snippet-files/Snippets.java index 4d4a5a0516d..53db6f76094 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/snippet-files/Snippets.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/snippet-files/Snippets.java @@ -25,12 +25,16 @@ package jdk.jfr.snippets; import jdk.jfr.AnnotationElement; +import jdk.jfr.BooleanFlag; import jdk.jfr.ValueDescriptor; import jdk.jfr.EventFactory; +import jdk.jfr.EventType; import jdk.jfr.Event; import jdk.jfr.Name; import jdk.jfr.Label; +import jdk.jfr.DataAmount; import jdk.jfr.Description; +import jdk.jfr.Enabled; import jdk.jfr.Category; import jdk.jfr.ContentType; import jdk.jfr.Period; @@ -39,6 +43,7 @@ import jdk.jfr.StackTrace; import jdk.jfr.MetadataDefinition; import jdk.jfr.Relational; import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.consumer.RecordingStream; import jdk.jfr.Configuration; import jdk.jfr.SettingDefinition; import jdk.jfr.SettingControl; @@ -53,10 +58,13 @@ import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -83,6 +91,77 @@ public class Snippets { // @end } + // @start region="BooleanFlagOverview" + @BooleanFlag + @Name("example.Rollback") + @Label("Rollback") + @Description("Include transactions that are rollbacked") + public static class RollbackSetting extends SettingControl { + private boolean value = true; + + @Override + public String combine(Set values) { + return values.contains("true") ? "true" : "false"; + } + + @Override + public void setValue(String settingValue) { + value = "true".equals(settingValue); + } + + @Override + public String getValue() { + return Boolean.toString(value); + } + + public boolean shouldEmit() { + return value; + } + } + + @Name("example.Transaction") + public static class TransactionEvent extends Event { + @Label("Context") + String context; + + @Label("Rollback") + boolean rollback; + + @SettingDefinition + @Name("rollback") + public boolean rollback(RollbackSetting rollbackSetting) { + return rollback && rollbackSetting.shouldEmit(); + } + } + // @end + + static class ConfigurationOverview { + // @start region="ConfigurationxsOverview" + public static void main(String... args) throws Exception { + if (args.length == 0) { + System.out.println("Configurations:"); + for (Configuration c : Configuration.getConfigurations()) { + System.out.println("Name: " + c.getName()); + System.out.println("Label: " + c.getLabel()); + System.out.println("Description: " + c.getDescription()); + System.out.println("Provider: " + c.getProvider()); + System.out.println(); + } + } else { + String name = args[0]; + Configuration c = Configuration.getConfiguration(name); + try (Recording r = new Recording(c)) { + System.out.println("Starting recording with settings:"); + for (Map.Entry setting : c.getSettings().entrySet()) { + System.out.println(setting.getKey() + " = " + setting.getValue()); + } + r.start(); + } + } + } + // @end + } + record CPU(String id, float temperature) { } @@ -161,6 +240,47 @@ public class Snippets { } // @end + // @start region="DataAmountOverview" + @Name("com.example.ImageRender") + @Label("Image Render") + public class ImageRender extends Event { + @Label("Height") + long height; + + @Label("Width") + long width; + + @Label("Color Depth") + @DataAmount(DataAmount.BITS) + int colorDepth; + + @Label("Memory Size") + @DataAmount // bytes by default + long memorySize; + } + // @end + + // @start region="EnabledOverview" + @Name("StopWatch") + @Label("Stop Watch") + @Category("Debugging") + @StackTrace(false) + @Enabled(false) + static public class StopWatchEvent extends Event { + } + + public void update() { + StopWatchEvent e = new StopWatchEvent(); + e.begin(); + code: // @replace regex='code:' replacement="..." + e.commit(); + } + // @end + /* + // @start region="EnabledOverviewCommandLine" + java -XX:StartFlightRecording:StopWatch#enabled=true ... + // @end + */ // @start region="EventOverview" public class Example { @@ -205,16 +325,40 @@ public class Snippets { void EventSettingOverview() throws Exception { // @start region="EventSettingOverview" - Recording r = new Recording(); - r.enable("jdk.CPULoad") - .withPeriod(Duration.ofSeconds(1)); - r.enable("jdk.FileWrite") - .withoutStackTrace() - .withThreshold(Duration.ofNanos(10)); - r.start(); - Thread.sleep(10_000); - r.stop(); - r.dump(Files.createTempFile("recording", ".jfr")); + try (Recording r = new Recording()) { + r.enable("jdk.CPULoad") + .withPeriod(Duration.ofSeconds(1)); + r.enable("jdk.FileWrite") + .withoutStackTrace() + .withThreshold(Duration.ofNanos(10)); + r.start(); + Thread.sleep(10_000); + r.stop(); + r.dump(Files.createTempFile("recording", ".jfr")); + } + // @end + } + void EventTypeOverview() { + // @start region="EventTypeOverview" + for (EventType eventType : FlightRecorder.getFlightRecorder().getEventTypes()) { + System.out.println("Event Type: " + eventType.getName()); + if (eventType.getLabel() != null) { + System.out.println("Label: " + eventType.getLabel()); + } + if (eventType.getDescription() != null) { + System.out.println("Description: " + eventType.getDescription()); + } + StringJoiner s = new StringJoiner(" / "); + for (String category : eventType.getCategoryNames()) { + s.add(category); + } + System.out.println("Category: " + s); + System.out.println("Fields: " + eventType.getFields().size()); + System.out.println("Annotations: " + eventType.getAnnotationElements().size()); + System.out.println("Settings: " + eventType.getSettingDescriptors().size()); + System.out.println("Enabled: " + eventType.isEnabled()); + System.out.println(); + } // @end } @@ -261,6 +405,54 @@ public class Snippets { } // @end + void PeriodOverview() { + // @start region = "PeriodOverview" + @Period("1 s") + @Name("Counter") + class CountEvent extends Event { + int count; + } + @Period("3 s") + @Name("Fizz") + class FizzEvent extends Event { + } + @Period("5 s") + @Name("Buzz") + class BuzzEvent extends Event { + } + + var counter = new AtomicInteger(); + FlightRecorder.addPeriodicEvent(CountEvent.class, () -> { + CountEvent event = new CountEvent(); + event.count = counter.incrementAndGet(); + event.commit(); + }); + FlightRecorder.addPeriodicEvent(FizzEvent.class, () -> { + new FizzEvent().commit(); + }); + FlightRecorder.addPeriodicEvent(BuzzEvent.class, () -> { + new BuzzEvent().commit(); + }); + + var sb = new StringBuilder(); + var last = new AtomicInteger(); + var current = new AtomicInteger(); + try (var r = new RecordingStream()) { + r.onEvent("Counter", e -> current.set(e.getValue("count"))); + r.onEvent("Fizz", e -> sb.append("Fizz")); + r.onEvent("Buzz", e -> sb.append("Buzz")); + r.onFlush(() -> { + if (current.get() != last.get()) { + System.out.println(sb.isEmpty() ? current : sb); + last.set(current.get()); + sb.setLength(0); + } + }); + r.start(); + } + // @end + } + // @start region="RelationalOverview" @MetadataDefinition @Relational @@ -303,12 +495,13 @@ public class Snippets { void RecordingnOverview() throws Exception { // @start region="RecordingOverview" Configuration c = Configuration.getConfiguration("default"); - Recording r = new Recording(c); - r.start(); - System.gc(); - Thread.sleep(5000); - r.stop(); - r.dump(Files.createTempFile("my-recording", ".jfr")); + try (Recording r = new Recording(c)) { + r.start(); + System.gc(); + Thread.sleep(5000); + r.stop(); + r.dump(Files.createTempFile("my-recording", ".jfr")); + } // @end } @@ -410,4 +603,42 @@ public class Snippets { } } // @end + + static class ValueDsecriptorOverview { + // @start region="ValueDescriptorOverview" + void printTypes() { + Map> typeMap = new LinkedHashMap<>(); + for (EventType eventType : FlightRecorder.getFlightRecorder().getEventTypes()) { + findTypes(typeMap, eventType.getName(), eventType.getFields()); + } + for (String type : typeMap.keySet()) { + System.out.println("Type: " + type); + for (ValueDescriptor field : typeMap.get(type)) { + System.out.println(" Field: " + field.getName()); + String arrayBrackets = field.isArray() ? "[]" : ""; + System.out.println(" Type: " + field.getTypeName() + arrayBrackets); + if (field.getLabel() != null) { + System.out.println(" Label: " + field.getLabel()); + } + if (field.getDescription() != null) { + System.out.println(" Description: " + field.getDescription()); + } + if (field.getContentType() != null) { + System.out.println(" Content Types: " + field.getContentType()); + } + } + System.out.println(); + } + } + + void findTypes(Map> typeMap, String typeName, List fields) { + if (!typeMap.containsKey(typeName)) { + typeMap.put(typeName, fields); + for (ValueDescriptor subField : fields) { + findTypes(typeMap, subField.getTypeName(), subField.getFields()); + } + } + } + // @end + } }