Skip to content

Commit

Permalink
ctf.core: Implement parsing of clock fragments
Browse files Browse the repository at this point in the history
Add logic to parse CTF2 clock fragments and therefore allow for
timestamp readings in CTF2. This implementation can be tested with a
modified version of [1] that will be shared (updated to include
CTF2-SPECRC-7.0 terminology) and can be compared with the equivalent
CTF1 trace [2].
Babeltrace has yet to share tests that include clock origins, so those
cannot be tested yet. An additional change was made to remove the
datastream TODO that was left over from a previous patch.

[1]https://review.lttng.org/plugins/gitiles/babeltrace/+/refs/heads/ctf2/tests/data/ctf-traces/2/succeed/barectf-event-before-packet/

[2]https://review.lttng.org/plugins/gitiles/babeltrace/+/refs/heads/ctf2/tests/data/ctf-traces/1/succeed/barectf-event-before-packet/

Change-Id: I46c2de5305b72ce8e4ef4834708b5887bd811bfc
Signed-off-by: Sehr Moosabhoy <sehr.moosabhoy@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/203658
Tested-by: Marco Miller <marco.miller@ericsson.com>
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Reviewed-by: Marco Miller <marco.miller@ericsson.com>
  • Loading branch information
Sehr Moosabhoy authored and marco-miller committed Aug 14, 2023
1 parent d92746e commit 112e72d
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 44 deletions.
Expand Up @@ -312,11 +312,122 @@ public class IOstructgenTest {
+ " \"type\": \"trace-class\"\n"
+ "}\n";

private static final String jsonClockFragment = Utils.RECORD_SEPARATOR + "{\n"
+ " \"frequency\": 1000000000,\n"
+ " \"name\": \"default\",\n"
+ " \"offset-from-origin\": {\n"
+ " \"cycles\": 0,\n"
+ " \"seconds\": 1434072888\n"
+ " },\n"
+ " \"origin\": \"unix-epoch\", \n"
+ " \"type\": \"clock-class\"\n"
+ "}\n";

private static final String jsonEmptyDataStream = Utils.RECORD_SEPARATOR
+ "{\n"
+ " \"type\": \"data-stream-class\"\n"
+ "}";

private static final String jsonClockedDataStream = Utils.RECORD_SEPARATOR
+ "{\n"
+ " \"default-clock-class-name\": \"default\",\n"
+ " \"event-record-header-field-class\": {\n"
+ " \"member-classes\": [\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 64,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 64,\n"
+ " \"roles\": [\n"
+ " \"default-clock-timestamp\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"timestamp\"\n"
+ " },\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 16,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 16,\n"
+ " \"roles\": [\n"
+ " \"event-record-class-id\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"id\"\n"
+ " }\n"
+ " ],\n"
+ " \"type\": \"structure\"\n"
+ " },\n"
+ " \"packet-context-field-class\": {\n"
+ " \"member-classes\": [\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 64,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 64,\n"
+ " \"roles\": [\n"
+ " \"default-clock-timestamp\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"timestamp_begin\"\n"
+ " },\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 64,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 64,\n"
+ " \"roles\": [\n"
+ " \"packet-end-default-clock-timestamp\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"timestamp_end\"\n"
+ " },\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 32,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 32,\n"
+ " \"roles\": [\n"
+ " \"packet-total-length\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"packet_size\"\n"
+ " },\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 32,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 32,\n"
+ " \"roles\": [\n"
+ " \"packet-content-length\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"content_size\"\n"
+ " },\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 32,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 32,\n"
+ " \"roles\": [\n"
+ " \"discarded-event-record-counter-snapshot\"\n"
+ " ],\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"events_discarded\"\n"
+ " }\n"
+ " ],\n"
+ " \"type\": \"structure\"\n"
+ " },\n"
+ " \"type\": \"data-stream-class\"\n"
+ "}\n";

private static final String jsonPacketContextDataStream = Utils.RECORD_SEPARATOR
+ "{\n"
+ " \"packet-context-field-class\": {\n"
Expand Down Expand Up @@ -351,6 +462,26 @@ public class IOstructgenTest {
+ " \"type\": \"data-stream-class\"\n"
+ "}\n";

private static final String jsonClockedEventRecord = Utils.RECORD_SEPARATOR
+ "{\n"
+ " \"name\": \"simple_uint32\",\n"
+ " \"payload-field-class\": {\n"
+ " \"member-classes\": [\n"
+ " {\n"
+ " \"field-class\": {\n"
+ " \"alignment\": 32,\n"
+ " \"byte-order\": \"little-endian\",\n"
+ " \"length\": 32,\n"
+ " \"type\": \"fixed-length-unsigned-integer\"\n"
+ " },\n"
+ " \"name\": \"value\"\n"
+ " }\n"
+ " ],\n"
+ " \"type\": \"structure\"\n"
+ " },\n"
+ " \"type\": \"event-record-class\""
+ "}";

private static final String jsonEventRecord = Utils.RECORD_SEPARATOR
+ "{\n"
+ " \"name\": \"string\",\n"
Expand Down Expand Up @@ -386,6 +517,8 @@ public class IOstructgenTest {
+ jsonEmptyDataStream + jsonEventRecord;
private static final String packetContextJson = jsonPreamble + jsonPacketHeaderTrace
+ jsonPacketContextDataStream + jsonEventRecord;
private static final String clockedJson = jsonPreamble + jsonPacketHeaderTrace + jsonClockFragment
+ jsonClockedDataStream + jsonClockedEventRecord;

static final String tempTraceDir = CtfCoreTestPlugin.getTemporaryDirPath()
+ File.separator + "tempTrace";
Expand Down Expand Up @@ -647,4 +780,17 @@ public void jsonPacketContextTest() throws CTFException {
trace = new CTFTrace(tempTraceDir);
assertNotNull(trace);
}

/**
* Test with clocked trace
*
* @throws CTFException
* something wrong happened
*/
@Test
public void jsonClockedTest() throws CTFException {
createDummyTrace(clockedJson);
trace = new CTFTrace(tempTraceDir);
assertNotNull(trace);
}
}
Expand Up @@ -42,6 +42,7 @@
import org.eclipse.tracecompass.ctf.core.trace.ICTFPacketDescriptor;
import org.eclipse.tracecompass.internal.ctf.core.event.types.composite.EventHeaderDefinition;
import org.eclipse.tracecompass.internal.ctf.core.trace.CTFStream;
import org.eclipse.tracecompass.internal.ctf.core.utils.JsonMetadataStrings;

import com.google.common.collect.ImmutableList;

Expand Down Expand Up @@ -128,7 +129,17 @@ public EventDefinition createDefinition(StructDeclaration streamEventContextDecl
StructDefinition streamEventContext = streamEventContextDecl != null ? streamEventContextDecl.createDefinition(trace, ILexicalScope.STREAM_EVENT_CONTEXT, input) : null;
StructDefinition eventContext = fContext != null ? fContext.createFieldDefinition(eventHeaderDef, trace, ILexicalScope.CONTEXT, input) : null;
StructDefinition eventPayload = fFields != null ? fFields.createFieldDefinition(eventHeaderDef, trace, ILexicalScope.FIELDS, input) : null;
long timestamp = calculateTimestamp(eventHeaderDef, prevTimestamp, eventPayload, eventContext);
String timestampVariable = null;
long timestamp;
if (trace != null) {
timestampVariable = Long.valueOf(2).equals(trace.getMajor()) ? JsonMetadataStrings.DEFAULT_CLOCK_TIMESTAMP : CTFStrings.TIMESTAMP;
timestamp = calculateTimestamp(eventHeaderDef, prevTimestamp, eventPayload, eventContext, timestampVariable);
} else {
timestamp = calculateTimestamp(eventHeaderDef, prevTimestamp, eventPayload, eventContext, CTFStrings.TIMESTAMP);
if (timestamp == 0) {
calculateTimestamp(eventHeaderDef, prevTimestamp, eventPayload, eventContext, JsonMetadataStrings.DEFAULT_CLOCK_TIMESTAMP);
}
}

int cpu = (int) packetDescriptor.getTargetId();
return new EventDefinition(
Expand All @@ -143,7 +154,7 @@ public EventDefinition createDefinition(StructDeclaration streamEventContextDecl
packetDescriptor);
}

private static long calculateTimestamp(@Nullable ICompositeDefinition eventHeaderDef, long prevTimestamp, StructDefinition eventPayload, StructDefinition eventContext) throws CTFIOException {
private static long calculateTimestamp(@Nullable ICompositeDefinition eventHeaderDef, long prevTimestamp, StructDefinition eventPayload, StructDefinition eventContext, String timestampVariable) throws CTFIOException {
long timestamp = 0;
Definition def = null;
if (eventHeaderDef instanceof EventHeaderDefinition) {
Expand All @@ -152,15 +163,16 @@ private static long calculateTimestamp(@Nullable ICompositeDefinition eventHeade
def = eventHeaderDefinition;
} else if (eventHeaderDef instanceof StructDefinition) {
StructDefinition structDefinition = (StructDefinition) eventHeaderDef;
def = structDefinition.lookupDefinition(CTFStrings.TIMESTAMP);
def = structDefinition.lookupDefinition(timestampVariable);
} else if (eventHeaderDef != null) {
throw new CTFIOException("Event header def is not a Struct or an Event Header"); //$NON-NLS-1$
}
if (def == null && eventPayload != null) {
def = eventPayload.lookupDefinition(CTFStrings.TIMESTAMP);

if (def == null && eventPayload != null && eventPayload.getDefinition(timestampVariable) != null) {
def = eventPayload.lookupDefinition(timestampVariable);
}
if (def == null && eventContext != null) {
def = eventContext.lookupDefinition(CTFStrings.TIMESTAMP);
if (def == null && eventContext != null && eventContext.getDefinition(timestampVariable) != null) {
def = eventContext.lookupDefinition(timestampVariable);
}
if (def instanceof IntegerDefinition) {
IntegerDefinition timestampDef = (IntegerDefinition) def;
Expand Down
Expand Up @@ -156,7 +156,7 @@ private void parseRoot(ICTFMetadataNode root) throws ParseException {
hasStreams = true;
} else if (CTFParser.tokenNames[CTFParser.EVENT].equals(type) || JsonMetadataStrings.FRAGMENT_EVENT_RECORD.equals(type)) {
events.add(child);
} else if (CTFParser.tokenNames[CTFParser.CLOCK].equals(type)) {
} else if (CTFParser.tokenNames[CTFParser.CLOCK].equals(type) || JsonMetadataStrings.FRAGMENT_CLOCK.equals(type)) {
CTFClock ctfClock = ClockParser.INSTANCE.parse(child, null);
String nameValue = ctfClock.getName();
fTrace.addClock(nameValue, ctfClock);
Expand All @@ -167,9 +167,6 @@ private void parseRoot(ICTFMetadataNode root) throws ParseException {
} else if (JsonMetadataStrings.FRAGMENT_PREAMBLE.equals(type)) {
// FIXME: support Preamble fragment (CTF2 spec 5.5)
// https://diamon.org/ctf/files/CTF2-SPECRC-7.0rA.html#preamble-frag
} else if (JsonMetadataStrings.FRAGMENT_DATA_STREAM.equals(type)) {
// FIXME: support Data Stream fragment (CTF2 spec 5.9)
// https://diamon.org/ctf/files/CTF2-SPECRC-7.0rA.html#dsc-frag
} else {
throw childTypeError(child);
}
Expand Down
Expand Up @@ -15,6 +15,7 @@

import org.eclipse.tracecompass.internal.ctf.core.event.types.ICTFMetadataNode;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName;

Expand All @@ -30,9 +31,9 @@ public class JsonClockMetadataNode extends CTFJsonMetadataNode {
@SerializedName("name")
private final String fName;
@SerializedName("frequency")
private final int fFrequency;
private final Long fFrequency;
@SerializedName("origin")
private JsonObject fOrigin;
private JsonElement fOrigin;
@SerializedName("offset-from-origin")
private JsonObject fOffset;
@SerializedName("precision")
Expand All @@ -54,7 +55,7 @@ public class JsonClockMetadataNode extends CTFJsonMetadataNode {
* @param frequency
* the frequency of the clock described in this node
*/
public JsonClockMetadataNode(ICTFMetadataNode parent, String type, String value, String name, int frequency) {
public JsonClockMetadataNode(ICTFMetadataNode parent, String type, String value, String name, Long frequency) {
super(parent, type, value);
this.fName = name;
this.fFrequency = frequency;
Expand All @@ -65,7 +66,7 @@ public JsonClockMetadataNode(ICTFMetadataNode parent, String type, String value,
*
* @return the frequency
*/
public int getFrequency() {
public Long getFrequency() {
return fFrequency;
}

Expand All @@ -74,7 +75,7 @@ public int getFrequency() {
*
* @return the origin
*/
public Object getOrigin() {
public JsonElement getOrigin() {
return fOrigin;
}

Expand Down
@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2023 Ericsson
*
* 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:
* Sehr Moosabhoy - Initial implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.event.metadata;

import org.eclipse.tracecompass.internal.ctf.core.event.types.ICTFMetadataNode;

import com.google.gson.annotations.SerializedName;

/**
* Node to store the clock origin object for CTF2 traces
*
* @author Sehr Moosabhoy
*/
public class JsonClockOriginMetadataNode extends CTFJsonMetadataNode {

@SerializedName("namespace")
private String fNamespace;
@SerializedName("name")
private final String fName;
@SerializedName("uid")
private final String fUid;

/**
* Constructor for a JsonClockOriginMetadataNode
*
* @param parent
* the parent of this node
* @param type
* the type of this node
* @param value
* the value of this node
* @param name
* the name of the clock origin described in this node
* @param uid
* the uid of the clock origin described in this node
*/
public JsonClockOriginMetadataNode(ICTFMetadataNode parent, String type, String value, String name, String uid) {
super(parent, type, value);
fName = name;
fUid = uid;
}

/**
* Get the namespace of the node
*
* @return the namespace
*/
public String getNamespace() {
return fNamespace;
}

/**
* Get the name of the node
*
* @return the name
*/
public String getName() {
return fName;
}

/**
* Get the uid of the node
*
* @return the uid
*/
public String getUid() {
return fUid;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof JsonClockOriginMetadataNode) {
JsonClockOriginMetadataNode clock = (JsonClockOriginMetadataNode) obj;
if (fName.equals(clock.getName()) && fNamespace.equals(clock.getNamespace()) && fUid.equals(clock.getUid())) {
return true;
}
}
return false;
}

@Override
public int hashCode() {
return super.hashCode();
}
}

0 comments on commit 112e72d

Please sign in to comment.