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