/******************************************************************************* * 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; } }