/*******************************************************************************
* Copyright (c) 2016 Ericsson 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:
* Marc Khouzam (Ericsson) - initial API and implementation
* Mikhail Khodjaiants (Mentor Graphics) - initial API and implementation
* Intel Corporation - Added Reverse Debugging BTrace support
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.debug.core.model.IConnectHandler;
import org.eclipse.cdt.debug.core.model.IDebugNewExecutableHandler;
import org.eclipse.cdt.debug.core.model.IMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.debug.core.model.IResumeWithoutSignalHandler;
import org.eclipse.cdt.debug.core.model.IReverseResumeHandler;
import org.eclipse.cdt.debug.core.model.IReverseStepIntoHandler;
import org.eclipse.cdt.debug.core.model.IReverseStepOverHandler;
import org.eclipse.cdt.debug.core.model.IReverseToggleHandler;
import org.eclipse.cdt.debug.core.model.ISaveTraceDataHandler;
import org.eclipse.cdt.debug.core.model.IStartTracingHandler;
import org.eclipse.cdt.debug.core.model.IStepIntoSelectionHandler;
import org.eclipse.cdt.debug.core.model.ISteppingModeTarget;
import org.eclipse.cdt.debug.core.model.IStopTracingHandler;
import org.eclipse.cdt.debug.core.model.IUncallHandler;
import org.eclipse.cdt.debug.ui.IPinProvider;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfResumeCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepIntoSelectionCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepOverCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfStepReturnCommand;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfSteppingModeTarget;
import org.eclipse.cdt.dsf.debug.ui.actions.DsfSuspendCommand;
import org.eclipse.cdt.dsf.debug.ui.sourcelookup.DsfSourceDisplayAdapter;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.DefaultRefreshAllTarget;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.IRefreshAllTarget;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.DefaultDsfModelSelectionPolicyFactory;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.commands.ISelectNextTraceRecordHandler;
import org.eclipse.cdt.dsf.gdb.internal.commands.ISelectPrevTraceRecordHandler;
import org.eclipse.cdt.dsf.gdb.internal.ui.actions.DsfTerminateCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.actions.GdbDisconnectCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.actions.GdbRestartCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.actions.GdbSteppingModeTarget;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbConnectCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbDebugNewExecutableCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbResumeWithoutSignalCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbReverseResumeCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbReverseStepIntoCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbReverseStepOverCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbReverseToggleCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbSaveTraceDataCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbSelectNextTraceRecordCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbSelectPrevTraceRecordCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStartTracingCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbStopTracingCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.commands.GdbUncallCommand;
import org.eclipse.cdt.dsf.gdb.internal.ui.memory.GdbMemoryBlockAddressInfoRetrieval;
import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.GdbViewModelAdapter;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMAdapter;
import org.eclipse.cdt.ui.text.c.hover.ICEditorTextHover;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.commands.IDisconnectHandler;
import org.eclipse.debug.core.commands.IRestartHandler;
import org.eclipse.debug.core.commands.IResumeHandler;
import org.eclipse.debug.core.commands.IStepIntoHandler;
import org.eclipse.debug.core.commands.IStepOverHandler;
import org.eclipse.debug.core.commands.IStepReturnHandler;
import org.eclipse.debug.core.commands.ISuspendHandler;
import org.eclipse.debug.core.commands.ITerminateHandler;
import org.eclipse.debug.core.model.IDebugModelProvider;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerInputProvider;
import org.eclipse.debug.ui.contexts.ISuspendTrigger;
import org.eclipse.debug.ui.sourcelookup.ISourceDisplay;
/**
* This class creates and holds the different adapters registered with the DSF session
* as well as the adapters for the launch.
*/
@Immutable
public class GdbSessionAdapters {
private final ILaunch fLaunch;
private final DsfSession fSession;
private final Map<Class<?>, Object> fLaunchAdapters = new HashMap<>();
private final Class<?>[] fLaunchAdapterTypes;
public GdbSessionAdapters(ILaunch launch, DsfSession session, Class<?>[] launchAdapterTypes) {
fLaunch = launch;
fSession = session;
fLaunchAdapterTypes = launchAdapterTypes;
createAdapters();
}
/**
* Creates all model and launch adapters.
*/
protected void createAdapters() {
for (Class<?> adapterType : getModelAdapters()) {
Object adapter = createModelAdapter(adapterType, getLaunch(), getSession());
if (adapter != null) {
getSession().registerModelAdapter(adapterType, adapter);
}
}
for (Class<?> adapterType : fLaunchAdapterTypes) {
Object adapter = createLaunchAdapter(adapterType, getLaunch(), getSession());
if (adapter != null) {
fLaunchAdapters.put(adapterType, adapter);
}
}
}
/**
* Returns the adapter object registered with the session for the given adapter type
* or null if no adapter is registered.
*/
@SuppressWarnings("unchecked")
public <T> T getModelAdapter(Class<T> adapterType) {
return (T)fSession.getModelAdapter(adapterType);
}
/**
* Returns the adapter object registered with {@link ILaunch} for the given adapter type
* or null if no adapter is registered.
*/
@SuppressWarnings("unchecked")
public <T> T getLaunchAdapter(Class<T> adapterType) {
if (adapterType.equals(ITerminateHandler.class) ||
adapterType.equals(IConnectHandler.class) ||
adapterType.equals(IDisconnectHandler.class) ||
adapterType.equals(IDebugNewExecutableHandler.class)) {
// These launch adapters re-use the session adapters.
// Return them directly instead of including them
// in fLaunchAdapters to avoid trying to dispose of them
// twice when dispose() is called.
return (T)fSession.getModelAdapter(adapterType);
}
return (T)fLaunchAdapters.get(adapterType);
}
public void dispose() {
for (Class<?> adapterType : getModelAdapters()) {
Object adapter = getSession().getModelAdapter(adapterType);
if (adapter != null) {
getSession().unregisterModelAdapter(adapterType);
disposeAdapter(adapter);
}
}
for (Class<?> adapterType : fLaunchAdapterTypes) {
Object adapter = fLaunchAdapters.remove(adapterType);
if (adapter != null) {
disposeAdapter(adapter);
}
}
}
/**
* Returns all adapter types registered with the session.
* Clients can override this method to add a new adapter type and
* then override {@link GdbSessionAdapters.createModelAdapter()}
* to provide the adapter object.
*/
protected List<Class<?>> getModelAdapters() {
// Return a list to which elements can be added
return new ArrayList<>(Arrays.asList(
SteppingController.class,
IViewerInputProvider.class,
ISteppingModeTarget.class,
ISourceDisplay.class,
IStepIntoHandler.class,
IStepIntoSelectionHandler.class,
IReverseStepIntoHandler.class,
IStepOverHandler.class,
IReverseStepOverHandler.class,
IStepReturnHandler.class,
IUncallHandler.class,
ISuspendHandler.class,
IResumeHandler.class,
IReverseResumeHandler.class,
IResumeWithoutSignalHandler.class,
IRestartHandler.class,
ITerminateHandler.class,
IDebugNewExecutableHandler.class,
IConnectHandler.class,
IDisconnectHandler.class,
IModelSelectionPolicyFactory.class,
IRefreshAllTarget.class,
IReverseToggleHandler.class,
IStartTracingHandler.class,
IStopTracingHandler.class,
ISaveTraceDataHandler.class,
ISelectNextTraceRecordHandler.class,
ISelectPrevTraceRecordHandler.class,
IPinProvider.class,
IDebugModelProvider.class,
ILaunch.class,
ICEditorTextHover.class,
IMemoryBlockAddressInfoRetrieval.class));
}
/**
* Creates the adapter object for the given adapter type to register it with {@link ILaunch}.
* Clients can override this method to provide their own adapters.
*/
@SuppressWarnings("unchecked")
protected <T> T createLaunchAdapter(Class<T> adapterType, ILaunch launch, DsfSession session) {
if (adapterType.equals(IElementContentProvider.class) ||
adapterType.equals(IModelProxyFactory.class) ||
adapterType.equals(IColumnPresentationFactory.class)) {
return (T)getViewModelAdapter();
}
if (adapterType.equals(ISuspendTrigger.class)) {
return (T)new GdbSuspendTrigger(session, launch);
}
return null;
}
/**
* Creates the adapter object for the given adapter type to register it with the model.
* Clients can override this method to provide their own adapters.
*/
@SuppressWarnings("unchecked")
protected <T> T createModelAdapter(Class<T> adapterType, ILaunch launch, DsfSession session) {
if (SteppingController.class.equals(adapterType)) {
return (T)new SteppingController(session);
}
if (IViewerInputProvider.class.equals(adapterType)) {
return (T)new GdbViewModelAdapter(session, getSteppingController());
}
if (ISteppingModeTarget.class.equals(adapterType)) {
return (T)new GdbSteppingModeTarget(session);
}
if (ISourceDisplay.class.equals(adapterType)) {
return launch.getSourceLocator() instanceof ISourceLookupDirector ?
(T)new DsfSourceDisplayAdapter(
session,
(ISourceLookupDirector)launch.getSourceLocator(),
getSteppingController()
) : null;
}
if (IStepIntoHandler.class.equals(adapterType)) {
return (T)new DsfStepIntoCommand(session, getSteppingModeTarget());
}
if (IStepIntoSelectionHandler.class.equals(adapterType)) {
return (T)new DsfStepIntoSelectionCommand(session);
}
if (IReverseStepIntoHandler.class.equals(adapterType)) {
return (T)new GdbReverseStepIntoCommand(session, getSteppingModeTarget());
}
if (IStepOverHandler.class.equals(adapterType)) {
return (T)new DsfStepOverCommand(session, getSteppingModeTarget());
}
if (IReverseStepOverHandler.class.equals(adapterType)) {
return (T)new GdbReverseStepOverCommand(session, getSteppingModeTarget());
}
if (IStepReturnHandler.class.equals(adapterType)) {
return (T)new DsfStepReturnCommand(session);
}
if (IUncallHandler.class.equals(adapterType)) {
return (T)new GdbUncallCommand(session, getSteppingModeTarget());
}
if (ISuspendHandler.class.equals(adapterType)) {
return (T)new DsfSuspendCommand(session);
}
if (IResumeHandler.class.equals(adapterType)) {
return (T)new DsfResumeCommand(session);
}
if (IReverseResumeHandler.class.equals(adapterType)) {
return (T)new GdbReverseResumeCommand(session);
}
if (IResumeWithoutSignalHandler.class.equals(adapterType)) {
return (T)new GdbResumeWithoutSignalCommand(session);
}
if (IRestartHandler.class.equals(adapterType)) {
return (T)new GdbRestartCommand(session, getLaunch());
}
if (ITerminateHandler.class.equals(adapterType)) {
return (T)new DsfTerminateCommand(session);
}
if (IDebugNewExecutableHandler.class.equals(adapterType)) {
return (T)new GdbDebugNewExecutableCommand(session, launch);
}
if (IConnectHandler.class.equals(adapterType)) {
return (T)new GdbConnectCommand(session, launch);
}
if (IDisconnectHandler.class.equals(adapterType)) {
return (T)new GdbDisconnectCommand(session);
}
if (IModelSelectionPolicyFactory.class.equals(adapterType)) {
return (T)new DefaultDsfModelSelectionPolicyFactory();
}
if (IRefreshAllTarget.class.equals(adapterType)) {
return (T)new DefaultRefreshAllTarget();
}
if (IReverseToggleHandler.class.equals(adapterType)) {
return (T)new GdbReverseToggleCommand(session);
}
if (IStartTracingHandler.class.equals(adapterType)) {
return (T)new GdbStartTracingCommand(session);
}
if (IStopTracingHandler.class.equals(adapterType)) {
return (T)new GdbStopTracingCommand(session);
}
if (ISaveTraceDataHandler.class.equals(adapterType)) {
return (T)new GdbSaveTraceDataCommand(session);
}
if (ISelectNextTraceRecordHandler.class.equals(adapterType)) {
return (T)new GdbSelectNextTraceRecordCommand(session);
}
if (ISelectPrevTraceRecordHandler.class.equals(adapterType)) {
return (T)new GdbSelectPrevTraceRecordCommand(session);
}
if (IPinProvider.class.equals(adapterType)) {
return (T)new GdbPinProvider(session);
}
if (IDebugModelProvider.class.equals(adapterType)) {
return (T)new IDebugModelProvider() {
// @see org.eclipse.debug.core.model.IDebugModelProvider#getModelIdentifiers()
@Override
public String[] getModelIdentifiers() {
return new String[] {
GdbLaunchDelegate.GDB_DEBUG_MODEL_ID,
ICBreakpoint.C_BREAKPOINTS_DEBUG_MODEL_ID,
"org.eclipse.cdt.gdb" //$NON-NLS-1$
};
}
};
}
/*
* Registering the launch as an adapter, ensures that this launch,
* and debug model ID will be associated with all DMContexts from this
* session.
*/
if (ILaunch.class.equals(adapterType)) {
return (T)launch;
}
/*
* Register debug hover adapter (bug 309001).
*/
if (ICEditorTextHover.class.equals(adapterType)) {
return (T)new GdbDebugTextHover();
}
if (IMemoryBlockAddressInfoRetrieval.class.equals(adapterType)) {
return (T) new GdbMemoryBlockAddressInfoRetrieval(session);
}
return null;
}
/**
* Returns the method that will be called to dispose the given object.
*
* @param adapter the object to dispose
* @return "dispose()" method or null if the given object doesn't have "dispose()" method
*
* Clients can override this method to provide dispose methods different than "dispose()"
* for specific adapters.
*/
protected Method getDisposeMethod(Object adapter) {
if (adapter != null) {
try {
return adapter.getClass().getMethod("dispose"); //$NON-NLS-1$
}
catch(NoSuchMethodException | SecurityException e) {
// ignore
}
}
return null;
}
protected DsfSession getSession() {
return fSession;
}
protected ILaunch getLaunch() {
return fLaunch;
}
private void disposeAdapter(Object adapter) {
try {
Method dispose = getDisposeMethod(adapter);
if (dispose != null) {
dispose.invoke(adapter);
}
}
catch(SecurityException | IllegalAccessException | IllegalArgumentException e) {
// ignore
}
catch(InvocationTargetException e) {
GdbPlugin.log(e.getTargetException());
}
}
protected DsfSteppingModeTarget getSteppingModeTarget() {
ISteppingModeTarget target = (ISteppingModeTarget)fSession.getModelAdapter(ISteppingModeTarget.class);
if (target instanceof DsfSteppingModeTarget) {
return (DsfSteppingModeTarget)target;
}
return null;
}
protected SteppingController getSteppingController() {
return (SteppingController)fSession.getModelAdapter(SteppingController.class);
}
protected IVMAdapter getViewModelAdapter() {
IViewerInputProvider provider = (IViewerInputProvider)fSession.getModelAdapter(IViewerInputProvider.class);
if (provider instanceof IVMAdapter) {
return (IVMAdapter)provider;
}
return null;
}
}