| /******************************************************************************* |
| * Copyright (c) 2011, 2018 Ericsson, Ecole Polytechnique de Montreal and others |
| * |
| * All rights reserved. This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Matthew Khouzam - Initial API and implementation |
| * Alexandre Montplaisir - Initial API and implementation |
| * Simon Delisle - Replace LinkedList by TreeSet in callsitesByName attribute |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.ctf.core.trace; |
| |
| import java.io.File; |
| import java.io.FileFilter; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Serializable; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.channels.FileChannel; |
| import java.nio.channels.FileChannel.MapMode; |
| import java.nio.file.StandardOpenOption; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.ctf.core.CTFException; |
| import org.eclipse.tracecompass.ctf.core.CTFStrings; |
| import org.eclipse.tracecompass.ctf.core.event.CTFClock; |
| import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer; |
| import org.eclipse.tracecompass.ctf.core.event.metadata.DeclarationScope; |
| import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope; |
| import org.eclipse.tracecompass.ctf.core.event.scope.ILexicalScope; |
| import org.eclipse.tracecompass.ctf.core.event.types.AbstractArrayDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.Definition; |
| import org.eclipse.tracecompass.ctf.core.event.types.IDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.IntegerDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.types.StructDefinition; |
| import org.eclipse.tracecompass.internal.ctf.core.Activator; |
| import org.eclipse.tracecompass.internal.ctf.core.SafeMappedByteBuffer; |
| import org.eclipse.tracecompass.internal.ctf.core.event.metadata.MetadataStrings; |
| import org.eclipse.tracecompass.internal.ctf.core.event.metadata.ParseException; |
| import org.eclipse.tracecompass.internal.ctf.core.trace.CTFStream; |
| import org.eclipse.tracecompass.internal.ctf.core.utils.Utils; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| /** |
| * A CTF trace on the file system. |
| * |
| * Represents a trace on the filesystem. It is responsible of parsing the |
| * metadata, creating declarations data structures, indexing the event packets |
| * (in other words, all the work that can be shared between readers), but the |
| * actual reading of events is left to TraceReader. |
| * |
| * TODO: internalize CTFTrace and DeclarationScope |
| * |
| * @author Matthew Khouzam |
| * @version $Revision: 1.0 $ |
| */ |
| public class CTFTrace implements IDefinitionScope { |
| |
| @Override |
| public String toString() { |
| /* Only for debugging, shouldn't be externalized */ |
| return "CTFTrace [path=" + fPath + ", major=" + fMajor + ", minor=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| + fMinor + ", uuid=" + fUuid + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * The trace directory on the filesystem. |
| */ |
| private final File fPath; |
| |
| /** |
| * Major CTF version number |
| */ |
| private Long fMajor; |
| |
| /** |
| * Minor CTF version number |
| */ |
| private Long fMinor; |
| |
| /** |
| * Trace UUID |
| */ |
| private UUID fUuid; |
| |
| /** |
| * Trace byte order |
| */ |
| private ByteOrder fByteOrder; |
| |
| /** |
| * Packet header structure declaration |
| */ |
| private StructDeclaration fPacketHeaderDecl = null; |
| |
| /** |
| * The clock of the trace |
| */ |
| private CTFClock fSingleClock = null; |
| |
| /** |
| * Packet header structure definition |
| * |
| * This is only used when opening the trace files, to read the first packet |
| * header and see if they are valid trace files. |
| */ |
| private StructDefinition fPacketHeaderDef; |
| |
| /** |
| * Collection of streams contained in the trace. |
| */ |
| private final Map<Long, ICTFStream> fStreams = new HashMap<>(); |
| |
| /** |
| * Collection of environment variables set by the tracer |
| */ |
| private Map<String, String> fEnvironment = new HashMap<>(); |
| |
| /** |
| * Collection of all the clocks in a system. |
| */ |
| private final Map<String, CTFClock> fClocks = new HashMap<>(); |
| |
| /** Handlers for the metadata files */ |
| private static final FileFilter METADATA_FILE_FILTER = new MetadataFileFilter(); |
| private static final Comparator<File> METADATA_COMPARATOR = new MetadataComparator(); |
| |
| private final DeclarationScope fScope = new DeclarationScope(null, MetadataStrings.TRACE); |
| |
| private boolean fUUIDMismatchWarning = false; |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Trace constructor. |
| * |
| * @param path |
| * Filesystem path of the trace directory |
| * @throws CTFException |
| * If no CTF trace was found at the path |
| */ |
| public CTFTrace(String path) throws CTFException { |
| this(new File(path)); |
| } |
| |
| /** |
| * Trace constructor. |
| * |
| * @param path |
| * Filesystem path of the trace directory. |
| * @throws CTFException |
| * If no CTF trace was found at the path |
| */ |
| public CTFTrace(File path) throws CTFException { |
| fPath = path; |
| final Metadata metadata = new Metadata(this); |
| |
| /* Set up the internal containers for this trace */ |
| if (!fPath.exists()) { |
| throw new CTFException("Trace (" + path.getPath() + ") doesn't exist. Deleted or moved?"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| if (!fPath.isDirectory()) { |
| throw new CTFException("Path must be a valid directory " + fPath); //$NON-NLS-1$ |
| } |
| |
| /* Open and parse the metadata file */ |
| metadata.parseFile(); |
| |
| init(path); |
| } |
| |
| /** |
| * Streamed constructor |
| */ |
| public CTFTrace() { |
| fPath = null; |
| } |
| |
| private void init(File path) throws CTFException { |
| |
| /* Open all the trace files */ |
| |
| /* List files not called metadata and not hidden. */ |
| File[] files = path.listFiles(METADATA_FILE_FILTER); |
| if (files == null) { |
| throw new CTFException("Trace (" + path.getPath() + ") cannot be read. Deleted or moved?"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| Arrays.sort(files, METADATA_COMPARATOR); |
| |
| /* Try to open each file */ |
| for (File streamFile : files) { |
| openStreamInput(streamFile); |
| } |
| |
| /* Create their index */ |
| for (ICTFStream stream : getStreams()) { |
| Set<CTFStreamInput> inputs = stream.getStreamInputs(); |
| for (CTFStreamInput s : inputs) { |
| addStream(s); |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Getters/Setters/Predicates |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Gets an event declaration list for a given streamID |
| * |
| * @param streamId |
| * The ID of the stream from which to read |
| * @return The list of event declarations |
| */ |
| public Collection<@Nullable IEventDeclaration> getEventDeclarations(Long streamId) { |
| ICTFStream stream = fStreams.get(streamId); |
| if (stream == null) { |
| return null; |
| } |
| return stream.getEventDeclarations(); |
| } |
| |
| /** |
| * Method getStream gets the stream for a given id |
| * |
| * @param id |
| * Long the id of the stream |
| * @return Stream the stream that we need |
| * @since 2.0 |
| */ |
| public ICTFStream getStream(Long id) { |
| if (id == null) { |
| return fStreams.get(0L); |
| } |
| return fStreams.get(id); |
| } |
| |
| /** |
| * Method nbStreams gets the number of available streams |
| * |
| * @return int the number of streams |
| */ |
| public int nbStreams() { |
| return fStreams.size(); |
| } |
| |
| /** |
| * Method setMajor sets the major version of the trace (DO NOT USE) |
| * |
| * @param major |
| * long the major version |
| */ |
| public void setMajor(long major) { |
| fMajor = major; |
| } |
| |
| /** |
| * Method setMinor sets the minor version of the trace (DO NOT USE) |
| * |
| * @param minor |
| * long the minor version |
| */ |
| public void setMinor(long minor) { |
| fMinor = minor; |
| } |
| |
| /** |
| * Method setUUID sets the UUID of a trace |
| * |
| * @param uuid |
| * UUID |
| */ |
| public void setUUID(UUID uuid) { |
| fUuid = uuid; |
| } |
| |
| /** |
| * Method setByteOrder sets the byte order |
| * |
| * @param byteOrder |
| * ByteOrder of the trace, can be little-endian or big-endian |
| */ |
| public void setByteOrder(ByteOrder byteOrder) { |
| fByteOrder = byteOrder; |
| } |
| |
| /** |
| * Method setPacketHeader sets the packet header of a trace (DO NOT USE) |
| * |
| * @param packetHeader |
| * StructDeclaration the header in structdeclaration form |
| */ |
| public void setPacketHeader(StructDeclaration packetHeader) { |
| fPacketHeaderDecl = packetHeader; |
| } |
| |
| /** |
| * Method majorIsSet is the major version number set? |
| * |
| * @return boolean is the major set? |
| */ |
| public boolean majorIsSet() { |
| return fMajor != null; |
| } |
| |
| /** |
| * Method minorIsSet. is the minor version number set? |
| * |
| * @return boolean is the minor set? |
| */ |
| public boolean minorIsSet() { |
| return fMinor != null; |
| } |
| |
| /** |
| * Method UUIDIsSet is the UUID set? |
| * |
| * @return boolean is the UUID set? |
| */ |
| public boolean uuidIsSet() { |
| return fUuid != null; |
| } |
| |
| /** |
| * Method byteOrderIsSet is the byteorder set? |
| * |
| * @return boolean is the byteorder set? |
| */ |
| public boolean byteOrderIsSet() { |
| return fByteOrder != null; |
| } |
| |
| /** |
| * Method packetHeaderIsSet is the packet header set? |
| * |
| * @return boolean is the packet header set? |
| */ |
| public boolean packetHeaderIsSet() { |
| return fPacketHeaderDecl != null; |
| } |
| |
| /** |
| * Method getUUID gets the trace UUID |
| * |
| * @return UUID gets the trace UUID |
| */ |
| public UUID getUUID() { |
| return fUuid; |
| } |
| |
| /** |
| * Method getMajor gets the trace major version |
| * |
| * @return long gets the trace major version |
| */ |
| public long getMajor() { |
| return fMajor; |
| } |
| |
| /** |
| * Method getMinor gets the trace minor version |
| * |
| * @return long gets the trace minor version |
| */ |
| public long getMinor() { |
| return fMinor; |
| } |
| |
| /** |
| * Method getByteOrder gets the trace byte order |
| * |
| * @return ByteOrder gets the trace byte order |
| */ |
| public final ByteOrder getByteOrder() { |
| return fByteOrder; |
| } |
| |
| /** |
| * Method getPacketHeader gets the trace packet header |
| * |
| * @return StructDeclaration gets the trace packet header |
| */ |
| public StructDeclaration getPacketHeader() { |
| return fPacketHeaderDecl; |
| } |
| |
| /** |
| * Method getTraceDirectory gets the trace directory |
| * |
| * @return File the path in "File" format. |
| */ |
| public File getTraceDirectory() { |
| return fPath; |
| } |
| |
| /** |
| * Get all the streams as an iterable. |
| * |
| * @return Iterable<Stream> an iterable over streams. |
| */ |
| public Iterable<ICTFStream> getStreams() { |
| return fStreams.values(); |
| } |
| |
| /** |
| * Method getPath gets the path of the trace directory |
| * |
| * @return String the path of the trace directory, in string format. |
| * @see java.io.File#getPath() |
| */ |
| public String getPath() { |
| return (fPath != null) ? fPath.getPath() : ""; //$NON-NLS-1$ |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Operations |
| // ------------------------------------------------------------------------ |
| |
| private void addStream(CTFStreamInput s) { |
| |
| /* |
| * add the stream |
| */ |
| ICTFStream stream = s.getStream(); |
| fStreams.put(stream.getId(), stream); |
| |
| /* |
| * index the trace |
| */ |
| s.setupIndex(); |
| } |
| |
| /** |
| * Tries to open the given file, reads the first packet header of the file and |
| * check its validity. This will add a file to a stream as a streaminput |
| * |
| * @param streamFile |
| * A trace file in the trace directory. |
| * @param index |
| * Which index in the class' streamFileChannel array this file must |
| * use |
| * @return the {@link CTFStream} or null if invalid |
| * @throws CTFException |
| * if there is a file error |
| */ |
| private ICTFStream openStreamInput(File streamFile) throws CTFException { |
| ByteBuffer byteBuffer; |
| BitBuffer streamBitBuffer; |
| ICTFStream stream; |
| |
| if (!streamFile.canRead()) { |
| throw new CTFException("Unreadable file : " //$NON-NLS-1$ |
| + streamFile.getPath()); |
| } |
| if (streamFile.length() == 0) { |
| return null; |
| } |
| try (FileChannel fc = FileChannel.open(streamFile.toPath(), StandardOpenOption.READ)) { |
| /* Map one memory page of 4 kiB */ |
| byteBuffer = SafeMappedByteBuffer.map(fc, MapMode.READ_ONLY, 0, (int) Math.min(fc.size(), 4096L)); |
| /* Create a BitBuffer with this mapping and the trace byte order */ |
| streamBitBuffer = new BitBuffer(byteBuffer, this.getByteOrder()); |
| if (fPacketHeaderDecl != null) { |
| /* Read the packet header */ |
| fPacketHeaderDef = fPacketHeaderDecl.createDefinition(this, ILexicalScope.PACKET_HEADER, streamBitBuffer); |
| } |
| } catch (IOException e) { |
| /* Shouldn't happen at this stage if every other check passed */ |
| throw new CTFException(e); |
| } |
| final StructDefinition packetHeaderDef = getPacketHeaderDef(); |
| if (packetHeaderDef != null) { |
| if (!validateMagicNumber(packetHeaderDef)) { |
| return null; |
| } |
| |
| validateUUID(packetHeaderDef); |
| |
| /* Read the stream ID */ |
| IDefinition streamIDDef = packetHeaderDef.lookupDefinition(MetadataStrings.STREAM_ID); |
| |
| if (streamIDDef instanceof IntegerDefinition) { |
| /* This doubles as a null check */ |
| long streamID = ((IntegerDefinition) streamIDDef).getValue(); |
| stream = fStreams.get(streamID); |
| } else { |
| /* No stream_id in the packet header */ |
| stream = getStream(null); |
| } |
| |
| } else { |
| /* No packet header, we suppose there is only one stream */ |
| stream = getStream(null); |
| } |
| |
| if (stream == null) { |
| throw new CTFException("Unexpected end of stream " + fPath + " ( " + byteBuffer.position() + " / " + (byteBuffer.position() + byteBuffer.remaining()) + " )"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| } |
| |
| if (!(stream instanceof CTFStream)) { |
| throw new CTFException("Stream is not a CTFStream, but rather a " + stream.getClass().getCanonicalName()); //$NON-NLS-1$ |
| } |
| CTFStream ctfStream = (CTFStream) stream; |
| /* |
| * Create the stream input and add a reference to the streamInput in the stream. |
| */ |
| ctfStream.addInput(new CTFStreamInput(ctfStream, streamFile)); |
| return ctfStream; |
| } |
| |
| private void validateUUID(StructDefinition packetHeaderDef) throws CTFException { |
| IDefinition lookupDefinition = packetHeaderDef.lookupDefinition("uuid"); //$NON-NLS-1$ |
| AbstractArrayDefinition uuidDef = (AbstractArrayDefinition) lookupDefinition; |
| if (uuidDef != null) { |
| UUID otheruuid = Utils.getUUIDfromDefinition(uuidDef); |
| if (!fUuid.equals(otheruuid) && !fUUIDMismatchWarning) { |
| fUUIDMismatchWarning = true; |
| Activator.log(IStatus.WARNING, "Reading CTF trace: UUID mismatch for trace " + this); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| private static boolean validateMagicNumber(StructDefinition packetHeaderDef) { |
| IntegerDefinition magicDef = (IntegerDefinition) packetHeaderDef.lookupDefinition(CTFStrings.MAGIC); |
| if (magicDef != null) { |
| int magic = (int) magicDef.getValue(); |
| return (magic == Utils.CTF_MAGIC); |
| } |
| return true; |
| } |
| |
| /** |
| * Determines whether the file is packet-based by looking at the TSDL or CTF |
| * magic number. If it is packet-based, it also gives information about the |
| * endianness of the trace using the detectedByteOrder attribute. |
| * |
| * @param file |
| * the file to test. |
| * @param magicNumber |
| * The magic number to verify |
| * @return The byte order if the file is packet-based. (starts with a magic |
| * number), null if not packet based |
| * @throws CTFException |
| * If the file is not found. |
| * @since 2.2 |
| */ |
| public static ByteOrder startsWithMagicNumber(File file, int magicNumber) |
| throws CTFException { |
| |
| int magicSize = Integer.BYTES; |
| if (file.length() < magicSize) { |
| return null; |
| } |
| byte[] magic = new byte[magicSize]; |
| try (InputStream is = new FileInputStream(file)) { |
| is.read(magic, 0, magic.length); |
| } catch (IOException e) { |
| throw new CTFIOException(e); |
| } |
| ByteBuffer bb = ByteBuffer.wrap(magic); |
| if (isMagicWithEndianness(bb, ByteOrder.LITTLE_ENDIAN, magicNumber)) { |
| return ByteOrder.LITTLE_ENDIAN; |
| } |
| if (isMagicWithEndianness(bb, ByteOrder.BIG_ENDIAN, magicNumber)) { |
| return ByteOrder.BIG_ENDIAN; |
| } |
| return null; |
| } |
| |
| private static boolean isMagicWithEndianness(ByteBuffer byteBuffer, ByteOrder endianness, int magicNumber) { |
| byteBuffer.position(0); |
| byteBuffer.order(endianness); |
| int magic = byteBuffer.getInt(); |
| return (magic == magicNumber); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // IDefinitionScope |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * @since 1.0 |
| */ |
| @Override |
| public ILexicalScope getScopePath() { |
| return ILexicalScope.TRACE; |
| } |
| |
| /** |
| * Looks up a definition from packet |
| * |
| * @param lookupPath |
| * String |
| * @return Definition |
| * @see org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope#lookupDefinition(String) |
| */ |
| @Override |
| public Definition lookupDefinition(String lookupPath) { |
| if (lookupPath.equals(ILexicalScope.TRACE_PACKET_HEADER.getPath())) { |
| return getPacketHeaderDef(); |
| } |
| return null; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Live trace reading |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Add a new stream file to support new streams while the trace is being read. |
| * |
| * @param streamFile |
| * the file of the stream |
| * @throws CTFException |
| * A stream had an issue being read |
| */ |
| public void addStreamFile(File streamFile) throws CTFException { |
| openStreamInput(streamFile); |
| } |
| |
| /** |
| * Registers a new stream to the trace. |
| * |
| * @param stream |
| * A stream object. |
| * @throws ParseException |
| * If there was some problem reading the metadata |
| * @since 2.0 |
| */ |
| public void addStream(ICTFStream stream) throws ParseException { |
| /* |
| * If there is already a stream without id (the null key), it must be the only |
| * one |
| */ |
| if (fStreams.get(null) != null) { |
| throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
| } |
| |
| /* |
| * If the stream we try to add has no key set, it must be the only one. Thus, if |
| * the streams container is not empty, it is not valid. |
| */ |
| if ((!stream.isIdSet()) && (!fStreams.isEmpty())) { |
| throw new ParseException("Stream without id with multiple streams"); //$NON-NLS-1$ |
| } |
| |
| /* |
| * If a stream with the same ID already exists, it is not valid. |
| */ |
| ICTFStream existingStream = fStreams.get(stream.getId()); |
| if (existingStream != null) { |
| throw new ParseException("Stream id already exists"); //$NON-NLS-1$ |
| } |
| |
| /* This stream is valid and has a unique id. */ |
| fStreams.put(stream.getId(), stream); |
| } |
| |
| /** |
| * Gets the Environment variables from the trace metadata (See CTF spec) |
| * |
| * @return The environment variables in the form of an unmodifiable map (key, |
| * value) |
| */ |
| public Map<String, String> getEnvironment() { |
| return Collections.unmodifiableMap(fEnvironment); |
| } |
| |
| /** |
| * Add a clock to the clock list |
| * |
| * @param nameValue |
| * the name of the clock (full name with scope) |
| * @param ctfClock |
| * the clock |
| */ |
| public void addClock(String nameValue, CTFClock ctfClock) { |
| fClocks.put(nameValue, ctfClock); |
| } |
| |
| /** |
| * gets the clock with a specific name |
| * |
| * @param name |
| * the name of the clock. |
| * @return the clock |
| */ |
| public CTFClock getClock(String name) { |
| return fClocks.get(name); |
| } |
| |
| /** |
| * gets the clock if there is only one. (this is 100% of the use cases as of |
| * June 2012) |
| * |
| * @return the clock |
| */ |
| public final CTFClock getClock() { |
| if (fSingleClock != null && fClocks.size() == 1) { |
| return fSingleClock; |
| } |
| if (fClocks.size() == 1) { |
| fSingleClock = fClocks.get(fClocks.keySet().iterator().next()); |
| return fSingleClock; |
| } |
| return null; |
| } |
| |
| /** |
| * Gets the clock offset with respect to POSIX.1 Epoch in cycles |
| * |
| * @return the clock offset with respect to POSIX.1 Epoch in cycles |
| */ |
| public final long getOffset() { |
| if (getClock() == null) { |
| return 0; |
| } |
| return fSingleClock.getClockOffset(); |
| } |
| |
| /** |
| * Gets the time scale in nanoseconds/cycle |
| * |
| * @return the time scale in nanoseconds/cycle |
| */ |
| private double getTimeScale() { |
| if (getClock() == null) { |
| return 1.0; |
| } |
| return fSingleClock.getClockScale(); |
| } |
| |
| /** |
| * Gets the current first packet start time |
| * |
| * @return the current start time |
| */ |
| public long getCurrentStartTime() { |
| long currentStart = Long.MAX_VALUE; |
| for (ICTFStream stream : fStreams.values()) { |
| for (CTFStreamInput si : stream.getStreamInputs()) { |
| currentStart = Math.min(currentStart, si.getIndex().getElement(0).getTimestampBegin()); |
| } |
| } |
| return timestampCyclesToNanos(currentStart); |
| } |
| |
| /** |
| * Gets the current last packet end time |
| * |
| * @return the current end time |
| */ |
| public long getCurrentEndTime() { |
| long currentEnd = Long.MIN_VALUE; |
| for (ICTFStream stream : fStreams.values()) { |
| for (CTFStreamInput si : stream.getStreamInputs()) { |
| currentEnd = Math.max(currentEnd, si.getTimestampEnd()); |
| } |
| } |
| return timestampCyclesToNanos(currentEnd); |
| } |
| |
| /** |
| * Does the trace need to time scale? |
| * |
| * @return if the trace is in ns or cycles. |
| */ |
| private boolean clockNeedsScale() { |
| if (getClock() == null) { |
| return false; |
| } |
| return fSingleClock.isClockScaled(); |
| } |
| |
| /** |
| * Gets the inverse time scale in cycles/nanosecond |
| * |
| * @return the inverse time scale in cycles/nanosecond |
| */ |
| private double getInverseTimeScale() { |
| if (getClock() == null) { |
| return 1.0; |
| } |
| return fSingleClock.getClockAntiScale(); |
| } |
| |
| /** |
| * Gets the clock cycles count for a specified time |
| * |
| * @param cycles |
| * clock cycles relative to clock offset |
| * @return time in nanoseconds relative to POSIX.1 Epoch |
| */ |
| public long timestampCyclesToNanos(long cycles) { |
| long retVal = cycles + getOffset(); |
| /* |
| * this fix is since quite often the offset will be > than 53 bits and therefore |
| * the conversion will be lossy |
| */ |
| if (clockNeedsScale()) { |
| retVal = (long) (retVal * getTimeScale()); |
| } |
| return retVal; |
| } |
| |
| /** |
| * Gets the time for a specified clock cycle count |
| * |
| * @param nanos |
| * time in nanoseconds relative to POSIX.1 Epoch |
| * @return clock cycles relative to clock offset |
| */ |
| public long timestampNanoToCycles(long nanos) { |
| long retVal; |
| /* |
| * this fix is since quite often the offset will be > than 53 bits and therefore |
| * the conversion will be lossy |
| */ |
| if (clockNeedsScale()) { |
| retVal = (long) (nanos * getInverseTimeScale()); |
| } else { |
| retVal = nanos; |
| } |
| return retVal - getOffset(); |
| } |
| |
| /** |
| * Add a new stream |
| * |
| * @param id |
| * the ID of the stream |
| * @param streamFile |
| * new file in the stream |
| * @throws CTFException |
| * The file must exist |
| */ |
| public void addStream(long id, File streamFile) throws CTFException { |
| final File file = streamFile; |
| if (file == null) { |
| throw new CTFException("cannot create a stream with no file"); //$NON-NLS-1$ |
| } |
| ICTFStream stream = fStreams.computeIfAbsent(id, ctfStream -> new CTFStream(this)); |
| if (stream instanceof CTFStream) { |
| CTFStream ctfStream = (CTFStream) stream; |
| ctfStream.addInput(new CTFStreamInput(stream, file)); |
| } else { |
| throw new CTFException("Stream does not support adding input"); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Gets the current trace scope |
| * |
| * @return the current declaration scope |
| * |
| * @since 1.1 |
| */ |
| public DeclarationScope getScope() { |
| return fScope; |
| } |
| |
| /** |
| * Gets the packet header definition (UUID, magic number and such) |
| * |
| * @return the packet header definition |
| * |
| * @since 2.0 |
| */ |
| public StructDefinition getPacketHeaderDef() { |
| return fPacketHeaderDef; |
| } |
| |
| /** |
| * Sets the environment map |
| * |
| * @param parseEnvironment |
| * The environment map |
| * @since 2.0 |
| */ |
| public void setEnvironment(@NonNull Map<String, String> parseEnvironment) { |
| fEnvironment = ImmutableMap.copyOf(parseEnvironment); |
| } |
| } |
| |
| class MetadataFileFilter implements FileFilter { |
| |
| @Override |
| public boolean accept(File pathname) { |
| if (pathname.isDirectory()) { |
| return false; |
| } |
| if (pathname.isHidden()) { |
| return false; |
| } |
| return !(pathname.getName().equals("metadata")); |
| } |
| |
| } |
| |
| class MetadataComparator implements Comparator<File>, Serializable { |
| |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public int compare(File o1, File o2) { |
| return o1.getName().compareTo(o2.getName()); |
| } |
| } |