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  * @library /test/lib
  55  * @run main/othervm jdk.jfr.api.consumer.TestRecordedObject
  56  */
  57 public class TestRecordedObject {
  58 
  59     private final static boolean BOOLEAN_VALUE = true;
  60     private final static byte VALUE = 47;
  61     private final static String STRING_VALUE = "47";
  62     private final static Class<?> CLASS_VALUE = String.class;
  63     private final static Thread THREAD_VALUE = Thread.currentThread();
  64     private final static Instant INSTANT_VALUE = Instant.now();
  65     private final static Duration DURATION_VALUE = Duration.ofSeconds(47);
  66 
  67     @StackTrace(false)
  68     static final class EventWithValues extends Event {
  69         boolean booleanField = BOOLEAN_VALUE;
  70         byte byteField = VALUE;
  71         char charField = VALUE;
  72         short shortField = VALUE;
  73         int intField = VALUE;
  74         long longField = VALUE;
  75         float floatField = VALUE;
  76         double doubleField = VALUE;
  77         String stringField = STRING_VALUE;
  78         Class<?> classField = CLASS_VALUE;
  79         Thread threadField = THREAD_VALUE;
  80         @Timespan(Timespan.NANOSECONDS)
  81         long durationField = DURATION_VALUE.toNanos();
  82         @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
  83         long instantField = INSTANT_VALUE.toEpochMilli();
  84         Thread nullField = null;
  85         Class<?> nullField2 = null;
  86 
  87         @Timespan(Timespan.MICROSECONDS)
  88         long durationMicros = DURATION_VALUE.toNanos() / 1000;
  89 
  90         @Timespan(Timespan.MILLISECONDS)
  91         long durationMillis = DURATION_VALUE.toMillis();
  92 
  93         @Timespan(Timespan.SECONDS)
  94         long durationSeconds = DURATION_VALUE.toSeconds();
  95 
  96         @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH)
  97         long instantMillis = 1000;
  98 
  99         @Timestamp(Timespan.TICKS)
 100         long instantTicks = 0;
 101 
 102         @Unsigned
 103         byte unsignedByte = Byte.MIN_VALUE;
 104         @Unsigned
 105         char unsignedChar = 'q';
 106         @Unsigned
 107         short unsignedShort = Short.MIN_VALUE;
 108         @Unsigned
 109         int unsignedInt = Integer.MIN_VALUE;
 110         @Unsigned
 111         long unsignedLong = Long.MIN_VALUE; // unsigned should be ignored
 112         @Unsigned
 113         float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored
 114         @Unsigned
 115         double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored
 116 
 117     }
 118 
 119     private final static Set<String> ALL = createAll();
 120 
 121     public static void main(String[] args) throws Throwable {
 122 
 123         RecordedObject event = makeRecordedObject();
 124 
 125         // Primitives
 126         testGetBoolean(event);
 127         testGetByte(event);
 128         testGetChar(event);
 129         testGetShort(event);
 130         testGetInt(event);
 131         testGetLong(event);
 132         testGetDouble(event);
 133         testGetFloat(event);
 134 
 135         // // Complex types
 136         testGetString(event);
 137         testGetInstant(event);
 138         testGetDuration(event);
 139         testGetThread(event);
 140         testGetClass(event);
 141 
 142         // Misc.
 143         testNestedNames(event);
 144         testTimeUnits(event);
 145         testUnsigned(event);
 146     }
 147 
 148     private static void testUnsigned(RecordedObject event) {
 149         // Unsigned byte value
 150         Asserts.assertEquals(event.getByte("unsignedByte"), Byte.MIN_VALUE);
 151         Asserts.assertEquals(event.getInt("unsignedByte"), Byte.toUnsignedInt(Byte.MIN_VALUE));
 152         Asserts.assertEquals(event.getLong("unsignedByte"), Byte.toUnsignedLong(Byte.MIN_VALUE));
 153         Asserts.assertEquals(event.getShort("unsignedByte"), (short)Byte.toUnsignedInt(Byte.MIN_VALUE));
 154 
 155         // Unsigned char, nothing should happen, it is unsigned
 156         Asserts.assertEquals(event.getChar("unsignedChar"), 'q');
 157         Asserts.assertEquals(event.getInt("unsignedChar"), (int)'q');
 158         Asserts.assertEquals(event.getLong("unsignedChar"), (long)'q');
 159 
 160         // Unsigned short
 161         Asserts.assertEquals(event.getShort("unsignedShort"), Short.MIN_VALUE);
 162         Asserts.assertEquals(event.getInt("unsignedShort"), Short.toUnsignedInt(Short.MIN_VALUE));
 163         Asserts.assertEquals(event.getLong("unsignedShort"), Short.toUnsignedLong(Short.MIN_VALUE));
 164 
 165         // Unsigned int
 166         Asserts.assertEquals(event.getInt("unsignedInt"), Integer.MIN_VALUE);
 167         Asserts.assertEquals(event.getLong("unsignedInt"), Integer.toUnsignedLong(Integer.MIN_VALUE));
 168 
 169         // Unsigned long, nothing should happen
 170         Asserts.assertEquals(event.getLong("unsignedLong"), Long.MIN_VALUE);
 171 
 172         // Unsigned float, nothing should happen
 173         Asserts.assertEquals(event.getFloat("unsignedFloat"), Float.MIN_VALUE);
 174 
 175         // Unsigned double, nothing should happen
 176         Asserts.assertEquals(event.getDouble("unsignedDouble"), Double.MIN_VALUE);
 177     }
 178 
 179     private static void testTimeUnits(RecordedObject event) {
 180         Asserts.assertEquals(event.getDuration("durationMicros"), DURATION_VALUE);
 181         Asserts.assertEquals(event.getDuration("durationMillis"), DURATION_VALUE);
 182         Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE);
 183         Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L);
 184         if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) {
 185             throw new AssertionError("Expected start time of JVM to before call to Instant.now()");
 186         }
 187     }
 188 
 189     private static void testNestedNames(RecordedObject event) {
 190         RecordedThread t = event.getValue("threadField");
 191 
 192         // Nested with getValue
 193         try {
 194             event.getValue("nullField.javaName");
 195             throw new AssertionError("Expected NullPointerException");
 196         } catch (NullPointerException npe) {
 197             // OK, expected;
 198         }
 199         try {
 200             event.getValue("nullField.does.not.exist");
 201             throw new AssertionError("Expected IllegalArgumentException");
 202         } catch (IllegalArgumentException iae) {
 203             // OK, expected;
 204         }
 205 
 206         // Nested getLong
 207         try {
 208             event.getLong("nullField.javaName");
 209             throw new AssertionError("Expected NullPointerException");
 210         } catch (NullPointerException npe) {
 211             // OK, expected;
 212         }
 213         try {
 214             event.getLong("nullField.does.not.exist");
 215             throw new AssertionError("Expected IllegalArgumentException");
 216         } catch (IllegalArgumentException npe) {
 217             // OK, expected;
 218         }
 219         if (t.getOSThreadId() != event.getLong("threadField.osThreadId")) {
 220             throw new AssertionError("Incorrect result from nested long value");
 221         }
 222 
 223         // Nested getString
 224         try {
 225             event.getString("nullField.osThreadId");
 226             throw new AssertionError("Expected IllegalArgumentException");
 227         } catch (IllegalArgumentException npe) {
 228             // OK, expected;
 229         }
 230         try {
 231             event.getLong("nullField.does.not.exist");
 232             throw new AssertionError("Expected IllegalArgumentException");
 233         } catch (IllegalArgumentException npe) {
 234             // OK, expected;
 235         }
 236         if (!t.getJavaName().equals(event.getString("threadField.javaName"))) {
 237             throw new AssertionError("Incorrect result from nested long value");
 238         }
 239 
 240         // Nested getClass
 241         try {
 242             event.getClass("nullField.osThreadId");
 243             throw new AssertionError("Expected IllegalArgumentException");
 244         } catch (IllegalArgumentException npe) {
 245             // OK, expected;
 246         }
 247         try {
 248             event.getClass("nullField.does.not.exist");
 249             throw new AssertionError("Expected IllegalArgumentException");
 250         } catch (IllegalArgumentException npe) {
 251             // OK, expected;
 252         }
 253 
 254         // Nested getThread
 255         try {
 256             event.getThread("nullField2.name");
 257             throw new AssertionError("Expected IllegalArgumentException");
 258         } catch (IllegalArgumentException npe) {
 259             // OK, expected;
 260         }
 261         try {
 262             event.getThread("nullField2.does.not.exist");
 263             throw new AssertionError("Expected IllegalArgumentException");
 264         } catch (IllegalArgumentException npe) {
 265             // OK, expected;
 266         }
 267     }
 268 
 269     private static void testGetBoolean(RecordedObject e) {
 270         assertGetter(x -> e.getBoolean(x), BOOLEAN_VALUE, "boolean");
 271     }
 272 
 273     private static void testGetByte(RecordedObject e) {
 274         assertGetter(x -> e.getByte(x), (byte) VALUE, "byte");
 275     }
 276 
 277     private static void testGetChar(RecordedObject e) {
 278         assertGetter(x -> e.getChar(x), (char) VALUE, "char");
 279     }
 280 
 281     private static void testGetShort(RecordedObject e) {
 282         assertGetter(x -> e.getShort(x), (short) VALUE, "byte", "short");
 283     }
 284 
 285     private static void testGetInt(RecordedObject e) {
 286         assertGetter(x -> e.getInt(x), (int) VALUE, "byte", "char", "short", "int");
 287     }
 288 
 289     private static void testGetLong(RecordedObject e) {
 290         assertGetter(x -> e.getLong(x), (long) VALUE, "byte", "char", "short", "int", "long");
 291     }
 292 
 293     private static void testGetFloat(RecordedObject e) {
 294         assertGetter(x -> e.getFloat(x), (float) VALUE, "byte", "char", "short", "int", "long", "float");
 295     }
 296 
 297     private static void testGetDouble(RecordedObject e) {
 298         assertGetter(x -> e.getDouble(x), (double) VALUE, "byte", "char", "short", "int", "long", "float", "double");
 299     }
 300 
 301     private static void testGetString(RecordedObject e) {
 302         assertGetter(x -> e.getString(x), STRING_VALUE, "string");
 303     }
 304 
 305     private static void testGetInstant(RecordedObject e) {
 306         assertGetter(x -> e.getInstant(x), Instant.ofEpochMilli(INSTANT_VALUE.toEpochMilli()), "instant");
 307     }
 308 
 309     private static void testGetDuration(RecordedObject e) {
 310         assertGetter(x -> e.getDuration(x), DURATION_VALUE, "duration");
 311     }
 312 
 313     private static void testGetThread(RecordedObject e) {
 314         RecordedThread thread = e.getValue("threadField");
 315         if (!thread.getJavaName().equals(THREAD_VALUE.getName())) {
 316             throw new AssertionError("Expected thread to have name " + THREAD_VALUE.getName());
 317         }
 318         assertGetter(x -> {
 319             // OK to access nullField if it is correct type
 320             // Chose a second null field with class type
 321             if ("nullField".equals(x)) {
 322                 return e.getThread("nullField2");
 323             } else {
 324                 return e.getThread(x);
 325             }
 326 
 327         }, thread, "thread");
 328     }
 329 
 330     private static void testGetClass(RecordedObject e) {
 331         RecordedClass clazz = e.getValue("classField");
 332         if (!clazz.getName().equals(CLASS_VALUE.getName())) {
 333             throw new AssertionError("Expected class to have name " + CLASS_VALUE.getName());
 334         }
 335         assertGetter(x -> e.getClass(x), clazz, "class");
 336     }
 337 
 338     private static <T> void assertGetter(Function<String, T> f, T expectedValue, String... validTypes) {
 339         Set<String> valids = new HashSet<String>(Arrays.asList(validTypes));
 340         Set<String> invalids = new HashSet<String>(ALL);
 341         invalids.removeAll(valids);
 342         for (String valid : valids) {
 343             T result = f.apply(valid + "Field");
 344             if (!expectedValue.equals(result)) {
 345                 throw new AssertionError("Incorrect return value " + result + ". Expected " + expectedValue);
 346             }
 347         }
 348         for (String invalid : invalids) {
 349             try {
 350                 f.apply(invalid + "Field");
 351             } catch (IllegalArgumentException iae) {
 352                 // OK, as expected
 353             } catch (Exception e) {
 354                 throw new AssertionError("Unexpected exception for invalid field " + invalid + ". " + e.getClass().getName() + " : " + e.getMessage(), e);
 355             }
 356         }
 357         String[] illegals = { "missingField", "nullField.javaName.does.not.exist", "nullField" };
 358         for (String illegal : illegals) {
 359             try {
 360                 f.apply(illegal);
 361                 throw new AssertionError("Expected IllegalArgumentException when accessing " + illegal);
 362             } catch (IllegalArgumentException iae) {
 363                 // OK, as expected
 364             } catch (Exception e) {
 365                 throw new AssertionError("Expected IllegalArgumentException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
 366             }
 367         }
 368         try {
 369             f.apply(null);
 370             throw new AssertionError("Expected NullpointerException exception when passing in null value");
 371         } catch (NullPointerException iae) {
 372             // OK, as expected
 373         } catch (Exception e) {
 374             throw new AssertionError("Expected NullpointerException. Got " + e.getClass().getName() + " : " + e.getMessage(), e);
 375         }
 376     }
 377 
 378     private static RecordedObject makeRecordedObject() throws IOException {
 379         Recording r = new Recording();
 380         r.start();
 381         EventWithValues t = new EventWithValues();
 382         t.commit();
 383         r.stop();
 384         List<RecordedEvent> events = Events.fromRecording(r);
 385         Events.hasEvents(events);
 386         return events.get(0);
 387     }
 388 
 389     private static Set<String> createAll() {
 390         Set<String> set = new HashSet<>();
 391         set.add("boolean");
 392         set.add("byte");
 393         set.add("char");
 394         set.add("short");
 395         set.add("int");
 396         set.add("long");
 397         set.add("float");
 398         set.add("double");
 399         set.add("string");
 400         set.add("class");
 401         set.add("thread");
 402         set.add("instant");
 403         set.add("duration");
 404         return set;
 405     }
 406 }