1 /*
   2  * Copyright (c) 2018, 2019, 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.event.io;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.net.Socket;
  31 
  32 import jdk.jfr.consumer.RecordedEvent;
  33 import jdk.jfr.consumer.RecordedThread;
  34 import jdk.testlibrary.jfr.EventField;
  35 import jdk.testlibrary.jfr.EventNames;
  36 import jdk.testlibrary.jfr.Events;
  37 
  38 // Contains data from a JFR IO event.
  39 public class IOEvent {
  40     public final String thread;
  41     public final EventType eventType;
  42     public final long size;
  43     public final String address;  // Name of file or socket address.
  44     public final boolean endOfStream;
  45 
  46     // Constructor is private. Use IOEvent.create...
  47     private IOEvent(String thread, EventType eventType, long size, String address, boolean endOfStream) {
  48         this.thread = thread;
  49         this.eventType = eventType;
  50         this.size = size;
  51         this.address = address;
  52         this.endOfStream = endOfStream;
  53     }
  54 
  55     @Override
  56     public String toString() {
  57         String key = String.format("thread: %s, type: %s, size: %d, address: %s endOfStream: %s", thread, eventType, size, address, endOfStream);
  58         return key;
  59     }
  60 
  61     @Override
  62     public boolean equals(Object object) {
  63         if (object == null || !(object instanceof IOEvent)) {
  64             return false;
  65         }
  66         return toString().equals(object.toString());
  67     }
  68 
  69     @Override
  70     public int hashCode() {
  71         return toString().hashCode();
  72     }
  73 
  74     public static final String EVENT_UNKNOWN = "unknown-event??";
  75     public static final String EVENT_FILE_FORCE = EventNames.FileForce;
  76     public static final String EVENT_FILE_READ = EventNames.FileRead;
  77     public static final String EVENT_FILE_WRITE = EventNames.FileWrite;
  78     public static final String EVENT_SOCKET_READ = EventNames.SocketRead;
  79     public static final String EVENT_SOCKET_WRITE = EventNames.SocketWrite;
  80 
  81     public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketRead, SocketWrite }
  82 
  83     private static final String[] eventPaths = {
  84         EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE
  85     };
  86 
  87     public static boolean isWriteEvent(EventType eventType) {
  88         return (eventType == EventType.SocketWrite || eventType == EventType.FileWrite);
  89     }
  90 
  91     public static boolean isReadEvent(EventType eventType) {
  92         return (eventType == EventType.SocketRead || eventType == EventType.FileRead);
  93     }
  94 
  95     public static boolean isFileEvent(EventType eventType) {
  96         return (eventType == EventType.FileForce || eventType == EventType.FileWrite || eventType == EventType.FileRead);
  97     }
  98 
  99     public static IOEvent createSocketWriteEvent(long size, Socket s) {
 100         if (size < 0) {
 101             size = 0;
 102         }
 103         return new IOEvent(Thread.currentThread().getName(), EventType.SocketWrite, size, getAddress(s), false);
 104     }
 105 
 106     public static IOEvent createSocketReadEvent(long size, Socket s) {
 107         boolean endOfStream = false;
 108         if (size < 0) {
 109             size = 0;
 110             endOfStream = true;
 111         }
 112         return new IOEvent(Thread.currentThread().getName(), EventType.SocketRead, size, getAddress(s), endOfStream);
 113     }
 114 
 115     public static IOEvent createFileForceEvent(File file) {
 116         String address = null;
 117         try {
 118             address = file.getCanonicalPath();
 119         } catch(IOException ex) {
 120             throw new RuntimeException();
 121         }
 122         return new IOEvent(Thread.currentThread().getName(), EventType.FileForce, 0, address, false);
 123     }
 124 
 125     public static IOEvent createFileReadEvent(long size, File file) {
 126         boolean endOfStream = false;
 127         if (size < 0) {
 128             endOfStream = true;
 129             size = 0;
 130         }
 131         String address = null;
 132         try {
 133             address = file.getCanonicalPath();
 134         } catch(IOException ex) {
 135                 throw new RuntimeException();
 136         }
 137         return new IOEvent(Thread.currentThread().getName(), EventType.FileRead, size, address, endOfStream);
 138     }
 139 
 140     public static IOEvent createFileWriteEvent(long size, File file) {
 141         if (size < 0) {
 142             size = 0;
 143         }
 144         String address = null;
 145         try {
 146             address = file.getCanonicalPath();
 147         } catch(IOException ex) {
 148                 throw new RuntimeException();
 149         }
 150         return new IOEvent(Thread.currentThread().getName(), EventType.FileWrite, size, address, false);
 151     }
 152 
 153     public static EventType getEventType(RecordedEvent event) {
 154         final String path = event.getEventType().getName();
 155         for (int i = 0; i < eventPaths.length; ++i) {
 156             if (path.endsWith(eventPaths[i])) {
 157                 return EventType.values()[i];
 158             }
 159         }
 160         return EventType.UnknownEvent;
 161     }
 162 
 163     public static IOEvent createTestEvent(RecordedEvent event) {
 164         EventType eventType = getEventType(event);
 165         if (eventType == EventType.UnknownEvent) {
 166             return null;
 167         }
 168         EventField ev = Events.assertField(event, "eventThread");
 169         RecordedThread t = ev.getValue();
 170         String thread = t.getJavaName();
 171         long size = 0L;
 172         if (isWriteEvent(eventType)) {
 173             size = Events.assertField(event, "bytesWritten").getValue();
 174         } else if (isReadEvent(eventType)) {
 175             size = Events.assertField(event, "bytesRead").getValue();
 176         }
 177         String address = getEventAddress(event);
 178         boolean endOfStream = false;
 179         if (event.hasField("endOfStream")) {
 180             endOfStream = event.getValue("endOfStream");
 181         }
 182         if (event.hasField("endOfFile")) {
 183             endOfStream = event.getValue("endOfFile");
 184         }
 185         return new IOEvent(thread, eventType, size, address, endOfStream);
 186     }
 187 
 188     public static String getEventAddress(RecordedEvent event) {
 189         if (isFileEvent(getEventType(event))) {
 190             String address = Events.assertField(event, "path").getValue();
 191             // must ensure canonical format
 192             String canonical_path = null;
 193             try {
 194                 canonical_path = new File(address).getCanonicalPath();
 195             } catch (IOException ex) {
 196                 throw new RuntimeException();
 197             }
 198             return canonical_path;
 199         } else {
 200             return String.format("%s/%s:%d",
 201                                 event.getValue("host"),
 202                                 event.getValue("address"),
 203                                 event.getValue("port"));
 204         }
 205     }
 206 
 207     private static String getAddress(Socket s) {
 208         return s.getInetAddress().toString() + ":" + s.getPort();
 209     }
 210 }