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