/* * Copyright (c) 2018, 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.api.consumer; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; import jdk.jfr.Event; import jdk.jfr.Recording; import jdk.jfr.StackTrace; import jdk.jfr.Timespan; import jdk.jfr.Timestamp; import jdk.jfr.Unsigned; import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedObject; import jdk.jfr.consumer.RecordedThread; import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; /** * @test * @summary Verifies the methods of the RecordedObject * @key jfr * * @library /lib / * @run main/othervm jdk.jfr.api.consumer.TestRecordedObject */ public class TestRecordedObject { private final static boolean BOOLEAN_VALUE = true; private final static byte VALUE = 47; private final static String STRING_VALUE = "47"; private final static Class CLASS_VALUE = String.class; private final static Thread THREAD_VALUE = Thread.currentThread(); private final static Instant INSTANT_VALUE = Instant.now(); private final static Duration DURATION_VALUE = Duration.ofSeconds(47); @StackTrace(false) static final class EventWithValues extends Event { boolean booleanField = BOOLEAN_VALUE; byte byteField = VALUE; char charField = VALUE; short shortField = VALUE; int intField = VALUE; long longField = VALUE; float floatField = VALUE; double doubleField = VALUE; String stringField = STRING_VALUE; Class classField = CLASS_VALUE; Thread threadField = THREAD_VALUE; @Timespan(Timespan.NANOSECONDS) long durationField = DURATION_VALUE.toNanos(); @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) long instantField = INSTANT_VALUE.toEpochMilli(); Thread nullField = null; Class nullField2 = null; @Timespan(Timespan.MICROSECONDS) long durationMicros = DURATION_VALUE.toNanos() / 1000; @Timespan(Timespan.MILLISECONDS) long durationMillis = DURATION_VALUE.toMillis(); @Timespan(Timespan.SECONDS) //long durationSeconds = (DURATION_VALUE.toMinutes() * 60); long durationSeconds = DURATION_VALUE.toMillis() / 1000; @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) long instantMillis = 1000; @Timestamp(Timespan.TICKS) long instantTicks = 0; @Unsigned byte unsignedByte = Byte.MIN_VALUE; @Unsigned char unsignedChar = 'q'; @Unsigned short unsignedShort = Short.MIN_VALUE; @Unsigned int unsignedInt = Integer.MIN_VALUE; @Unsigned long unsignedLong = Long.MIN_VALUE; // unsigned should be ignored @Unsigned float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored @Unsigned double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored } private final static Set ALL = createAll(); public static void main(String[] args) throws Throwable { RecordedObject event = makeRecordedObject(); // Primitives testGetBoolean(event); testGetByte(event); testGetChar(event); testGetShort(event); testGetInt(event); testGetLong(event); testGetDouble(event); testGetFloat(event); // // Complex types testGetString(event); testGetInstant(event); testGetDuration(event); testGetThread(event); testGetClass(event); // Misc. testNestedNames(event); testTimeUnits(event); testUnsigned(event); } private static void testUnsigned(RecordedObject event) { // Unsigned byte value Asserts.assertEquals(event.getByte("unsignedByte"), Byte.MIN_VALUE); Asserts.assertEquals(event.getInt("unsignedByte"), Byte.toUnsignedInt(Byte.MIN_VALUE)); Asserts.assertEquals(event.getLong("unsignedByte"), Byte.toUnsignedLong(Byte.MIN_VALUE)); Asserts.assertEquals(event.getShort("unsignedByte"), (short)Byte.toUnsignedInt(Byte.MIN_VALUE)); // Unsigned char, nothing should happen, it is unsigned Asserts.assertEquals(event.getChar("unsignedChar"), 'q'); Asserts.assertEquals(event.getInt("unsignedChar"), (int)'q'); Asserts.assertEquals(event.getLong("unsignedChar"), (long)'q'); // Unsigned short Asserts.assertEquals(event.getShort("unsignedShort"), Short.MIN_VALUE); Asserts.assertEquals(event.getInt("unsignedShort"), Short.toUnsignedInt(Short.MIN_VALUE)); Asserts.assertEquals(event.getLong("unsignedShort"), Short.toUnsignedLong(Short.MIN_VALUE)); // Unsigned int Asserts.assertEquals(event.getInt("unsignedInt"), Integer.MIN_VALUE); Asserts.assertEquals(event.getLong("unsignedInt"), Integer.toUnsignedLong(Integer.MIN_VALUE)); // Unsigned long, nothing should happen Asserts.assertEquals(event.getLong("unsignedLong"), Long.MIN_VALUE); // Unsigned float, nothing should happen Asserts.assertEquals(event.getFloat("unsignedFloat"), Float.MIN_VALUE); // Unsigned double, nothing should happen Asserts.assertEquals(event.getDouble("unsignedDouble"), Double.MIN_VALUE); } private static void testTimeUnits(RecordedObject event) { Asserts.assertEquals(event.getDuration("durationMicros"), DURATION_VALUE); Asserts.assertEquals(event.getDuration("durationMillis"), DURATION_VALUE); Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE); Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L); if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) { throw new AssertionError("Expected start time of JVM to before call to Instant.now()"); } } private static void testNestedNames(RecordedObject event) { RecordedThread t = event.getValue("threadField"); // Nested with getValue try { event.getValue("nullField.javaName"); throw new AssertionError("Expected NullPointerException"); } catch (NullPointerException npe) { // OK, expected; } try { event.getValue("nullField.does.not.exist"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException iae) { // OK, expected; } // Nested getLong try { event.getLong("nullField.javaName"); throw new AssertionError("Expected NullPointerException"); } catch (NullPointerException npe) { // OK, expected; } try { event.getLong("nullField.does.not.exist"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } if (t.getOSThreadId() != event.getLong("threadField.osThreadId")) { throw new AssertionError("Incorrect result from nested long value"); } // Nested getString try { event.getString("nullField.osThreadId"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } try { event.getLong("nullField.does.not.exist"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } if (!t.getJavaName().equals(event.getString("threadField.javaName"))) { throw new AssertionError("Incorrect result from nested long value"); } // Nested getClass try { event.getClass("nullField.osThreadId"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } try { event.getClass("nullField.does.not.exist"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } // Nested getThread try { event.getThread("nullField2.name"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } try { event.getThread("nullField2.does.not.exist"); throw new AssertionError("Expected IllegalArgumentException"); } catch (IllegalArgumentException npe) { // OK, expected; } } private static void testGetBoolean(RecordedObject e) { assertGetter(x -> e.getBoolean(x), BOOLEAN_VALUE, "boolean"); } private static void testGetByte(RecordedObject e) { assertGetter(x -> e.getByte(x), (byte) VALUE, "byte"); } private static void testGetChar(RecordedObject e) { assertGetter(x -> e.getChar(x), (char) VALUE, "char"); } private static void testGetShort(RecordedObject e) { assertGetter(x -> e.getShort(x), (short) VALUE, "byte", "short"); } private static void testGetInt(RecordedObject e) { assertGetter(x -> e.getInt(x), (int) VALUE, "byte", "char", "short", "int"); } private static void testGetLong(RecordedObject e) { assertGetter(x -> e.getLong(x), (long) VALUE, "byte", "char", "short", "int", "long"); } private static void testGetFloat(RecordedObject e) { assertGetter(x -> e.getFloat(x), (float) VALUE, "byte", "char", "short", "int", "long", "float"); } private static void testGetDouble(RecordedObject e) { assertGetter(x -> e.getDouble(x), (double) VALUE, "byte", "char", "short", "int", "long", "float", "double"); } private static void testGetString(RecordedObject e) { assertGetter(x -> e.getString(x), STRING_VALUE, "string"); } private static void testGetInstant(RecordedObject e) { assertGetter(x -> e.getInstant(x), Instant.ofEpochMilli(INSTANT_VALUE.toEpochMilli()), "instant"); } private static void testGetDuration(RecordedObject e) { assertGetter(x -> e.getDuration(x), DURATION_VALUE, "duration"); } private static void testGetThread(RecordedObject e) { RecordedThread thread = e.getValue("threadField"); if (!thread.getJavaName().equals(THREAD_VALUE.getName())) { throw new AssertionError("Expected thread to have name " + THREAD_VALUE.getName()); } assertGetter(x -> { // OK to access nullField if it is correct type // Chose a second null field with class type if ("nullField".equals(x)) { return e.getThread("nullField2"); } else { return e.getThread(x); } }, thread, "thread"); } private static void testGetClass(RecordedObject e) { RecordedClass clazz = e.getValue("classField"); if (!clazz.getName().equals(CLASS_VALUE.getName())) { throw new AssertionError("Expected class to have name " + CLASS_VALUE.getName()); } assertGetter(x -> e.getClass(x), clazz, "class"); } private static void assertGetter(Function f, T expectedValue, String... validTypes) { Set valids = new HashSet(Arrays.asList(validTypes)); Set invalids = new HashSet(ALL); invalids.removeAll(valids); for (String valid : valids) { T result = f.apply(valid + "Field"); if (!expectedValue.equals(result)) { throw new AssertionError("Incorrect return value " + result + ". Expected " + expectedValue); } } for (String invalid : invalids) { try { f.apply(invalid + "Field"); } catch (IllegalArgumentException iae) { // OK, as expected } catch (Exception e) { throw new AssertionError("Unexpected exception for invalid field " + invalid + ". " + e.getClass().getName() + " : " + e.getMessage(), e); } } String[] illegals = { "missingField", "nullField.javaName.does.not.exist", "nullField" }; for (String illegal : illegals) { try { f.apply(illegal); throw new AssertionError("Expected IllegalArgumentException when accessing " + illegal); } catch (IllegalArgumentException iae) { // OK, as expected } catch (Exception e) { throw new AssertionError("Expected IllegalArgumentException. Got " + e.getClass().getName() + " : " + e.getMessage(), e); } } try { f.apply(null); throw new AssertionError("Expected NullpointerException exception when passing in null value"); } catch (NullPointerException iae) { // OK, as expected } catch (Exception e) { throw new AssertionError("Expected NullpointerException. Got " + e.getClass().getName() + " : " + e.getMessage(), e); } } private static RecordedObject makeRecordedObject() throws IOException { Recording r = new Recording(); r.start(); EventWithValues t = new EventWithValues(); t.commit(); r.stop(); List events = Events.fromRecording(r); Events.hasEvents(events); return events.get(0); } private static Set createAll() { Set set = new HashSet<>(); set.add("boolean"); set.add("byte"); set.add("char"); set.add("short"); set.add("int"); set.add("long"); set.add("float"); set.add("double"); set.add("string"); set.add("class"); set.add("thread"); set.add("instant"); set.add("duration"); return set; } }