/*******************************************************************************
* Copyright (c) 2011, 2014 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 v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Matthew Khouzam - Initial API and implementation
* Contributors: Simon Marchi - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.ctf.core.event;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
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.IEventDeclaration;
import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer;
import org.eclipse.tracecompass.ctf.core.event.scope.ILexicalScope;
import org.eclipse.tracecompass.ctf.core.event.types.Definition;
import org.eclipse.tracecompass.ctf.core.event.types.ICompositeDefinition;
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.ctf.core.trace.CTFIOException;
import org.eclipse.tracecompass.ctf.core.trace.CTFStreamInputReader;
import org.eclipse.tracecompass.ctf.core.trace.CTFTrace;
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;
/**
* Representation of one type of event. A bit like "int" or "long" but for trace
* events.
*/
public class EventDeclaration implements IEventDeclaration {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
/**
* Name of the event
*/
private String fName;
/**
* Event context structure declaration
*/
private StructDeclaration fContext = null;
/**
* Event fields structure declaration
*/
private StructDeclaration fFields = null;
/**
* Stream to which belongs this event.
*/
private @Nullable CTFStream fStream = null;
/**
* Loglevel of an event
*/
private long fLogLevel;
/** Map of this event type's custom CTF attributes */
private final Map<String, String> fCustomAttributes = new HashMap<>();
private int fId = (int) UNSET_EVENT_ID;
// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------
/**
* Default constructor. Use the setters afterwards to set the fields
* accordingly.
*/
public EventDeclaration() {
}
/**
* Creates an instance of EventDefinition corresponding to this declaration.
*
* @param streamEventContextDecl
* event context
* @param packetDescriptor
* current packet
* @param packetContext
* packet context
* @param eventHeaderDef
* The event header definition
* @param input
* the bitbuffer input source
* @param prevTimestamp
* The timestamp when the event was taken
* @return A new EventDefinition.
* @throws CTFException
* As a bitbuffer is used to read, it could have wrapped
* IOExceptions.
*/
public EventDefinition createDefinition(StructDeclaration streamEventContextDecl, ICTFPacketDescriptor packetDescriptor, ICompositeDefinition packetContext, ICompositeDefinition eventHeaderDef, @NonNull BitBuffer input, long prevTimestamp)
throws CTFException {
final CTFStream stream = fStream;
final CTFTrace trace = stream == null ? null : stream.getTrace();
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);
int cpu = (int) packetDescriptor.getTargetId();
return new EventDefinition(
this,
cpu,
timestamp,
eventHeaderDef,
streamEventContext,
eventContext,
packetContext,
eventPayload,
packetDescriptor);
}
private static long calculateTimestamp(@Nullable ICompositeDefinition eventHeaderDef, long prevTimestamp, StructDefinition eventPayload, StructDefinition eventContext) throws CTFIOException {
long timestamp = 0;
Definition def = null;
if (eventHeaderDef instanceof EventHeaderDefinition) {
EventHeaderDefinition eventHeaderDefinition = (EventHeaderDefinition) eventHeaderDef;
timestamp = calculateTimestamp(eventHeaderDefinition.getTimestamp(), eventHeaderDefinition.getTimestampLength(), prevTimestamp);
def = eventHeaderDefinition;
} else if (eventHeaderDef instanceof StructDefinition) {
StructDefinition structDefinition = (StructDefinition) eventHeaderDef;
def = structDefinition.lookupDefinition(CTFStrings.TIMESTAMP);
} 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 && eventContext != null) {
def = eventContext.lookupDefinition(CTFStrings.TIMESTAMP);
}
if (def instanceof IntegerDefinition) {
IntegerDefinition timestampDef = (IntegerDefinition) def;
timestamp = calculateTimestamp(timestampDef, prevTimestamp);
}
return timestamp;
}
@Override
public EventDefinition createDefinition(CTFStreamInputReader streamInputReader, @NonNull BitBuffer input, long timestamp) throws CTFException {
StructDeclaration streamEventContextDecl = streamInputReader.getStreamEventContextDecl();
final @Nullable CTFStream stream = fStream;
final CTFTrace trace = stream == null ? null : stream.getTrace();
StructDefinition streamEventContext = streamEventContextDecl != null ? streamEventContextDecl.createDefinition(trace, ILexicalScope.STREAM_EVENT_CONTEXT, input) : null;
ICompositeDefinition packetContext = streamInputReader.getCurrentPacketReader().getCurrentPacketEventHeader();
StructDefinition eventContext = fContext != null ? fContext.createDefinition(trace, ILexicalScope.CONTEXT, input) : null;
StructDefinition eventPayload = fFields != null ? fFields.createDefinition(trace, ILexicalScope.FIELDS, input) : null;
// a bit lttng specific
// CTF doesn't require a timestamp,
// but it's passed to us
return new EventDefinition(
this,
streamInputReader.getCPU(),
timestamp,
null,
streamEventContext,
eventContext,
packetContext,
eventPayload,
streamInputReader.getCurrentPacketReader().getCurrentPacket());
}
// ------------------------------------------------------------------------
// Getters/Setters/Predicates
// ------------------------------------------------------------------------
/**
* Sets a name for an event Declaration
*
* @param name
* the name
*/
public void setName(String name) {
fName = name;
}
@Override
public String getName() {
return fName;
}
/**
* Sets the context for an event declaration (see CTF specification)
*
* @param context
* the context in structdeclaration format
*/
public void setContext(StructDeclaration context) {
fContext = context;
}
/**
* Sets the fields of an event declaration
*
* @param fields
* the fields in structdeclaration format
*/
public void setFields(StructDeclaration fields) {
fFields = fields;
}
@Override
public StructDeclaration getFields() {
return fFields;
}
@Override
public StructDeclaration getContext() {
return fContext;
}
/**
* Sets the id of an event declaration
*
* @param id
* the id
*/
public void setId(long id) {
if (id < 0 || id > Integer.MAX_VALUE) {
throw new IllegalArgumentException("id out of range"); //$NON-NLS-1$
}
fId = (int) id;
}
@Override
public Long getId() {
return Long.valueOf(fId);
}
/**
* Faster get id assuming you have less than a billion event types
*
* @return the event id
*/
public int id() {
return fId;
}
/**
* Sets the stream of an event declaration
*
* @param stream
* the stream
*/
public void setStream(CTFStream stream) {
fStream = stream;
}
@Override
public CTFStream getStream() {
return fStream;
}
/**
* Is the name of the event declaration set
*
* @return is the name set?
*/
public boolean nameIsSet() {
return fName != null;
}
/**
* Is the context set
*
* @return is the context set
*/
public boolean contextIsSet() {
return fContext != null;
}
/**
* Is a field set?
*
* @return Is the field set?
*/
public boolean fieldsIsSet() {
return fFields != null;
}
/**
* Is the id set?
*
* @return is the id set?
*/
public boolean idIsSet() {
return (fId != UNSET_EVENT_ID);
}
/**
* Is the stream set?
*
* @return is the stream set?
*/
public boolean streamIsSet() {
return fStream != null;
}
@Override
public long getLogLevel() {
return fLogLevel;
}
/**
* Sets the log level
*
* @param level
* the log level
*/
public void setLogLevel(long level) {
fLogLevel = level;
}
@Override
public Set<String> getCustomAttributes() {
return fCustomAttributes.keySet();
}
@Override
public String getCustomAttribute(String key) {
return fCustomAttributes.get(key);
}
/**
* Sets a custom attribute value.
*
* @param key
* the key of the attribute
* @param value
* the value of the attribute
*/
public void setCustomAttribute(String key, String value) {
fCustomAttributes.put(key, value);
}
/**
* Calculates the timestamp value of the event, possibly using the timestamp
* from the last event.
*
* @param timestampDef
* Integer definition of the timestamp.
* @return The calculated timestamp value.
*/
private static long calculateTimestamp(IntegerDefinition timestampDef, long lastTimestamp) {
int len = timestampDef.getDeclaration().getLength();
final long value = timestampDef.getValue();
return calculateTimestamp(value, len, lastTimestamp);
}
private static long calculateTimestamp(final long value, int len, long prevTimestamp) {
long newval;
long majorasbitmask;
long lastTimestamp = prevTimestamp;
/*
* If the timestamp length is 64 bits, it is a full timestamp.
*/
if (len == Long.SIZE) {
lastTimestamp = value;
return lastTimestamp;
}
/*
* Bit mask to keep / remove all old / new bits.
*/
majorasbitmask = (1L << len) - 1;
/*
* If the new value is smaller than the corresponding bits of the last
* timestamp, we assume an overflow of the compact representation.
*/
newval = value;
if (newval < (lastTimestamp & majorasbitmask)) {
newval = newval + (1L << len);
}
/* Keep only the high bits of the old value */
lastTimestamp = lastTimestamp & ~majorasbitmask;
/* Then add the low bits of the new value */
lastTimestamp = lastTimestamp + newval;
return lastTimestamp;
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof EventDeclaration)) {
return false;
}
EventDeclaration other = (EventDeclaration) obj;
if (fId != (other.fId)) {
return false;
}
if (!Objects.equals(fContext, other.fContext)) {
return false;
}
if (!Objects.equals(fFields, other.fFields)) {
return false;
}
if (!Objects.equals(fName, other.fName)) {
return false;
}
if (!Objects.equals(fStream, other.fStream)) {
return false;
}
if (!fCustomAttributes.equals(other.fCustomAttributes)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result)
+ ((fContext == null) ? 0 : fContext.hashCode());
result = (prime * result) + ((fFields == null) ? 0 : fFields.hashCode());
result = (prime * result) + fId;
result = (prime * result) + ((fName == null) ? 0 : fName.hashCode());
final CTFStream stream = fStream;
result = (prime * result) + ((stream == null) ? 0 : stream.hashCode());
result = (prime * result) + fCustomAttributes.hashCode();
return result;
}
}