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