src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java
changeset 50113 caf115bb98ad
child 52850 f527b24990d7
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2016, 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.consumer;
       
    27 
       
    28 import java.io.Closeable;
       
    29 import java.io.EOFException;
       
    30 import java.io.File;
       
    31 import java.io.IOException;
       
    32 import java.nio.file.NoSuchFileException;
       
    33 import java.nio.file.Path;
       
    34 import java.util.ArrayList;
       
    35 import java.util.HashSet;
       
    36 import java.util.List;
       
    37 
       
    38 import jdk.jfr.EventType;
       
    39 import jdk.jfr.internal.MetadataDescriptor;
       
    40 import jdk.jfr.internal.consumer.ChunkHeader;
       
    41 import jdk.jfr.internal.consumer.RecordingInput;
       
    42 
       
    43 /**
       
    44  * A recording file.
       
    45  * <p>
       
    46  * The following example shows how read and print all events in a recording file.
       
    47  *
       
    48  * <pre>
       
    49  * <code>
       
    50  * try (RecordingFile recordingFile = new RecordingFile(Paths.get("recording.jfr"))) {
       
    51  *   while (recordingFile.hasMoreEvents()) {
       
    52  *     RecordedEvent event = recordingFile.readEvent();
       
    53  *     System.out.println(event);
       
    54  *   }
       
    55  * }
       
    56  * </code>
       
    57  * </pre>
       
    58  *
       
    59  * @since 9
       
    60  */
       
    61 public final class RecordingFile implements Closeable {
       
    62 
       
    63     private final File file;
       
    64     private RecordingInput input;
       
    65     private ChunkParser chunkParser;
       
    66     private RecordedEvent nextEvent;
       
    67     private boolean eof;
       
    68 
       
    69     /**
       
    70      * Creates a recording file.
       
    71      *
       
    72      * @param file the path of the file to open, not {@code null}
       
    73      * @throws IOException if it's not a valid recording file, or an I/O error
       
    74      *         occurred
       
    75      * @throws NoSuchFileException if the {@code file} can't be located
       
    76      *
       
    77      * @throws SecurityException if a security manager exists and its
       
    78      *         {@code checkRead} method denies read access to the file.
       
    79      */
       
    80     public RecordingFile(Path file) throws IOException {
       
    81         this.file = file.toFile();
       
    82         this.input = new RecordingInput(this.file);
       
    83         findNext();
       
    84     }
       
    85 
       
    86     /**
       
    87      * Reads the next event in the recording.
       
    88      *
       
    89      * @return the next event, not {@code null}
       
    90      *
       
    91      * @throws EOFException if no more events exist in the recording file
       
    92      * @throws IOException if an I/O error occurs.
       
    93      *
       
    94      * @see #hasMoreEvents()
       
    95      */
       
    96     public RecordedEvent readEvent() throws IOException {
       
    97         if (eof) {
       
    98             ensureOpen();
       
    99             throw new EOFException();
       
   100         }
       
   101         RecordedEvent event = nextEvent;
       
   102         nextEvent = chunkParser.readEvent();
       
   103         if (nextEvent == null) {
       
   104             findNext();
       
   105         }
       
   106         return event;
       
   107     }
       
   108 
       
   109     /**
       
   110      * Returns {@code true} if unread events exist in the recording file,
       
   111      * {@code false} otherwise.
       
   112      *
       
   113      * @return {@code true} if unread events exist in the recording, {@code false}
       
   114      *         otherwise.
       
   115      */
       
   116     public boolean hasMoreEvents() {
       
   117         return !eof;
       
   118     }
       
   119 
       
   120     /**
       
   121      * Returns a list of all event types in this recording.
       
   122      *
       
   123      * @return a list of event types, not {@code null}
       
   124      * @throws IOException if an I/O error occurred while reading from the file
       
   125      *
       
   126      * @see #hasMoreEvents()
       
   127      */
       
   128     public List<EventType> readEventTypes() throws IOException {
       
   129         ensureOpen();
       
   130         List<EventType> types = new ArrayList<>();
       
   131         HashSet<Long> foundIds = new HashSet<>();
       
   132         try (RecordingInput ri = new RecordingInput(file)) {
       
   133             ChunkHeader ch = new ChunkHeader(ri);
       
   134             aggregateTypeForChunk(ch, types, foundIds);
       
   135             while (!ch.isLastChunk()) {
       
   136                 ch = ch.nextHeader();
       
   137                 aggregateTypeForChunk(ch, types, foundIds);
       
   138             }
       
   139         }
       
   140         return types;
       
   141     }
       
   142 
       
   143     private static void aggregateTypeForChunk(ChunkHeader ch, List<EventType> types, HashSet<Long> foundIds) throws IOException {
       
   144         MetadataDescriptor m = ch.readMetadata();
       
   145         for (EventType t : m.getEventTypes()) {
       
   146             if (!foundIds.contains(t.getId())) {
       
   147                 types.add(t);
       
   148                 foundIds.add(t.getId());
       
   149             }
       
   150         }
       
   151     }
       
   152 
       
   153     /**
       
   154      * Closes this recording file and releases any system resources that are
       
   155      * associated with it.
       
   156      *
       
   157      * @throws IOException if an I/O error occurred
       
   158      */
       
   159     public void close() throws IOException {
       
   160         if (input != null) {
       
   161             eof = true;
       
   162             input.close();
       
   163             chunkParser = null;
       
   164             input = null;
       
   165             nextEvent = null;
       
   166         }
       
   167     }
       
   168 
       
   169     /**
       
   170      * Returns a list of all events in a file.
       
   171      * <p>
       
   172      * This method is intended for simple cases where it's convenient to read all
       
   173      * events in a single operation. It isn't intended for reading large files.
       
   174      *
       
   175      * @param path the path to the file, not {@code null}
       
   176      *
       
   177      * @return the events from the file as a {@code List} object; whether the
       
   178      *         {@code List} is modifiable or not is implementation dependent and
       
   179      *         therefore not specified, not {@code null}
       
   180      *
       
   181      * @throws IOException if an I/O error occurred, it's not a Flight Recorder
       
   182      *         file or a version of a JFR file that can't be parsed
       
   183      *
       
   184      * @throws SecurityException if a security manager exists and its
       
   185      *         {@code checkRead} method denies read access to the file.
       
   186      */
       
   187     public static List<RecordedEvent> readAllEvents(Path path) throws IOException {
       
   188         try (RecordingFile r = new RecordingFile(path)) {
       
   189             List<RecordedEvent> list = new ArrayList<>();
       
   190             while (r.hasMoreEvents()) {
       
   191                 list.add(r.readEvent());
       
   192             }
       
   193             return list;
       
   194         }
       
   195     }
       
   196 
       
   197     // either sets next to an event or sets eof to true
       
   198     private void findNext() throws IOException {
       
   199         while (nextEvent == null) {
       
   200             if (chunkParser == null) {
       
   201                 chunkParser = new ChunkParser(input);
       
   202             } else if (!chunkParser.isLastChunk()) {
       
   203                 chunkParser = chunkParser.nextChunkParser();
       
   204             } else {
       
   205                 eof = true;
       
   206                 return;
       
   207             }
       
   208             nextEvent = chunkParser.readEvent();
       
   209         }
       
   210     }
       
   211 
       
   212     private void ensureOpen() throws IOException {
       
   213         if (input == null) {
       
   214             throw new IOException("Stream Closed");
       
   215         }
       
   216     }
       
   217 }