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 }