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.FileNotFoundException; 29 import java.io.FileWriter; 30 import java.io.IOException; 31 import java.io.RandomAccessFile; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.Paths; 35 import java.util.Arrays; 36 import java.util.HashSet; 37 import java.util.List; 38 import java.util.StringJoiner; 39 40 41 import jdk.jfr.Event; 42 import jdk.jfr.EventType; 43 import jdk.jfr.FlightRecorder; 44 import jdk.jfr.Name; 45 import jdk.jfr.Recording; 46 import jdk.jfr.Registered; 47 import jdk.jfr.consumer.RecordedEvent; 48 import jdk.jfr.consumer.RecordingFile; 49 import jdk.test.lib.Asserts; 50 51 /* 52 * @test 53 * @summary Verifies that all methods in RecordingFIle are working 54 * @key jfr 55 * @library /test/lib 56 * @run main/othervm jdk.jfr.api.consumer.TestRecordingFile 57 */ 58 public class TestRecordingFile { 59 60 static class TestEvent1 extends Event { 61 } 62 63 static class TestEvent2 extends Event { 64 } 65 66 static class TestEvent3 extends Event { 67 } 68 69 private static String TEST_CLASS_BASE = "TestRecordingFile$TestEvent"; 70 private final static int EVENT_COUNT = 3; 71 private final static int HEADER_SIZE = 68; 72 private final static long METADATA_OFFSET = 24; 73 74 public static void main(String[] args) throws Throwable { 75 76 // create some recording data 77 Recording r = new Recording(); 78 r.enable(TestEvent1.class).withoutStackTrace(); 79 r.enable(TestEvent2.class).withoutStackTrace(); 80 r.enable(TestEvent3.class).withoutStackTrace(); 81 r.start(); 82 TestEvent1 t1 = new TestEvent1(); 83 t1.commit(); 84 TestEvent2 t2 = new TestEvent2(); 85 t2.commit(); 86 TestEvent3 t3 = new TestEvent3(); 87 t3.commit(); 88 r.stop(); 89 Path valid = Files.createTempFile("three-event-recording", ".jfr"); 90 r.dump(valid); 91 r.close(); 92 93 Path brokenWithZeros = createBrokenWIthZeros(valid); 94 Path brokenMetadata = createBrokenMetadata(valid); 95 // prepare event sets 96 testNewRecordingFile(valid, brokenWithZeros); 97 testIterate(valid, brokenWithZeros); 98 testReadAllEvents(valid, brokenWithZeros); 99 testReadEventTypes(valid, brokenMetadata); 100 testClose(valid); 101 testReadEventTypesMultiChunk(); 102 testReadEventTypeWithUnregistration(false, false); 103 testReadEventTypeWithUnregistration(false, true); 104 testReadEventTypeWithUnregistration(true, false); 105 testReadEventTypeWithUnregistration(true, true); 106 } 107 108 private static void testReadEventTypeWithUnregistration(boolean disk, boolean background) throws Exception { 109 FlightRecorder.register(Event1.class); 110 FlightRecorder.register(Event2.class); 111 FlightRecorder.register(Event3.class); 112 Recording backgrundRecording = new Recording(); 113 if (disk) { 114 backgrundRecording.setToDisk(disk); 115 } 116 if (background) { 117 backgrundRecording.start(); 118 } 119 recordAndVerify(disk, background,new int[] {1,2, 3}, new int[] {}); 120 FlightRecorder.unregister(Event2.class); 121 recordAndVerify(disk, background, new int[] {1, 3}, new int[] {2}); 122 FlightRecorder.unregister(Event1.class); 123 FlightRecorder.register(Event2.class); 124 recordAndVerify(disk,background, new int[] {2, 3}, new int[] {1}); 125 FlightRecorder.unregister(Event3.class); 126 FlightRecorder.register(Event3.class); 127 FlightRecorder.unregister(Event2.class); 128 FlightRecorder.unregister(Event3.class); 129 FlightRecorder.register(Event1.class); 130 FlightRecorder.unregister(Event1.class); 131 FlightRecorder.register(Event1.class); 132 FlightRecorder.register(Event2.class); 133 recordAndVerify(disk, background,new int[] {1, 2}, new int[] {3}); 134 if (background) { 135 backgrundRecording.close(); 136 } 137 } 138 139 private static void recordAndVerify(boolean disk, boolean background, int[] shouldExist, int[] shouldNotExist) throws Exception { 140 StringJoiner sb = new StringJoiner("-"); 141 for (int i = 0; i <shouldExist.length; i++) { 142 sb.add(Integer.toString(shouldExist[i])); 143 } 144 System.out.println("Verifying recordings: disk=" + disk + " background=" + background); 145 System.out.println("Should exist: " + Arrays.toString(shouldExist)); 146 System.out.println("Should not exist: " + Arrays.toString(shouldNotExist)); 147 148 Path p = Files.createTempFile(sb.toString(), ".jfr"); 149 System.out.println("Filename: " + p); 150 try (Recording r = new Recording()) { 151 r.start(); 152 r.stop(); 153 r.dump(p); 154 try (RecordingFile f = new RecordingFile(p)) { 155 List<EventType> types = f.readEventTypes(); 156 for (int i = 0; i< shouldExist.length; i++) { 157 assertHasEventType(types, "Event" + shouldExist[i]); 158 } 159 for (int i = 0; i< shouldNotExist.length; i++) { 160 assertMissingEventType(types, "Event" + shouldNotExist[i]); 161 } 162 } 163 } 164 System.out.println(); 165 System.out.println(); 166 } 167 168 @Registered(false) 169 @Name("Event1") 170 private static class Event1 extends Event { 171 } 172 @Registered(false) 173 @Name("Event2") 174 private static class Event2 extends Event { 175 } 176 @Registered(false) 177 @Name("Event3") 178 private static class Event3 extends Event { 179 } 180 181 private static void testReadEventTypesMultiChunk() throws Exception { 182 183 Path twoEventTypes = Files.createTempFile("two-event-types", ".jfr"); 184 Path threeEventTypes = Files.createTempFile("three-event-types", ".jfr"); 185 try (Recording r1 = new Recording()) { 186 r1.start(); 187 FlightRecorder.register(Event1.class); 188 try (Recording r2 = new Recording()) { 189 r2.start(); 190 FlightRecorder.register(Event2.class); 191 192 // Ensure that metadata are written twice. 193 try (Recording rotator = new Recording()) { 194 rotator.start(); 195 rotator.stop(); 196 } 197 r2.stop(); 198 r2.dump(twoEventTypes);; 199 } 200 FlightRecorder.register(Event3.class); 201 r1.stop(); 202 r1.dump(threeEventTypes);; 203 } 204 try (RecordingFile f = new RecordingFile(twoEventTypes)) { 205 List<EventType> types = f.readEventTypes(); 206 assertUniqueEventTypes(types); 207 assertHasEventType(types, "Event1"); 208 assertHasEventType(types, "Event2"); 209 assertMissingEventType(types, "Event3"); 210 } 211 try (RecordingFile f = new RecordingFile(twoEventTypes)) { 212 List<EventType> types = f.readEventTypes(); 213 assertUniqueEventTypes(types); 214 assertHasEventType(types, "Event1"); 215 assertHasEventType(types, "Event2"); 216 assertMissingEventType(types, "Event3"); 217 } 218 219 } 220 221 private static void assertMissingEventType(List<EventType> types,String name) throws Exception { 222 EventType type = findEventType(types, name); 223 if (type != null) { 224 throw new Exception("Found unexpected event type " + name); 225 } 226 } 227 228 private static void assertHasEventType(List<EventType> types,String name) throws Exception { 229 EventType type = findEventType(types, name); 230 if (type == null) { 231 throw new Exception("Missing event type " + name); 232 } 233 } 234 235 private static EventType findEventType(List<EventType> types, String name) { 236 for (EventType t : types) { 237 if (t.getName().equals(name)) { 238 return t; 239 } 240 } 241 return null; 242 } 243 244 private static void assertUniqueEventTypes(List<EventType> types) { 245 HashSet<Long> ids = new HashSet<>(); 246 for (EventType type : types) { 247 ids.add(type.getId()); 248 } 249 Asserts.assertEquals(types.size(), ids.size(), "Event types repeated. " + types); 250 } 251 252 private static Path createBrokenWIthZeros(Path valid) throws Exception { 253 try { 254 Path broken = Files.createTempFile("broken-events", ".jfr"); 255 Files.delete(broken); 256 Files.copy(valid, broken); 257 RandomAccessFile raf = new RandomAccessFile(broken.toFile(), "rw"); 258 raf.seek(HEADER_SIZE); 259 int size = (int) Files.size(broken); 260 byte[] ones = new byte[size - HEADER_SIZE]; 261 for (int i = 0; i < ones.length; i++) { 262 ones[i] = (byte) 0xFF; 263 } 264 raf.write(ones, 0, ones.length); 265 raf.close(); 266 return broken; 267 } catch (IOException ioe) { 268 throw new Exception("Could not produce a broken file " + valid, ioe); 269 } 270 } 271 272 private static Path createBrokenMetadata(Path valid) throws Exception { 273 try { 274 Path broken = Files.createTempFile("broken-metadata", ".jfr"); 275 Files.delete(broken); 276 Files.copy(valid, broken); 277 RandomAccessFile raf = new RandomAccessFile(broken.toFile(), "rw"); 278 raf.seek(METADATA_OFFSET); 279 long metadataOffset = raf.readLong(); 280 raf.seek(metadataOffset); 281 raf.writeLong(Long.MAX_VALUE); 282 raf.writeLong(Long.MAX_VALUE); 283 raf.close(); 284 return broken; 285 } catch (IOException ioe) { 286 throw new Exception("Could not produce a broken EventSet from file " + valid, ioe); 287 } 288 } 289 290 private static void testReadEventTypes(Path valid, Path broken) throws Exception { 291 try (RecordingFile validFile = new RecordingFile(valid)) { 292 List<EventType> types = validFile.readEventTypes(); 293 if (types.size() < EVENT_COUNT) { 294 throw new Exception("Expected at least " + EVENT_COUNT + " event type but got " + types.toString()); 295 } 296 int counter = 0; 297 for (Class<?> testClass : Arrays.asList(TestEvent1.class, TestEvent2.class, TestEvent3.class)) { 298 for (EventType t : types) { 299 if (t.getName().equals(testClass.getName())) { 300 counter++; 301 } 302 } 303 } 304 if (counter != 3) { 305 throw new Exception("Returned incorrect event types"); 306 } 307 } 308 try (RecordingFile brokenFile = new RecordingFile(broken)) { 309 brokenFile.readEventTypes(); 310 throw new Exception("Expected IOException when getting Event Types from broken recording"); 311 } catch (IOException ise) { 312 // OK 313 } 314 } 315 316 private static void testNewRecordingFile(Path valid, Path broken) throws Exception { 317 try (RecordingFile r = new RecordingFile(null)) { 318 throw new Exception("Expected NullPointerException"); 319 } catch (NullPointerException npe) { 320 // OK 321 } 322 try (RecordingFile r = new RecordingFile(Paths.get("hjhjsdfhkjshdfkj.jfr"))) { 323 throw new Exception("Expected FileNotFoundException"); 324 } catch (FileNotFoundException npe) { 325 // OK 326 } 327 Path testFile = Files.createTempFile("test-file", ".jfr"); 328 try (RecordingFile r = new RecordingFile(testFile)) { 329 throw new Exception("Expected IOException if file is empty"); 330 } catch (IOException e) { 331 // OK 332 } 333 FileWriter fr = new FileWriter(testFile.toFile()); 334 fr.write("whatever"); 335 fr.close(); 336 try (RecordingFile r = new RecordingFile(Paths.get("hjhjsdfhkjshdfkj.jfr"))) { 337 throw new Exception("Expected IOException if magic is incorrect"); 338 } catch (IOException e) { 339 // OK 340 } 341 342 try (RecordingFile r = new RecordingFile(valid)) { 343 } 344 } 345 346 private static void testClose(Path valid) throws Exception { 347 RecordingFile f = new RecordingFile(valid); 348 f.close(); 349 350 try { 351 f.readEvent(); 352 throw new Exception("Should not be able to read event from closed recording"); 353 } catch (IOException e) { 354 if (!e.getMessage().equals("Stream Closed")) { 355 throw new Exception("Expected 'Stream Closed' in exception message for a closed stream. Got '" + e.getMessage() +"'."); 356 } 357 // OK 358 } 359 try { 360 f.readEventTypes(); 361 throw new Exception("Should not be able to read event from closed recording"); 362 } catch (IOException e) { 363 if (!e.getMessage().equals("Stream Closed")) { 364 throw new Exception("Expected 'Stream Closed' in exception message for a closed stream. Got '" + e.getMessage() +"'."); 365 } 366 // OK 367 } 368 // close twice 369 f.close(); 370 } 371 372 private static void testIterate(Path valid, Path broken) throws Exception { 373 try (RecordingFile validFile = new RecordingFile(valid)) { 374 for (int i = 0; i < EVENT_COUNT; i++) { 375 if (!validFile.hasMoreEvents()) { 376 throw new Exception("Not all events available"); 377 } 378 RecordedEvent r = validFile.readEvent(); 379 if (r == null) { 380 throw new Exception("Missing event"); 381 } 382 if (!r.getEventType().getName().contains(TEST_CLASS_BASE)) { 383 throw new Exception("Incorrect event in recording file " + r); 384 } 385 } 386 if (validFile.hasMoreEvents()) { 387 throw new Exception("Should not be more than " + EVENT_COUNT + " in recording"); 388 } 389 } 390 try (RecordingFile brokenFile = new RecordingFile(broken)) { 391 brokenFile.readEvent(); 392 throw new Exception("Expected IOException for broken recording"); 393 } catch (IOException ise) { 394 // OK 395 } 396 } 397 398 private static void testReadAllEvents(Path valid, Path broken) throws Exception { 399 try { 400 RecordingFile.readAllEvents(broken); 401 throw new Exception("Expected IOException when reading all events for broken recording"); 402 } catch (IOException ioe) { 403 // OK as expected 404 } 405 } 406 }