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