| /******************************************************************************* |
| * Copyright (c) 2011, 2015 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: |
| * Alexandre Montplaisir - Initial API and implementation |
| * Bernd Hufmann - Updated for source and model lookup interfaces |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.ctf.core.event; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration; |
| import org.eclipse.tracecompass.ctf.core.event.IEventDefinition; |
| import org.eclipse.tracecompass.ctf.core.event.types.ICompositeDefinition; |
| import org.eclipse.tracecompass.ctf.core.trace.ICTFStream; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfCustomAttributes; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventType; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEventField; |
| import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect; |
| import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCallsiteAspect; |
| import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfCallsite; |
| import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfModelLookup; |
| import org.eclipse.tracecompass.tmf.core.event.lookup.ITmfSourceLookup; |
| import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; |
| import org.eclipse.tracecompass.tmf.ctf.core.CtfConstants; |
| import org.eclipse.tracecompass.tmf.ctf.core.trace.CtfTmfTrace; |
| |
| /** |
| * A wrapper class around CTF's Event Definition/Declaration that maps all types |
| * of Declaration to native Java types. |
| * |
| * @author Alexandre Montplaisir |
| * @since 3.0 |
| */ |
| @NonNullByDefault |
| public class CtfTmfEvent extends TmfEvent |
| implements ITmfSourceLookup, ITmfModelLookup, ITmfCustomAttributes { |
| |
| // ------------------------------------------------------------------------ |
| // Constants |
| // ------------------------------------------------------------------------ |
| |
| private static final String EMPTY_CTF_EVENT_NAME = "Empty CTF event"; //$NON-NLS-1$ |
| |
| // ------------------------------------------------------------------------ |
| // Support attributes |
| // Not part of this event's "definition", but used to populate lazy-loaded |
| // fields. |
| // ------------------------------------------------------------------------ |
| |
| private final @Nullable IEventDeclaration fEventDeclaration; |
| private final IEventDefinition fEvent; |
| |
| // ------------------------------------------------------------------------ |
| // Attributes |
| // ------------------------------------------------------------------------ |
| |
| /* Fields that are introduced by and part of this event's definition. */ |
| private final int fSourceCpu; |
| private final String fChannel; |
| |
| /** |
| * Field to override {@link TmfEvent#getName()}, to bypass the type-getting |
| */ |
| private final String fEventName; |
| |
| /** Lazy-loaded field containing the event's payload */ |
| private transient @Nullable ITmfEventField fContent; |
| |
| /** Lazy-loaded field for the type, overriding TmfEvent's field */ |
| private transient @Nullable CtfTmfEventType fEventType; |
| |
| private final @Nullable ICTFStream fStream; |
| private final Map<String, Object> fPacketAttributes; |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Constructor, used by {@link CtfTmfEventFactory#createEvent}. |
| * |
| * Only subclasses should call this. It is imperative that the subclass also |
| * has a constructor with the EXACT same parameter signature, because the |
| * factory will look for a constructor with the same arguments. |
| * |
| * @param trace |
| * The trace to which this event belongs |
| * @param rank |
| * The rank of the event |
| * @param timestamp |
| * The timestamp |
| * @param channel |
| * The CTF channel of this event |
| * @param cpu |
| * The event's CPU |
| * @param declaration |
| * The event declaration |
| * @param eventDefinition |
| * The event definition |
| * @since 2.0 |
| */ |
| protected CtfTmfEvent(CtfTmfTrace trace, |
| long rank, |
| ITmfTimestamp timestamp, |
| String channel, |
| int cpu, |
| IEventDeclaration declaration, |
| IEventDefinition eventDefinition) { |
| super(trace, |
| rank, |
| timestamp, |
| /* |
| * Event type. We don't use TmfEvent's field here, we |
| * re-implement getType(). |
| */ |
| null, |
| /* |
| * Content handled with a lazy-loaded field re-implemented in |
| * getContent(). |
| */ |
| null); |
| |
| fEventDeclaration = declaration; |
| fSourceCpu = cpu; |
| fEventName = checkNotNull(declaration.getName()); |
| fEvent = eventDefinition; |
| fChannel = channel; |
| fStream = fEvent.getDeclaration().getStream(); |
| fPacketAttributes = eventDefinition.getPacketAttributes(); |
| } |
| |
| /** |
| * Inner constructor to create "null" events. Don't use this directly in |
| * normal usage, use {@link CtfTmfEventFactory#getNullEvent(CtfTmfTrace)} to |
| * get an instance of an empty event. |
| * |
| * There is no need to give higher visibility to this method than package |
| * visible. |
| * |
| * @param trace |
| * The trace associated with this event |
| */ |
| CtfTmfEvent(CtfTmfTrace trace) { |
| super(trace, |
| ITmfContext.UNKNOWN_RANK, |
| TmfTimestamp.fromNanos(-1), |
| null, |
| new TmfEventField("", null, new CtfTmfEventField[0])); //$NON-NLS-1$ |
| fSourceCpu = -1; |
| fEventName = EMPTY_CTF_EVENT_NAME; |
| fEventDeclaration = null; |
| fEvent = NullEventDefinition.INSTANCE; |
| fChannel = ""; //$NON-NLS-1$ |
| fStream = null; |
| fPacketAttributes = Collections.emptyMap(); |
| } |
| |
| /** |
| * Default constructor. Do not use directly, but it needs to be present |
| * because it's used in extension points, and the framework will use this |
| * constructor to get the class type. |
| * |
| * @deprecated Should not be called by normal code |
| */ |
| @Deprecated |
| public CtfTmfEvent() { |
| super(); |
| fSourceCpu = -1; |
| fEventName = EMPTY_CTF_EVENT_NAME; |
| fEventDeclaration = null; |
| fEvent = NullEventDefinition.INSTANCE; |
| fChannel = ""; //$NON-NLS-1$ |
| fStream = null; |
| fPacketAttributes = Collections.emptyMap(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Getters/Setters/Predicates |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Gets the cpu core the event was recorded on. |
| * |
| * @return The cpu id for a given source. In lttng it's from CPUINFO |
| */ |
| public int getCPU() { |
| return fSourceCpu; |
| } |
| |
| /** |
| * Return the CTF trace's channel from which this event originates. |
| * |
| * @return The event's channel |
| * @since 2.0 |
| */ |
| public String getChannel() { |
| return fChannel; |
| } |
| |
| /** |
| * Get the stream Id |
| * |
| * @return the stream ID or -1 if the stream is null |
| * @since 2.0 |
| */ |
| public long getStreamId() { |
| ICTFStream stream = fStream; |
| if (stream == null) { |
| return -1; |
| } |
| return stream.getId(); |
| } |
| |
| /** |
| * Get the event unscaled time. This is often the number of cycles run. In |
| * some cases it is the same as the actual timestamp. |
| * |
| * @return the event unscaled time in long format. |
| * @since 3.0 |
| */ |
| public long getUnscaledTime() { |
| return fEvent.getTimestamp(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // TmfEvent |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public CtfTmfTrace getTrace() { |
| /* |
| * Should be of the right type, since we take a CtfTmfTrace at the |
| * constructor |
| */ |
| return (CtfTmfTrace) super.getTrace(); |
| } |
| |
| @Override |
| public synchronized ITmfEventType getType() { |
| CtfTmfEventType type = fEventType; |
| if (type == null) { |
| type = new CtfTmfEventType(fEventName, getContent()); |
| |
| /* |
| * Register the event type in the owning trace, but only if there is |
| * one |
| */ |
| getTrace().registerEventType(type); |
| fEventType = type; |
| } |
| return type; |
| } |
| |
| @Override |
| public String getName() { |
| return fEventName; |
| } |
| |
| @Override |
| public synchronized ITmfEventField getContent() { |
| ITmfEventField content = fContent; |
| if (content == null) { |
| content = new TmfEventField( |
| ITmfEventField.ROOT_FIELD_ID, null, parseFields(fEvent)); |
| fContent = content; |
| } |
| return content; |
| } |
| |
| /** |
| * Extract the field information from the structDefinition haze-inducing |
| * mess, and put them into something ITmfEventField can cope with. |
| */ |
| private static CtfTmfEventField[] parseFields(IEventDefinition eventDef) { |
| List<CtfTmfEventField> fields = new ArrayList<>(); |
| |
| ICompositeDefinition structFields = eventDef.getFields(); |
| if (structFields != null) { |
| if (structFields.getFieldNames() != null) { |
| for (String fn : structFields.getFieldNames()) { |
| fields.add(CtfTmfEventField.parseField(structFields.getDefinition(fn), fn)); |
| } |
| } |
| } |
| /* Add context information as CtfTmfEventField */ |
| ICompositeDefinition structContext = eventDef.getContext(); |
| if (structContext != null) { |
| for (String contextName : structContext.getFieldNames()) { |
| /* Prefix field name */ |
| String curContextName = CtfConstants.CONTEXT_FIELD_PREFIX + contextName; |
| fields.add(CtfTmfEventField.parseField(structContext.getDefinition(contextName), curContextName)); |
| } |
| } |
| |
| return fields.toArray(new @NonNull CtfTmfEventField[fields.size()]); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // ITmfCustomAttributes |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public Set<String> listCustomAttributes() { |
| IEventDeclaration declaration = fEventDeclaration; |
| if (declaration == null) { |
| return new HashSet<>(); |
| } |
| return declaration.getCustomAttributes(); |
| } |
| |
| @Override |
| public @Nullable String getCustomAttribute(@Nullable String name) { |
| IEventDeclaration declaration = fEventDeclaration; |
| if (declaration == null) { |
| return null; |
| } |
| return declaration.getCustomAttribute(name); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // ITmfSourceLookup |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Get the call site for this event. |
| * |
| * @return the call site information, or null if there is none |
| * @since 3.0 |
| */ |
| @Override |
| public @Nullable ITmfCallsite getCallsite() { |
| ITmfCallsite callsite = null; |
| |
| ITmfEventField ipField = getContent().getField(CtfConstants.CONTEXT_FIELD_PREFIX + CtfConstants.IP_KEY); |
| if (ipField != null && ipField.getValue() instanceof Long) { |
| long ip = (Long) ipField.getValue(); |
| callsite = getTrace().getCallsite(fEventName, ip); |
| } |
| |
| if (callsite == null) { |
| callsite = getTrace().getCallsite(fEventName); |
| } |
| return callsite; |
| } |
| |
| @Override |
| public @Nullable List<@NonNull ITmfCallsite> getCallsites() { |
| for (ITmfEventAspect<?> aspect : getTrace().getEventAspects()) { |
| if (aspect instanceof TmfCallsiteAspect) { |
| List<ITmfCallsite> callsite = ((TmfCallsiteAspect) aspect).resolve(this); |
| if (callsite != null) { |
| return callsite; |
| } |
| } |
| } |
| return ITmfSourceLookup.super.getCallsites(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| // ITmfModelLookup |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public @Nullable String getModelUri() { |
| return getCustomAttribute(CtfConstants.MODEL_URI_KEY); |
| } |
| |
| /** |
| * Gets the packet attributes. The result is an instance of one of the |
| * following classes: <code>Entry<String, Long></code>, <code>Long</code>, |
| * <code>String</code> or <code>Double</code>. The map contains pairs of key |
| * and values where the key and value can never be null. |
| * |
| * @return gets the packet attributes |
| * @since 2.0 |
| */ |
| public Map<String, Object> getPacketAttributes() { |
| return fPacketAttributes; |
| } |
| |
| /** |
| * Get the packet context |
| * |
| * @return the packet context |
| * @since 4.2 |
| */ |
| public @Nullable ICompositeDefinition getPacketContext() { |
| return fEvent.getPacketContext(); |
| } |
| |
| /** |
| * Get the stream context |
| * |
| * @return the stream context |
| * @since 4.2 |
| */ |
| public @Nullable ICompositeDefinition getStreamContext() { |
| return fEvent.getStreamContext(); |
| } |
| |
| /** |
| * Get the event context |
| * |
| * @return the event context |
| * @since 4.2 |
| */ |
| public @Nullable ICompositeDefinition getEventContext() { |
| return fEvent.getEventContext(); |
| } |
| // ------------------------------------------------------------------------ |
| // Object |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = super.hashCode(); |
| result = prime * result + getCPU(); |
| result = prime * result + getChannel().hashCode(); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object obj) { |
| if (!super.equals(obj)) { |
| return false; |
| } |
| /* super.equals() checks that the classes are the same */ |
| CtfTmfEvent other = checkNotNull((CtfTmfEvent) obj); |
| if (getCPU() != other.getCPU()) { |
| return false; |
| } |
| return (getChannel().equals(other.getChannel())); |
| } |
| |
| } |