| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2011-2012 Ericsson, Ecole Polytechnique de Montreal and others |
| 3 | * |
| 4 | * All rights reserved. This program and the accompanying materials are made |
| 5 | * available under the terms of the Eclipse Public License v1.0 which |
| 6 | * accompanies this distribution, and is available at |
| 7 | * http://www.eclipse.org/legal/epl-v10.html |
| 8 | * |
| 9 | * Contributors: Matthew Khouzam - Initial API and implementation |
| 10 | * Contributors: Simon Marchi - Initial API and implementation |
| 11 | *******************************************************************************/ |
| 12 | |
| 13 | package org.eclipse.linuxtools.ctf.core.trace; |
| 14 | |
| 15 | import java.io.File; |
| 16 | import java.io.IOException; |
| 17 | import java.nio.MappedByteBuffer; |
| 18 | import java.nio.channels.FileChannel; |
| 19 | import java.nio.channels.FileChannel.MapMode; |
| 20 | import java.util.UUID; |
| 21 | |
| 22 | import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer; |
| 23 | import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition; |
| 24 | import org.eclipse.linuxtools.ctf.core.event.types.Definition; |
| 25 | import org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope; |
| 26 | import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition; |
| 27 | import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition; |
| 28 | |
| 29 | /** |
| 30 | * <b><u>StreamInput</u></b> |
| 31 | * <p> |
| 32 | * Represents a trace file that belongs to a certain stream. |
| 33 | */ |
| 34 | public class StreamInput implements IDefinitionScope { |
| 35 | |
| 36 | // ------------------------------------------------------------------------ |
| 37 | // Attributes |
| 38 | // ------------------------------------------------------------------------ |
| 39 | |
| 40 | /** |
| 41 | * The associated Stream |
| 42 | */ |
| 43 | private final Stream stream; |
| 44 | |
| 45 | /** |
| 46 | * FileChannel to the trace file |
| 47 | */ |
| 48 | private final FileChannel fileChannel; |
| 49 | |
| 50 | /** |
| 51 | * Information on the file (used for debugging) |
| 52 | */ |
| 53 | public final File file; |
| 54 | |
| 55 | /** |
| 56 | * The packet index of this input |
| 57 | */ |
| 58 | private final StreamInputPacketIndex index = new StreamInputPacketIndex(); |
| 59 | |
| 60 | private long timestampEnd; |
| 61 | |
| 62 | // ------------------------------------------------------------------------ |
| 63 | // Constructors |
| 64 | // ------------------------------------------------------------------------ |
| 65 | |
| 66 | /** |
| 67 | * Constructs a StreamInput. |
| 68 | * |
| 69 | * @param stream |
| 70 | * The stream to which this StreamInput belongs to. |
| 71 | * @param fileChannel |
| 72 | * The FileChannel to the trace file. |
| 73 | * @param file |
| 74 | * Information about the trace file (for debugging purposes). |
| 75 | */ |
| 76 | public StreamInput(Stream stream, FileChannel fileChannel, File file) { |
| 77 | this.stream = stream; |
| 78 | this.fileChannel = fileChannel; |
| 79 | this.file = file; |
| 80 | } |
| 81 | |
| 82 | // ------------------------------------------------------------------------ |
| 83 | // Getters/Setters/Predicates |
| 84 | // ------------------------------------------------------------------------ |
| 85 | |
| 86 | public Stream getStream() { |
| 87 | return stream; |
| 88 | } |
| 89 | |
| 90 | public StreamInputPacketIndex getIndex() { |
| 91 | return index; |
| 92 | } |
| 93 | |
| 94 | public FileChannel getFileChannel() { |
| 95 | return fileChannel; |
| 96 | } |
| 97 | |
| 98 | public String getFilename() { |
| 99 | return file.getName(); |
| 100 | } |
| 101 | |
| 102 | public long getTimestampEnd() { |
| 103 | return timestampEnd; |
| 104 | } |
| 105 | |
| 106 | public void setTimestampEnd(long timestampEnd) { |
| 107 | this.timestampEnd = timestampEnd; |
| 108 | } |
| 109 | |
| 110 | @Override |
| 111 | public String getPath() { |
| 112 | return ""; //$NON-NLS-1$ |
| 113 | } |
| 114 | |
| 115 | // ------------------------------------------------------------------------ |
| 116 | // Operations |
| 117 | // ------------------------------------------------------------------------ |
| 118 | |
| 119 | @Override |
| 120 | public Definition lookupDefinition(String lookupPath) { |
| 121 | /* TODO: lookup in different dynamic scopes is not supported yet. */ |
| 122 | return null; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Create the index for this trace file. |
| 127 | * |
| 128 | * @throws CTFReaderException |
| 129 | */ |
| 130 | public void createIndex() throws CTFReaderException { |
| 131 | /* |
| 132 | * The size of the file in bytes |
| 133 | */ |
| 134 | long fileSizeBytes = 0; |
| 135 | try { |
| 136 | fileSizeBytes = fileChannel.size(); |
| 137 | } catch (IOException e) { |
| 138 | throw new CTFReaderException(e); |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | * Offset of the current packet in bytes |
| 143 | */ |
| 144 | long packetOffsetBytes = 0; |
| 145 | |
| 146 | /* |
| 147 | * Initial size, it should map at least the packet header + context |
| 148 | * size. |
| 149 | * |
| 150 | * TODO: use a less arbitrary size. |
| 151 | */ |
| 152 | long mapSize = 4096; |
| 153 | |
| 154 | /* |
| 155 | * Definition of trace packet header |
| 156 | */ |
| 157 | StructDefinition tracePacketHeaderDef = null; |
| 158 | |
| 159 | /* |
| 160 | * Definition of trace stream packet context |
| 161 | */ |
| 162 | StructDefinition streamPacketContextDef = null; |
| 163 | |
| 164 | /* |
| 165 | * The BitBuffer to extract data from the StreamInput |
| 166 | */ |
| 167 | BitBuffer bitBuffer = new BitBuffer(); |
| 168 | bitBuffer.order(this.getStream().getTrace().getByteOrder()); |
| 169 | |
| 170 | /* |
| 171 | * Create the definitions we need to read the packet headers + contexts |
| 172 | */ |
| 173 | if (getStream().getTrace().getPacketHeader() != null) { |
| 174 | tracePacketHeaderDef = getStream().getTrace().getPacketHeader().createDefinition( |
| 175 | this, "trace.packet.header"); //$NON-NLS-1$ |
| 176 | } |
| 177 | |
| 178 | if (getStream().getPacketContextDecl() != null) { |
| 179 | streamPacketContextDef = getStream().getPacketContextDecl().createDefinition( |
| 180 | this, "stream.packet.context"); //$NON-NLS-1$ |
| 181 | } |
| 182 | |
| 183 | /* |
| 184 | * Scan through the packets of the file. |
| 185 | */ |
| 186 | while (packetOffsetBytes < fileSizeBytes) { |
| 187 | /* |
| 188 | * If there is less data remaining than what we want to map, reduce |
| 189 | * the map size. |
| 190 | */ |
| 191 | if ((fileSizeBytes - packetOffsetBytes) < mapSize) { |
| 192 | mapSize = fileSizeBytes - packetOffsetBytes; |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | * Map the packet. |
| 197 | */ |
| 198 | MappedByteBuffer bb; |
| 199 | try { |
| 200 | bb = fileChannel.map(MapMode.READ_ONLY, packetOffsetBytes, |
| 201 | mapSize); |
| 202 | } catch (IOException e) { |
| 203 | throw new CTFReaderException(e); |
| 204 | } |
| 205 | bitBuffer.setByteBuffer(bb); |
| 206 | |
| 207 | /* |
| 208 | * Create the index entry |
| 209 | */ |
| 210 | StreamInputPacketIndexEntry packetIndex = new StreamInputPacketIndexEntry( |
| 211 | packetOffsetBytes); |
| 212 | |
| 213 | /* |
| 214 | * Read the trace packet header if it exists. |
| 215 | */ |
| 216 | if (tracePacketHeaderDef != null) { |
| 217 | tracePacketHeaderDef.read(bitBuffer); |
| 218 | |
| 219 | /* |
| 220 | * Check the CTF magic number |
| 221 | */ |
| 222 | IntegerDefinition magicDef = (IntegerDefinition) tracePacketHeaderDef.lookupDefinition("magic"); //$NON-NLS-1$ |
| 223 | if (magicDef != null) { |
| 224 | int magic = (int) magicDef.getValue(); |
| 225 | if (magic != Utils.CTF_MAGIC) { |
| 226 | throw new CTFReaderException( |
| 227 | "CTF magic mismatch " + Integer.toHexString(magic) + " vs " + Integer.toHexString(Utils.CTF_MAGIC)); //$NON-NLS-1$//$NON-NLS-2$ |
| 228 | } |
| 229 | |
| 230 | } |
| 231 | |
| 232 | /* |
| 233 | * Check the trace UUID |
| 234 | */ |
| 235 | ArrayDefinition uuidDef = (ArrayDefinition) tracePacketHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$ |
| 236 | if (uuidDef != null) { |
| 237 | byte[] uuidArray = new byte[16]; |
| 238 | |
| 239 | for (int i = 0; i < 16; i++) { |
| 240 | IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef.getElem(i); |
| 241 | uuidArray[i] = (byte) uuidByteDef.getValue(); |
| 242 | } |
| 243 | |
| 244 | UUID uuid = Utils.makeUUID(uuidArray); |
| 245 | |
| 246 | if (!getStream().getTrace().getUUID().equals(uuid)) { |
| 247 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | * Check that the stream id did not change |
| 253 | */ |
| 254 | IntegerDefinition streamIDDef = (IntegerDefinition) tracePacketHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$ |
| 255 | if (streamIDDef != null) { |
| 256 | long streamID = streamIDDef.getValue(); |
| 257 | |
| 258 | if (streamID != getStream().getId()) { |
| 259 | throw new CTFReaderException( |
| 260 | "Stream ID changing within a StreamInput"); //$NON-NLS-1$ |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | /* |
| 266 | * Read the stream packet context if it exists. |
| 267 | */ |
| 268 | if (streamPacketContextDef != null) { |
| 269 | streamPacketContextDef.read(bitBuffer); |
| 270 | |
| 271 | /* |
| 272 | * Read the content size in bits |
| 273 | */ |
| 274 | IntegerDefinition contentSizeDef = (IntegerDefinition) streamPacketContextDef.lookupDefinition("content_size"); //$NON-NLS-1$ |
| 275 | if (contentSizeDef != null) { |
| 276 | packetIndex.contentSizeBits = (int) contentSizeDef.getValue(); |
| 277 | } else { |
| 278 | packetIndex.contentSizeBits = (int) (fileSizeBytes * 8); |
| 279 | } |
| 280 | |
| 281 | /* |
| 282 | * Read the packet size in bits |
| 283 | */ |
| 284 | IntegerDefinition packetSizeDef = (IntegerDefinition) streamPacketContextDef.lookupDefinition("packet_size"); //$NON-NLS-1$ |
| 285 | if (packetSizeDef != null) { |
| 286 | packetIndex.packetSizeBits = (int) packetSizeDef.getValue(); |
| 287 | } else { |
| 288 | if (packetIndex.contentSizeBits != 0) { |
| 289 | packetIndex.packetSizeBits = packetIndex.contentSizeBits; |
| 290 | } else { |
| 291 | packetIndex.packetSizeBits = (int) (fileSizeBytes * 8); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | /* |
| 296 | * Read the begin timestamp |
| 297 | */ |
| 298 | IntegerDefinition timestampBeginDef = (IntegerDefinition) streamPacketContextDef.lookupDefinition("timestamp_begin"); //$NON-NLS-1$ |
| 299 | if (timestampBeginDef != null) { |
| 300 | packetIndex.timestampBegin = timestampBeginDef.getValue(); |
| 301 | } |
| 302 | |
| 303 | /* |
| 304 | * Read the end timestamp |
| 305 | */ |
| 306 | IntegerDefinition timestampEndDef = (IntegerDefinition) streamPacketContextDef.lookupDefinition("timestamp_end"); //$NON-NLS-1$ |
| 307 | if (timestampEndDef != null) { |
| 308 | setTimestampEnd(packetIndex.timestampEnd = timestampEndDef.getValue()); |
| 309 | } |
| 310 | } else { |
| 311 | /* |
| 312 | * If there is no packet context, infer the content and packet |
| 313 | * size from the file size (assume that there is only one packet |
| 314 | * and no padding) |
| 315 | */ |
| 316 | packetIndex.contentSizeBits = (int) (fileSizeBytes * 8); |
| 317 | packetIndex.packetSizeBits = (int) (fileSizeBytes * 8); |
| 318 | } |
| 319 | |
| 320 | /* Basic validation */ |
| 321 | if (packetIndex.contentSizeBits > packetIndex.packetSizeBits) { |
| 322 | throw new CTFReaderException("Content size > packet size"); //$NON-NLS-1$ |
| 323 | } |
| 324 | |
| 325 | if (packetIndex.packetSizeBits > ((fileSizeBytes - packetIndex.offsetBytes) * 8)) { |
| 326 | throw new CTFReaderException( |
| 327 | "Not enough data remaining in the file for the size of this packet"); //$NON-NLS-1$ |
| 328 | } |
| 329 | |
| 330 | /* |
| 331 | * Offset in the file, in bits |
| 332 | */ |
| 333 | packetIndex.dataOffsetBits = bitBuffer.position(); |
| 334 | |
| 335 | /* |
| 336 | * Add the packet index entry to the index |
| 337 | */ |
| 338 | index.addEntry(packetIndex); |
| 339 | |
| 340 | /* |
| 341 | * Update the counting packet offset |
| 342 | */ |
| 343 | packetOffsetBytes += (packetIndex.packetSizeBits + 7) / 8; |
| 344 | |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | } |