blob: 0bad87b2b764fb24b7c5871682b228bbde77562f [file] [log] [blame]
/*******************************************************************************
* 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:
* Marc Dumais - Initial implementation
* Francois Chouinard - Initial API and implementation
* Patrick Tasse - Updated for TMF 2.0
* Matthew Khouzam - update validate
*******************************************************************************/
package org.eclipse.tracecompass.internal.gdbtrace.core.trace;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tracecompass.internal.gdbtrace.core.GdbTraceCorePlugin;
import org.eclipse.tracecompass.internal.gdbtrace.core.event.GdbTraceEvent;
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.timestamp.ITmfTimestamp;
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;
/**
* GDB Tracepoint extension of TmfTrace. This class implements the necessary
* methods and functionalities so that a GDB tracepoint file can be used by the
* TMF framework as a "tracer".
* <p>
*
* @author Marc Dumais
* @author Francois Chouinard
* @author Matthew Khouzam
*/
@SuppressWarnings("restriction")
public class GdbTrace extends TmfTrace {
// ------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------
private static final int CACHE_SIZE = 20;
private static final byte[] HEADER = new byte[] {0x7f, 'T', 'R', 'A', 'C', 'E'};
/** The qualified name for the 'executable' persistent property */
public static final QualifiedName EXEC_KEY = new QualifiedName(GdbTraceCorePlugin.PLUGIN_ID, "executable"); //$NON-NLS-1$
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
// Interface to access GDB Tracepoints
private DsfGdbAdaptor fGdbTpRef;
private long fNbFrames = 0;
// The trace location
long fLocation;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Default constructor
*/
public GdbTrace() {
setCacheSize(CACHE_SIZE);
}
@Override
public IStatus validate(IProject project, String path) {
File file = new File(path);
if (!file.exists()) {
return new Status(IStatus.ERROR, GdbTraceCorePlugin.PLUGIN_ID,
NLS.bind(Messages.GdbTrace_FileNotFound, path));
}
if (!file.isFile()) {
return new Status(IStatus.ERROR, GdbTraceCorePlugin.PLUGIN_ID,
NLS.bind(Messages.GdbTrace_GdbTracesMustBeAFile, path));
}
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[HEADER.length];
int read = stream.read(buffer);
if (read != HEADER.length || !Arrays.equals(buffer, HEADER)) {
return new Status(IStatus.ERROR, GdbTraceCorePlugin.PLUGIN_ID,
NLS.bind(Messages.GdbTrace_NotGdbTraceFile, path));
}
} catch (IOException e) {
return new Status(IStatus.ERROR, GdbTraceCorePlugin.PLUGIN_ID,
NLS.bind(Messages.GdbTrace_IOException, path), e);
}
return new TraceValidationStatus(100, GdbTraceCorePlugin.PLUGIN_ID);
}
@Override
public void initTrace(IResource resource, String path, Class<? extends ITmfEvent> type) throws TmfTraceException {
try {
String tracedExecutable = resource.getPersistentProperty(EXEC_KEY);
if (tracedExecutable == null) {
throw new TmfTraceException(Messages.GdbTrace_ExecutableNotSet);
}
String defaultGdbCommand = Platform.getPreferencesService().getString(GdbPlugin.PLUGIN_ID,
IGdbDebugPreferenceConstants.PREF_DEFAULT_GDB_COMMAND,
IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT, null);
fGdbTpRef = new DsfGdbAdaptor(this, defaultGdbCommand, path, tracedExecutable);
fNbFrames = getNbFrames();
} catch (CoreException e) {
throw new TmfTraceException(Messages.GdbTrace_FailedToInitializeTrace, e);
}
super.initTrace(resource, path, type);
}
@Override
public synchronized void dispose() {
if (fGdbTpRef != null) {
fGdbTpRef.dispose();
}
super.dispose();
}
/**
* @return GDB-DSF session id
*/
public String getDsfSessionId() {
return fGdbTpRef.getSessionId();
}
/**
* @return the number of frames in current tp session
*/
public synchronized long getNbFrames() {
fNbFrames = fGdbTpRef.getNumberOfFrames();
return fNbFrames;
}
// ------------------------------------------------------------------------
// TmfTrace
// ------------------------------------------------------------------------
@Override
public Iterable<ITmfEventAspect<?>> getEventAspects() {
return GdbEventAspects.getAspects();
}
@Override
public synchronized TmfContext seekEvent(ITmfLocation location) {
fLocation = (location != null) ? ((Long) location.getLocationInfo()) : 0;
return new TmfContext(new TmfLongLocation(fLocation), fLocation);
}
@Override
public synchronized ITmfContext seekEvent(double ratio) {
TmfContext context = seekEvent((long) ratio * getNbEvents());
return context;
}
@Override
public double getLocationRatio(ITmfLocation location) {
if (getNbEvents() > 0 && location instanceof TmfLongLocation) {
return (double) ((TmfLongLocation) location).getLocationInfo() / getNbEvents();
}
return 0;
}
@Override
public ITmfLocation getCurrentLocation() {
return new TmfLongLocation(fLocation);
}
@Override
public GdbTraceEvent parseEvent(ITmfContext context) {
if (context.getRank() >= fNbFrames) {
return null;
}
// work-around to ensure that the select and parse of trace frame will
// be atomic
GdbTraceEvent event = fGdbTpRef.selectAndReadFrame(context.getRank());
fLocation++;
return event;
}
@Override
public synchronized TmfContext seekEvent(ITmfTimestamp timestamp) {
long rank = timestamp.getValue();
return seekEvent(rank);
}
@Override
public synchronized TmfContext seekEvent(long rank) {
fLocation = rank;
TmfContext context = new TmfContext(new TmfLongLocation(fLocation), rank);
return context;
}
/**
* Select a frame and update the visualization
*
* @param rank
* the rank
*/
public synchronized void selectFrame(long rank) {
fGdbTpRef.selectDataFrame(rank, true);
}
}