Commit | Line | Data |
---|---|---|
866e5b51 | 1 | /******************************************************************************* |
fe75d403 | 2 | * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal and others |
866e5b51 FC |
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 | * | |
4311ac8b MAL |
9 | * Contributors: |
10 | * Matthew Khouzam - Initial API and implementation | |
11 | * Alexandre Montplaisir - Initial API and implementation | |
890f9136 | 12 | * Simon Delisle - Replace LinkedList by TreeSet in callsitesByName attribute |
866e5b51 FC |
13 | *******************************************************************************/ |
14 | ||
15 | package org.eclipse.linuxtools.ctf.core.trace; | |
16 | ||
17 | import java.io.File; | |
18 | import java.io.FileFilter; | |
19 | import java.io.FileInputStream; | |
20 | import java.io.IOException; | |
debcffff | 21 | import java.io.Serializable; |
866e5b51 FC |
22 | import java.nio.ByteOrder; |
23 | import java.nio.MappedByteBuffer; | |
24 | import java.nio.channels.FileChannel; | |
25 | import java.nio.channels.FileChannel.MapMode; | |
26 | import java.util.Arrays; | |
a95fddf5 | 27 | import java.util.Collections; |
866e5b51 FC |
28 | import java.util.Comparator; |
29 | import java.util.HashMap; | |
aa572e22 | 30 | import java.util.Iterator; |
d0d3aa1b AM |
31 | import java.util.LinkedList; |
32 | import java.util.List; | |
866e5b51 | 33 | import java.util.Map; |
aa572e22 | 34 | import java.util.Map.Entry; |
866e5b51 | 35 | import java.util.Set; |
4c9d2941 | 36 | import java.util.TreeSet; |
866e5b51 FC |
37 | import java.util.UUID; |
38 | ||
4c9d2941 | 39 | import org.eclipse.linuxtools.ctf.core.event.CTFCallsite; |
866e5b51 | 40 | import org.eclipse.linuxtools.ctf.core.event.CTFClock; |
788ddcbc | 41 | import org.eclipse.linuxtools.ctf.core.event.EventDefinition; |
8e964be1 | 42 | import org.eclipse.linuxtools.ctf.core.event.IEventDeclaration; |
486efb2e | 43 | import org.eclipse.linuxtools.ctf.core.event.io.BitBuffer; |
866e5b51 FC |
44 | import org.eclipse.linuxtools.ctf.core.event.types.ArrayDefinition; |
45 | import org.eclipse.linuxtools.ctf.core.event.types.Definition; | |
46 | import org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope; | |
47 | import org.eclipse.linuxtools.ctf.core.event.types.IntegerDefinition; | |
48 | import org.eclipse.linuxtools.ctf.core.event.types.StructDeclaration; | |
49 | import org.eclipse.linuxtools.ctf.core.event.types.StructDefinition; | |
890f9136 | 50 | import org.eclipse.linuxtools.internal.ctf.core.event.CTFCallsiteComparator; |
ce2388e0 | 51 | import org.eclipse.linuxtools.internal.ctf.core.event.metadata.exceptions.ParseException; |
866e5b51 FC |
52 | |
53 | /** | |
d37aaa7f FC |
54 | * A CTF trace on the file system. |
55 | * | |
866e5b51 FC |
56 | * Represents a trace on the filesystem. It is responsible of parsing the |
57 | * metadata, creating declarations data structures, indexing the event packets | |
58 | * (in other words, all the work that can be shared between readers), but the | |
59 | * actual reading of events is left to TraceReader. | |
debcffff | 60 | * |
866e5b51 FC |
61 | * @author Matthew Khouzam |
62 | * @version $Revision: 1.0 $ | |
63 | */ | |
64 | public class CTFTrace implements IDefinitionScope { | |
65 | ||
866e5b51 FC |
66 | @SuppressWarnings("nls") |
67 | @Override | |
68 | public String toString() { | |
69 | /* Only for debugging, shouldn't be externalized */ | |
fe75d403 MK |
70 | return "CTFTrace [path=" + fPath + ", major=" + fMajor + ", minor=" |
71 | + fMinor + ", uuid=" + fUuid + "]"; | |
866e5b51 FC |
72 | } |
73 | ||
74 | /** | |
75 | * The trace directory on the filesystem. | |
76 | */ | |
fe75d403 | 77 | private final File fPath; |
866e5b51 | 78 | |
866e5b51 FC |
79 | /** |
80 | * Major CTF version number | |
81 | */ | |
fe75d403 | 82 | private Long fMajor; |
866e5b51 FC |
83 | |
84 | /** | |
85 | * Minor CTF version number | |
86 | */ | |
fe75d403 | 87 | private Long fMinor; |
866e5b51 FC |
88 | |
89 | /** | |
90 | * Trace UUID | |
91 | */ | |
fe75d403 | 92 | private UUID fUuid; |
866e5b51 FC |
93 | |
94 | /** | |
95 | * Trace byte order | |
96 | */ | |
fe75d403 | 97 | private ByteOrder fByteOrder; |
866e5b51 FC |
98 | |
99 | /** | |
100 | * Packet header structure declaration | |
101 | */ | |
fe75d403 | 102 | private StructDeclaration fPacketHeaderDecl = null; |
866e5b51 | 103 | |
1d7277f3 MK |
104 | /** |
105 | * The clock of the trace | |
106 | */ | |
fe75d403 | 107 | private CTFClock fSingleClock; |
1d7277f3 | 108 | |
866e5b51 FC |
109 | /** |
110 | * Packet header structure definition | |
debcffff | 111 | * |
866e5b51 FC |
112 | * This is only used when opening the trace files, to read the first packet |
113 | * header and see if they are valid trace files. | |
114 | */ | |
fe75d403 | 115 | private StructDefinition fPacketHeaderDef; |
866e5b51 FC |
116 | |
117 | /** | |
118 | * Collection of streams contained in the trace. | |
119 | */ | |
fe75d403 | 120 | private final Map<Long, Stream> fStreams = new HashMap<>(); |
866e5b51 FC |
121 | |
122 | /** | |
123 | * Collection of environment variables set by the tracer | |
124 | */ | |
fe75d403 | 125 | private final Map<String, String> fEnvironment = new HashMap<>(); |
866e5b51 FC |
126 | |
127 | /** | |
128 | * Collection of all the clocks in a system. | |
129 | */ | |
fe75d403 | 130 | private final Map<String, CTFClock> fClocks = new HashMap<>(); |
866e5b51 | 131 | |
5d1c6919 | 132 | /** FileInputStreams to the streams */ |
fe75d403 | 133 | private final List<FileInputStream> fFileInputStreams = new LinkedList<>(); |
26ea03d2 | 134 | |
c88e827d | 135 | /** Handlers for the metadata files */ |
0594c61c AM |
136 | private static final FileFilter METADATA_FILE_FILTER = new MetadataFileFilter(); |
137 | private static final Comparator<File> METADATA_COMPARATOR = new MetadataComparator(); | |
866e5b51 | 138 | |
4c9d2941 | 139 | /** Callsite helpers */ |
fe75d403 | 140 | private CTFCallsiteComparator fCtfCallsiteComparator = new CTFCallsiteComparator(); |
890f9136 | 141 | |
fe75d403 | 142 | private Map<String, TreeSet<CTFCallsite>> fCallsitesByName = new HashMap<>(); |
0594c61c | 143 | |
4c9d2941 | 144 | /** Callsite helpers */ |
fe75d403 | 145 | private TreeSet<CTFCallsite> fCallsitesByIP = new TreeSet<>(); |
788ddcbc | 146 | |
866e5b51 FC |
147 | // ------------------------------------------------------------------------ |
148 | // Constructors | |
149 | // ------------------------------------------------------------------------ | |
150 | ||
151 | /** | |
152 | * Trace constructor. | |
debcffff | 153 | * |
866e5b51 | 154 | * @param path |
be6df2d8 AM |
155 | * Filesystem path of the trace directory |
156 | * @throws CTFReaderException | |
157 | * If no CTF trace was found at the path | |
866e5b51 FC |
158 | */ |
159 | public CTFTrace(String path) throws CTFReaderException { | |
160 | this(new File(path)); | |
aa572e22 | 161 | |
866e5b51 FC |
162 | } |
163 | ||
164 | /** | |
165 | * Trace constructor. | |
debcffff | 166 | * |
866e5b51 FC |
167 | * @param path |
168 | * Filesystem path of the trace directory. | |
169 | * @throws CTFReaderException | |
be6df2d8 | 170 | * If no CTF trace was found at the path |
866e5b51 | 171 | */ |
866e5b51 | 172 | public CTFTrace(File path) throws CTFReaderException { |
fe75d403 | 173 | fPath = path; |
0594c61c | 174 | final Metadata metadata = new Metadata(this); |
866e5b51 | 175 | |
c88e827d | 176 | /* Set up the internal containers for this trace */ |
fe75d403 | 177 | if (!fPath.exists()) { |
8a95ce5a BH |
178 | throw new CTFReaderException("Trace (" + path.getPath() + ") doesn't exist. Deleted or moved?"); //$NON-NLS-1$ //$NON-NLS-2$ |
179 | } | |
d0d3aa1b | 180 | |
fe75d403 | 181 | if (!fPath.isDirectory()) { |
d0d3aa1b AM |
182 | throw new CTFReaderException("Path must be a valid directory"); //$NON-NLS-1$ |
183 | } | |
c88e827d AM |
184 | |
185 | /* Open and parse the metadata file */ | |
58129ff7 | 186 | metadata.parseFile(); |
c88e827d | 187 | |
58129ff7 MK |
188 | init(path); |
189 | } | |
190 | ||
191 | /** | |
192 | * Streamed constructor | |
fe75d403 | 193 | * |
58129ff7 MK |
194 | * @since 3.0 |
195 | */ | |
196 | public CTFTrace() { | |
fe75d403 | 197 | fPath = null; |
58129ff7 MK |
198 | init(); |
199 | } | |
200 | ||
201 | private void init() { | |
c88e827d | 202 | /* Create the definitions needed to read things from the files */ |
fe75d403 MK |
203 | if (fPacketHeaderDecl != null) { |
204 | fPacketHeaderDef = fPacketHeaderDecl.createDefinition(this, "packet.header"); //$NON-NLS-1$ | |
c88e827d | 205 | } |
58129ff7 MK |
206 | } |
207 | ||
208 | private void init(File path) throws CTFReaderException { | |
209 | ||
210 | init(); | |
211 | ||
212 | /* Open all the trace files */ | |
c88e827d AM |
213 | |
214 | /* List files not called metadata and not hidden. */ | |
0594c61c AM |
215 | File[] files = path.listFiles(METADATA_FILE_FILTER); |
216 | Arrays.sort(files, METADATA_COMPARATOR); | |
fe75d403 | 217 | |
c88e827d | 218 | /* Try to open each file */ |
d0d3aa1b AM |
219 | for (File streamFile : files) { |
220 | openStreamInput(streamFile); | |
c88e827d | 221 | } |
788ddcbc | 222 | |
c88e827d | 223 | /* Create their index */ |
f7c5789a EB |
224 | for (Stream stream : getStreams()) { |
225 | Set<StreamInput> inputs = stream.getStreamInputs(); | |
c88e827d | 226 | for (StreamInput s : inputs) { |
fe75d403 | 227 | addStream(s); |
c88e827d AM |
228 | } |
229 | } | |
866e5b51 FC |
230 | } |
231 | ||
5d1c6919 PT |
232 | /** |
233 | * Dispose the trace | |
a7297cd3 | 234 | * |
5d1c6919 PT |
235 | * @since 2.0 |
236 | */ | |
237 | public void dispose() { | |
fe75d403 | 238 | for (FileInputStream fis : fFileInputStreams) { |
5d1c6919 | 239 | if (fis != null) { |
26ea03d2 | 240 | try { |
5d1c6919 | 241 | fis.close(); |
26ea03d2 | 242 | } catch (IOException e) { |
aa572e22 | 243 | // do nothing it's ok, we tried to close it. |
26ea03d2 AM |
244 | } |
245 | } | |
246 | } | |
a7297cd3 SD |
247 | // Invoke GC to release MappedByteBuffer objects (Java bug JDK-4724038) |
248 | System.gc(); | |
26ea03d2 AM |
249 | } |
250 | ||
866e5b51 FC |
251 | // ------------------------------------------------------------------------ |
252 | // Getters/Setters/Predicates | |
253 | // ------------------------------------------------------------------------ | |
254 | ||
aa572e22 | 255 | /** |
be6df2d8 AM |
256 | * Gets an event declaration hash map for a given streamID |
257 | * | |
258 | * @param streamId | |
259 | * The ID of the stream from which to read | |
260 | * @return The Hash map with the event declarations | |
0594c61c | 261 | * @since 2.0 |
788ddcbc | 262 | */ |
0594c61c | 263 | public Map<Long, IEventDeclaration> getEvents(Long streamId) { |
fe75d403 | 264 | return fStreams.get(streamId).getEvents(); |
788ddcbc MK |
265 | } |
266 | ||
aa572e22 | 267 | /** |
788ddcbc | 268 | * Gets an event Declaration hashmap for a given StreamInput |
7ff6d3cf MK |
269 | * |
270 | * @param id | |
271 | * the StreamInput | |
272 | * @return an empty hashmap, please see deprecated | |
e6809677 | 273 | * @since 2.0 |
a7297cd3 SD |
274 | * @deprecated You should be using |
275 | * {@link StreamInputReader#getEventDefinitions()} instead. | |
788ddcbc | 276 | */ |
7ff6d3cf | 277 | @Deprecated |
0594c61c | 278 | public Map<Long, EventDefinition> getEventDefs(StreamInput id) { |
3de23137 | 279 | return new HashMap<>(); |
788ddcbc MK |
280 | } |
281 | ||
282 | /** | |
283 | * Get an event by it's ID | |
aa572e22 | 284 | * |
be6df2d8 AM |
285 | * @param streamId |
286 | * The ID of the stream from which to read | |
788ddcbc MK |
287 | * @param id |
288 | * the ID of the event | |
289 | * @return the event declaration | |
8e964be1 | 290 | * @since 2.0 |
aa572e22 | 291 | */ |
8e964be1 | 292 | public IEventDeclaration getEventType(long streamId, long id) { |
788ddcbc | 293 | return getEvents(streamId).get(id); |
aa572e22 MK |
294 | } |
295 | ||
866e5b51 FC |
296 | /** |
297 | * Method getStream gets the stream for a given id | |
debcffff | 298 | * |
866e5b51 FC |
299 | * @param id |
300 | * Long the id of the stream | |
301 | * @return Stream the stream that we need | |
e6809677 | 302 | * @since 2.0 |
866e5b51 FC |
303 | */ |
304 | public Stream getStream(Long id) { | |
fe75d403 | 305 | return fStreams.get(id); |
866e5b51 FC |
306 | } |
307 | ||
308 | /** | |
309 | * Method nbStreams gets the number of available streams | |
debcffff | 310 | * |
866e5b51 FC |
311 | * @return int the number of streams |
312 | */ | |
313 | public int nbStreams() { | |
fe75d403 | 314 | return fStreams.size(); |
866e5b51 FC |
315 | } |
316 | ||
317 | /** | |
318 | * Method setMajor sets the major version of the trace (DO NOT USE) | |
debcffff | 319 | * |
866e5b51 FC |
320 | * @param major |
321 | * long the major version | |
322 | */ | |
323 | public void setMajor(long major) { | |
fe75d403 | 324 | fMajor = major; |
866e5b51 FC |
325 | } |
326 | ||
327 | /** | |
328 | * Method setMinor sets the minor version of the trace (DO NOT USE) | |
debcffff | 329 | * |
866e5b51 FC |
330 | * @param minor |
331 | * long the minor version | |
332 | */ | |
333 | public void setMinor(long minor) { | |
fe75d403 | 334 | fMinor = minor; |
866e5b51 FC |
335 | } |
336 | ||
337 | /** | |
338 | * Method setUUID sets the UUID of a trace | |
debcffff | 339 | * |
866e5b51 FC |
340 | * @param uuid |
341 | * UUID | |
342 | */ | |
343 | public void setUUID(UUID uuid) { | |
fe75d403 | 344 | fUuid = uuid; |
866e5b51 FC |
345 | } |
346 | ||
347 | /** | |
348 | * Method setByteOrder sets the byte order | |
debcffff | 349 | * |
866e5b51 FC |
350 | * @param byteOrder |
351 | * ByteOrder of the trace, can be little-endian or big-endian | |
352 | */ | |
353 | public void setByteOrder(ByteOrder byteOrder) { | |
fe75d403 | 354 | fByteOrder = byteOrder; |
866e5b51 FC |
355 | } |
356 | ||
357 | /** | |
358 | * Method setPacketHeader sets the packet header of a trace (DO NOT USE) | |
debcffff | 359 | * |
866e5b51 FC |
360 | * @param packetHeader |
361 | * StructDeclaration the header in structdeclaration form | |
362 | */ | |
363 | public void setPacketHeader(StructDeclaration packetHeader) { | |
fe75d403 | 364 | fPacketHeaderDecl = packetHeader; |
866e5b51 FC |
365 | } |
366 | ||
367 | /** | |
07804639 | 368 | * Method majorIsSet is the major version number set? |
debcffff | 369 | * |
866e5b51 | 370 | * @return boolean is the major set? |
c4767854 | 371 | * @since 3.0 |
866e5b51 | 372 | */ |
07804639 | 373 | public boolean majorIsSet() { |
fe75d403 | 374 | return fMajor != null; |
866e5b51 FC |
375 | } |
376 | ||
377 | /** | |
378 | * Method minorIsSet. is the minor version number set? | |
debcffff | 379 | * |
866e5b51 FC |
380 | * @return boolean is the minor set? |
381 | */ | |
382 | public boolean minorIsSet() { | |
fe75d403 | 383 | return fMinor != null; |
866e5b51 FC |
384 | } |
385 | ||
386 | /** | |
387 | * Method UUIDIsSet is the UUID set? | |
debcffff | 388 | * |
866e5b51 | 389 | * @return boolean is the UUID set? |
0594c61c | 390 | * @since 2.0 |
866e5b51 | 391 | */ |
0594c61c | 392 | public boolean uuidIsSet() { |
fe75d403 | 393 | return fUuid != null; |
866e5b51 FC |
394 | } |
395 | ||
396 | /** | |
397 | * Method byteOrderIsSet is the byteorder set? | |
debcffff | 398 | * |
866e5b51 FC |
399 | * @return boolean is the byteorder set? |
400 | */ | |
401 | public boolean byteOrderIsSet() { | |
fe75d403 | 402 | return fByteOrder != null; |
866e5b51 FC |
403 | } |
404 | ||
405 | /** | |
406 | * Method packetHeaderIsSet is the packet header set? | |
debcffff | 407 | * |
866e5b51 FC |
408 | * @return boolean is the packet header set? |
409 | */ | |
410 | public boolean packetHeaderIsSet() { | |
fe75d403 | 411 | return fPacketHeaderDecl != null; |
866e5b51 FC |
412 | } |
413 | ||
414 | /** | |
415 | * Method getUUID gets the trace UUID | |
debcffff | 416 | * |
866e5b51 FC |
417 | * @return UUID gets the trace UUID |
418 | */ | |
419 | public UUID getUUID() { | |
fe75d403 | 420 | return fUuid; |
866e5b51 FC |
421 | } |
422 | ||
423 | /** | |
424 | * Method getMajor gets the trace major version | |
debcffff | 425 | * |
866e5b51 FC |
426 | * @return long gets the trace major version |
427 | */ | |
428 | public long getMajor() { | |
fe75d403 | 429 | return fMajor; |
866e5b51 FC |
430 | } |
431 | ||
432 | /** | |
433 | * Method getMinor gets the trace minor version | |
debcffff | 434 | * |
866e5b51 FC |
435 | * @return long gets the trace minor version |
436 | */ | |
437 | public long getMinor() { | |
fe75d403 | 438 | return fMinor; |
866e5b51 FC |
439 | } |
440 | ||
441 | /** | |
442 | * Method getByteOrder gets the trace byte order | |
debcffff | 443 | * |
866e5b51 FC |
444 | * @return ByteOrder gets the trace byte order |
445 | */ | |
0594c61c | 446 | public final ByteOrder getByteOrder() { |
fe75d403 | 447 | return fByteOrder; |
866e5b51 FC |
448 | } |
449 | ||
450 | /** | |
451 | * Method getPacketHeader gets the trace packet header | |
debcffff | 452 | * |
866e5b51 FC |
453 | * @return StructDeclaration gets the trace packet header |
454 | */ | |
455 | public StructDeclaration getPacketHeader() { | |
fe75d403 | 456 | return fPacketHeaderDecl; |
866e5b51 FC |
457 | } |
458 | ||
459 | /** | |
460 | * Method getTraceDirectory gets the trace directory | |
debcffff | 461 | * |
866e5b51 FC |
462 | * @return File the path in "File" format. |
463 | */ | |
464 | public File getTraceDirectory() { | |
fe75d403 | 465 | return fPath; |
866e5b51 FC |
466 | } |
467 | ||
468 | /** | |
f7c5789a | 469 | * Get all the streams as an iterable. |
debcffff | 470 | * |
f7c5789a | 471 | * @return Iterable<Stream> an iterable over streams. |
951bb9d9 | 472 | * @since 3.0 |
866e5b51 | 473 | */ |
f7c5789a | 474 | public Iterable<Stream> getStreams() { |
fe75d403 | 475 | return fStreams.values(); |
866e5b51 FC |
476 | } |
477 | ||
478 | /** | |
479 | * Method getPath gets the path of the trace directory | |
debcffff | 480 | * |
866e5b51 FC |
481 | * @return String the path of the trace directory, in string format. |
482 | * @see java.io.File#getPath() | |
483 | */ | |
484 | @Override | |
485 | public String getPath() { | |
fe75d403 | 486 | return (fPath != null) ? fPath.getPath() : ""; //$NON-NLS-1$ |
866e5b51 FC |
487 | } |
488 | ||
489 | // ------------------------------------------------------------------------ | |
490 | // Operations | |
491 | // ------------------------------------------------------------------------ | |
492 | ||
fe75d403 MK |
493 | private void addStream(StreamInput s) { |
494 | ||
495 | /* | |
496 | * Copy the events | |
497 | */ | |
498 | Iterator<Entry<Long, IEventDeclaration>> it = s.getStream() | |
499 | .getEvents().entrySet().iterator(); | |
500 | while (it.hasNext()) { | |
501 | Entry<Long, IEventDeclaration> pairs = it.next(); | |
502 | Long eventNum = pairs.getKey(); | |
503 | IEventDeclaration eventDec = pairs.getValue(); | |
504 | getEvents(s.getStream().getId()).put(eventNum, eventDec); | |
505 | } | |
506 | ||
507 | /* | |
508 | * index the trace | |
509 | */ | |
510 | s.setupIndex(); | |
511 | } | |
512 | ||
866e5b51 FC |
513 | /** |
514 | * Tries to open the given file, reads the first packet header of the file | |
fe75d403 | 515 | * and check its validity. This will add a file to a stream as a streaminput |
debcffff | 516 | * |
866e5b51 FC |
517 | * @param streamFile |
518 | * A trace file in the trace directory. | |
26ea03d2 AM |
519 | * @param index |
520 | * Which index in the class' streamFileChannel array this file | |
521 | * must use | |
866e5b51 | 522 | * @throws CTFReaderException |
fe75d403 | 523 | * if there is a file error |
866e5b51 | 524 | */ |
fe75d403 | 525 | private Stream openStreamInput(File streamFile) throws CTFReaderException { |
866e5b51 FC |
526 | MappedByteBuffer byteBuffer; |
527 | BitBuffer streamBitBuffer; | |
d0d3aa1b AM |
528 | Stream stream; |
529 | FileChannel fc; | |
866e5b51 FC |
530 | |
531 | if (!streamFile.canRead()) { | |
532 | throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$ | |
533 | + streamFile.getPath()); | |
534 | } | |
535 | ||
fe75d403 | 536 | FileInputStream fis = null; |
866e5b51 FC |
537 | try { |
538 | /* Open the file and get the FileChannel */ | |
fe75d403 MK |
539 | fis = new FileInputStream(streamFile); |
540 | fFileInputStreams.add(fis); | |
5d1c6919 | 541 | fc = fis.getChannel(); |
866e5b51 FC |
542 | |
543 | /* Map one memory page of 4 kiB */ | |
92bdd7d4 | 544 | byteBuffer = fc.map(MapMode.READ_ONLY, 0, (int) Math.min(fc.size(), 4096L)); |
866e5b51 | 545 | } catch (IOException e) { |
fe75d403 MK |
546 | if (fis != null) { |
547 | fFileInputStreams.remove(fis); | |
548 | } | |
866e5b51 | 549 | /* Shouldn't happen at this stage if every other check passed */ |
0594c61c | 550 | throw new CTFReaderException(e); |
866e5b51 FC |
551 | } |
552 | ||
553 | /* Create a BitBuffer with this mapping and the trace byte order */ | |
554 | streamBitBuffer = new BitBuffer(byteBuffer, this.getByteOrder()); | |
555 | ||
fe75d403 | 556 | if (fPacketHeaderDef != null) { |
866e5b51 | 557 | /* Read the packet header */ |
fe75d403 | 558 | fPacketHeaderDef.read(streamBitBuffer); |
866e5b51 FC |
559 | |
560 | /* Check the magic number */ | |
fe75d403 | 561 | IntegerDefinition magicDef = (IntegerDefinition) fPacketHeaderDef |
aa572e22 | 562 | .lookupDefinition("magic"); //$NON-NLS-1$ |
866e5b51 FC |
563 | int magic = (int) magicDef.getValue(); |
564 | if (magic != Utils.CTF_MAGIC) { | |
565 | throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$ | |
566 | } | |
567 | ||
568 | /* Check UUID */ | |
fe75d403 | 569 | ArrayDefinition uuidDef = (ArrayDefinition) fPacketHeaderDef |
aa572e22 | 570 | .lookupDefinition("uuid"); //$NON-NLS-1$ |
866e5b51 FC |
571 | if (uuidDef != null) { |
572 | byte[] uuidArray = new byte[Utils.UUID_LEN]; | |
573 | ||
574 | for (int i = 0; i < Utils.UUID_LEN; i++) { | |
aa572e22 MK |
575 | IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef |
576 | .getElem(i); | |
866e5b51 FC |
577 | uuidArray[i] = (byte) uuidByteDef.getValue(); |
578 | } | |
579 | ||
580 | UUID otheruuid = Utils.makeUUID(uuidArray); | |
581 | ||
fe75d403 | 582 | if (!fUuid.equals(otheruuid)) { |
866e5b51 FC |
583 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ |
584 | } | |
585 | } | |
586 | ||
1fbaecd1 | 587 | /* Read the stream ID */ |
fe75d403 | 588 | Definition streamIDDef = fPacketHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$ |
1fbaecd1 | 589 | |
a7297cd3 SD |
590 | if (streamIDDef instanceof IntegerDefinition) { // this doubles as a |
591 | // null check | |
1fbaecd1 | 592 | long streamID = ((IntegerDefinition) streamIDDef).getValue(); |
fe75d403 | 593 | stream = fStreams.get(streamID); |
1fbaecd1 AM |
594 | } else { |
595 | /* No stream_id in the packet header */ | |
fe75d403 | 596 | stream = fStreams.get(null); |
1fbaecd1 AM |
597 | } |
598 | ||
866e5b51 FC |
599 | } else { |
600 | /* No packet header, we suppose there is only one stream */ | |
fe75d403 | 601 | stream = fStreams.get(null); |
d0d3aa1b | 602 | } |
866e5b51 | 603 | |
4311ac8b MAL |
604 | if (stream == null) { |
605 | throw new CTFReaderException("Unexpected end of stream"); //$NON-NLS-1$ | |
606 | } | |
607 | ||
d0d3aa1b AM |
608 | /* Create the stream input */ |
609 | StreamInput streamInput = new StreamInput(stream, fc, streamFile); | |
866e5b51 | 610 | |
d0d3aa1b AM |
611 | /* Add a reference to the streamInput in the stream */ |
612 | stream.addInput(streamInput); | |
fe75d403 MK |
613 | |
614 | return stream; | |
866e5b51 FC |
615 | } |
616 | ||
617 | /** | |
618 | * Looks up a definition from packet | |
debcffff | 619 | * |
866e5b51 FC |
620 | * @param lookupPath |
621 | * String | |
622 | * @return Definition | |
623 | * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String) | |
624 | */ | |
625 | @Override | |
626 | public Definition lookupDefinition(String lookupPath) { | |
627 | if (lookupPath.equals("trace.packet.header")) { //$NON-NLS-1$ | |
fe75d403 | 628 | return fPacketHeaderDef; |
866e5b51 FC |
629 | } |
630 | return null; | |
631 | } | |
632 | ||
633 | /** | |
fe75d403 MK |
634 | * Add a new stream file to support new streams while the trace is being |
635 | * read. | |
636 | * | |
637 | * @param streamFile | |
638 | * the file of the stream | |
639 | * @throws CTFReaderException | |
640 | * A stream had an issue being read | |
fe75d403 MK |
641 | * @since 3.0 |
642 | */ | |
643 | public void addStreamFile(File streamFile) throws CTFReaderException { | |
644 | openStreamInput(streamFile); | |
645 | } | |
646 | ||
647 | /** | |
648 | * Registers a new stream to the trace. | |
debcffff | 649 | * |
866e5b51 FC |
650 | * @param stream |
651 | * A stream object. | |
866e5b51 | 652 | * @throws ParseException |
be6df2d8 | 653 | * If there was some problem reading the metadata |
e6809677 | 654 | * @since 2.0 |
866e5b51 FC |
655 | */ |
656 | public void addStream(Stream stream) throws ParseException { | |
657 | ||
fe75d403 MK |
658 | /* |
659 | * Init if not done before | |
660 | */ | |
661 | init(); | |
662 | ||
866e5b51 FC |
663 | /* |
664 | * If there is already a stream without id (the null key), it must be | |
665 | * the only one | |
666 | */ | |
fe75d403 | 667 | if (fStreams.get(null) != null) { |
866e5b51 FC |
668 | throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
669 | } | |
670 | ||
671 | /* | |
1d7277f3 MK |
672 | * If the stream we try to add has the null key, it must be the only |
673 | * one. Thus, if the streams container is not empty, it is not valid. | |
866e5b51 | 674 | */ |
fe75d403 | 675 | if ((stream.getId() == null) && (fStreams.size() != 0)) { |
866e5b51 FC |
676 | throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
677 | } | |
678 | ||
fe75d403 MK |
679 | /* |
680 | * If a stream with the same ID already exists, it is not valid. | |
681 | */ | |
682 | Stream existingStream = fStreams.get(stream.getId()); | |
683 | if (existingStream != null) { | |
866e5b51 FC |
684 | throw new ParseException("Stream id already exists"); //$NON-NLS-1$ |
685 | } | |
686 | ||
29a7d6ee | 687 | /* This stream is valid and has a unique id. */ |
fe75d403 | 688 | fStreams.put(stream.getId(), stream); |
866e5b51 FC |
689 | } |
690 | ||
9ac2eb62 | 691 | /** |
29a7d6ee | 692 | * Gets the Environment variables from the trace metadata (See CTF spec) |
a7297cd3 | 693 | * |
a95fddf5 EB |
694 | * @return The environment variables in the form of an unmodifiable map |
695 | * (key, value) | |
486efb2e | 696 | * @since 2.0 |
9ac2eb62 | 697 | */ |
791072b0 | 698 | public Map<String, String> getEnvironment() { |
fe75d403 | 699 | return Collections.unmodifiableMap(fEnvironment); |
866e5b51 FC |
700 | } |
701 | ||
9ac2eb62 MK |
702 | /** |
703 | * Add a variable to the environment variables | |
a7297cd3 SD |
704 | * |
705 | * @param varName | |
706 | * the name of the variable | |
707 | * @param varValue | |
708 | * the value of the variable | |
9ac2eb62 | 709 | */ |
c88e827d | 710 | public void addEnvironmentVar(String varName, String varValue) { |
fe75d403 | 711 | fEnvironment.put(varName, varValue); |
866e5b51 FC |
712 | } |
713 | ||
9ac2eb62 MK |
714 | /** |
715 | * Add a clock to the clock list | |
a7297cd3 SD |
716 | * |
717 | * @param nameValue | |
718 | * the name of the clock (full name with scope) | |
719 | * @param ctfClock | |
720 | * the clock | |
9ac2eb62 | 721 | */ |
866e5b51 | 722 | public void addClock(String nameValue, CTFClock ctfClock) { |
fe75d403 | 723 | fClocks.put(nameValue, ctfClock); |
866e5b51 FC |
724 | } |
725 | ||
9ac2eb62 MK |
726 | /** |
727 | * gets the clock with a specific name | |
a7297cd3 SD |
728 | * |
729 | * @param name | |
730 | * the name of the clock. | |
9ac2eb62 MK |
731 | * @return the clock |
732 | */ | |
c88e827d | 733 | public CTFClock getClock(String name) { |
fe75d403 | 734 | return fClocks.get(name); |
866e5b51 FC |
735 | } |
736 | ||
9ac2eb62 | 737 | /** |
1d7277f3 MK |
738 | * gets the clock if there is only one. (this is 100% of the use cases as of |
739 | * June 2012) | |
740 | * | |
9ac2eb62 MK |
741 | * @return the clock |
742 | */ | |
8ecc80f3 | 743 | public final CTFClock getClock() { |
fe75d403 MK |
744 | if (fClocks.size() == 1) { |
745 | fSingleClock = fClocks.get(fClocks.keySet().iterator().next()); | |
746 | return fSingleClock; | |
866e5b51 FC |
747 | } |
748 | return null; | |
749 | } | |
750 | ||
9ac2eb62 MK |
751 | /** |
752 | * gets the time offset of a clock with respect to UTC in nanoseconds | |
1d7277f3 | 753 | * |
9ac2eb62 MK |
754 | * @return the time offset of a clock with respect to UTC in nanoseconds |
755 | */ | |
8ecc80f3 | 756 | public final long getOffset() { |
c88e827d | 757 | if (getClock() == null) { |
ce2388e0 FC |
758 | return 0; |
759 | } | |
fe75d403 | 760 | return fSingleClock.getClockOffset(); |
1d7277f3 MK |
761 | } |
762 | ||
763 | /** | |
764 | * gets the time offset of a clock with respect to UTC in nanoseconds | |
765 | * | |
766 | * @return the time offset of a clock with respect to UTC in nanoseconds | |
767 | */ | |
0594c61c | 768 | private double getTimeScale() { |
1d7277f3 MK |
769 | if (getClock() == null) { |
770 | return 1.0; | |
771 | } | |
fe75d403 | 772 | return fSingleClock.getClockScale(); |
1d7277f3 MK |
773 | } |
774 | ||
775 | /** | |
776 | * Does the trace need to time scale? | |
777 | * | |
778 | * @return if the trace is in ns or cycles. | |
779 | */ | |
0594c61c | 780 | private boolean clockNeedsScale() { |
1d7277f3 MK |
781 | if (getClock() == null) { |
782 | return false; | |
783 | } | |
fe75d403 | 784 | return fSingleClock.isClockScaled(); |
1d7277f3 MK |
785 | } |
786 | ||
787 | /** | |
788 | * the inverse clock for returning to a scale. | |
789 | * | |
790 | * @return 1.0 / scale | |
791 | */ | |
0594c61c | 792 | private double getInverseTimeScale() { |
1d7277f3 MK |
793 | if (getClock() == null) { |
794 | return 1.0; | |
795 | } | |
fe75d403 | 796 | return fSingleClock.getClockAntiScale(); |
1d7277f3 MK |
797 | } |
798 | ||
799 | /** | |
800 | * @param cycles | |
801 | * clock cycles since boot | |
802 | * @return time in nanoseconds UTC offset | |
486efb2e | 803 | * @since 2.0 |
1d7277f3 MK |
804 | */ |
805 | public long timestampCyclesToNanos(long cycles) { | |
806 | long retVal = cycles + getOffset(); | |
807 | /* | |
808 | * this fix is since quite often the offset will be > than 53 bits and | |
809 | * therefore the conversion will be lossy | |
810 | */ | |
811 | if (clockNeedsScale()) { | |
812 | retVal = (long) (retVal * getTimeScale()); | |
813 | } | |
814 | return retVal; | |
815 | } | |
816 | ||
817 | /** | |
818 | * @param nanos | |
819 | * time in nanoseconds UTC offset | |
820 | * @return clock cycles since boot. | |
486efb2e | 821 | * @since 2.0 |
1d7277f3 MK |
822 | */ |
823 | public long timestampNanoToCycles(long nanos) { | |
824 | long retVal; | |
825 | /* | |
826 | * this fix is since quite often the offset will be > than 53 bits and | |
827 | * therefore the conversion will be lossy | |
828 | */ | |
829 | if (clockNeedsScale()) { | |
830 | retVal = (long) (nanos * getInverseTimeScale()); | |
831 | } else { | |
832 | retVal = nanos; | |
833 | } | |
834 | return retVal - getOffset(); | |
ce2388e0 FC |
835 | } |
836 | ||
4c9d2941 MK |
837 | /** |
838 | * Adds a callsite | |
839 | * | |
840 | * @param eventName | |
841 | * the event name of the callsite | |
842 | * @param funcName | |
843 | * the name of the callsite function | |
844 | * @param ip | |
845 | * the ip of the callsite | |
846 | * @param fileName | |
847 | * the filename of the callsite | |
848 | * @param lineNumber | |
849 | * the line number of the callsite | |
850 | */ | |
851 | public void addCallsite(String eventName, String funcName, long ip, | |
852 | String fileName, long lineNumber) { | |
853 | final CTFCallsite cs = new CTFCallsite(eventName, funcName, ip, | |
854 | fileName, lineNumber); | |
fe75d403 | 855 | TreeSet<CTFCallsite> csl = fCallsitesByName.get(eventName); |
4c9d2941 | 856 | if (csl == null) { |
fe75d403 MK |
857 | csl = new TreeSet<>(fCtfCallsiteComparator); |
858 | fCallsitesByName.put(eventName, csl); | |
4c9d2941 MK |
859 | } |
860 | ||
890f9136 | 861 | csl.add(cs); |
4c9d2941 | 862 | |
fe75d403 | 863 | fCallsitesByIP.add(cs); |
4c9d2941 MK |
864 | } |
865 | ||
866 | /** | |
890f9136 | 867 | * Gets the set of callsites associated to an event name. O(1) |
4c9d2941 MK |
868 | * |
869 | * @param eventName | |
870 | * the event name | |
890f9136 SD |
871 | * @return the callsite set can be empty |
872 | * @since 3.0 | |
4c9d2941 | 873 | */ |
890f9136 | 874 | public TreeSet<CTFCallsite> getCallsiteCandidates(String eventName) { |
fe75d403 | 875 | TreeSet<CTFCallsite> retVal = fCallsitesByName.get(eventName); |
890f9136 | 876 | if (retVal == null) { |
fe75d403 | 877 | retVal = new TreeSet<>(fCtfCallsiteComparator); |
4c9d2941 MK |
878 | } |
879 | return retVal; | |
880 | } | |
881 | ||
882 | /** | |
883 | * The I'm feeling lucky of getCallsiteCandidates O(1) | |
884 | * | |
885 | * @param eventName | |
886 | * the event name | |
887 | * @return the first callsite that has that event name, can be null | |
888 | * @since 1.2 | |
889 | */ | |
890 | public CTFCallsite getCallsite(String eventName) { | |
fe75d403 | 891 | TreeSet<CTFCallsite> callsites = fCallsitesByName.get(eventName); |
60fb38b8 | 892 | if (callsites != null) { |
890f9136 | 893 | return callsites.first(); |
60fb38b8 PT |
894 | } |
895 | return null; | |
4c9d2941 MK |
896 | } |
897 | ||
898 | /** | |
899 | * Gets a callsite from the instruction pointer O(log(n)) | |
900 | * | |
901 | * @param ip | |
902 | * the instruction pointer to lookup | |
903 | * @return the callsite just before that IP in the list remember the IP is | |
904 | * backwards on X86, can be null if no callsite is before the IP. | |
905 | * @since 1.2 | |
906 | */ | |
907 | public CTFCallsite getCallsite(long ip) { | |
908 | CTFCallsite cs = new CTFCallsite(null, null, ip, null, 0L); | |
fe75d403 | 909 | return fCallsitesByIP.ceiling(cs); |
4c9d2941 MK |
910 | } |
911 | ||
912 | /** | |
913 | * Gets a callsite using the event name and instruction pointer O(log(n)) | |
914 | * | |
915 | * @param eventName | |
916 | * the name of the event | |
917 | * @param ip | |
918 | * the instruction pointer | |
919 | * @return the closest matching callsite, can be null | |
920 | */ | |
921 | public CTFCallsite getCallsite(String eventName, long ip) { | |
fe75d403 | 922 | final TreeSet<CTFCallsite> candidates = fCallsitesByName.get(eventName); |
4c9d2941 | 923 | final CTFCallsite dummyCs = new CTFCallsite(null, null, ip, null, -1); |
890f9136 SD |
924 | final CTFCallsite callsite = candidates.ceiling(dummyCs); |
925 | if (callsite == null) { | |
926 | return candidates.floor(dummyCs); | |
4c9d2941 | 927 | } |
890f9136 | 928 | return callsite; |
4c9d2941 | 929 | } |
866e5b51 | 930 | } |
c88e827d AM |
931 | |
932 | class MetadataFileFilter implements FileFilter { | |
933 | ||
934 | @Override | |
935 | public boolean accept(File pathname) { | |
936 | if (pathname.isDirectory()) { | |
937 | return false; | |
938 | } | |
939 | if (pathname.isHidden()) { | |
940 | return false; | |
941 | } | |
942 | if (pathname.getName().equals("metadata")) { //$NON-NLS-1$ | |
943 | return false; | |
944 | } | |
945 | return true; | |
946 | } | |
947 | ||
948 | } | |
949 | ||
debcffff | 950 | class MetadataComparator implements Comparator<File>, Serializable { |
c88e827d | 951 | |
8fd82db5 FC |
952 | private static final long serialVersionUID = 1L; |
953 | ||
c88e827d AM |
954 | @Override |
955 | public int compare(File o1, File o2) { | |
956 | return o1.getName().compareTo(o2.getName()); | |
957 | } | |
958 | } |