From 1f88f6136772a7ad8ce54d7f46992a229b6946ca Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Tue, 8 Oct 2024 11:18:30 -0700 Subject: [PATCH] ros2: support pub-sub message tracking using rmw layer only (#24) Signed-off-by: Christophe Bedard --- .../analysis/AbstractRos2StateProvider.java | 21 ++ .../messages/Ros2MessagesStateProvider.java | 194 +++++++++++------- .../objects/Ros2ObjectsStateProvider.java | 56 ++--- .../internal/ros2/core/trace/Ros2Trace.java | 67 +++++- 4 files changed, 233 insertions(+), 105 deletions(-) diff --git a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/AbstractRos2StateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/AbstractRos2StateProvider.java index 394315b88..575da6805 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/AbstractRos2StateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/AbstractRos2StateProvider.java @@ -21,11 +21,13 @@ import org.eclipse.tracecompass.incubator.internal.ros2.core.model.HostProcessPointer; import org.eclipse.tracecompass.incubator.internal.ros2.core.model.HostThread; import org.eclipse.tracecompass.incubator.internal.ros2.core.model.objects.Ros2ObjectHandle; +import org.eclipse.tracecompass.incubator.internal.ros2.core.trace.Ros2Trace; import org.eclipse.tracecompass.incubator.internal.ros2.core.trace.layout.IRos2EventLayout; import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace; +import org.osgi.framework.Version; /** * Abstract ROS 2 state provider with some common utilities. @@ -34,9 +36,18 @@ */ public abstract class AbstractRos2StateProvider extends AbstractTmfStateProvider { + /** + * First tracetools version that includes the source_timestamp for a + * published message in the rmw_publish tracepoint. See: + * https://github.com/ros2/ros2_tracing/pull/74. + */ + private static final @NonNull Version RMW_SOURCE_TIMESTAMP_MINIMUM_VERSION = new Version("8.0.0"); //$NON-NLS-1$ + /** The event layout */ protected static final IRos2EventLayout LAYOUT = IRos2EventLayout.getDefault(); + private final boolean fIsPubSourceTimestampAvailableFromRmw; + /** * Constructor * @@ -47,6 +58,16 @@ public abstract class AbstractRos2StateProvider extends AbstractTmfStateProvider */ protected AbstractRos2StateProvider(ITmfTrace trace, String id) { super(Objects.requireNonNull(trace), Objects.requireNonNull(id)); + // Version in trace needs to be >= the minimum version + fIsPubSourceTimestampAvailableFromRmw = ((Ros2Trace) getTrace()).getTracetoolsVersion().compareTo(RMW_SOURCE_TIMESTAMP_MINIMUM_VERSION) >= 0; + } + + /** + * @return whether the source_timestamp value on the publication side is + * available from the rmw layer; if not, it is available from DDS + */ + protected boolean isPubSourceTimestampAvailableFromRmw() { + return fIsPubSourceTimestampAvailableFromRmw; } /** diff --git a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/messages/Ros2MessagesStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/messages/Ros2MessagesStateProvider.java index aaa190f8d..14da04893 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/messages/Ros2MessagesStateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/messages/Ros2MessagesStateProvider.java @@ -66,6 +66,7 @@ public class Ros2MessagesStateProvider extends AbstractRos2StateProvider { // Publications private Collection fKnownDdsWriters = new ArrayList<>(); + private Collection fKnownRmwPublishers = new ArrayList<>(); private Map fRclcppPublishEvents = Maps.newHashMap(); private Map fRclPublishEvents = Maps.newHashMap(); private Map fDdsWritePreEvents = Maps.newHashMap(); @@ -116,62 +117,80 @@ protected void eventHandle(@NonNull ITmfEvent event) { } eventHandleHelpers(event); + // If we can get the source_timestamp from rmw, don't process DDS events + if (!isPubSourceTimestampAvailableFromRmw()) { + eventHandlePublishDds(event, ss, timestamp); + } eventHandlePublish(event, ss, timestamp); eventHandleTake(ss, event, timestamp); eventHandleCallback(event, ss, timestamp); } private void eventHandleHelpers(@NonNull ITmfEvent event) { - // dds:create_writer - if (isEvent(event, LAYOUT.eventDdsCreateWriter())) { - /** - * Many DDS writers are internal to: - * - *
-             *   (1) the RMW implementation; and
-             *   (2) the DDS implementation.
-             * 
- * - * To simplify processing of DDS write-related events (i.e., - * dds:write), we keep a list of known/"valid" DDS writers and only - * consider events concerning those DDS writers. - * - * For (1), we can filter out DDS writers using the topic name. Most - * DDS writer topic names have prefixes. These prefixes are - * implementation details and are not part of the actual ROS 2 topic - * name: - * - *
-             *   * "rt" for publication/subscription topics
-             *   * "rq" and "rr" for service request and service reply topics, respectively
-             * 
- * - * Some DDS writers with a topic name not starting with those - * prefixes are internal to RMW: - * - *
-             *   * "ros_discovery_info" for internal discovery in RMW (per RMW context)
-             * 
- * - * For (2), we do not actually get dds:create_writer events for - * those DDS writers (at least currently), so just going with an - * allow-list allows us to filter those out. - */ - String topicName = (String) getField(event, LAYOUT.fieldTopicName()); - if (!topicName.equals("ros_discovery_info")) { //$NON-NLS-1$ - HostProcessPointer writer = hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldWriter())); - fKnownDdsWriters.add(writer); + /** + * Many DDS writers and rmw publishers are internal to: + * + *
+         *   (1) the rmw implementation; and
+         *   (2) the DDS implementation.
+         * 
+ * + * To simplify processing of DDS write-related and rmw publish-related + * events (i.e., dds:write, ros2:rmw_publish), we keep a list of + * known/"valid" DDS writers and rmw publishers and only consider events + * concerning those DDS writers or rmw publishers. + */ + if (!isPubSourceTimestampAvailableFromRmw()) { + // dds:create_writer + if (isEvent(event, LAYOUT.eventDdsCreateWriter())) { + /** + * For DDS: + * + * For (1), we can filter out DDS writers using the topic name. + * Most DDS writer topic names have prefixes. These prefixes are + * implementation details and are not part of the actual ROS 2 + * topic name: + * + *
+                 *   * "rt" for publication/subscription topics
+                 *   * "rq" and "rr" for service request and service reply topics, respectively
+                 * 
+ * + * Some DDS writers with a topic name not starting with those + * prefixes are internal to rmw: + * + *
+                 *   * "ros_discovery_info" for internal discovery in rmw (per rmw context)
+                 * 
+ * + * For (2), we do not actually get dds:create_writer events for + * those DDS writers (at least currently), so just going with an + * allow-list allows us to filter those out. + */ + String topicName = (String) getField(event, LAYOUT.fieldTopicName()); + if (!topicName.equals("ros_discovery_info")) { //$NON-NLS-1$ + HostProcessPointer writer = hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldWriter())); + fKnownDdsWriters.add(writer); + } + } + } else { + // rcl_publisher_init + if (isEvent(event, LAYOUT.eventRclPublisherInit())) { + /** + * For rmw: + * + * Build an allow-list of rmw publishers that belong to an rcl + * publisher by looking at ros2:rcl_publisher_init events, + * because then that means they're not implementation details of + * rmw or DDS. + */ + Ros2ObjectHandle rmwPublisher = handleFrom(event, (long) getField(event, LAYOUT.fieldRmwPublisherHandle())); + fKnownRmwPublishers.add(rmwPublisher); } } } private void eventHandlePublish(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long timestamp) { - eventHandlePublishRos2(event); - - eventHandlePublishDds(event, ss, timestamp); - } - - private void eventHandlePublishRos2(@NonNull ITmfEvent event) { // rclcpp_publish if (isEvent(event, LAYOUT.eventRclcppPublish())) { HostProcessPointer message = hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldMessage())); @@ -186,7 +205,11 @@ else if (isEvent(event, LAYOUT.eventRclPublish())) { // Add to temporary map fRclPublishEvents.put(message, event); } - // TODO rmw_publish, use rmw-level timestamp + // rmw_publish + // We only need these events if we can get the source_timestamp + else if (isPubSourceTimestampAvailableFromRmw() && isEvent(event, LAYOUT.eventRmwPublish())) { + handleRmwPublish(event, ss, timestamp); + } } private void eventHandlePublishDds(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long timestamp) { @@ -214,7 +237,38 @@ private void eventHandlePublishDdsWrite(@NonNull ITmfEvent event, ITmfStateSyste if (null == message) { return; } + handlePublish(event, ss, timestamp, message); + } + private HostProcessPointer getDdsMessage(@NonNull ITmfEvent event, HostProcessPointer writer) { + /** + * Currently, with the Fast DDS instrumentation, the dds:write event + * does not contain the message/data pointer. We need to get it from a + * separate previous event, dds:write_pre. + */ + if (hasField(event, LAYOUT.fieldData())) { + return hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldData())); + } + ITmfEvent ddsWritePre = fDdsWritePreEvents.remove(writer); + if (null == ddsWritePre) { + Activator.getInstance().logError("could not get corresponding dds:write_pre event for writer=0x" + Long.toHexString(writer.getPointer())); //$NON-NLS-1$ + return null; + } + return hostProcessPointerFrom(ddsWritePre, (long) getField(ddsWritePre, LAYOUT.fieldData())); + } + + private void handleRmwPublish(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long timestamp) { + // First check if we know the rmw publisher + Ros2ObjectHandle rmwPublisher = handleFrom(event, (long) getField(event, LAYOUT.fieldRmwPublisherHandle())); + if (!fKnownRmwPublishers.contains(rmwPublisher)) { + return; + } + + HostProcessPointer message = hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldMessage())); + handlePublish(event, ss, timestamp, message); + } + + private void handlePublish(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long endPubTimestamp, @NonNull HostProcessPointer message) { /** * Get corresponding rcl_publish event, since it's the main r*_publish * event. @@ -225,7 +279,7 @@ private void eventHandlePublishDdsWrite(@NonNull ITmfEvent event, ITmfStateSyste return; } Ros2ObjectHandle publisherHandle = handleFrom(rclPublish, (long) getField(rclPublish, LAYOUT.fieldPublisherHandle())); - Ros2PublisherObject publisherObject = Ros2ObjectsUtil.getPublisherObjectFromHandle(fObjectsSs, timestamp, publisherHandle); + Ros2PublisherObject publisherObject = Ros2ObjectsUtil.getPublisherObjectFromHandle(fObjectsSs, endPubTimestamp, publisherHandle); if (null == publisherObject) { /** * FIXME this happens with publishers for /rosout for some reason. @@ -237,31 +291,6 @@ private void eventHandlePublishDdsWrite(@NonNull ITmfEvent event, ITmfStateSyste // Get corresponding rclcpp_publish event ITmfEvent rclcppPublish = fRclcppPublishEvents.remove(message); - - addPublicationInstance(event, ss, timestamp, publisherObject, message, rclcppPublish); - } - - private HostProcessPointer getDdsMessage(@NonNull ITmfEvent event, HostProcessPointer writer) { - /** - * Currently, with the Fast DDS instrumentation, the dds:write event - * does not contain the message/data pointer. We need to get it from a - * separate previous event, dds:write_pre. - */ - if (hasField(event, LAYOUT.fieldData())) { - return hostProcessPointerFrom(event, (long) getField(event, LAYOUT.fieldData())); - } - ITmfEvent ddsWritePre = fDdsWritePreEvents.remove(writer); - if (null == ddsWritePre) { - Activator.getInstance().logError("could not get corresponding dds:write_pre event for writer=0x" + Long.toHexString(writer.getPointer())); //$NON-NLS-1$ - return null; - } - return hostProcessPointerFrom(ddsWritePre, (long) getField(ddsWritePre, LAYOUT.fieldData())); - } - - private void addPublicationInstance(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss, long timestamp, Ros2PublisherObject publisherObject, @NonNull HostProcessPointer message, ITmfEvent rclcppPublish) { - long sourceTimestamp = (long) getField(event, LAYOUT.fieldTimestamp()); - long tid = getTid(event); - /** * Some rcl_publish events (e.g., for internal rcl-level publisher) do * not have a corresponding rclcpp_publish event. @@ -270,27 +299,34 @@ private void addPublicationInstance(@NonNull ITmfEvent event, ITmfStateSystemBui // TODO support this, using the rcl_publish timestamp instead return; } + long pubTimestamp = rclcppPublish.getTimestamp().toNanos(); + long sourceTimestamp = (long) getField(event, LAYOUT.fieldTimestamp()); + HostThread thread = hostThreadFrom(event); + Ros2PubInstance pubInstance = new Ros2PubInstance(publisherObject.getHandle(), thread.getTid(), message, sourceTimestamp); + addPublicationInstance(ss, pubTimestamp, endPubTimestamp, thread, publisherObject, pubInstance); + } - Integer pubQuark = Ros2MessagesUtil.getPublisherQuarkAndAdd(ss, fObjectsSs, timestamp, publisherObject.getHandle()); + private void addPublicationInstance(ITmfStateSystemBuilder ss, long pubTimestamp, long endPubTimestamp, @NonNull HostThread thread, Ros2PublisherObject publisherObject, @NonNull Ros2PubInstance pubInstance) { + Integer pubQuark = Ros2MessagesUtil.getPublisherQuarkAndAdd(ss, fObjectsSs, endPubTimestamp, publisherObject.getHandle()); if (null == pubQuark) { return; } - // Mark publication event using state from rclcpp_pub->dds:write - Ros2PubInstance pubInstance = new Ros2PubInstance(publisherObject.getHandle(), tid, message, sourceTimestamp); + // Mark publication event using state from rclcpp_pub->(dds:write or + // rmw_publish) ss.modifyAttribute(pubTimestamp, pubInstance, pubQuark); - ss.modifyAttribute(timestamp, null, pubQuark); + ss.modifyAttribute(endPubTimestamp, null, pubQuark); // Keep pub event for pub-sub links /** * TODO match using publisher GID when rmw_cyclonedds correctly supports * it. */ - Ros2MessageTimestamp messageSourceTimestamp = new Ros2MessageTimestamp(sourceTimestamp, publisherObject.getTopicName()); - fPublications.put(messageSourceTimestamp, new Pair<>(publisherObject.getHandle(), timestamp)); + Ros2MessageTimestamp messageSourceTimestamp = new Ros2MessageTimestamp(pubInstance.getSourceTimestamp(), publisherObject.getTopicName()); + fPublications.put(messageSourceTimestamp, new Pair<>(publisherObject.getHandle(), endPubTimestamp)); // Add publication to multimap for in-callback links - fCallbackPublications.put(hostThreadFrom(event), new Pair<>(publisherObject.getHandle(), pubTimestamp)); + fCallbackPublications.put(thread, new Pair<>(publisherObject.getHandle(), pubTimestamp)); } private void eventHandleTake(@NonNull ITmfStateSystemBuilder ss, @NonNull ITmfEvent event, long timestamp) { diff --git a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/objects/Ros2ObjectsStateProvider.java b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/objects/Ros2ObjectsStateProvider.java index 4fe9b31f2..6d9a4f125 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/objects/Ros2ObjectsStateProvider.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/analysis/objects/Ros2ObjectsStateProvider.java @@ -93,7 +93,6 @@ protected void eventHandle(@NonNull ITmfEvent event) { ITmfStateSystemBuilder ss = Objects.requireNonNull(getStateSystemBuilder()); long timestamp = event.getTimestamp().toNanos(); - // TODO(christophebedard) handle rcl_init? eventHandleNode(event, ss, timestamp); eventHandlePublisher(event, ss); eventHandleSubscription(event, ss); @@ -118,11 +117,12 @@ private static void eventHandleNode(@NonNull ITmfEvent event, ITmfStateSystemBui } private void eventHandlePublisher(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss) { - eventHandlePublisherDdsCreate(event); + if (!isPubSourceTimestampAvailableFromRmw()) { + eventHandlePublisherDdsCreate(event); + } eventHandlePublisherInit(event, ss); } - private void eventHandlePublisherDdsCreate(@NonNull ITmfEvent event) { // dds:create_writer if (isEvent(event, LAYOUT.eventDdsCreateWriter())) { @@ -142,7 +142,6 @@ private void eventHandlePublisherDdsCreate(@NonNull ITmfEvent event) { } } - private void eventHandlePublisherInit(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss) { // rmw_publisher_init if (isEvent(event, LAYOUT.eventRmwPublisherInit())) { @@ -180,30 +179,35 @@ private void eventHandlePublisherInit(@NonNull ITmfEvent event, ITmfStateSystemB long[] rmwGid = (long[]) getField(rmwPublisherInit, LAYOUT.fieldGid()); Gid gid = getDdsGidFromRmwGidArray(rmwGid); - // Get corresponding dds:create_writer event - ITmfEvent ddsCreateWriter = fDdsCreateWriterEvents.remove(gid); - if (null == ddsCreateWriter) { - Activator.getInstance().logError("could find corresponding dds:dds_create writer for gid=" + gid.toString()); //$NON-NLS-1$ - return; + long publisherTimestamp = rmwPublisherInit.getTimestamp().toNanos(); + HostProcessPointer ddsWriter = hostProcessPointerFrom(rmwPublisherInit, 0L); + if (!isPubSourceTimestampAvailableFromRmw()) { + // Get corresponding dds:create_writer event + ITmfEvent ddsCreateWriter = fDdsCreateWriterEvents.remove(gid); + if (null == ddsCreateWriter) { + Activator.getInstance().logError("could find corresponding dds:dds_create writer for gid=" + gid.toString()); //$NON-NLS-1$ + return; + } + ddsWriter = hostProcessPointerFrom(ddsCreateWriter, (long) getField(ddsCreateWriter, LAYOUT.fieldWriter())); + // Use timestamp from dds:create_writer event + publisherTimestamp = ddsCreateWriter.getTimestamp().toNanos(); } - HostProcessPointer ddsWriter = hostProcessPointerFrom(ddsCreateWriter, (long) getField(ddsCreateWriter, LAYOUT.fieldWriter())); // Add to pubs list Ros2PublisherObject pubObject = new Ros2PublisherObject(publisherHandle, rmwPublisherHandle, topicName, nodeHandle, gid, ddsWriter); int pubQuark = Ros2ObjectsUtil.getPublisherQuarkAndAdd(ss, pubObject.getHandle()); - // Use timestamp from dds:create_writer event - long ddsTimestamp = ddsCreateWriter.getTimestamp().toNanos(); - ss.modifyAttribute(ddsTimestamp, pubObject, pubQuark); + ss.modifyAttribute(publisherTimestamp, pubObject, pubQuark); } } private void eventHandleSubscription(@NonNull ITmfEvent event, ITmfStateSystemBuilder ss) { - eventHandleSubscriptionDdsCreate(event); + if (!isPubSourceTimestampAvailableFromRmw()) { + eventHandleSubscriptionDdsCreate(event); + } eventHandleSubscriptionInit(event); eventHandleSubscriptionCallbackAdded(event, ss); } - private void eventHandleSubscriptionDdsCreate(@NonNull ITmfEvent event) { // dds:create_reader if (isEvent(event, LAYOUT.eventDdsCreateReader())) { @@ -286,13 +290,19 @@ private void eventHandleSubscriptionCallbackAdded(@NonNull ITmfEvent event, ITmf long[] gidRmw = (long[]) getField(rmwSubscriptionInit, LAYOUT.fieldGid()); Gid gid = getDdsGidFromRmwGidArray(gidRmw); - // Get corresponding dds:create_reader event - ITmfEvent ddsCreateReader = fDdsCreateReaderEvents.remove(gid); - if (null == ddsCreateReader) { - Activator.getInstance().logError("could not find corresponding dds:create_reader event for gid=" + gid.toString()); //$NON-NLS-1$ - return; + long subscriptionTimestamp = rmwSubscriptionInit.getTimestamp().toNanos(); + HostProcessPointer ddsReader = hostProcessPointerFrom(rmwSubscriptionInit, 0L); + if (!isPubSourceTimestampAvailableFromRmw()) { + // Get corresponding dds:create_reader event + ITmfEvent ddsCreateReader = fDdsCreateReaderEvents.remove(gid); + if (null == ddsCreateReader) { + Activator.getInstance().logError("could not find corresponding dds:create_reader event for gid=" + gid.toString()); //$NON-NLS-1$ + return; + } + // Use timestamp from dds:create_reader event + subscriptionTimestamp = ddsCreateReader.getTimestamp().toNanos(); + ddsReader = hostProcessPointerFrom(ddsCreateReader, (long) getField(ddsCreateReader, LAYOUT.fieldReader())); } - HostProcessPointer ddsReader = hostProcessPointerFrom(ddsCreateReader, (long) getField(ddsCreateReader, LAYOUT.fieldReader())); // Add callback owner info to map fCallbackOwners.put(callback, new Pair<>(Ros2CallbackType.SUBSCRIPTION, subscriptionHandle)); @@ -301,9 +311,7 @@ private void eventHandleSubscriptionCallbackAdded(@NonNull ITmfEvent event, ITmf Ros2SubscriptionObject subscriptionObject = new Ros2SubscriptionObject( subscriptionHandle, rmwSubscriptionHandle, topicName, nodeHandle, gid, ddsReader, subscription, callback); int subQuark = Ros2ObjectsUtil.getSubscriptionQuarkAndAdd(ss, subscriptionObject.getHandle()); - // Use timestamp from dds:create_reader event - long ddsTimestamp = ddsCreateReader.getTimestamp().toNanos(); - ss.modifyAttribute(ddsTimestamp, subscriptionObject, subQuark); + ss.modifyAttribute(subscriptionTimestamp, subscriptionObject, subQuark); } } diff --git a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/trace/Ros2Trace.java b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/trace/Ros2Trace.java index 593dbebac..bf18f0e5c 100644 --- a/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/trace/Ros2Trace.java +++ b/tracetypes/org.eclipse.tracecompass.incubator.ros2.core/src/org/eclipse/tracecompass/incubator/internal/ros2/core/trace/Ros2Trace.java @@ -25,10 +25,16 @@ import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect; import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException; +import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus; +import org.eclipse.tracecompass.tmf.ctf.core.context.CtfLocation; +import org.eclipse.tracecompass.tmf.ctf.core.context.CtfLocationInfo; +import org.eclipse.tracecompass.tmf.ctf.core.context.CtfTmfContext; +import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEvent; import org.eclipse.tracecompass.tmf.ctf.core.event.CtfTmfEventFactory; import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace; import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTraceValidationStatus; +import org.osgi.framework.Version; import com.google.common.collect.ImmutableSet; @@ -51,9 +57,16 @@ public class Ros2Trace extends CtfTmfTrace { ROS_ASPECTS = builder.build(); } + /** + * Last version before the 'version' field was added. As it turns out, the + * field was added in 0.1.0. + */ + private static final @NonNull String TRACETOOLS_VERSION_UNKNOWN = "0.0.0"; //$NON-NLS-1$ + private @NonNull Collection> fRos2TraceAspects = ImmutableSet.copyOf(ROS_ASPECTS); private @Nullable IRos2EventLayout fLayout = null; + private @Nullable Version fTracetoolsVersion = null; /** * Default constructor @@ -86,14 +99,26 @@ protected Ros2Trace(@NonNull CtfTmfEventFactory factory) { return layout; } + /** + * @return the version of the ROS 2 tracetools package used to generate the + * trace + */ + public @NonNull Version getTracetoolsVersion() { + Version tracetoolsVersion = fTracetoolsVersion; + if (null == tracetoolsVersion) { + throw new IllegalStateException("Cannot get the tracetools version of a non-initialized trace"); //$NON-NLS-1$ + } + return tracetoolsVersion; + } + @Override public void initTrace(IResource resource, String path, Class eventType) throws TmfTraceException { super.initTrace(resource, path, eventType); // Determine the event layout to use - IRos2EventLayout layout = getLayout(); - fLayout = layout; + fLayout = getLayout(); + fTracetoolsVersion = getTracetoolsVersion(fLayout); } private static @NonNull IRos2EventLayout getLayout() { @@ -101,6 +126,44 @@ public void initTrace(IResource resource, String path, return Objects.requireNonNull(IRos2EventLayout.getDefault()); } + private @NonNull Version getTracetoolsVersion(IRos2EventLayout layout) { + return getVersionFromEvent(layout.eventRclInit(), layout); + } + + private @NonNull Version getVersionFromEvent(@NonNull String versionEventName, IRos2EventLayout layout) { + CtfTmfEvent event = seekEvent(versionEventName); + if (null == event) { + /** + * This could happen if the tracepoint is not enabled, in which case + * we just have to assume a version. + */ + Activator.getInstance().logError("Cannot get event " + versionEventName); //$NON-NLS-1$ + return new Version(TRACETOOLS_VERSION_UNKNOWN); + } + String versionStr = (String) event.getContent().getFieldValue(Object.class, layout.fieldVersion()); + if (null == versionStr) { + /** + * If the event exists, but the field doesn't exist, default to the + * version right before the field was added. + */ + return new Version(TRACETOOLS_VERSION_UNKNOWN); + } + return new Version(versionStr); + } + + private @Nullable CtfTmfEvent seekEvent(@NonNull String eventName) { + // Seek the first event and then advance until we get the right event + CtfLocation location = new CtfLocation(new CtfLocationInfo(0L, 0L)); + ITmfContext result = seekEvent(location); + CtfTmfContext context = (CtfTmfContext) result; + while (!context.getCurrentEvent().getName().equals(eventName)) { + if (!context.advance()) { + return null; + } + } + return context.getCurrentEvent(); + } + @Override public Iterable> getEventAspects() { return fRos2TraceAspects;