1 /*******************************************************************************
2 * Copyright (c) 2011-2012 Ericsson, Ecole Polytechnique de Montreal and others
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
9 * Contributors: Matthew Khouzam - Initial API and implementation
10 * Contributors: Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.linuxtools
.ctf
.core
.trace
;
16 import java
.io
.FileFilter
;
17 import java
.io
.FileInputStream
;
18 import java
.io
.IOException
;
19 import java
.nio
.ByteOrder
;
20 import java
.nio
.MappedByteBuffer
;
21 import java
.nio
.channels
.FileChannel
;
22 import java
.nio
.channels
.FileChannel
.MapMode
;
23 import java
.util
.Arrays
;
24 import java
.util
.Comparator
;
25 import java
.util
.HashMap
;
28 import java
.util
.UUID
;
30 import org
.eclipse
.linuxtools
.ctf
.core
.CtfCorePlugin
;
31 import org
.eclipse
.linuxtools
.ctf
.core
.event
.CTFClock
;
32 import org
.eclipse
.linuxtools
.ctf
.core
.event
.io
.BitBuffer
;
33 import org
.eclipse
.linuxtools
.ctf
.core
.event
.metadata
.exceptions
.ParseException
;
34 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.ArrayDefinition
;
35 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.Definition
;
36 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IDefinitionScope
;
37 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.IntegerDefinition
;
38 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDeclaration
;
39 import org
.eclipse
.linuxtools
.ctf
.core
.event
.types
.StructDefinition
;
42 * <b><u>CTFTrace</u></b>
44 * Represents a trace on the filesystem. It is responsible of parsing the
45 * metadata, creating declarations data structures, indexing the event packets
46 * (in other words, all the work that can be shared between readers), but the
47 * actual reading of events is left to TraceReader.
49 * @author Matthew Khouzam
50 * @version $Revision: 1.0 $
52 public class CTFTrace
implements IDefinitionScope
{
54 // ------------------------------------------------------------------------
56 // ------------------------------------------------------------------------
61 * @see java.lang.Object#toString()
63 @SuppressWarnings("nls")
65 public String
toString() {
66 /* Only for debugging, shouldn't be externalized */
67 return "CTFTrace [path=" + path
+ ", major=" + major
+ ", minor="
68 + minor
+ ", uuid=" + uuid
+ "]";
72 * The trace directory on the filesystem.
74 private final File path
;
77 * The metadata parsing object.
79 private final Metadata metadata
;
82 * Major CTF version number
87 * Minor CTF version number
99 private ByteOrder byteOrder
;
102 * Packet header structure declaration
104 private StructDeclaration packetHeaderDecl
;
107 * Packet header structure definition
109 * This is only used when opening the trace files, to read the first packet
110 * header and see if they are valid trace files.
112 private StructDefinition packetHeaderDef
;
115 * Collection of streams contained in the trace.
117 private final HashMap
<Long
, Stream
> streams
= new HashMap
<Long
, Stream
>();
120 * Collection of environment variables set by the tracer
122 private final HashMap
<String
, String
> environment
= new HashMap
<String
, String
>();
125 * Collection of all the clocks in a system.
127 private final HashMap
<String
, CTFClock
> clocks
= new HashMap
<String
, CTFClock
>();
130 // ------------------------------------------------------------------------
132 // ------------------------------------------------------------------------
138 * Filesystem path of the trace directory.
139 * @throws IOException
141 public CTFTrace(String path
) throws CTFReaderException
{
142 this(new File(path
));
149 * Filesystem path of the trace directory.
150 * @throws CTFReaderException
152 @SuppressWarnings("unqualified-field-access")
153 public CTFTrace(File path
) throws CTFReaderException
{
156 metadata
= new Metadata(this);
158 if (!this.path
.isDirectory()) {
159 throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
165 // ------------------------------------------------------------------------
166 // Getters/Setters/Predicates
167 // ------------------------------------------------------------------------
170 * Method getStream gets the stream for a given id
173 * Long the id of the stream
174 * @return Stream the stream that we need
176 public Stream
getStream(Long id
) {
177 return streams
.get(id
);
181 * Method nbStreams gets the number of available streams
183 * @return int the number of streams
185 public int nbStreams() {
186 return streams
.size();
190 * Method setMajor sets the major version of the trace (DO NOT USE)
193 * long the major version
195 public void setMajor(long major
) {
200 * Method setMinor sets the minor version of the trace (DO NOT USE)
203 * long the minor version
205 public void setMinor(long minor
) {
210 * Method setUUID sets the UUID of a trace
215 public void setUUID(UUID uuid
) {
220 * Method setByteOrder sets the byte order
223 * ByteOrder of the trace, can be little-endian or big-endian
225 public void setByteOrder(ByteOrder byteOrder
) {
226 this.byteOrder
= byteOrder
;
230 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
232 * @param packetHeader
233 * StructDeclaration the header in structdeclaration form
235 public void setPacketHeader(StructDeclaration packetHeader
) {
236 this.packetHeaderDecl
= packetHeader
;
240 * Method majortIsSet is the major version number set?
242 * @return boolean is the major set?
244 public boolean majortIsSet() {
245 return major
!= null;
249 * Method minorIsSet. is the minor version number set?
251 * @return boolean is the minor set?
253 public boolean minorIsSet() {
254 return minor
!= null;
258 * Method UUIDIsSet is the UUID set?
260 * @return boolean is the UUID set?
262 public boolean UUIDIsSet() {
267 * Method byteOrderIsSet is the byteorder set?
269 * @return boolean is the byteorder set?
271 public boolean byteOrderIsSet() {
272 return byteOrder
!= null;
276 * Method packetHeaderIsSet is the packet header set?
278 * @return boolean is the packet header set?
280 public boolean packetHeaderIsSet() {
281 return packetHeaderDecl
!= null;
285 * Method getUUID gets the trace UUID
287 * @return UUID gets the trace UUID
289 public UUID
getUUID() {
294 * Method getMajor gets the trace major version
296 * @return long gets the trace major version
298 public long getMajor() {
303 * Method getMinor gets the trace minor version
305 * @return long gets the trace minor version
307 public long getMinor() {
312 * Method getByteOrder gets the trace byte order
314 * @return ByteOrder gets the trace byte order
316 public ByteOrder
getByteOrder() {
321 * Method getPacketHeader gets the trace packet header
323 * @return StructDeclaration gets the trace packet header
325 public StructDeclaration
getPacketHeader() {
326 return packetHeaderDecl
;
330 * Method getTraceDirectory gets the trace directory
332 * @return File the path in "File" format.
334 public File
getTraceDirectory() {
339 * Method getStreams get all the streams in a map format.
341 * @return Map<Long,Stream> a map of all the streams.
343 public Map
<Long
, Stream
> getStreams() {
348 * Method getPath gets the path of the trace directory
350 * @return String the path of the trace directory, in string format.
351 * @see java.io.File#getPath()
354 public String
getPath() {
355 return path
.getPath();
358 // ------------------------------------------------------------------------
360 // ------------------------------------------------------------------------
363 * Opens the trace and creates the index.
365 * @throws CTFReaderException
367 private void open() throws CTFReaderException
{
368 /* Open and parse the metadata file */
371 if (CtfCorePlugin
.getDefault() != null) {
372 CtfCorePlugin
.getDefault().log(metadata
.toString());
374 /* Open all the trace files */
377 /* Create their index */
378 createStreamInputIndexes();
382 * Parses the metadata
384 * @throws CTFReaderException
386 private void openTraceMetadata() throws CTFReaderException
{
391 * Creates the definitions needed by the Trace class to open the trace
394 private void createDefinitions() {
395 if (packetHeaderDecl
!= null) {
396 packetHeaderDef
= packetHeaderDecl
.createDefinition(this,
397 "packet.header"); //$NON-NLS-1$
402 * Creates the indexes of all the trace files.
404 * @throws CTFReaderException
406 private void createStreamInputIndexes() throws CTFReaderException
{
407 for (Map
.Entry
<Long
, Stream
> stream
: streams
.entrySet()) {
408 Set
<StreamInput
> inputs
= stream
.getValue().getStreamInputs();
409 for (StreamInput s
: inputs
) {
416 * Tries to open every file in the trace directory (except metadata).
418 * @throws CTFReaderException
420 private void openStreamInputs() throws CTFReaderException
{
421 /* Create the definitions needed to read things from the files */
424 /* List files not called metadata and not hidden. */
425 File
[] files
= path
.listFiles(new FileFilter() {
428 public boolean accept(File pathname
) {
430 if (pathname
.isDirectory()) {
434 if (pathname
.isHidden()) {
438 if (pathname
.getName().equals("metadata")) { //$NON-NLS-1$
445 Arrays
.sort(files
, new Comparator
<File
>() {
448 public int compare(File o1
, File o2
) {
450 return o1
.getName().compareTo(o2
.getName());
455 /* Try to open each file */
456 for (File s
: files
) {
462 * Tries to open the given file, reads the first packet header of the file
463 * and check its validity.
466 * A trace file in the trace directory.
467 * @throws CTFReaderException
469 private void openStreamInput(File streamFile
) throws CTFReaderException
{
470 FileChannel streamFileChannel
;
471 MappedByteBuffer byteBuffer
;
472 BitBuffer streamBitBuffer
;
474 if (!streamFile
.canRead()) {
475 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
476 + streamFile
.getPath());
480 /* Open the file and get the FileChannel */
481 streamFileChannel
= new FileInputStream(streamFile
).getChannel();
483 /* Map one memory page of 4 kiB */
484 byteBuffer
= streamFileChannel
.map(MapMode
.READ_ONLY
, 0, 4096);
485 } catch (IOException e
) {
486 /* Shouldn't happen at this stage if every other check passed */
487 throw new CTFReaderException();
490 /* Create a BitBuffer with this mapping and the trace byte order */
491 streamBitBuffer
= new BitBuffer(byteBuffer
, this.getByteOrder());
493 if (packetHeaderDef
!= null) {
494 /* Read the packet header */
495 packetHeaderDef
.read(streamBitBuffer
);
497 /* Check the magic number */
498 IntegerDefinition magicDef
= (IntegerDefinition
) packetHeaderDef
.lookupDefinition("magic"); //$NON-NLS-1$
499 int magic
= (int) magicDef
.getValue();
500 if (magic
!= Utils
.CTF_MAGIC
) {
501 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
505 ArrayDefinition uuidDef
= (ArrayDefinition
) packetHeaderDef
.lookupDefinition("uuid"); //$NON-NLS-1$
506 assert ((uuidDef
!= null) && (uuidDef
.getDeclaration().getLength() == Utils
.UUID_LEN
));
507 if (uuidDef
!= null) {
508 byte[] uuidArray
= new byte[Utils
.UUID_LEN
];
510 for (int i
= 0; i
< Utils
.UUID_LEN
; i
++) {
511 IntegerDefinition uuidByteDef
= (IntegerDefinition
) uuidDef
.getElem(i
);
512 uuidArray
[i
] = (byte) uuidByteDef
.getValue();
515 UUID otheruuid
= Utils
.makeUUID(uuidArray
);
517 if (!this.uuid
.equals(otheruuid
)) {
518 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
523 // TODO: it hasn't been checked that the stream_id field exists and
526 IntegerDefinition streamIDDef
= (IntegerDefinition
) packetHeaderDef
.lookupDefinition("stream_id"); //$NON-NLS-1$
527 assert (streamIDDef
!= null);
529 long streamID
= streamIDDef
.getValue();
531 /* Get the stream to which this trace file belongs to */
532 Stream stream
= streams
.get(streamID
);
534 /* Create the stream input */
535 StreamInput streamInput
= new StreamInput(stream
,
536 streamFileChannel
, streamFile
);
538 /* Add a reference to the streamInput in the stream */
539 stream
.addInput(streamInput
);
541 /* No packet header, we suppose there is only one stream */
542 Stream stream
= streams
.get(null);
544 /* Create the stream input */
545 StreamInput streamInput
= new StreamInput(stream
,
546 streamFileChannel
, streamFile
);
548 /* Add a reference to the streamInput in the stream */
549 stream
.addInput(streamInput
);
554 * Looks up a definition from packet
559 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
562 public Definition
lookupDefinition(String lookupPath
) {
563 if (lookupPath
.equals("trace.packet.header")) { //$NON-NLS-1$
564 return packetHeaderDef
;
570 * Adds a new stream to the trace.
575 * @throws ParseException
577 public void addStream(Stream stream
) throws ParseException
{
580 * If there is already a stream without id (the null key), it must be
583 if (streams
.get(null) != null) {
584 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
588 * If the stream we try to add has the null key, it must be the only
589 * one. Thus, if the streams container is not empty, it is not valid.
591 if ((stream
.getId() == null) && (streams
.size() != 0)) {
592 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
595 /* If a stream with the same ID already exists, it is not valid. */
596 if (streams
.get(stream
.getId()) != null) {
597 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
600 /* It should be ok now. */
601 streams
.put(stream
.getId(), stream
);
604 public HashMap
<String
, String
> getEnvironment() {
608 public String
lookupEnvironment( String key
)
610 return environment
.get(key
);
613 public void addEnvironmentVar( String varName
, String varValue
)
615 environment
.put(varName
, varValue
);
618 public void addClock(String nameValue
, CTFClock ctfClock
) {
619 clocks
.put(nameValue
, ctfClock
);
622 public CTFClock
getClock(String name
){
623 return clocks
.get(name
);
626 public CTFClock
getClock(){
627 if( clocks
.size() == 1 )
629 String key
= (String
) clocks
.keySet().toArray()[0];
630 return clocks
.get(key
);