/******************************************************************************* * Copyright (c) 2014, 2015 Ericsson * * 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: * Vincent Perot - Initial API and implementation * Alexandre Montplaisir - Update to new ITmfEventAspect API * Patrick Tasse - Make pcap aspects singletons *******************************************************************************/ package org.eclipse.tracecompass.internal.tmf.pcap.core.trace; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyString; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.annotation.DefaultLocation; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.internal.pcap.core.packet.BadPacketException; import org.eclipse.tracecompass.internal.pcap.core.protocol.pcap.PcapPacket; import org.eclipse.tracecompass.internal.pcap.core.trace.BadPcapFileException; import org.eclipse.tracecompass.internal.pcap.core.trace.PcapFile; import org.eclipse.tracecompass.internal.pcap.core.util.LinkTypeHelper; import org.eclipse.tracecompass.internal.tmf.pcap.core.Activator; import org.eclipse.tracecompass.internal.tmf.pcap.core.event.PcapEvent; import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapDestinationAspect; import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapProtocolAspect; import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapReferenceAspect; import org.eclipse.tracecompass.internal.tmf.pcap.core.event.aspect.PcapSourceAspect; import org.eclipse.tracecompass.internal.tmf.pcap.core.util.PcapEventFactory; import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.event.aspect.TmfBaseAspects; import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect; import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException; import org.eclipse.tracecompass.tmf.core.project.model.ITmfPropertiesProvider; import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; import org.eclipse.tracecompass.tmf.core.trace.TmfContext; import org.eclipse.tracecompass.tmf.core.trace.TmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus; import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation; import org.eclipse.tracecompass.tmf.core.trace.location.TmfLongLocation; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** * Class that represents a TMF Pcap Trace. It is used to make the glue between * the Pcap parser and TMF. * * TODO handle fields in TmfEventType for the filter view. * * @author Vincent Perot */ public class PcapTrace extends TmfTrace implements ITmfPropertiesProvider { /** pcap trace type id as defined in plugin.xml */ public static final String TRACE_TYPE_ID = "org.eclipse.linuxtools.tmf.pcap.core.pcaptrace"; //$NON-NLS-1$ private static final Collection<ITmfEventAspect<?>> PCAP_ASPECTS = ImmutableList.of( TmfBaseAspects.getTimestampAspect(), PcapSourceAspect.INSTANCE, PcapDestinationAspect.INSTANCE, PcapReferenceAspect.INSTANCE, PcapProtocolAspect.INSTANCE, TmfBaseAspects.getContentsAspect() ); private static final String EMPTY_STRING = ""; //$NON-NLS-1$ private static final int CONFIDENCE = 50; private @Nullable PcapFile fPcapFile; private @Nullable Map<String, String> fTraceProperties = null; @Override public synchronized ITmfLocation getCurrentLocation() { PcapFile pcap = fPcapFile; if (pcap == null) { return new TmfLongLocation(0); } return new TmfLongLocation(pcap.getCurrentRank()); } @Override public synchronized double getLocationRatio(@Nullable ITmfLocation location) { TmfLongLocation loc = (TmfLongLocation) location; PcapFile pcap = fPcapFile; if (loc == null || pcap == null) { return 0; } try { return (pcap.getTotalNbPackets() == 0 ? 0 : ((double) loc.getLocationInfo()) / pcap.getTotalNbPackets()); } catch (IOException | BadPcapFileException e) { String message = e.getMessage(); if (message == null) { message = EMPTY_STRING; } Activator.logError(message, e); return 0; } } @Override @NonNullByDefault({DefaultLocation.TYPE_ARGUMENT}) public synchronized void initTrace(@Nullable IResource resource, @Nullable String path, @Nullable Class<? extends ITmfEvent> type) throws TmfTraceException { super.initTrace(resource, path, type); if (path == null) { throw new TmfTraceException("No path has been specified."); //$NON-NLS-1$ } Path filePath = checkNotNull(Paths.get(path)); try { fPcapFile = new PcapFile(filePath); } catch (IOException | BadPcapFileException e) { throw new TmfTraceException(e.getMessage(), e); } } @Override public Iterable<ITmfEventAspect<?>> getEventAspects() { return PCAP_ASPECTS; } @Override public synchronized @Nullable PcapEvent parseEvent(@Nullable ITmfContext context) { if (context == null) { return null; } long rank = context.getRank(); PcapPacket packet = null; PcapFile pcap = fPcapFile; if (pcap == null) { return null; } try { pcap.seekPacket(rank); packet = pcap.parseNextPacket(); } catch (ClosedChannelException e) { /* * This is handled independently and happens when the user closes * the trace while it is being parsed. It simply stops the parsing. * No need to log a error. */ return null; } catch (IOException | BadPcapFileException | BadPacketException e) { String message = e.getMessage(); if (message == null) { message = EMPTY_STRING; } Activator.logError(message, e); return null; } if (packet == null) { return null; } // Generate an event from this packet and return it. return PcapEventFactory.createEvent(packet, pcap, this); } @Override public synchronized ITmfContext seekEvent(double ratio) { long position; PcapFile pcap = fPcapFile; if (pcap == null) { return new TmfContext(new TmfLongLocation(0), 0); } try { /* * The ratio is between 0 and 1. We multiply it by the total number * of packets to get the position. */ position = (long) (ratio * pcap.getTotalNbPackets()); } catch (IOException | BadPcapFileException e) { String message = e.getMessage(); if (message == null) { message = EMPTY_STRING; } Activator.logError(message, e); return new TmfContext(new TmfLongLocation(0), 0); } TmfLongLocation loc = new TmfLongLocation(position); return new TmfContext(loc, loc.getLocationInfo()); } @Override public synchronized ITmfContext seekEvent(@Nullable ITmfLocation location) { TmfLongLocation loc = (TmfLongLocation) location; if (loc == null) { return new TmfContext(new TmfLongLocation(0)); } return new TmfContext(loc, loc.getLocationInfo()); } @Override public IStatus validate(@Nullable IProject project, @Nullable String path) { // All validations are made when making a new pcap file. if (path == null) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, EMPTY_STRING); } Path filePath = checkNotNull(Paths.get(path)); try (PcapFile file = new PcapFile(filePath)) { } catch (IOException | BadPcapFileException e) { return new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.toString()); } return new TraceValidationStatus(CONFIDENCE, Activator.PLUGIN_ID); } @Override public synchronized void dispose() { super.dispose(); PcapFile pcap = fPcapFile; if (pcap == null) { return; } try { pcap.close(); fPcapFile = null; } catch (IOException e) { String message = e.getMessage(); if (message == null) { message = EMPTY_STRING; } Activator.logError(message, e); return; } } @Override public synchronized Map<String, String> getProperties() { PcapFile pcap = fPcapFile; if (pcap == null) { return Collections.emptyMap(); } if (fTraceProperties != null) { return fTraceProperties; } ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); builder.put(nullToEmptyString(Messages.PcapTrace_Version), String.format("%d%c%d", pcap.getMajorVersion(), '.', pcap.getMinorVersion())); //$NON-NLS-1$ builder.put(nullToEmptyString(Messages.PcapTrace_TimeZoneCorrection), pcap.getTimeZoneCorrection() + " s"); //$NON-NLS-1$ builder.put(nullToEmptyString(Messages.PcapTrace_TimestampAccuracy), String.valueOf(pcap.getTimeAccuracy())); builder.put(nullToEmptyString(Messages.PcapTrace_MaxSnapLength), pcap.getSnapLength() + " bytes"); //$NON-NLS-1$ builder.put(nullToEmptyString(Messages.PcapTrace_LinkLayerHeaderType), LinkTypeHelper.toString((int) pcap.getDataLinkType()) + " (" + pcap.getDataLinkType() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ builder.put(nullToEmptyString(Messages.PcapTrace_FileEndianness), nullToEmptyString(pcap.getByteOrder().toString())); fTraceProperties = builder.build(); return fTraceProperties; } }