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 | */ | |
dd9752d5 | 64 | public class CTFTrace implements IDefinitionScope, AutoCloseable { |
866e5b51 | 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 | * |
dd9752d5 | 235 | * @since 3.0 |
5d1c6919 | 236 | */ |
dd9752d5 AM |
237 | @Override |
238 | public void close() { | |
fe75d403 | 239 | for (FileInputStream fis : fFileInputStreams) { |
5d1c6919 | 240 | if (fis != null) { |
26ea03d2 | 241 | try { |
5d1c6919 | 242 | fis.close(); |
26ea03d2 | 243 | } catch (IOException e) { |
aa572e22 | 244 | // do nothing it's ok, we tried to close it. |
26ea03d2 AM |
245 | } |
246 | } | |
247 | } | |
248 | } | |
249 | ||
866e5b51 FC |
250 | // ------------------------------------------------------------------------ |
251 | // Getters/Setters/Predicates | |
252 | // ------------------------------------------------------------------------ | |
253 | ||
aa572e22 | 254 | /** |
be6df2d8 AM |
255 | * Gets an event declaration hash map for a given streamID |
256 | * | |
257 | * @param streamId | |
258 | * The ID of the stream from which to read | |
259 | * @return The Hash map with the event declarations | |
0594c61c | 260 | * @since 2.0 |
788ddcbc | 261 | */ |
0594c61c | 262 | public Map<Long, IEventDeclaration> getEvents(Long streamId) { |
fe75d403 | 263 | return fStreams.get(streamId).getEvents(); |
788ddcbc MK |
264 | } |
265 | ||
aa572e22 | 266 | /** |
788ddcbc | 267 | * Gets an event Declaration hashmap for a given StreamInput |
7ff6d3cf MK |
268 | * |
269 | * @param id | |
270 | * the StreamInput | |
271 | * @return an empty hashmap, please see deprecated | |
e6809677 | 272 | * @since 2.0 |
a7297cd3 SD |
273 | * @deprecated You should be using |
274 | * {@link StreamInputReader#getEventDefinitions()} instead. | |
788ddcbc | 275 | */ |
7ff6d3cf | 276 | @Deprecated |
0594c61c | 277 | public Map<Long, EventDefinition> getEventDefs(StreamInput id) { |
3de23137 | 278 | return new HashMap<>(); |
788ddcbc MK |
279 | } |
280 | ||
281 | /** | |
282 | * Get an event by it's ID | |
aa572e22 | 283 | * |
be6df2d8 AM |
284 | * @param streamId |
285 | * The ID of the stream from which to read | |
788ddcbc MK |
286 | * @param id |
287 | * the ID of the event | |
288 | * @return the event declaration | |
8e964be1 | 289 | * @since 2.0 |
aa572e22 | 290 | */ |
8e964be1 | 291 | public IEventDeclaration getEventType(long streamId, long id) { |
788ddcbc | 292 | return getEvents(streamId).get(id); |
aa572e22 MK |
293 | } |
294 | ||
866e5b51 FC |
295 | /** |
296 | * Method getStream gets the stream for a given id | |
debcffff | 297 | * |
866e5b51 FC |
298 | * @param id |
299 | * Long the id of the stream | |
300 | * @return Stream the stream that we need | |
e6809677 | 301 | * @since 2.0 |
866e5b51 FC |
302 | */ |
303 | public Stream getStream(Long id) { | |
fe75d403 | 304 | return fStreams.get(id); |
866e5b51 FC |
305 | } |
306 | ||
307 | /** | |
308 | * Method nbStreams gets the number of available streams | |
debcffff | 309 | * |
866e5b51 FC |
310 | * @return int the number of streams |
311 | */ | |
312 | public int nbStreams() { | |
fe75d403 | 313 | return fStreams.size(); |
866e5b51 FC |
314 | } |
315 | ||
316 | /** | |
317 | * Method setMajor sets the major version of the trace (DO NOT USE) | |
debcffff | 318 | * |
866e5b51 FC |
319 | * @param major |
320 | * long the major version | |
321 | */ | |
322 | public void setMajor(long major) { | |
fe75d403 | 323 | fMajor = major; |
866e5b51 FC |
324 | } |
325 | ||
326 | /** | |
327 | * Method setMinor sets the minor version of the trace (DO NOT USE) | |
debcffff | 328 | * |
866e5b51 FC |
329 | * @param minor |
330 | * long the minor version | |
331 | */ | |
332 | public void setMinor(long minor) { | |
fe75d403 | 333 | fMinor = minor; |
866e5b51 FC |
334 | } |
335 | ||
336 | /** | |
337 | * Method setUUID sets the UUID of a trace | |
debcffff | 338 | * |
866e5b51 FC |
339 | * @param uuid |
340 | * UUID | |
341 | */ | |
342 | public void setUUID(UUID uuid) { | |
fe75d403 | 343 | fUuid = uuid; |
866e5b51 FC |
344 | } |
345 | ||
346 | /** | |
347 | * Method setByteOrder sets the byte order | |
debcffff | 348 | * |
866e5b51 FC |
349 | * @param byteOrder |
350 | * ByteOrder of the trace, can be little-endian or big-endian | |
351 | */ | |
352 | public void setByteOrder(ByteOrder byteOrder) { | |
fe75d403 | 353 | fByteOrder = byteOrder; |
866e5b51 FC |
354 | } |
355 | ||
356 | /** | |
357 | * Method setPacketHeader sets the packet header of a trace (DO NOT USE) | |
debcffff | 358 | * |
866e5b51 FC |
359 | * @param packetHeader |
360 | * StructDeclaration the header in structdeclaration form | |
361 | */ | |
362 | public void setPacketHeader(StructDeclaration packetHeader) { | |
fe75d403 | 363 | fPacketHeaderDecl = packetHeader; |
866e5b51 FC |
364 | } |
365 | ||
366 | /** | |
07804639 | 367 | * Method majorIsSet is the major version number set? |
debcffff | 368 | * |
866e5b51 | 369 | * @return boolean is the major set? |
c4767854 | 370 | * @since 3.0 |
866e5b51 | 371 | */ |
07804639 | 372 | public boolean majorIsSet() { |
fe75d403 | 373 | return fMajor != null; |
866e5b51 FC |
374 | } |
375 | ||
376 | /** | |
377 | * Method minorIsSet. is the minor version number set? | |
debcffff | 378 | * |
866e5b51 FC |
379 | * @return boolean is the minor set? |
380 | */ | |
381 | public boolean minorIsSet() { | |
fe75d403 | 382 | return fMinor != null; |
866e5b51 FC |
383 | } |
384 | ||
385 | /** | |
386 | * Method UUIDIsSet is the UUID set? | |
debcffff | 387 | * |
866e5b51 | 388 | * @return boolean is the UUID set? |
0594c61c | 389 | * @since 2.0 |
866e5b51 | 390 | */ |
0594c61c | 391 | public boolean uuidIsSet() { |
fe75d403 | 392 | return fUuid != null; |
866e5b51 FC |
393 | } |
394 | ||
395 | /** | |
396 | * Method byteOrderIsSet is the byteorder set? | |
debcffff | 397 | * |
866e5b51 FC |
398 | * @return boolean is the byteorder set? |
399 | */ | |
400 | public boolean byteOrderIsSet() { | |
fe75d403 | 401 | return fByteOrder != null; |
866e5b51 FC |
402 | } |
403 | ||
404 | /** | |
405 | * Method packetHeaderIsSet is the packet header set? | |
debcffff | 406 | * |
866e5b51 FC |
407 | * @return boolean is the packet header set? |
408 | */ | |
409 | public boolean packetHeaderIsSet() { | |
fe75d403 | 410 | return fPacketHeaderDecl != null; |
866e5b51 FC |
411 | } |
412 | ||
413 | /** | |
414 | * Method getUUID gets the trace UUID | |
debcffff | 415 | * |
866e5b51 FC |
416 | * @return UUID gets the trace UUID |
417 | */ | |
418 | public UUID getUUID() { | |
fe75d403 | 419 | return fUuid; |
866e5b51 FC |
420 | } |
421 | ||
422 | /** | |
423 | * Method getMajor gets the trace major version | |
debcffff | 424 | * |
866e5b51 FC |
425 | * @return long gets the trace major version |
426 | */ | |
427 | public long getMajor() { | |
fe75d403 | 428 | return fMajor; |
866e5b51 FC |
429 | } |
430 | ||
431 | /** | |
432 | * Method getMinor gets the trace minor version | |
debcffff | 433 | * |
866e5b51 FC |
434 | * @return long gets the trace minor version |
435 | */ | |
436 | public long getMinor() { | |
fe75d403 | 437 | return fMinor; |
866e5b51 FC |
438 | } |
439 | ||
440 | /** | |
441 | * Method getByteOrder gets the trace byte order | |
debcffff | 442 | * |
866e5b51 FC |
443 | * @return ByteOrder gets the trace byte order |
444 | */ | |
0594c61c | 445 | public final ByteOrder getByteOrder() { |
fe75d403 | 446 | return fByteOrder; |
866e5b51 FC |
447 | } |
448 | ||
449 | /** | |
450 | * Method getPacketHeader gets the trace packet header | |
debcffff | 451 | * |
866e5b51 FC |
452 | * @return StructDeclaration gets the trace packet header |
453 | */ | |
454 | public StructDeclaration getPacketHeader() { | |
fe75d403 | 455 | return fPacketHeaderDecl; |
866e5b51 FC |
456 | } |
457 | ||
458 | /** | |
459 | * Method getTraceDirectory gets the trace directory | |
debcffff | 460 | * |
866e5b51 FC |
461 | * @return File the path in "File" format. |
462 | */ | |
463 | public File getTraceDirectory() { | |
fe75d403 | 464 | return fPath; |
866e5b51 FC |
465 | } |
466 | ||
467 | /** | |
f7c5789a | 468 | * Get all the streams as an iterable. |
debcffff | 469 | * |
f7c5789a | 470 | * @return Iterable<Stream> an iterable over streams. |
951bb9d9 | 471 | * @since 3.0 |
866e5b51 | 472 | */ |
f7c5789a | 473 | public Iterable<Stream> getStreams() { |
fe75d403 | 474 | return fStreams.values(); |
866e5b51 FC |
475 | } |
476 | ||
477 | /** | |
478 | * Method getPath gets the path of the trace directory | |
debcffff | 479 | * |
866e5b51 FC |
480 | * @return String the path of the trace directory, in string format. |
481 | * @see java.io.File#getPath() | |
482 | */ | |
483 | @Override | |
484 | public String getPath() { | |
fe75d403 | 485 | return (fPath != null) ? fPath.getPath() : ""; //$NON-NLS-1$ |
866e5b51 FC |
486 | } |
487 | ||
488 | // ------------------------------------------------------------------------ | |
489 | // Operations | |
490 | // ------------------------------------------------------------------------ | |
491 | ||
fe75d403 MK |
492 | private void addStream(StreamInput s) { |
493 | ||
494 | /* | |
495 | * Copy the events | |
496 | */ | |
497 | Iterator<Entry<Long, IEventDeclaration>> it = s.getStream() | |
498 | .getEvents().entrySet().iterator(); | |
499 | while (it.hasNext()) { | |
500 | Entry<Long, IEventDeclaration> pairs = it.next(); | |
501 | Long eventNum = pairs.getKey(); | |
502 | IEventDeclaration eventDec = pairs.getValue(); | |
503 | getEvents(s.getStream().getId()).put(eventNum, eventDec); | |
504 | } | |
505 | ||
506 | /* | |
507 | * index the trace | |
508 | */ | |
509 | s.setupIndex(); | |
510 | } | |
511 | ||
866e5b51 FC |
512 | /** |
513 | * Tries to open the given file, reads the first packet header of the file | |
fe75d403 | 514 | * and check its validity. This will add a file to a stream as a streaminput |
debcffff | 515 | * |
866e5b51 FC |
516 | * @param streamFile |
517 | * A trace file in the trace directory. | |
26ea03d2 AM |
518 | * @param index |
519 | * Which index in the class' streamFileChannel array this file | |
520 | * must use | |
866e5b51 | 521 | * @throws CTFReaderException |
fe75d403 | 522 | * if there is a file error |
866e5b51 | 523 | */ |
fe75d403 | 524 | private Stream openStreamInput(File streamFile) throws CTFReaderException { |
866e5b51 FC |
525 | MappedByteBuffer byteBuffer; |
526 | BitBuffer streamBitBuffer; | |
d0d3aa1b AM |
527 | Stream stream; |
528 | FileChannel fc; | |
866e5b51 FC |
529 | |
530 | if (!streamFile.canRead()) { | |
531 | throw new CTFReaderException("Unreadable file : " //$NON-NLS-1$ | |
532 | + streamFile.getPath()); | |
533 | } | |
534 | ||
fe75d403 | 535 | FileInputStream fis = null; |
866e5b51 FC |
536 | try { |
537 | /* Open the file and get the FileChannel */ | |
fe75d403 MK |
538 | fis = new FileInputStream(streamFile); |
539 | fFileInputStreams.add(fis); | |
5d1c6919 | 540 | fc = fis.getChannel(); |
866e5b51 FC |
541 | |
542 | /* Map one memory page of 4 kiB */ | |
92bdd7d4 | 543 | byteBuffer = fc.map(MapMode.READ_ONLY, 0, (int) Math.min(fc.size(), 4096L)); |
866e5b51 | 544 | } catch (IOException e) { |
fe75d403 MK |
545 | if (fis != null) { |
546 | fFileInputStreams.remove(fis); | |
547 | } | |
866e5b51 | 548 | /* Shouldn't happen at this stage if every other check passed */ |
0594c61c | 549 | throw new CTFReaderException(e); |
866e5b51 FC |
550 | } |
551 | ||
552 | /* Create a BitBuffer with this mapping and the trace byte order */ | |
553 | streamBitBuffer = new BitBuffer(byteBuffer, this.getByteOrder()); | |
554 | ||
fe75d403 | 555 | if (fPacketHeaderDef != null) { |
866e5b51 | 556 | /* Read the packet header */ |
fe75d403 | 557 | fPacketHeaderDef.read(streamBitBuffer); |
866e5b51 FC |
558 | |
559 | /* Check the magic number */ | |
fe75d403 | 560 | IntegerDefinition magicDef = (IntegerDefinition) fPacketHeaderDef |
aa572e22 | 561 | .lookupDefinition("magic"); //$NON-NLS-1$ |
866e5b51 FC |
562 | int magic = (int) magicDef.getValue(); |
563 | if (magic != Utils.CTF_MAGIC) { | |
564 | throw new CTFReaderException("CTF magic mismatch"); //$NON-NLS-1$ | |
565 | } | |
566 | ||
567 | /* Check UUID */ | |
fe75d403 | 568 | ArrayDefinition uuidDef = (ArrayDefinition) fPacketHeaderDef |
aa572e22 | 569 | .lookupDefinition("uuid"); //$NON-NLS-1$ |
866e5b51 FC |
570 | if (uuidDef != null) { |
571 | byte[] uuidArray = new byte[Utils.UUID_LEN]; | |
572 | ||
573 | for (int i = 0; i < Utils.UUID_LEN; i++) { | |
aa572e22 MK |
574 | IntegerDefinition uuidByteDef = (IntegerDefinition) uuidDef |
575 | .getElem(i); | |
866e5b51 FC |
576 | uuidArray[i] = (byte) uuidByteDef.getValue(); |
577 | } | |
578 | ||
579 | UUID otheruuid = Utils.makeUUID(uuidArray); | |
580 | ||
fe75d403 | 581 | if (!fUuid.equals(otheruuid)) { |
866e5b51 FC |
582 | throw new CTFReaderException("UUID mismatch"); //$NON-NLS-1$ |
583 | } | |
584 | } | |
585 | ||
1fbaecd1 | 586 | /* Read the stream ID */ |
fe75d403 | 587 | Definition streamIDDef = fPacketHeaderDef.lookupDefinition("stream_id"); //$NON-NLS-1$ |
1fbaecd1 | 588 | |
a7297cd3 SD |
589 | if (streamIDDef instanceof IntegerDefinition) { // this doubles as a |
590 | // null check | |
1fbaecd1 | 591 | long streamID = ((IntegerDefinition) streamIDDef).getValue(); |
fe75d403 | 592 | stream = fStreams.get(streamID); |
1fbaecd1 AM |
593 | } else { |
594 | /* No stream_id in the packet header */ | |
fe75d403 | 595 | stream = fStreams.get(null); |
1fbaecd1 AM |
596 | } |
597 | ||
866e5b51 FC |
598 | } else { |
599 | /* No packet header, we suppose there is only one stream */ | |
fe75d403 | 600 | stream = fStreams.get(null); |
d0d3aa1b | 601 | } |
866e5b51 | 602 | |
4311ac8b MAL |
603 | if (stream == null) { |
604 | throw new CTFReaderException("Unexpected end of stream"); //$NON-NLS-1$ | |
605 | } | |
606 | ||
d0d3aa1b AM |
607 | /* Create the stream input */ |
608 | StreamInput streamInput = new StreamInput(stream, fc, streamFile); | |
866e5b51 | 609 | |
d0d3aa1b AM |
610 | /* Add a reference to the streamInput in the stream */ |
611 | stream.addInput(streamInput); | |
fe75d403 MK |
612 | |
613 | return stream; | |
866e5b51 FC |
614 | } |
615 | ||
616 | /** | |
617 | * Looks up a definition from packet | |
debcffff | 618 | * |
866e5b51 FC |
619 | * @param lookupPath |
620 | * String | |
621 | * @return Definition | |
622 | * @see org.eclipse.linuxtools.ctf.core.event.types.IDefinitionScope#lookupDefinition(String) | |
623 | */ | |
624 | @Override | |
625 | public Definition lookupDefinition(String lookupPath) { | |
626 | if (lookupPath.equals("trace.packet.header")) { //$NON-NLS-1$ | |
fe75d403 | 627 | return fPacketHeaderDef; |
866e5b51 FC |
628 | } |
629 | return null; | |
630 | } | |
631 | ||
632 | /** | |
fe75d403 MK |
633 | * Add a new stream file to support new streams while the trace is being |
634 | * read. | |
635 | * | |
636 | * @param streamFile | |
637 | * the file of the stream | |
638 | * @throws CTFReaderException | |
639 | * A stream had an issue being read | |
fe75d403 MK |
640 | * @since 3.0 |
641 | */ | |
642 | public void addStreamFile(File streamFile) throws CTFReaderException { | |
643 | openStreamInput(streamFile); | |
644 | } | |
645 | ||
646 | /** | |
647 | * Registers a new stream to the trace. | |
debcffff | 648 | * |
866e5b51 FC |
649 | * @param stream |
650 | * A stream object. | |
866e5b51 | 651 | * @throws ParseException |
be6df2d8 | 652 | * If there was some problem reading the metadata |
e6809677 | 653 | * @since 2.0 |
866e5b51 FC |
654 | */ |
655 | public void addStream(Stream stream) throws ParseException { | |
656 | ||
fe75d403 MK |
657 | /* |
658 | * Init if not done before | |
659 | */ | |
660 | init(); | |
661 | ||
866e5b51 FC |
662 | /* |
663 | * If there is already a stream without id (the null key), it must be | |
664 | * the only one | |
665 | */ | |
fe75d403 | 666 | if (fStreams.get(null) != null) { |
866e5b51 FC |
667 | throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
668 | } | |
669 | ||
670 | /* | |
1d7277f3 MK |
671 | * If the stream we try to add has the null key, it must be the only |
672 | * one. Thus, if the streams container is not empty, it is not valid. | |
866e5b51 | 673 | */ |
fe75d403 | 674 | if ((stream.getId() == null) && (fStreams.size() != 0)) { |
866e5b51 FC |
675 | throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
676 | } | |
677 | ||
fe75d403 MK |
678 | /* |
679 | * If a stream with the same ID already exists, it is not valid. | |
680 | */ | |
681 | Stream existingStream = fStreams.get(stream.getId()); | |
682 | if (existingStream != null) { | |
866e5b51 FC |
683 | throw new ParseException("Stream id already exists"); //$NON-NLS-1$ |
684 | } | |
685 | ||
29a7d6ee | 686 | /* This stream is valid and has a unique id. */ |
fe75d403 | 687 | fStreams.put(stream.getId(), stream); |
866e5b51 FC |
688 | } |
689 | ||
9ac2eb62 | 690 | /** |
29a7d6ee | 691 | * Gets the Environment variables from the trace metadata (See CTF spec) |
a7297cd3 | 692 | * |
a95fddf5 EB |
693 | * @return The environment variables in the form of an unmodifiable map |
694 | * (key, value) | |
486efb2e | 695 | * @since 2.0 |
9ac2eb62 | 696 | */ |
791072b0 | 697 | public Map<String, String> getEnvironment() { |
fe75d403 | 698 | return Collections.unmodifiableMap(fEnvironment); |
866e5b51 FC |
699 | } |
700 | ||
9ac2eb62 MK |
701 | /** |
702 | * Add a variable to the environment variables | |
a7297cd3 SD |
703 | * |
704 | * @param varName | |
705 | * the name of the variable | |
706 | * @param varValue | |
707 | * the value of the variable | |
9ac2eb62 | 708 | */ |
c88e827d | 709 | public void addEnvironmentVar(String varName, String varValue) { |
fe75d403 | 710 | fEnvironment.put(varName, varValue); |
866e5b51 FC |
711 | } |
712 | ||
9ac2eb62 MK |
713 | /** |
714 | * Add a clock to the clock list | |
a7297cd3 SD |
715 | * |
716 | * @param nameValue | |
717 | * the name of the clock (full name with scope) | |
718 | * @param ctfClock | |
719 | * the clock | |
9ac2eb62 | 720 | */ |
866e5b51 | 721 | public void addClock(String nameValue, CTFClock ctfClock) { |
fe75d403 | 722 | fClocks.put(nameValue, ctfClock); |
866e5b51 FC |
723 | } |
724 | ||
9ac2eb62 MK |
725 | /** |
726 | * gets the clock with a specific name | |
a7297cd3 SD |
727 | * |
728 | * @param name | |
729 | * the name of the clock. | |
9ac2eb62 MK |
730 | * @return the clock |
731 | */ | |
c88e827d | 732 | public CTFClock getClock(String name) { |
fe75d403 | 733 | return fClocks.get(name); |
866e5b51 FC |
734 | } |
735 | ||
9ac2eb62 | 736 | /** |
1d7277f3 MK |
737 | * gets the clock if there is only one. (this is 100% of the use cases as of |
738 | * June 2012) | |
739 | * | |
9ac2eb62 MK |
740 | * @return the clock |
741 | */ | |
8ecc80f3 | 742 | public final CTFClock getClock() { |
fe75d403 MK |
743 | if (fClocks.size() == 1) { |
744 | fSingleClock = fClocks.get(fClocks.keySet().iterator().next()); | |
745 | return fSingleClock; | |
866e5b51 FC |
746 | } |
747 | return null; | |
748 | } | |
749 | ||
9ac2eb62 MK |
750 | /** |
751 | * gets the time offset of a clock with respect to UTC in nanoseconds | |
1d7277f3 | 752 | * |
9ac2eb62 MK |
753 | * @return the time offset of a clock with respect to UTC in nanoseconds |
754 | */ | |
8ecc80f3 | 755 | public final long getOffset() { |
c88e827d | 756 | if (getClock() == null) { |
ce2388e0 FC |
757 | return 0; |
758 | } | |
fe75d403 | 759 | return fSingleClock.getClockOffset(); |
1d7277f3 MK |
760 | } |
761 | ||
762 | /** | |
763 | * gets the time offset of a clock with respect to UTC in nanoseconds | |
764 | * | |
765 | * @return the time offset of a clock with respect to UTC in nanoseconds | |
766 | */ | |
0594c61c | 767 | private double getTimeScale() { |
1d7277f3 MK |
768 | if (getClock() == null) { |
769 | return 1.0; | |
770 | } | |
fe75d403 | 771 | return fSingleClock.getClockScale(); |
1d7277f3 MK |
772 | } |
773 | ||
774 | /** | |
775 | * Does the trace need to time scale? | |
776 | * | |
777 | * @return if the trace is in ns or cycles. | |
778 | */ | |
0594c61c | 779 | private boolean clockNeedsScale() { |
1d7277f3 MK |
780 | if (getClock() == null) { |
781 | return false; | |
782 | } | |
fe75d403 | 783 | return fSingleClock.isClockScaled(); |
1d7277f3 MK |
784 | } |
785 | ||
786 | /** | |
787 | * the inverse clock for returning to a scale. | |
788 | * | |
789 | * @return 1.0 / scale | |
790 | */ | |
0594c61c | 791 | private double getInverseTimeScale() { |
1d7277f3 MK |
792 | if (getClock() == null) { |
793 | return 1.0; | |
794 | } | |
fe75d403 | 795 | return fSingleClock.getClockAntiScale(); |
1d7277f3 MK |
796 | } |
797 | ||
798 | /** | |
799 | * @param cycles | |
800 | * clock cycles since boot | |
801 | * @return time in nanoseconds UTC offset | |
486efb2e | 802 | * @since 2.0 |
1d7277f3 MK |
803 | */ |
804 | public long timestampCyclesToNanos(long cycles) { | |
805 | long retVal = cycles + getOffset(); | |
806 | /* | |
807 | * this fix is since quite often the offset will be > than 53 bits and | |
808 | * therefore the conversion will be lossy | |
809 | */ | |
810 | if (clockNeedsScale()) { | |
811 | retVal = (long) (retVal * getTimeScale()); | |
812 | } | |
813 | return retVal; | |
814 | } | |
815 | ||
816 | /** | |
817 | * @param nanos | |
818 | * time in nanoseconds UTC offset | |
819 | * @return clock cycles since boot. | |
486efb2e | 820 | * @since 2.0 |
1d7277f3 MK |
821 | */ |
822 | public long timestampNanoToCycles(long nanos) { | |
823 | long retVal; | |
824 | /* | |
825 | * this fix is since quite often the offset will be > than 53 bits and | |
826 | * therefore the conversion will be lossy | |
827 | */ | |
828 | if (clockNeedsScale()) { | |
829 | retVal = (long) (nanos * getInverseTimeScale()); | |
830 | } else { | |
831 | retVal = nanos; | |
832 | } | |
833 | return retVal - getOffset(); | |
ce2388e0 FC |
834 | } |
835 | ||
4c9d2941 MK |
836 | /** |
837 | * Adds a callsite | |
838 | * | |
839 | * @param eventName | |
840 | * the event name of the callsite | |
841 | * @param funcName | |
842 | * the name of the callsite function | |
843 | * @param ip | |
844 | * the ip of the callsite | |
845 | * @param fileName | |
846 | * the filename of the callsite | |
847 | * @param lineNumber | |
848 | * the line number of the callsite | |
849 | */ | |
850 | public void addCallsite(String eventName, String funcName, long ip, | |
851 | String fileName, long lineNumber) { | |
852 | final CTFCallsite cs = new CTFCallsite(eventName, funcName, ip, | |
853 | fileName, lineNumber); | |
fe75d403 | 854 | TreeSet<CTFCallsite> csl = fCallsitesByName.get(eventName); |
4c9d2941 | 855 | if (csl == null) { |
fe75d403 MK |
856 | csl = new TreeSet<>(fCtfCallsiteComparator); |
857 | fCallsitesByName.put(eventName, csl); | |
4c9d2941 MK |
858 | } |
859 | ||
890f9136 | 860 | csl.add(cs); |
4c9d2941 | 861 | |
fe75d403 | 862 | fCallsitesByIP.add(cs); |
4c9d2941 MK |
863 | } |
864 | ||
865 | /** | |
890f9136 | 866 | * Gets the set of callsites associated to an event name. O(1) |
4c9d2941 MK |
867 | * |
868 | * @param eventName | |
869 | * the event name | |
890f9136 SD |
870 | * @return the callsite set can be empty |
871 | * @since 3.0 | |
4c9d2941 | 872 | */ |
890f9136 | 873 | public TreeSet<CTFCallsite> getCallsiteCandidates(String eventName) { |
fe75d403 | 874 | TreeSet<CTFCallsite> retVal = fCallsitesByName.get(eventName); |
890f9136 | 875 | if (retVal == null) { |
fe75d403 | 876 | retVal = new TreeSet<>(fCtfCallsiteComparator); |
4c9d2941 MK |
877 | } |
878 | return retVal; | |
879 | } | |
880 | ||
881 | /** | |
882 | * The I'm feeling lucky of getCallsiteCandidates O(1) | |
883 | * | |
884 | * @param eventName | |
885 | * the event name | |
886 | * @return the first callsite that has that event name, can be null | |
887 | * @since 1.2 | |
888 | */ | |
889 | public CTFCallsite getCallsite(String eventName) { | |
fe75d403 | 890 | TreeSet<CTFCallsite> callsites = fCallsitesByName.get(eventName); |
60fb38b8 | 891 | if (callsites != null) { |
890f9136 | 892 | return callsites.first(); |
60fb38b8 PT |
893 | } |
894 | return null; | |
4c9d2941 MK |
895 | } |
896 | ||
897 | /** | |
898 | * Gets a callsite from the instruction pointer O(log(n)) | |
899 | * | |
900 | * @param ip | |
901 | * the instruction pointer to lookup | |
902 | * @return the callsite just before that IP in the list remember the IP is | |
903 | * backwards on X86, can be null if no callsite is before the IP. | |
904 | * @since 1.2 | |
905 | */ | |
906 | public CTFCallsite getCallsite(long ip) { | |
907 | CTFCallsite cs = new CTFCallsite(null, null, ip, null, 0L); | |
fe75d403 | 908 | return fCallsitesByIP.ceiling(cs); |
4c9d2941 MK |
909 | } |
910 | ||
911 | /** | |
912 | * Gets a callsite using the event name and instruction pointer O(log(n)) | |
913 | * | |
914 | * @param eventName | |
915 | * the name of the event | |
916 | * @param ip | |
917 | * the instruction pointer | |
918 | * @return the closest matching callsite, can be null | |
919 | */ | |
920 | public CTFCallsite getCallsite(String eventName, long ip) { | |
fe75d403 | 921 | final TreeSet<CTFCallsite> candidates = fCallsitesByName.get(eventName); |
4c9d2941 | 922 | final CTFCallsite dummyCs = new CTFCallsite(null, null, ip, null, -1); |
890f9136 SD |
923 | final CTFCallsite callsite = candidates.ceiling(dummyCs); |
924 | if (callsite == null) { | |
925 | return candidates.floor(dummyCs); | |
4c9d2941 | 926 | } |
890f9136 | 927 | return callsite; |
4c9d2941 | 928 | } |
866e5b51 | 929 | } |
c88e827d AM |
930 | |
931 | class MetadataFileFilter implements FileFilter { | |
932 | ||
933 | @Override | |
934 | public boolean accept(File pathname) { | |
935 | if (pathname.isDirectory()) { | |
936 | return false; | |
937 | } | |
938 | if (pathname.isHidden()) { | |
939 | return false; | |
940 | } | |
941 | if (pathname.getName().equals("metadata")) { //$NON-NLS-1$ | |
942 | return false; | |
943 | } | |
944 | return true; | |
945 | } | |
946 | ||
947 | } | |
948 | ||
debcffff | 949 | class MetadataComparator implements Comparator<File>, Serializable { |
c88e827d | 950 | |
8fd82db5 FC |
951 | private static final long serialVersionUID = 1L; |
952 | ||
c88e827d AM |
953 | @Override |
954 | public int compare(File o1, File o2) { | |
955 | return o1.getName().compareTo(o2.getName()); | |
956 | } | |
957 | } |