1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.api.consumer;
  27 
  28 import java.io.IOException;
  29 import java.time.Duration;
  30 import java.time.Instant;
  31 import java.util.Arrays;
  32 import java.util.HashSet;
  33 import java.util.List;
  34 import java.util.Set;
  35 import java.util.function.Function;
  36 
  37 import jdk.jfr.Event;
  38 import jdk.jfr.Recording;
  39 import jdk.jfr.StackTrace;
  40 import jdk.jfr.Timespan;
  41 import jdk.jfr.Timestamp;
  42 import jdk.jfr.Unsigned;
  43 import jdk.jfr.consumer.RecordedClass;
  44 import jdk.jfr.consumer.RecordedEvent;
  45 import jdk.jfr.consumer.RecordedObject;
  46 import jdk.jfr.consumer.RecordedThread;
  47 import jdk.test.lib.Asserts;
  48 import jdk.test.lib.jfr.Events;
  49 
  50 /**
  51  * @test
  52  * @summary Verifies the methods of the RecordedObject
  53  * @key jfr
  54  * @requires vm.hasJFR
  55  * @library /test/lib
  56  * @run main/othervm jdk.jfr.api.consumer.TestRecordedObject
  57  */
  58 public class TestRecordedObject {
  59 
  60     private final static boolean BOOLEAN_VALUE = true;
  61     private final static byte VALUE = 47;
  62     private final static String STRING_VALUE = "47";
  63     private final static Class<?> CLASS_VALUE = String.class;
  64     private final static Thread THREAD_VALUE = Thread.currentThread();
  65     private final static Instant INSTANT_VALUE = Instant.now();
  66     private final static Duration DURATION_VALUE = Duration.ofSeconds(47);
  67 
  68     @StackTrace(false)
  69     static final class EventWithValues extends Event {
  70         boolean booleanField = BOOLEAN_VALUE;
  71         byte byteField = VALUE;
  72         char charField = VALUE;
  73         short shortField = VALUE;
  74         int intField = VALUE;
  75         long longField = VALUE;
  76         float floatField = VALUE;
  77         double doubleField = VALUE;
  78         String stringField = STRING_VALUE;
  79         Class<?> classField = CLASS_VALUE;
  80         Thread threadField = THREAD_VALUE;
  81         @Timespan(Timespan.NANOSECONDS)
  82         long durationField = DURATION_VALUE.toNanos();
  83         @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
  84         long instantField = INSTANT_VALUE.toEpochMilli();
  85         Thread nullField = null;
  86         Class<?> nullField2 = null;
  87 
  88         @Timespan(Timespan.MICROSECONDS)
  89         long durationMicros = DURATION_VALUE.toNanos() / 1000;
  90 
  91         @Timespan(Timespan.MILLISECONDS)
  92         long durationMillis = DURATION_VALUE.toMillis();
  93 
  94         @Timespan(Timespan.SECONDS)
  95         long durationSeconds = DURATION_VALUE.toSeconds();
  96 
  97         @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
  98         long instantMillis = 1000;
  99 
 100         @Timestamp(Timespan.TICKS)
 101         long instantTicks = 0;
 102 
 103         @Unsigned
 104         byte unsignedByte = Byte.MIN_VALUE;
 105         @Unsigned
 106         char unsignedChar = 'q';
 107         @Unsigned
 108         short unsignedShort = Short.MIN_VALUE;
 109         @Unsigned
 110         int unsignedInt = Integer.MIN_VALUE;
 111         @Unsigned
 112         long unsignedLong = Long.MIN_VALUE; // unsigned should be ignored
 113         @Unsigned
 114         float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored
 115         @Unsigned
 116         double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored
 117 
 118     }
 119 
 120     private final static Set<String> ALL = createAll();
 121 
 122     public static void main(String[] args) throws Throwable {
 123 
 124         RecordedObject event = makeRecordedObject();
 125 
 126         // Primitives
 127         testGetBoolean(event);
 128         testGetByte(event);
 129         testGetChar(event);
 130         testGetShort(event);
 131         testGetInt(event);
 132         testGetLong(event);
 133         testGetDouble(event);
 134         testGetFloat(event);
 135 
 136         // // Complex types
 137         testGetString(event);
 138         testGetInstant(event);
 139         testGetDuration(event);
 140         testGetThread(event);
 141         testGetClass(event);
 142 
 143         // Misc.
 144         testNestedNames(event);
 145         testTimeUnits(event);
 146         testUnsigned(event);
 147     }
 148 
 149     private static void testUnsigned(RecordedObject event) {
 150         // Unsigned byte value
 151         Asserts.assertEquals(event.getByte("unsignedByte"), Byte.MIN_VALUE);
 152         Asserts.assertEquals(event.getInt("unsignedByte"), Byte.toUnsignedInt(Byte.MIN_VALUE));
 153         Asserts.assertEquals(event.getLong("unsignedByte"), Byte.toUnsignedLong(Byte.MIN_VALUE));
 154         Asserts.assertEquals(event.getShort("unsignedByte"), (short)Byte.toUnsignedInt(Byte.MIN_VALUE));
 155 
 156         // Unsigned char, nothing should happen, it is unsigned
 157         Asserts.assertEquals(event.getChar("unsignedChar"), 'q');
 158         Asserts.assertEquals(event.getInt("unsignedChar"), (int)'q');
 159         Asserts.assertEquals(event.getLong("unsignedChar"), (long)'q');
 160 
 161         // Unsigned short
 162         Asserts.assertEquals(event.getShort("unsignedShort"), Short.MIN_VALUE);
 163         Asserts.assertEquals(event.getInt("unsignedShort"), Short.toUnsignedInt(Short.MIN_VALUE));
 164         Asserts.assertEquals(event.getLong("unsignedShort"), Short.toUnsignedLong(Short.MIN_VALUE));
 165 
 166         // Unsigned int
 167         Asserts.assertEquals(event.getInt("unsignedInt"), Integer.MIN_VALUE);
 168         Asserts.assertEquals(event.getLong("unsignedInt"), Integer.toUnsignedLong(Integer.MIN_VALUE));
 169 
 170         // Unsigned long, nothing should happen
 171         Asserts.assertEquals(event.getLong("unsignedLong"), Long.MIN_VALUE);
 172 
 173         // Unsigned float, nothing should happen
 174         Asserts.assertEquals(event.getFloat("unsignedFloat"), Float.MIN_VALUE);
 175 
 176         // Unsigned double, nothing should happen
 177         Asserts.assertEquals(event.getDouble("unsignedDouble"), Double.MIN_VALUE);
 178     }
 179 
 180     private static void testTimeUnits(RecordedObject event) {
 181         Asserts.assertEquals(event.getDuration("durationMicros"), DURATION_VALUE);
 182         Asserts.assertEquals(event.getDuration("durationMillis"), DURATION_VALUE);
 183         Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE);
 184         Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L);
 185         if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) {
 186             throw new AssertionError("Expected start time of JVM to before call to Instant.now()");
 187         }
 188     }
 189 
 190     private static void testNestedNames(RecordedObject event) {
 191         RecordedThread t = event.getValue("threadField");
 192 
 193         // Nested with getValue
 194         try {
 195             event.getValue("nullField.javaName");
 196             throw new AssertionError("Expected NullPointerException");
 197         } catch (NullPointerException npe) {
 198             // OK, expected;
 199         }
 200         try {
 201             event.getValue("nullField.does.not.exist");
 202             throw new AssertionError("Expected IllegalArgumentException");
 203         } catch (IllegalArgumentException iae) {
 204             // OK, expected;
 205         }
 206 
 207         // Nested getLong
 208         try {
 209             event.getLong("nullField.javaName");
 210             throw new AssertionError("Expected NullPointerException");
 211         } catch (NullPointerException npe) {
 212             // OK, expected;
 213         }
 214         try {
 215             event.getLong("nullField.does.not.exist");
 216             throw new AssertionError("Expected IllegalArgumentException");
 217         } catch (IllegalArgumentException npe) {
 218             // OK, expected;
 219         }
 220         if (t.getOSThreadId() != event.getLong("threadField.osThreadId")) {
 221             throw new AssertionError("Incorrect result from nested long value");
 222         }
 223 
 224         // Nested getString
 225         try {
 226             event.getString("nullField.osThreadId");
 227             throw new AssertionError("Expected IllegalArgumentException");
 228         } catch (IllegalArgumentException npe) {
 229             // OK, expected;
 230         }
 231         try {
 232             event.getLong("nullField.does.not.exist");
 233             throw new AssertionError("Expected IllegalArgumentException");
 234         } catch (IllegalArgumentException npe) {
 235             // OK, expected;
 236         }
 237         if (!t.getJavaName().equals(event.getString("threadField.javaName"))) {
 238             throw new AssertionError("Incorrect result from nested long value");
 239         }
 240 
 241         // Nested getClass
 242         try {
 243             event.getClass("nullField.osThreadId");
 244             throw new AssertionError("Expected IllegalArgumentException");
 245         } catch (IllegalArgumentException npe) {
 246             // OK, expected;
 247         }
 248         try {
 249             event.getClass("nullField.does.not.exist");
 250             throw new AssertionError("Expected IllegalArgumentException");
 251         } catch (IllegalArgumentException npe) {
 252             // OK, expected;
 253         }
 254 
 255         // Nested getThread
 256         try {
 257             event.getThread("nullField2.name");
 258             throw new AssertionError("Expected IllegalArgumentException");
 259         } catch (IllegalArgumentException npe) {
 260             // OK, expected;
 261         }
 262         try {
 263             event.getThread("nullField2.does.not.exist");
 264             throw new AssertionError("Expected IllegalArgumentException");
 265         } catch (IllegalArgumentException npe) {
 266             // OK, expected;
 267         }
 268     }
 269 
 270     private static void testGetBoolean(RecordedObject e) {
 271         assertGetter(x -> e.getBoolean(x), BOOLEAN_VALUE, "boolean");
 272     }
 273 
 274     private static void testGetByte(RecordedObject e) {
 275         assertGetter(x -> e.getByte(x), (byte) VALUE, "byte");
 276     }
 277 
 278     private static void testGetChar(RecordedObject e) {
 279         assertGetter(x -> e.getChar(x), (char) VALUE, "char");
 280     }
 281 
 282     private static void testGetShort(RecordedObject e) {
 283         assertGetter(x -> e.getShort(x), (short) VALUE, "byte", "short");
 284     }
 285 
 286     private static void testGetInt(RecordedObject e) {
 287         assertGetter(x -> e.getInt(x), (int) VALUE, "byte", "char", "short", "int");
 288     }
 289 
 290     private static void testGetLong(RecordedObject e) {
 291         assertGetter(x -> e.getLong(x), (long) VALUE, "byte", "char", "short", "int", "long");
 292     }
 293 
 294     private static void testGetFloat(RecordedObject e) {
 295         assertGetter(x -> e.getFloat(x), (float) VALUE, "byte", "char", "short", "int", "long", "float");
 296     }
 297 
 298     private static void testGetDouble(RecordedObject e) {
 299         assertGetter(x -> e.getDouble(x), (double) VALUE, "byte", "char", "short", "int", "long", "float", "double");
 300     }
 301 
 302     private static void testGetString(RecordedObject e) {
 303         assertGetter(x -> e.getString(x), STRING_VALUE, "string");
 304     }
 305 
 306     private static void testGetInstant(RecordedObject e) {
 307         assertGetter(x -> e.getInstant(x), Instant.ofEpochMilli(INSTANT_VALUE.toEpochMilli()), "instant");
 308     }
 309 
 310     private static void testGetDuration(RecordedObject e) {
 311         assertGetter(x -> e.getDuration(x), DURATION_VALUE, "duration");
 312     }
 313 
 314     private static void testGetThread(RecordedObject e) {
 315         RecordedThread thread = e.getValue("threadField");
 316         if (!thread.getJavaName().equals(THREAD_VALUE.getName())) {
 317             throw new AssertionError("Expected thread to have name " + THREAD_VALUE.getName());
 318         }
 319         assertGetter(x -> {
 320             // OK to access nullField if it is correct type
 321             // Chose a second null field with class type
 322             if ("nullField".equals(x)) {
 323                 return e.getThread("nullField2");
 324             } else {
 325                 return e.getThread(x);
 326             }
 327 
 328         }, thread, "thread");
 329     }
 330 
 331     private static void testGetClass(RecordedObject e) {
 332         RecordedClass clazz = e.getValue("classField");
 333         if (!clazz.getName().equals(CLASS_VALUE.getName())) {
 334             throw new AssertionError("Expected class to have name " + CLASS_VALUE.getName());
 335         }
 336         assertGetter(x -> e.getClass(x), clazz, "class");
 337     }
 338 
 339     private static <T> void assertGetter(Function<String, T> f, T expectedValue, String... validTypes) {
 340         Set<String> valids = new HashSet<String>(Arrays.asList(validTypes));
 341         Set<String> invalids = new HashSet<String>(ALL);
 342         invalids.removeAll(valids);
 343         for (String valid : valids) {
 344             T result = f.apply(valid + "Field");
 345             if (!expectedValue.equals(result)) {
 346                 throw new AssertionError("Incorrect return value " + result + ". Expected " + expectedValue);
 347             }
 348         }
 349         for (String invalid : invalids) {
 350             try {
 351                 f.apply(invalid + "Field");
 352             } catch (IllegalArgumentException iae) {
 353                 // OK, as expected
 354             } catch (Exception e) {
 355                 throw new AssertionError("Unexpected exception for invalid field " + invalid + ". " + e.getClass().getName() + " : " + e.getMessage(), e);
 356             }
 357         }
 358         String[] illegals = { "missingField", "nullField.javaName.does.not.exist", "nullField" };
 359         for (String illegal : illegals) {
 360             try {
 361                 f.apply(illegal);
 362                 throw new AssertionError("Expected IllegalArgumentException when accessing " + illegal);
 363             } catch (IllegalArgumentException iae) {
 364                 // OK, as expected
 365             } catch (Exception e) {
 366                 throw new AssertionError("Expected IllegalArgumentException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
 367             }
 368         }
 369         try {
 370             f.apply(null);
 371             throw new AssertionError("Expected NullpointerException exception when passing in null value");
 372         } catch (NullPointerException iae) {
 373             // OK, as expected
 374         } catch (Exception e) {
 375             throw new AssertionError("Expected NullpointerException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
 376         }
 377     }
 378 
 379     private static RecordedObject makeRecordedObject() throws IOException {
 380         Recording r = new Recording();
 381         r.start();
 382         EventWithValues t = new EventWithValues();
 383         t.commit();
 384         r.stop();
 385         List<RecordedEvent> events = Events.fromRecording(r);
 386         Events.hasEvents(events);
 387         return events.get(0);
 388     }
 389 
 390     private static Set<String> createAll() {
 391         Set<String> set = new HashSet<>();
 392         set.add("boolean");
 393         set.add("byte");
 394         set.add("char");
 395         set.add("short");
 396         set.add("int");
 397         set.add("long");
 398         set.add("float");
 399         set.add("double");
 400         set.add("string");
 401         set.add("class");
 402         set.add("thread");
 403         set.add("instant");
 404         set.add("duration");
 405         return set;
 406     }
 407 }