ctf: Handle absence of stream and event IDs
[deliverable/tracecompass.git] / org.eclipse.linuxtools.ctf.core / src / org / eclipse / linuxtools / ctf / core / trace / CTFTrace.java
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: Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.linuxtools.ctf.core.trace;
14
15 import java.io.File;
16 import java.io.FileFilter;
17 import java.io.FileInputStream;
18 import java.io.IOException;
19 import java.io.Serializable;
20 import java.nio.ByteOrder;
21 import java.nio.MappedByteBuffer;
22 import java.nio.channels.FileChannel;
23 import java.nio.channels.FileChannel.MapMode;
24 import java.util.Arrays;
25 import java.util.Comparator;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.Set;
33 import java.util.UUID;
34
35 import org.eclipse.linuxtools.ctf.core.event.CTFClock;
36 import org.eclipse.linuxtools.ctf.core.event.EventDeclaration;
37 import org.eclipse.linuxtools.ctf.core.event.EventDefinition;
38 import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition;
39 import org.eclipse.linuxtools.ctf.core.event.types.Definition;
40 import org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope;
41 import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition;
42 import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration;
43 import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition;
44 import org.eclipse.linuxtools.internal.ctf.core.event.io.BitBuffer;
45 import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.ParseException;
46 import org.eclipse.linuxtools.internal.ctf.core.trace.Stream;
47 import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInput;
48 import org.eclipse.linuxtools.internal.ctf.core.trace.StreamInputPacketIndex;
49
50 /**
51 * A CTF trace on the file system.
52 *
53 * Represents a trace on the filesystem. It is responsible of parsing the
54 * metadata, creating declarations data structures, indexing the event packets
55 * (in other words, all the work that can be shared between readers), but the
56 * actual reading of events is left to TraceReader.
57 *
58 * @author Matthew Khouzam
59 * @version $Revision: 1.0 $
60 */
61 public class CTFTrace implements IDefinitionScope {
62
63 // ------------------------------------------------------------------------
64 // Attributes
65 // ------------------------------------------------------------------------
66
67 private static final String OFFSET = "offset"; //$NON-NLS-1$
68
69
70
71 /*
72 * (non-Javadoc)
73 *
74 * @see java.lang.Object#toString()
75 */
76 @SuppressWarnings("nls")
77 @Override
78 public String toString() {
79 /* Only for debugging, shouldn't be externalized */
80 return "CTFTrace [path=" + path + ", major=" + major + ", minor="
81 + minor + ", uuid=" + uuid + "]";
82 }
83
84 /**
85 * The trace directory on the filesystem.
86 */
87 private final File path;
88
89 /**
90 * The metadata parsing object.
91 */
92 private final Metadata metadata;
93
94 /**
95 * Major CTF version number
96 */
97 private Long major;
98
99 /**
100 * Minor CTF version number
101 */
102 private Long minor;
103
104 /**
105 * Trace UUID
106 */
107 private UUID uuid;
108
109 /**
110 * Trace byte order
111 */
112 private ByteOrder byteOrder;
113
114 /**
115 * Packet header structure declaration
116 */
117 private StructDeclaration packetHeaderDecl = null;
118
119 /**
120 * Packet header structure definition
121 *
122 * This is only used when opening the trace files, to read the first packet
123 * header and see if they are valid trace files.
124 */
125 private StructDefinition packetHeaderDef;
126
127 /**
128 * Collection of streams contained in the trace.
129 */
130 private final HashMap<Long, Stream> streams;
131
132 /**
133 * Collection of environment variables set by the tracer
134 */
135 private final HashMap<String, String> environment;
136
137 /**
138 * Collection of all the clocks in a system.
139 */
140 private final HashMap<String, CTFClock> clocks;
141
142 /** FileChannels to the streams */
143 private final List<FileChannel> streamFileChannels;
144
145 /** Handlers for the metadata files */
146 private final static FileFilter metadataFileFilter = new MetadataFileFilter();
147 private final static Comparator<File> metadataComparator = new MetadataComparator(); // $codepro.audit.disable
148 // fieldJavadoc
149
150 /** map of all the event types */
151 private final HashMap<Long,HashMap<Long, EventDeclaration>> eventDecs;
152 /** map of all the event types */
153 private final HashMap<StreamInput,HashMap<Long, EventDefinition>> eventDefs;
154 /** map of all the indexes */
155 private final HashMap<StreamInput, StreamInputPacketIndex> indexes;
156
157
158
159 // ------------------------------------------------------------------------
160 // Constructors
161 // ------------------------------------------------------------------------
162
163 /**
164 * Trace constructor.
165 *
166 * @param path
167 * Filesystem path of the trace directory
168 * @throws CTFReaderException
169 * If no CTF trace was found at the path
170 */
171 public CTFTrace(String path) throws CTFReaderException {
172 this(new File(path));
173
174 }
175
176 /**
177 * Trace constructor.
178 *
179 * @param path
180 * Filesystem path of the trace directory.
181 * @throws CTFReaderException
182 * If no CTF trace was found at the path
183 */
184 public CTFTrace(File path) throws CTFReaderException {
185 this.path = path;
186 this.metadata = new Metadata(this);
187
188 /* Set up the internal containers for this trace */
189 streams = new HashMap<Long, Stream>();
190 environment = new HashMap<String, String>();
191 clocks = new HashMap<String, CTFClock>();
192 streamFileChannels = new LinkedList<FileChannel>();
193 eventDecs = new HashMap<Long, HashMap<Long, EventDeclaration>>();
194 eventDefs = new HashMap<StreamInput, HashMap<Long, EventDefinition>>();
195
196 if (!this.path.isDirectory()) {
197 throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$
198 }
199
200 /* Open and parse the metadata file */
201 metadata.parse();
202
203 /* Open all the trace files */
204 /* Create the definitions needed to read things from the files */
205 if (packetHeaderDecl != null) {
206 packetHeaderDef = packetHeaderDecl.createDefinition(this,
207 "packet.header"); //$NON-NLS-1$
208 }
209
210 /* List files not called metadata and not hidden. */
211 File[] files = path.listFiles(metadataFileFilter);
212 Arrays.sort(files, metadataComparator);
213 indexes = new HashMap<StreamInput, StreamInputPacketIndex>();
214 /* Try to open each file */
215 for (File streamFile : files) {
216 openStreamInput(streamFile);
217 }
218
219 /* Create their index */
220 for (Map.Entry<Long, Stream> stream : streams.entrySet()) {
221 Set<StreamInput> inputs = stream.getValue().getStreamInputs();
222 for (StreamInput s : inputs) {
223 /*
224 * Copy the events
225 */
226 Iterator<Entry<Long, EventDeclaration>> it = s.getStream()
227 .getEvents().entrySet().iterator();
228 while (it.hasNext()) {
229 Map.Entry<Long, EventDeclaration> pairs = it.next();
230 Long eventNum = pairs.getKey();
231 EventDeclaration eventDec = pairs.getValue();
232 getEvents(s.getStream().getId()).put(eventNum, eventDec);
233 }
234
235 /*
236 * index the trace
237 */
238 s.setupIndex();
239 }
240 }
241 }
242
243 @Override
244 protected void finalize() throws Throwable {
245 /* If this trace gets closed, release the descriptors to the streams */
246 for (FileChannel fc : streamFileChannels) {
247 if (fc != null) {
248 try {
249 fc.close();
250 } catch (IOException e) {
251 // do nothing it's ok, we tried to close it.
252 }
253 }
254 }
255 super.finalize();
256
257 }
258
259 // ------------------------------------------------------------------------
260 // Getters/Setters/Predicates
261 // ------------------------------------------------------------------------
262
263 /**
264 * Gets an event declaration hash map for a given streamID
265 *
266 * @param streamId
267 * The ID of the stream from which to read
268 * @return The Hash map with the event declarations
269 */
270 public HashMap<Long, EventDeclaration> getEvents(Long streamId) {
271 return eventDecs.get(streamId);
272 }
273
274 /**
275 * Gets an index for a given StreamInput
276 * @param id the StreamInput
277 * @return The index
278 */
279 public StreamInputPacketIndex getIndex(StreamInput id){
280 if(! indexes.containsKey(id)){
281 indexes.put(id, new StreamInputPacketIndex());
282 }
283 return indexes.get(id);
284 }
285
286 /**
287 * Gets an event Declaration hashmap for a given StreamInput
288 * @param id the StreamInput
289 * @return the hashmap with the event definitions
290 */
291 public HashMap<Long, EventDefinition> getEventDefs(StreamInput id) {
292 if(! eventDefs.containsKey(id)){
293 eventDefs.put(id, new HashMap<Long, EventDefinition>());
294 }
295 return eventDefs.get(id);
296 }
297
298 /**
299 * Get an event by it's ID
300 *
301 * @param streamId
302 * The ID of the stream from which to read
303 * @param id
304 * the ID of the event
305 * @return the event declaration
306 */
307 public EventDeclaration getEventType(long streamId, long id) {
308 return getEvents(streamId).get(id);
309 }
310
311 /**
312 * Method getStream gets the stream for a given id
313 *
314 * @param id
315 * Long the id of the stream
316 * @return Stream the stream that we need
317 */
318 public Stream getStream(Long id) {
319 return streams.get(id);
320 }
321
322 /**
323 * Method nbStreams gets the number of available streams
324 *
325 * @return int the number of streams
326 */
327 public int nbStreams() {
328 return streams.size();
329 }
330
331 /**
332 * Method setMajor sets the major version of the trace (DO NOT USE)
333 *
334 * @param major
335 * long the major version
336 */
337 public void setMajor(long major) {
338 this.major = major;
339 }
340
341 /**
342 * Method setMinor sets the minor version of the trace (DO NOT USE)
343 *
344 * @param minor
345 * long the minor version
346 */
347 public void setMinor(long minor) {
348 this.minor = minor;
349 }
350
351 /**
352 * Method setUUID sets the UUID of a trace
353 *
354 * @param uuid
355 * UUID
356 */
357 public void setUUID(UUID uuid) {
358 this.uuid = uuid;
359 }
360
361 /**
362 * Method setByteOrder sets the byte order
363 *
364 * @param byteOrder
365 * ByteOrder of the trace, can be little-endian or big-endian
366 */
367 public void setByteOrder(ByteOrder byteOrder) {
368 this.byteOrder = byteOrder;
369 }
370
371 /**
372 * Method setPacketHeader sets the packet header of a trace (DO NOT USE)
373 *
374 * @param packetHeader
375 * StructDeclaration the header in structdeclaration form
376 */
377 public void setPacketHeader(StructDeclaration packetHeader) {
378 this.packetHeaderDecl = packetHeader;
379 }
380
381 /**
382 * Method majortIsSet is the major version number set?
383 *
384 * @return boolean is the major set?
385 */
386 public boolean majortIsSet() {
387 return major != null;
388 }
389
390 /**
391 * Method minorIsSet. is the minor version number set?
392 *
393 * @return boolean is the minor set?
394 */
395 public boolean minorIsSet() {
396 return minor != null;
397 }
398
399 /**
400 * Method UUIDIsSet is the UUID set?
401 *
402 * @return boolean is the UUID set?
403 */
404 public boolean UUIDIsSet() {
405 return uuid != null;
406 }
407
408 /**
409 * Method byteOrderIsSet is the byteorder set?
410 *
411 * @return boolean is the byteorder set?
412 */
413 public boolean byteOrderIsSet() {
414 return byteOrder != null;
415 }
416
417 /**
418 * Method packetHeaderIsSet is the packet header set?
419 *
420 * @return boolean is the packet header set?
421 */
422 public boolean packetHeaderIsSet() {
423 return packetHeaderDecl != null;
424 }
425
426 /**
427 * Method getUUID gets the trace UUID
428 *
429 * @return UUID gets the trace UUID
430 */
431 public UUID getUUID() {
432 return uuid;
433 }
434
435 /**
436 * Method getMajor gets the trace major version
437 *
438 * @return long gets the trace major version
439 */
440 public long getMajor() {
441 return major;
442 }
443
444 /**
445 * Method getMinor gets the trace minor version
446 *
447 * @return long gets the trace minor version
448 */
449 public long getMinor() {
450 return minor;
451 }
452
453 /**
454 * Method getByteOrder gets the trace byte order
455 *
456 * @return ByteOrder gets the trace byte order
457 */
458 public ByteOrder getByteOrder() {
459 return byteOrder;
460 }
461
462 /**
463 * Method getPacketHeader gets the trace packet header
464 *
465 * @return StructDeclaration gets the trace packet header
466 */
467 public StructDeclaration getPacketHeader() {
468 return packetHeaderDecl;
469 }
470
471 /**
472 * Method getTraceDirectory gets the trace directory
473 *
474 * @return File the path in "File" format.
475 */
476 public File getTraceDirectory() {
477 return path;
478 }
479
480 /**
481 * Method getStreams get all the streams in a map format.
482 *
483 * @return Map<Long,Stream> a map of all the streams.
484 */
485 public Map<Long, Stream> getStreams() {
486 return streams;
487 }
488
489 /**
490 * Method getPath gets the path of the trace directory
491 *
492 * @return String the path of the trace directory, in string format.
493 * @see java.io.File#getPath()
494 */
495 @Override
496 public String getPath() {
497 return path.getPath();
498 }
499
500 // ------------------------------------------------------------------------
501 // Operations
502 // ------------------------------------------------------------------------
503
504 /**
505 * Tries to open the given file, reads the first packet header of the file
506 * and check its validity.
507 *
508 * @param streamFile
509 * A trace file in the trace directory.
510 * @param index
511 * Which index in the class' streamFileChannel array this file
512 * must use
513 * @throws CTFReaderException
514 */
515 private void openStreamInput(File streamFile) throws CTFReaderException {
516 MappedByteBuffer byteBuffer;
517 BitBuffer streamBitBuffer;
518 Stream stream;
519 FileChannel fc;
520
521 if (!streamFile.canRead()) {
522 throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$
523 + streamFile.getPath());
524 }
525
526 try {
527 /* Open the file and get the FileChannel */
528 fc = new FileInputStream(streamFile).getChannel();
529 streamFileChannels.add(fc);
530
531 /* Map one memory page of 4 kiB */
532 byteBuffer = fc.map(MapMode.READ_ONLY, 0, 4096);
533 } catch (IOException e) {
534 /* Shouldn't happen at this stage if every other check passed */
535 throw new CTFReaderException();
536 }
537
538 /* Create a BitBuffer with this mapping and the trace byte order */
539 streamBitBuffer = new BitBuffer(byteBuffer, this.getByteOrder());
540
541 if (packetHeaderDef != null) {
542 /* Read the packet header */
543 packetHeaderDef.read(streamBitBuffer);
544
545 /* Check the magic number */
546 IntegerDefinition magicDef = (IntegerDefinition) packetHeaderDef
547 .lookupDefinition("magic"); //$NON-NLS-1$
548 int magic = (int) magicDef.getValue();
549 if (magic != Utils.CTF_MAGIC) {
550 throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$
551 }
552
553 /* Check UUID */
554 ArrayDefinition uuidDef = (ArrayDefinition) packetHeaderDef
555 .lookupDefinition("uuid"); //$NON-NLS-1$
556 if (uuidDef != null) {
557 byte[] uuidArray = new byte[Utils.UUID_LEN];
558
559 for (int i = 0; i < Utils.UUID_LEN; i++) {
560 IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef
561 .getElem(i);
562 uuidArray[i] = (byte) uuidByteDef.getValue();
563 }
564
565 UUID otheruuid = Utils.makeUUID(uuidArray);
566
567 if (!this.uuid.equals(otheruuid)) {
568 throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$
569 }
570 }
571
572 /* Read the stream ID */
573 Definition streamIDDef = packetHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$
574
575 if (streamIDDef instanceof IntegerDefinition) { //this doubles as a null check
576 long streamID = ((IntegerDefinition) streamIDDef).getValue();
577 stream = streams.get(streamID);
578 } else {
579 /* No stream_id in the packet header */
580 stream = streams.get(null);
581 }
582
583 } else {
584 /* No packet header, we suppose there is only one stream */
585 stream = streams.get(null);
586 }
587
588 /* Create the stream input */
589 StreamInput streamInput = new StreamInput(stream, fc, streamFile);
590
591 /* Add a reference to the streamInput in the stream */
592 stream.addInput(streamInput);
593 }
594
595 /**
596 * Looks up a definition from packet
597 *
598 * @param lookupPath
599 * String
600 * @return Definition
601 * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String)
602 */
603 @Override
604 public Definition lookupDefinition(String lookupPath) {
605 if (lookupPath.equals("trace.packet.header")) { //$NON-NLS-1$
606 return packetHeaderDef;
607 }
608 return null;
609 }
610
611 /**
612 * Adds a new stream to the trace.
613 *
614 * @param stream
615 * A stream object.
616 * @throws ParseException
617 * If there was some problem reading the metadata
618 */
619 public void addStream(Stream stream) throws ParseException {
620
621 /*
622 * If there is already a stream without id (the null key), it must be
623 * the only one
624 */
625 if (streams.get(null) != null) {
626 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
627 }
628
629 /*
630 * If the stream we try to add has the null key, it must be the onl * one. Thus, if the streams container is not empty, it is not valid.
631 */
632 if ((stream.getId() == null) && (streams.size() != 0)) {
633 throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$
634 }
635
636 /* If a stream with the same ID already exists, it is not valid. */
637 if (streams.get(stream.getId()) != null) {
638 throw new ParseException("Stream id already exists"); //$NON-NLS-1$
639 }
640
641 /* It should be ok now. */
642 streams.put(stream.getId(), stream);
643 eventDecs.put(stream.getId(), new HashMap<Long,EventDeclaration>());
644 }
645
646 /**
647 * gets the Environment variables from the trace metadata (See CTF spec)
648 * @return the environment variables in a hashmap form (key value)
649 */
650 public HashMap<String, String> getEnvironment() {
651 return environment;
652 }
653
654 /**
655 * Look up a specific environment variable
656 * @param key the key to look for
657 * @return the value of the variable, can be null.
658 */
659 public String lookupEnvironment(String key) {
660 return environment.get(key);
661 }
662
663 /**
664 * Add a variable to the environment variables
665 * @param varName the name of the variable
666 * @param varValue the value of the variable
667 */
668 public void addEnvironmentVar(String varName, String varValue) {
669 environment.put(varName, varValue);
670 }
671
672 /**
673 * Add a clock to the clock list
674 * @param nameValue the name of the clock (full name with scope)
675 * @param ctfClock the clock
676 */
677 public void addClock(String nameValue, CTFClock ctfClock) {
678 clocks.put(nameValue, ctfClock);
679 }
680
681 /**
682 * gets the clock with a specific name
683 * @param name the name of the clock.
684 * @return the clock
685 */
686 public CTFClock getClock(String name) {
687 return clocks.get(name);
688 }
689
690 private CTFClock singleClock;
691 private long singleOffset;
692
693 /**
694 * gets the clock if there is only one. (this is 100% of the use cases as of June 2012)
695 * @return the clock
696 */
697 public final CTFClock getClock() {
698 if (clocks.size() == 1) {
699 if (singleClock == null) {
700 singleClock = clocks.get(clocks.keySet().toArray()[0]);
701 if (singleClock.getProperty(OFFSET) != null) {
702 singleOffset = (Long) getClock().getProperty(OFFSET);
703 } else {
704 singleClock.addAttribute(OFFSET, 0);
705 }
706 }
707 return singleClock;
708 }
709 return null;
710 }
711
712 /**
713 * gets the time offset of a clock with respect to UTC in nanoseconds
714 * @return the time offset of a clock with respect to UTC in nanoseconds
715 */
716 public final long getOffset() {
717 if (getClock() == null) {
718 return 0;
719 }
720 return singleOffset;
721 }
722
723 /**
724 * Does a given stream contain any events?
725 * @param id the stream ID
726 * @return true if the stream has events.
727 */
728 public boolean hasEvents(Long id){
729 return eventDecs.containsKey(id);
730 }
731
732 /**
733 * Add an event declaration map to the events map.
734 * @param id the id of a stream
735 * @return the hashmap containing events.
736 */
737 public HashMap<Long, EventDeclaration> createEvents(Long id){
738 HashMap<Long, EventDeclaration> value = eventDecs.get(id);
739 if( value == null ) {
740 value = new HashMap<Long, EventDeclaration>();
741 eventDecs.put(id, value);
742 }
743 return value;
744 }
745
746 }
747
748 class MetadataFileFilter implements FileFilter {
749
750 @Override
751 public boolean accept(File pathname) {
752 if (pathname.isDirectory()) {
753 return false;
754 }
755 if (pathname.isHidden()) {
756 return false;
757 }
758 if (pathname.getName().equals("metadata")) { //$NON-NLS-1$
759 return false;
760 }
761 return true;
762 }
763
764 }
765
766 class MetadataComparator implements Comparator<File>, Serializable {
767
768 private static final long serialVersionUID = 1L;
769
770 @Override
771 public int compare(File o1, File o2) {
772 return o1.getName().compareTo(o2.getName());
773 }
774 }
This page took 0.060044 seconds and 5 git commands to generate.