diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.classpath b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.project b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.project new file mode 100644 index 00000000000..3e2c012e7ff --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.project @@ -0,0 +1,34 @@ + + + org.eclipse.cdt.dsf.gdb.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/.api_filters b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/.api_filters new file mode 100644 index 00000000000..f17a60ee7f9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/.api_filters @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/org.eclipse.jdt.core.prefs b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..e2b74ae779a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Tue Jun 24 11:04:56 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..6165783264e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/META-INF/MANIFEST.MF @@ -0,0 +1,27 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb.ui;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.cdt.dsf, + org.eclipse.cdt.dsf.ui, + org.eclipse.debug.ui, + org.eclipse.cdt.debug.core, + org.eclipse.cdt.dsf.gdb, + org.eclipse.cdt.debug.ui, + org.eclipse.cdt.core, + org.eclipse.cdt.ui, + org.eclipse.ui.ide, + org.eclipse.core.variables +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Export-Package: org.eclipse.cdt.dsf.gdb.internal.ui.actions;x-internal:=true, + org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints;x-internal:=true, + org.eclipse.cdt.dsf.gdb.internal.ui.launching;x-internal:=true, + org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel;x-internal:=true, + org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch;x-internal:=true diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/about.html b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/about.html @@ -0,0 +1,24 @@ + + + + +About +

About This Content

+ +

June 5, 2007

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/build.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/build.properties new file mode 100644 index 00000000000..4b33d1a92e1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/build.properties @@ -0,0 +1,18 @@ +############################################################################### +# Copyright (c) 2006, 2008 Wind River Systems 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: +# Wind River Systems - initial API and implementation +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + about.html,\ + icons/,\ + plugin.properties diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/c_app.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/c_app.gif new file mode 100644 index 00000000000..805a1d0ab8b Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/c_app.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/connect.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/connect.gif new file mode 100755 index 00000000000..866ad338250 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/connect.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/exec_obj.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/exec_obj.gif new file mode 100644 index 00000000000..7b3a92e0906 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/obj16/exec_obj.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/arguments_tab.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/arguments_tab.gif new file mode 100644 index 00000000000..44660b5f0b5 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/arguments_tab.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugger_tab.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugger_tab.gif new file mode 100644 index 00000000000..d90a29fead8 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/debugger_tab.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/main_tab.gif b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/main_tab.gif new file mode 100644 index 00000000000..b226e41c527 Binary files /dev/null and b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/icons/full/view16/main_tab.gif differ diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties new file mode 100644 index 00000000000..affa7716080 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2008 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: +# Ericsson - initial API and implementation +############################################################################### +pluginName=GDB DSF Debugger Integration UI +providerName=Eclipse.org + +action.connect.label = Connect... +action.connect.tooltip = Connect to a process diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml new file mode 100644 index 00000000000..2b4ad5f4e77 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/plugin.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java new file mode 100644 index 00000000000..6dd3e7cb61c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbAdapterFactory.java @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.cdt.debug.core.model.IRestart; +import org.eclipse.cdt.debug.core.model.ISteppingModeTarget; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +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.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.contexts.DsfSuspendTrigger; +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.actions.IConnect; +import org.eclipse.cdt.dsf.gdb.internal.ui.actions.DsfTerminateCommand; +import org.eclipse.cdt.dsf.gdb.internal.ui.actions.GdbConnectCommand; +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.viewmodel.GdbViewModelAdapter; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IAdapterFactory; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.core.commands.IDisconnectHandler; +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.ui.contexts.ISuspendTrigger; +import org.eclipse.debug.ui.sourcelookup.ISourceDisplay; + +/** + * This implementation of platform adapter factory only retrieves the adapters + * for the launch object. But it also manages the creation and destruction + * of the session-based adapters which are returned by the + * IDMContext.getAdapter() methods. + */ +@ThreadSafe +@SuppressWarnings({"restriction"}) +public class GdbAdapterFactory + implements IAdapterFactory, ILaunchesListener2 +{ + @Immutable + class SessionAdapterSet { + final GdbLaunch fLaunch; + final GdbViewModelAdapter fViewModelAdapter; + final DsfSourceDisplayAdapter fSourceDisplayAdapter; + final DsfStepIntoCommand fStepIntoCommand; + final DsfStepOverCommand fStepOverCommand; + final DsfStepReturnCommand fStepReturnCommand; + final DsfSuspendCommand fSuspendCommand; + final DsfResumeCommand fResumeCommand; + final GdbRestartCommand fRestartCommand; + final DsfTerminateCommand fTerminateCommand; + final GdbConnectCommand fConnectCommand; + final GdbDisconnectCommand fDisconnectCommand; + final IDebugModelProvider fDebugModelProvider; + final DsfSuspendTrigger fSuspendTrigger; + final DsfSteppingModeTarget fSteppingModeTarget; + final IModelSelectionPolicyFactory fModelSelectionPolicyFactory; + final SteppingController fSteppingController; + final DefaultRefreshAllTarget fRefreshAllTarget; + + SessionAdapterSet(GdbLaunch launch) { + fLaunch = launch; + DsfSession session = launch.getSession(); + + // register stepping controller + fSteppingController = new SteppingController(session); + session.registerModelAdapter(SteppingController.class, fSteppingController); + + fViewModelAdapter = new GdbViewModelAdapter(session, fSteppingController); + + if (launch.getSourceLocator() instanceof ISourceLookupDirector) { + fSourceDisplayAdapter = new DsfSourceDisplayAdapter(session, (ISourceLookupDirector)launch.getSourceLocator(), fSteppingController); + } else { + fSourceDisplayAdapter = null; + } + session.registerModelAdapter(ISourceDisplay.class, fSourceDisplayAdapter); + + fSteppingModeTarget= new DsfSteppingModeTarget(); + fStepIntoCommand = new DsfStepIntoCommand(session, fSteppingModeTarget); + fStepOverCommand = new DsfStepOverCommand(session, fSteppingModeTarget); + fStepReturnCommand = new DsfStepReturnCommand(session); + fSuspendCommand = new DsfSuspendCommand(session); + fResumeCommand = new DsfResumeCommand(session); + fRestartCommand = new GdbRestartCommand(session, fLaunch); + fTerminateCommand = new DsfTerminateCommand(session); + fConnectCommand = new GdbConnectCommand(session); + fDisconnectCommand = new GdbDisconnectCommand(session); + fSuspendTrigger = new DsfSuspendTrigger(session, fLaunch); + fModelSelectionPolicyFactory = new DefaultDsfModelSelectionPolicyFactory(); + fRefreshAllTarget = new DefaultRefreshAllTarget(); + + session.registerModelAdapter(ISteppingModeTarget.class, fSteppingModeTarget); + session.registerModelAdapter(IStepIntoHandler.class, fStepIntoCommand); + session.registerModelAdapter(IStepOverHandler.class, fStepOverCommand); + session.registerModelAdapter(IStepReturnHandler.class, fStepReturnCommand); + session.registerModelAdapter(ISuspendHandler.class, fSuspendCommand); + session.registerModelAdapter(IResumeHandler.class, fResumeCommand); + session.registerModelAdapter(IRestart.class, fRestartCommand); + session.registerModelAdapter(ITerminateHandler.class, fTerminateCommand); + session.registerModelAdapter(IConnect.class, fConnectCommand); + session.registerModelAdapter(IDisconnectHandler.class, fDisconnectCommand); + session.registerModelAdapter(IModelSelectionPolicyFactory.class, fModelSelectionPolicyFactory); + session.registerModelAdapter(IRefreshAllTarget.class, fRefreshAllTarget); + + fDebugModelProvider = new IDebugModelProvider() { + // @see org.eclipse.debug.core.model.IDebugModelProvider#getModelIdentifiers() + public String[] getModelIdentifiers() { + return new String[] { GdbLaunchDelegate.GDB_DEBUG_MODEL_ID }; + } + }; + session.registerModelAdapter(IDebugModelProvider.class, fDebugModelProvider); + + /* + * Registering the launch as an adapter, ensures that this launch, + * and debug model ID will be associated with all DMContexts from this + * session. + */ + session.registerModelAdapter(ILaunch.class, fLaunch); + } + + void dispose() { + DsfSession session = fLaunch.getSession(); + + fViewModelAdapter.dispose(); + + session.unregisterModelAdapter(ISourceDisplay.class); + if (fSourceDisplayAdapter != null) fSourceDisplayAdapter.dispose(); + + session.unregisterModelAdapter(SteppingController.class); + fSteppingController.dispose(); + + session.unregisterModelAdapter(ISteppingModeTarget.class); + session.unregisterModelAdapter(IStepIntoHandler.class); + session.unregisterModelAdapter(IStepOverHandler.class); + session.unregisterModelAdapter(IStepReturnHandler.class); + session.unregisterModelAdapter(ISuspendHandler.class); + session.unregisterModelAdapter(IResumeHandler.class); + session.unregisterModelAdapter(IRestart.class); + session.unregisterModelAdapter(ITerminateHandler.class); + session.unregisterModelAdapter(IConnect.class); + session.unregisterModelAdapter(IDisconnectHandler.class); + session.unregisterModelAdapter(IModelSelectionPolicyFactory.class); + session.unregisterModelAdapter(IRefreshAllTarget.class); + + fStepIntoCommand.dispose(); + fStepOverCommand.dispose(); + fStepReturnCommand.dispose(); + fSuspendCommand.dispose(); + fResumeCommand.dispose(); + fRestartCommand.dispose(); + fTerminateCommand.dispose(); + fConnectCommand.dispose(); + fDisconnectCommand.dispose(); + fSuspendTrigger.dispose(); + } + } + + /** + * Active adapter sets. They are accessed using the launch instance + * which owns the debug services session. + */ + private static Map fgLaunchAdapterSets = + Collections.synchronizedMap(new HashMap()); + + /** + * Map of launches for which adapter sets have already been disposed. + * This map (used as a set) is maintained in order to avoid re-creating an + * adapter set after the launch was removed from the launch manager, but + * while the launch is still being held by other classes which may + * request its adapters. A weak map is used to avoid leaking + * memory once the launches are no longer referenced. + *

+ * Access to this map is synchronized using the fgLaunchAdapterSets + * instance. + *

+ */ + private static Map fgDisposedLaunchAdapterSets = + new WeakHashMap(); + + static void disposeAdapterSet(ILaunch launch) { + synchronized(fgLaunchAdapterSets) { + if ( fgLaunchAdapterSets.containsKey(launch) ) { + fgLaunchAdapterSets.remove(launch).dispose(); + fgDisposedLaunchAdapterSets.put(launch, null); + } + } + } + + public GdbAdapterFactory() { + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } + + /** + * This method only actually returns adapters for the launch object. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Object adaptableObject, Class adapterType) { + if (!(adaptableObject instanceof GdbLaunch)) return null; + + GdbLaunch launch = (GdbLaunch)adaptableObject; + + // Check for valid session. + // Note: even if the session is no longer active, the adapter set + // should still be returned. This is because the view model may still + // need to show elements representing a terminated process/thread/etc. + DsfSession session = launch.getSession(); + if (session == null) return null; + + // Find the correct set of adapters based on the launch session-ID. If not found + // it means that we have a new launch and new session, and we have to create a + // new set of adapters. + + SessionAdapterSet adapterSet; + synchronized(fgLaunchAdapterSets) { + // The adapter set for the given launch was already disposed. + // Return a null adapter. + if (fgDisposedLaunchAdapterSets.containsKey(launch)) { + return null; + } + adapterSet = fgLaunchAdapterSets.get(launch); + if (adapterSet == null) { + adapterSet = new SessionAdapterSet(launch); + fgLaunchAdapterSets.put(launch, adapterSet); + } + } + + // Returns the adapter type for the launch object. + if (adapterType.equals(IElementContentProvider.class)) return adapterSet.fViewModelAdapter; + else if (adapterType.equals(IModelProxyFactory.class)) return adapterSet.fViewModelAdapter; + else if (adapterType.equals(IColumnPresentationFactory.class)) return adapterSet.fViewModelAdapter; + else if (adapterType.equals(ISuspendTrigger.class)) return adapterSet.fSuspendTrigger; + else return null; + } + + @SuppressWarnings("unchecked") + public Class[] getAdapterList() { + return new Class[] { + IElementContentProvider.class, IModelProxyFactory.class, ISuspendTrigger.class, + IColumnPresentationFactory.class + }; + } + + public void launchesRemoved(ILaunch[] launches) { + // Dispose the set of adapters for a launch only after the launch is + // removed. + for (ILaunch launch : launches) { + if (launch instanceof GdbLaunch) { + disposeAdapterSet(launch); + } + } + } + + public void launchesTerminated(ILaunch[] launches) { + } + + public void launchesAdded(ILaunch[] launches) { + } + + public void launchesChanged(ILaunch[] launches) { + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java new file mode 100644 index 00000000000..383de2ec65d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/GdbUIPlugin.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - modified to remove dependency on cdt.launch + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui; + +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class GdbUIPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf.gdb.ui"; //$NON-NLS-1$ + + // The shared instance + private static GdbUIPlugin plugin; + + private static BundleContext fgBundleContext; + + /** + * The constructor + */ + public GdbUIPlugin() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + disposeAdapterSets(); + plugin = null; + super.stop(context); + fgBundleContext = null; + } + + /** + * Dispose adapter sets for all launches. + */ + private void disposeAdapterSets() { + for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { + if (launch instanceof GdbLaunch) { + GdbAdapterFactory.disposeAdapterSet(launch); + } + } + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static GdbUIPlugin getDefault() { + return plugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + /** + * copied from org.eclipse.cdt.launch.internal.ui.LaunchUIPlugin + */ + private static Shell debugDialogShell; + + public static Shell getShell() { + if (getActiveWorkbenchShell() != null) { + return getActiveWorkbenchShell(); + } + if (debugDialogShell != null) { + if (!debugDialogShell.isDisposed()) + return debugDialogShell; + debugDialogShell = null; + } + IWorkbenchWindow[] windows = getDefault().getWorkbench().getWorkbenchWindows(); + return windows[0].getShell(); + } + + public static void setDialogShell(Shell shell) { + debugDialogShell = shell; + } + + /** + * Convenience method which returns the unique identifier of this plugin. + */ + public static String getUniqueIdentifier() { + if (getDefault() == null) { + // If the default instance is not yet initialized, + // return a static identifier. This identifier must + // match the plugin id defined in plugin.xml + return PLUGIN_ID; + } + return getDefault().getBundle().getSymbolicName(); + } + + /** + * Logs the specified status with this plug-in's log. + * + * @param status + * status to log + */ + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + /** + * Logs an internal error with the specified message. + * + * @param message + * the error message to log + */ + public static void logErrorMessage(String message) { + log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, message, null)); + } + + /** + * Logs an internal error with the specified throwable + * + * @param e + * the exception to be logged + */ + public static void log(Throwable e) { + log(new Status(IStatus.ERROR, getUniqueIdentifier(), IStatus.ERROR, e.getMessage(), e)); + } + + /** + * Returns the active workbench window + * + * @return the active workbench window + */ + public static IWorkbenchWindow getActiveWorkbenchWindow() { + return getDefault().getWorkbench().getActiveWorkbenchWindow(); + } + + public static IWorkbenchPage getActivePage() { + IWorkbenchWindow w = getActiveWorkbenchWindow(); + if (w != null) { + return w.getActivePage(); + } + return null; + } + + /** + * Returns the active workbench shell or null if none + * + * @return the active workbench shell or null if none + */ + public static Shell getActiveWorkbenchShell() { + IWorkbenchWindow window = getActiveWorkbenchWindow(); + if (window != null) { + return window.getShell(); + } + return null; + } + + public static void errorDialog(String message, IStatus status) { + log(status); + Shell shell = getActiveWorkbenchShell(); + if (shell != null) { + ErrorDialog.openError(shell, LaunchMessages.getString("LaunchUIPlugin.Error"), message, status); //$NON-NLS-1$ + } + } + + public static void errorDialog(String message, Throwable t) { + log(t); + Shell shell = getActiveWorkbenchShell(); + if (shell != null) { + IStatus status = new Status(IStatus.ERROR, getUniqueIdentifier(), 1, t.getMessage(), null); + ErrorDialog.openError(shell, LaunchMessages.getString("LaunchUIPlugin.Error"), message, status); //$NON-NLS-1$ + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ConnectActionDelegate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ConnectActionDelegate.java new file mode 100644 index 00000000000..9be142bd967 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ConnectActionDelegate.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.actions.AbstractVMProviderActionDelegate; +import org.eclipse.cdt.dsf.gdb.actions.IConnect; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.ui.contexts.DebugContextEvent; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IViewPart; + +/* + * Action to trigger a prompt for a process to attach to + */ +public class ConnectActionDelegate extends AbstractVMProviderActionDelegate { + + /* + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(IAction action) { + if (action.isEnabled()) { + // disable the action so it cannot be run again until an event or + // selection change updates the enablement + action.setEnabled(false); + + final IConnect connectCommand = getConnectCommand(); + if (connectCommand != null) { + connectCommand.connect(null); + } + } + } + + @Override + public void init(IViewPart view) { + super.init(view); + updateEnablement(); + } + + @Override + public void debugContextChanged(DebugContextEvent event) { + super.debugContextChanged(event); + updateEnablement(); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + super.selectionChanged(action, selection); + updateEnablement(); + } + + private void updateEnablement() { + boolean enabled = false; + final IConnect connectCommand = getConnectCommand(); + if (connectCommand != null) { + enabled = connectCommand.canConnect(); + } + getAction().setEnabled(enabled); + } + + private IConnect getConnectCommand() { + IConnect command = null; + Object element = getViewerInput(); + if (element instanceof IDMVMContext) { + IDMVMContext dmc = (IDMVMContext)element; + command = (IConnect)dmc.getAdapter(IConnect.class); + } else if (element instanceof GdbLaunch) { + GdbLaunch launch = (GdbLaunch)element; + command = (IConnect)launch.getSession().getModelAdapter(IConnect.class); + } + + return command; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java new file mode 100644 index 00000000000..bf600ec3346 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/DsfTerminateCommand.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfCommandRunnable; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IEnabledStateRequest; +import org.eclipse.debug.core.commands.ITerminateHandler; + +public class DsfTerminateCommand implements ITerminateHandler { + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public DsfTerminateCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + // Run control may not be avilable after a connection is terminated and shut down. + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1 || + !(request.getElements()[0] instanceof IDMVMContext) ) + { + request.setEnabled(false); + request.done(); + return; + } + + // Javac doesn't like the cast to "(AbstractDMVMLayoutNode.DMVMContext)" need to use the + // construct below and suppress warnings. + IDMVMContext vmc = (IDMVMContext)request.getElements()[0]; + final IExecutionDMContext dmc = DMContexts.getAncestorOfType(vmc.getDMContext(), IExecutionDMContext.class); + if (dmc == null) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.execute( + new DsfRunnable() { + public void run() { + // Get the processes service and the exec context. + IGDBBackend gdbBackend = fTracker.getService(IGDBBackend.class); + if (gdbBackend == null || dmc == null) { + // Context or service already invalid. + request.setEnabled(false); + request.done(); + } else { + // Check the terminate. + request.setEnabled(gdbBackend.getState() == IMIBackend.State.STARTED); + request.done(); + } + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null) { + gdbControl.terminate(new RequestMonitor(fExecutor, null)); + } + } + }); + return false; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbConnectCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbConnectCommand.java new file mode 100644 index 00000000000..bdaabae7616 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbConnectCommand.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.core.IProcessInfo; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.actions.IConnect; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IStatusHandler; + +public class GdbConnectCommand implements IConnect { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public GdbConnectCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + public boolean canConnect() { + Query canConnectQuery = new Query() { + @Override + public void execute(DataRequestMonitor rm) { + IProcesses procService = fTracker.getService(IProcesses.class); + ICommandControlService commandControl = fTracker.getService(ICommandControlService.class); + + if (procService != null && commandControl != null) { + procService.isDebuggerAttachSupported(commandControl.getContext(), rm); + } else { + rm.setData(false); + rm.done(); + } + } + }; + try { + fExecutor.execute(canConnectQuery); + return canConnectQuery.get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } catch (RejectedExecutionException e) { + // Can be thrown if the session is shutdown + } + + return false; + } + + // Need a job because prompter.handleStatus will block + class PromptForPidJob extends Job { + + // The list of processes used in the case of an ATTACH session + IProcessInfo[] fProcessList = null; + DataRequestMonitor fRequestMonitor; + + public PromptForPidJob(String name, IProcessInfo[] procs, DataRequestMonitor rm) { + super(name); + fProcessList = procs; + fRequestMonitor = rm; + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + IStatus promptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200/*STATUS_HANDLER_PROMPT*/, "", null); //$NON-NLS-1$//$NON-NLS-2$ + final IStatus processPromptStatus = new Status(IStatus.INFO, "org.eclipse.cdt.dsf.gdb.ui", 100, "", null); //$NON-NLS-1$//$NON-NLS-2$ + + final IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(promptStatus); + + final Status NO_PID_STATUS = new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, -1, + LaunchMessages.getString("LocalAttachLaunchDelegate.No_Process_ID_selected"), //$NON-NLS-1$ + null); + + if (prompter == null) { + fRequestMonitor.setStatus(NO_PID_STATUS); + fRequestMonitor.done(); + return Status.OK_STATUS; + } + + try { + Object result = prompter.handleStatus(processPromptStatus, fProcessList); + if (result instanceof Integer) { + fRequestMonitor.setData((Integer)result); + } else { + fRequestMonitor.setStatus(NO_PID_STATUS); + } + } catch (CoreException e) { + fRequestMonitor.setStatus(NO_PID_STATUS); + } + fRequestMonitor.done(); + + return Status.OK_STATUS; + } + }; + + public void connect(RequestMonitor requestMonitor) + { + // Create a fake rm to avoid null pointer exceptions + final RequestMonitor rm; + if (requestMonitor == null) { + rm = new RequestMonitor(fExecutor, null); + } else { + rm = requestMonitor; + } + + // Don't wait for the operation to finish because this + // method can be called from the UI thread, and it will + // block it, which is bad, because we need to use the UI + // thread to prompt the user for the process to choose. + // This is why we simply use a DsfRunnable. + fExecutor.execute(new DsfRunnable() { + public void run() { + final IProcesses procService = fTracker.getService(IProcesses.class); + ICommandControlService commandControl = fTracker.getService(ICommandControlService.class); + + if (procService != null && commandControl != null) { + final ICommandControlDMContext controlCtx = commandControl.getContext(); + procService.getRunningProcesses( + controlCtx, + new DataRequestMonitor(fExecutor, rm) { + @Override + protected void handleSuccess() { + + final List procInfoList = new ArrayList(); + + // For each process, obtain its name + // Once all the names are obtained, prompt the user for the pid to use + final CountingRequestMonitor countingRm = + new CountingRequestMonitor(fExecutor, rm) { + @Override + protected void handleSuccess() { + new PromptForPidJob( + "Prompt for Process", procInfoList.toArray(new IProcessInfo[0]), //$NON-NLS-1$ + new DataRequestMonitor(fExecutor, rm) { + @Override + protected void handleSuccess() { + // New cycle, look for service again + final IMIProcesses procService = fTracker.getService(IMIProcesses.class); + if (procService != null) { + IProcessDMContext procDmc = procService.createProcessContext(controlCtx, + Integer.toString(getData())); + procService.attachDebuggerToProcess(procDmc, new DataRequestMonitor(fExecutor, rm)); + } + } + }).schedule(); + } + }; + + // New cycle, look for service again + final IProcesses procService = fTracker.getService(IProcesses.class); + + if (procService != null) { + countingRm.setDoneCount(getData().length); + + for (IProcessDMContext processCtx : getData()) { + procService.getExecutionData( + processCtx, + new DataRequestMonitor (fExecutor, countingRm) { + @Override + protected void handleSuccess() { + int pid = 0; + try { + pid = Integer.parseInt(getData().getId()); + } catch (NumberFormatException e) { + } + procInfoList.add(new ProcessInfo(pid, getData().getName())); + countingRm.done(); + } + }); + } + } else { + countingRm.setDoneCount(1); + countingRm.done(); + } + } + }); + } else { + rm.done(); + } + } + }); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java new file mode 100644 index 00000000000..4cdb70cfa55 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbDisconnectCommand.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.ui.actions.DsfCommandRunnable; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.commands.IDebugCommandRequest; +import org.eclipse.debug.core.commands.IDisconnectHandler; +import org.eclipse.debug.core.commands.IEnabledStateRequest; + +public class GdbDisconnectCommand implements IDisconnectHandler { + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + + public GdbDisconnectCommand(DsfSession session) { + fExecutor = session.getExecutor(); + fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + public void canExecute(final IEnabledStateRequest request) { + if (request.getElements().length != 1) { + request.setEnabled(false); + request.done(); + return; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(getContext(), IContainerDMContext.class); + + getProcessService().canDetachDebuggerFromProcess( + containerDmc, + new DataRequestMonitor(fExecutor, null) { + @Override + protected void handleCompleted() { + request.setEnabled(isSuccess() && getData()); + request.done(); + } + }); + } + }); + } + + public boolean execute(final IDebugCommandRequest request) { + if (request.getElements().length != 1) { + request.done(); + return false; + } + + fExecutor.submit(new DsfCommandRunnable(fTracker, request.getElements()[0], request) { + @Override public void doExecute() { + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(getContext(), IContainerDMContext.class); + getProcessService().detachDebuggerFromProcess(containerDmc, new RequestMonitor(fExecutor, null)); + } + }); + return false; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbRestartCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbRestartCommand.java new file mode 100644 index 00000000000..317ee34afb4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/GdbRestartCommand.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.cdt.debug.core.model.IRestart; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IProcess; + +public class GdbRestartCommand implements IRestart { + private final DsfExecutor fExecutor; + private final DsfServicesTracker fTracker; + private final GdbLaunch fLaunch; + + public GdbRestartCommand(DsfSession session, GdbLaunch launch) { + fExecutor = session.getExecutor(); + fLaunch = launch; + fTracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId()); + } + + public void dispose() { + fTracker.dispose(); + } + + // Run control may not be available after a connection is terminated and shut down. + public boolean canRestart() { + Query canRestart = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + if (gdbControl != null) { + rm.setData(gdbControl.canRestart()); + } else { + rm.setData(false); + } + + rm.done(); + } + }; + + fExecutor.execute(canRestart); + try { + return canRestart.get(); + } catch (InterruptedException e1) { + } catch (ExecutionException e1) { + } + return false; + } + + + public void restart() throws DebugException + { + final AtomicReference execPathRef = new AtomicReference(); + Query restartQuery = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + final IGDBControl gdbControl = fTracker.getService(IGDBControl.class); + final IGDBBackend backend = fTracker.getService(IGDBBackend.class); + if (gdbControl != null && backend != null) { + execPathRef.set(backend.getProgramPath()); + gdbControl.initInferiorInputOutput(new RequestMonitor(fExecutor, rm) { + @Override + protected void handleSuccess() { + gdbControl.createInferiorProcess(); + gdbControl.restart(fLaunch, rm); + } + }); + } else { + rm.done(); + } + } + }; + + fExecutor.execute(restartQuery); + try { + restartQuery.get(); + } catch (InterruptedException e1) { + } catch (ExecutionException e1) { + } + + // Now that we restarted the inferior, we must add it to our launch + // we must do this here because we cannot do it in the executor, or else + // it deadlocks + // We must first remove the old inferior from our launch (since it uses + // the same name and we use that name to find the old one) + // + // Remove + String inferiorLabel = execPathRef.get().lastSegment(); + + IProcess[] launchProcesses = fLaunch.getProcesses(); + for (IProcess p : launchProcesses) { + if (p.getLabel().equals(inferiorLabel)) { + fLaunch.removeProcess(p); + break; + } + } + // Add + try { + fLaunch.addInferiorProcess(inferiorLabel); + } catch (CoreException e) { + throw new DebugException(e.getStatus()); + } + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ProcessInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ProcessInfo.java new file mode 100644 index 00000000000..71033928bc5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/actions/ProcessInfo.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.actions; + +import org.eclipse.cdt.core.IProcessInfo; +import org.eclipse.cdt.dsf.concurrent.Immutable; + +@Immutable +class ProcessInfo implements IProcessInfo, Comparable { + private final int pid; + private final String name; + + public ProcessInfo(String pidString, String name) { + int tmpPid = 0; + try { + tmpPid = Integer.parseInt(pidString); + } catch (NumberFormatException e) { + } + this.pid = tmpPid; + this.name = name; + } + + public ProcessInfo(int pid, String name) { + this.pid = pid; + this.name = name; + } + + /** + * @see org.eclipse.cdt.core.IProcessInfo#getName() + */ + public String getName() { + return name; + } + + /** + * @see org.eclipse.cdt.core.IProcessInfo#getPid() + */ + public int getPid() { + return pid; + } + + public int compareTo(ProcessInfo other) { + int nameCompare = getName().compareTo(other.getName()); + if (nameCompare != 0) return nameCompare; + else return (getPid() < other.getPid()) ? -1 : 1; + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/CBreakpointGdbThreadFilterPage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/CBreakpointGdbThreadFilterPage.java new file mode 100644 index 00000000000..7d585bce77e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/CBreakpointGdbThreadFilterPage.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints; + +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.dsf.gdb.breakpoints.CBreakpointGdbThreadsFilterExtension; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.dialogs.PropertyPage; + +public class CBreakpointGdbThreadFilterPage extends PropertyPage { + + private GdbThreadFilterEditor fThreadFilterEditor; + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createContents( Composite parent ) { + noDefaultAndApplyButton(); + Composite mainComposite = new Composite( parent, SWT.NONE ); + mainComposite.setFont( parent.getFont() ); + mainComposite.setLayout( new GridLayout() ); + mainComposite.setLayoutData( new GridData( GridData.FILL_BOTH ) ); + createThreadFilterEditor( mainComposite ); + setValid( true ); + return mainComposite; + } + + public ICBreakpoint getBreakpoint() { + return (ICBreakpoint)getElement().getAdapter(ICBreakpoint.class); + } + + public CBreakpointGdbThreadsFilterExtension getFilterExtension() { + ICBreakpoint bp = getBreakpoint(); + if (bp != null) { + try { + CBreakpointGdbThreadsFilterExtension filter = + (CBreakpointGdbThreadsFilterExtension) bp.getExtension( + GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, CBreakpointGdbThreadsFilterExtension.class); + filter.initialize(bp); + return filter; + } catch (CoreException e) {} + } + return null; + } + + protected void createThreadFilterEditor( Composite parent ) { + fThreadFilterEditor = new GdbThreadFilterEditor( parent, this ); + } + + protected GdbThreadFilterEditor getThreadFilterEditor() { + return fThreadFilterEditor; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.preference.IPreferencePage#performOk() + */ + @Override + public boolean performOk() { + doStore(); + return super.performOk(); + } + + /** + * Stores the values configured in this page. + */ + protected void doStore() { + fThreadFilterEditor.doStore(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/GdbThreadFilterEditor.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/GdbThreadFilterEditor.java new file mode 100644 index 00000000000..83f4fc68195 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/breakpoints/GdbThreadFilterEditor.java @@ -0,0 +1,553 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.debug.ui.CDebugUIPlugin; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.breakpoints.CBreakpointGdbThreadsFilterExtension; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.osgi.util.tracker.ServiceTracker; + +public class GdbThreadFilterEditor { + + /** + * Comment for ThreadFilterEditor. + */ + public class CheckHandler implements ICheckStateListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent) + */ + public void checkStateChanged(CheckStateChangedEvent event) { + Object element = event.getElement(); + if (element instanceof IContainerDMContext) { + checkTarget((IContainerDMContext) element, event.getChecked()); + } else if (element instanceof IExecutionDMContext) { + checkThread((IExecutionDMContext) element, event.getChecked()); + } + } + + /** + * Check or uncheck a debug target in the tree viewer. When a debug + * target is checked, attempt to check all of the target's threads by + * default. When a debug target is unchecked, uncheck all its threads. + */ + protected void checkTarget(IContainerDMContext target, boolean checked) { + getThreadViewer().setChecked(target, checked); + getThreadViewer().setGrayed(target, false); + getThreadViewer().expandToLevel(target, AbstractTreeViewer.ALL_LEVELS); + IExecutionDMContext[] threads = syncGetThreads(target); + for (int i = 0; i < threads.length; i++) { + getThreadViewer().setChecked(threads[i], checked); + getThreadViewer().setGrayed(threads[i], false); + } + } + + /** + * Check or uncheck a thread. Update the thread's debug target. + */ + protected void checkThread(IExecutionDMContext thread, boolean checked) { + getThreadViewer().setChecked(thread, checked); + IContainerDMContext target = DMContexts.getAncestorOfType(thread, IContainerDMContext.class); + IExecutionDMContext[] threads = syncGetThreads(target); + int checkedNumber = 0; + for (int i = 0; i < threads.length; i++) { + if (getThreadViewer().getChecked(threads[i])) { + ++checkedNumber; + } + } + if (checkedNumber == 0) { + getThreadViewer().setChecked(target, false); + getThreadViewer().setGrayed(target, false); + } else if (checkedNumber == threads.length) { + getThreadViewer().setChecked(target, true); + getThreadViewer().setGrayed(target, false); + } else { + getThreadViewer().setGrayChecked(target, true); + } + } + } + + /** + * Comment for ThreadFilterEditor. + */ + public class ThreadFilterContentProvider implements ITreeContentProvider { + + /** + * Constructor for ThreadFilterContentProvider. + */ + public ThreadFilterContentProvider() { + super(); + // TODO Auto-generated constructor stub + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) + */ + public Object[] getChildren(Object parent) { + if (parent instanceof IContainerDMContext) { + return syncGetThreads((IContainerDMContext) parent); + } + + if (parent instanceof ILaunchManager) { + List children = new ArrayList(); + ILaunch[] launches = ((ILaunchManager) parent).getLaunches(); + IContainerDMContext target; + for (int i = 0; i < launches.length; i++) { + if (launches[i] instanceof GdbLaunch) { + target = syncGetContainer((GdbLaunch) launches[i]); + if (target != null) { + children.add(target); + } + } + } + return children.toArray(); + } + return new Object[0]; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) + */ + public Object getParent(Object element) { + if (element instanceof IContainerDMContext) { + return DebugPlugin.getDefault().getLaunchManager(); + } + if (element instanceof IExecutionDMContext) { + return DMContexts.getAncestorOfType((IExecutionDMContext) element, IContainerDMContext.class); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) + */ + public boolean hasChildren(Object element) { + return getChildren(element).length > 0; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + } + + public class ThreadFilterLabelProvider extends LabelProvider { + + @Override + public Image getImage(Object element) { + if (element instanceof IContainerDMContext) { + return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET); + } else { + return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING); + } + } + + @Override + public String getText(Object element) { + if (element instanceof IContainerDMContext) { + return syncGetContainerLabel((IContainerDMContext)element); + } else { + return syncGetThreadLabel((IExecutionDMContext)element); + } + } + } + + + private CBreakpointGdbThreadFilterPage fPage; + + private CheckboxTreeViewer fThreadViewer; + + private ThreadFilterContentProvider fContentProvider; + + private CheckHandler fCheckHandler; + + /** + * Constructor for ThreadFilterEditor. + */ + public GdbThreadFilterEditor(Composite parent, CBreakpointGdbThreadFilterPage page) { + fPage = page; + fContentProvider = new ThreadFilterContentProvider(); + fCheckHandler = new CheckHandler(); + createThreadViewer(parent); + } + + protected CBreakpointGdbThreadFilterPage getPage() { + return fPage; + } + + private void createThreadViewer(Composite parent) { + Label label = new Label(parent, SWT.NONE); + label.setText("&Restrict to Selected Targets and Threads:"); //$NON-NLS-1$ + label.setFont(parent.getFont()); + label.setLayoutData(new GridData()); + GridData data = new GridData(GridData.FILL_BOTH); + data.heightHint = 100; + fThreadViewer = new CheckboxTreeViewer(parent, SWT.BORDER); + fThreadViewer.addCheckStateListener(fCheckHandler); + fThreadViewer.getTree().setLayoutData(data); + fThreadViewer.getTree().setFont(parent.getFont()); + fThreadViewer.setContentProvider(fContentProvider); + fThreadViewer.setLabelProvider(new ThreadFilterLabelProvider()); + fThreadViewer.setInput(DebugPlugin.getDefault().getLaunchManager()); + setInitialCheckedState(); + } + + /** + * Returns the debug targets that appear in the tree + */ + protected IContainerDMContext[] getDebugTargets() { + Object input = fThreadViewer.getInput(); + if (!(input instanceof ILaunchManager)) { + return new IContainerDMContext[0]; + } + List targets = new ArrayList(); + ILaunch[] launches = ((ILaunchManager) input).getLaunches(); + IContainerDMContext target; + for (int i = 0; i < launches.length; i++) { + if (launches[i] instanceof GdbLaunch) { + target = syncGetContainer((GdbLaunch) launches[i]); + if (target != null) { + targets.add(target); + } + } + } + return targets.toArray(new IContainerDMContext[targets.size()]); + } + + protected CheckboxTreeViewer getThreadViewer() { + return fThreadViewer; + } + + /** + * Sets the initial checked state of the tree viewer. The initial state + * should reflect the current state of the breakpoint. If the breakpoint has + * a thread filter in a given thread, that thread should be checked. + */ + protected void setInitialCheckedState() { + CBreakpointGdbThreadsFilterExtension filterExtension = fPage.getFilterExtension(); + try { + IContainerDMContext[] targets = filterExtension.getTargetFilters(); + + // TODO: Hack to properly initialize the target/thread list + // Should be done in filterExtension.initialize() but we don't know + // how to get the target list from an ICBreakpoint... + if (targets.length == 0) { + targets = getDebugTargets(); + for (IContainerDMContext target : targets) { + filterExtension.setTargetFilter(target); + } + } + // TODO: End of hack + + for (int i = 0; i < targets.length; i++) { + IExecutionDMContext[] filteredThreads = filterExtension.getThreadFilters(targets[i]); + if (filteredThreads != null) { + for (int j = 0; j < filteredThreads.length; ++j) + fCheckHandler.checkThread(filteredThreads[j], true); + } else { + fCheckHandler.checkTarget(targets[i], true); + } + } + } catch (CoreException e) { + CDebugUIPlugin.log(e); + } + } + + protected void doStore() { + CBreakpointGdbThreadsFilterExtension filterExtension = fPage.getFilterExtension(); + IContainerDMContext[] targets = getDebugTargets(); + for (int i = 0; i < targets.length; ++i) { + try { + if (getThreadViewer().getChecked(targets[i])) { + if (getThreadViewer().getGrayed(targets[i])) { + IExecutionDMContext[] threads = getTargetThreadFilters(targets[i]); + filterExtension.setThreadFilters(threads); + } else { + filterExtension.setTargetFilter(targets[i]); + } + } else { + filterExtension.removeTargetFilter(targets[i]); + } + DebugPlugin.getDefault().getBreakpointManager().fireBreakpointChanged(fPage.getBreakpoint()); + } catch (CoreException e) { + CDebugUIPlugin.log(e); + } + } + } + + private IExecutionDMContext[] getTargetThreadFilters(IContainerDMContext target) { + Object[] threads = ((ITreeContentProvider) getThreadViewer().getContentProvider()).getChildren(target); + ArrayList list = new ArrayList(threads.length); + for (int i = 0; i < threads.length; ++i) { + if (getThreadViewer().getChecked(threads[i])) + list.add(threads[i]); + } + return list.toArray(new IExecutionDMContext[list.size()]); + } + + private IContainerDMContext syncGetContainer(final GdbLaunch launch) { + final DsfSession session = launch.getSession(); + + class ContainerQuery extends Query { + @Override + protected void execute(DataRequestMonitor rm) { + if (!session.isActive()) { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Launch's session not active.")); //$NON-NLS-1$ + rm.done(); + return; + } + + ServiceTracker tracker1 = new ServiceTracker(GdbUIPlugin.getBundleContext(), ICommandControlService.class + .getName(), null); + tracker1.open(); + + ICommandControlService commandControl = (ICommandControlService) tracker1.getService(); + ServiceTracker tracker2 = new ServiceTracker(GdbUIPlugin.getBundleContext(), IMIProcesses.class + .getName(), null); + tracker2.open(); + IMIProcesses procService = (IMIProcesses) tracker2.getService(); + + if (commandControl != null && procService != null) { + IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + rm.setData(containerDmc); + } else { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "GDB Control or Process service not accessible.")); //$NON-NLS-1$ + } + rm.done(); + tracker1.close(); + tracker2.close(); + } + } + + ContainerQuery query = new ContainerQuery(); + try { + session.getExecutor().execute(query); + return query.get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + return null; + } + + private IExecutionDMContext[] syncGetThreads(final IContainerDMContext container) { + final DsfSession session = DsfSession.getSession(container.getSessionId()); + if (session == null) { + return new IExecutionDMContext[0]; + } + + class ThreadsQuery extends Query { + @Override + protected void execute(DataRequestMonitor rm) { + if (!session.isActive()) { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$ + rm.done(); + return; + } + + ServiceTracker tracker = new ServiceTracker(GdbUIPlugin.getBundleContext(), IRunControl.class + .getName(), null); + tracker.open(); + IRunControl runControl = (IRunControl) tracker.getService(); + if (runControl != null) { + runControl.getExecutionContexts(container, rm); + } else { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "GDB Control not accessible.")); //$NON-NLS-1$ + rm.done(); + } + tracker.close(); + } + } + + ThreadsQuery query = new ThreadsQuery(); + try { + session.getExecutor().execute(query); + return query.get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + return new IExecutionDMContext[0]; + } + + private String syncGetContainerLabel(final IContainerDMContext container) { + final DsfSession session = DsfSession.getSession(container.getSessionId()); + if (session == null) { + return "Error reading data"; //$NON-NLS-1$ + } + + class ContainerLabelQuery extends Query { + @Override + protected void execute(DataRequestMonitor rm) { + if (!session.isActive()) { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$ + rm.done(); + return; + } + + ServiceTracker tracker = new ServiceTracker(GdbUIPlugin.getBundleContext(), IGDBBackend.class + .getName(), null); + tracker.open(); + IGDBBackend backend = (IGDBBackend) tracker.getService(); + if (backend != null) { + rm.setData(backend.getProgramPath().toOSString()); + } else { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "GDB Backend not accessible.")); //$NON-NLS-1$ + } + rm.done(); + tracker.close(); + } + } + + ContainerLabelQuery query = new ContainerLabelQuery(); + try { + session.getExecutor().execute(query); + return query.get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + return "Error reading data"; //$NON-NLS-1$ + } + + private String syncGetThreadLabel(final IExecutionDMContext thread) { + final DsfSession session = DsfSession.getSession(thread.getSessionId()); + if (session == null) { + return "Error reading data"; //$NON-NLS-1$ + } + + class ThreadLabelQuery extends Query { + @Override + protected void execute(final DataRequestMonitor rm) { + if (!session.isActive()) { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$ + rm.done(); + return; + } + + ServiceTracker tracker = new ServiceTracker(GdbUIPlugin.getBundleContext(), IProcesses.class + .getName(), null); + tracker.open(); + IProcesses procService = (IProcesses) tracker.getService(); + if (procService != null) { + IThreadDMContext threadDmc = DMContexts.getAncestorOfType(thread, IThreadDMContext.class); + procService.getExecutionData(threadDmc, new DataRequestMonitor( + ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + final StringBuilder builder = new StringBuilder("Thread["); //$NON-NLS-1$ + builder.append(((IMIExecutionDMContext)thread).getThreadId()); + builder.append("] "); //$NON-NLS-1$ + builder.append(getData().getId()); + builder.append(getData().getName()); + + rm.setData(builder.toString()); + rm.done(); + } + }); + } else { + rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "IProcesses service not accessible.")); //$NON-NLS-1$ + rm.done(); + } + tracker.close(); + } + } + + ThreadLabelQuery query = new ThreadLabelQuery(); + try { + session.getExecutor().execute(query); + return query.get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + return "Error reading data"; //$NON-NLS-1$ + } + + private Status getFailStatus(int code, String message) { + return new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, code, message, null); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/AbstractCDebuggerTab.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/AbstractCDebuggerTab.java new file mode 100644 index 00000000000..99a5de88a7c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/AbstractCDebuggerTab.java @@ -0,0 +1,352 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * IBM Corporation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.util.Map; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.debug.core.ICDebugConfiguration; +import org.eclipse.cdt.debug.ui.CDebugUIPlugin; +import org.eclipse.cdt.debug.ui.ICDebuggerPage; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; + +public abstract class AbstractCDebuggerTab extends CLaunchConfigurationTab { + + protected ILaunchConfiguration fLaunchConfiguration; + protected ILaunchConfigurationWorkingCopy fWorkingCopy; + protected ICDebugConfiguration fCurrentDebugConfig; + + // Dynamic Debugger UI widgets + protected ICDebuggerPage fDynamicTab; + protected Composite fDynamicTabHolder; + private boolean fInitDefaults; + private Combo fDCombo; + private boolean fIsInitializing = false; + private boolean fPageUpdated; + + protected void setDebugConfig(ICDebugConfiguration config) { + fCurrentDebugConfig = config; + } + + protected ICDebugConfiguration getDebugConfig() { + return fCurrentDebugConfig; + } + + protected ICDebuggerPage getDynamicTab() { + return fDynamicTab; + } + + protected void setDynamicTab(ICDebuggerPage tab) { + fDynamicTab = tab; + } + + protected Composite getDynamicTabHolder() { + return fDynamicTabHolder; + } + + protected void setDynamicTabHolder(Composite tabHolder) { + fDynamicTabHolder = tabHolder; + } + + protected ILaunchConfigurationWorkingCopy getLaunchConfigurationWorkingCopy() { + return fWorkingCopy; + } + + protected void setLaunchConfiguration(ILaunchConfiguration launchConfiguration) { + fLaunchConfiguration = launchConfiguration; + setLaunchConfigurationWorkingCopy(null); + } + + protected ILaunchConfiguration getLaunchConfiguration() { + return fLaunchConfiguration; + } + + protected void setLaunchConfigurationWorkingCopy(ILaunchConfigurationWorkingCopy workingCopy) { + fWorkingCopy = workingCopy; + } + + /** + * Overridden here so that any error message in the dynamic UI gets + * returned. + * + * @see ILaunchConfigurationTab#getErrorMessage() + */ + @Override + public String getErrorMessage() { + ICDebuggerPage tab = getDynamicTab(); + if ( (super.getErrorMessage() != null) || (tab == null)) { + return super.getErrorMessage(); + } + return tab.getErrorMessage(); + } + + /** + * Notification that the user changed the selection of the Debugger. + */ + protected void handleDebuggerChanged() { + loadDynamicDebugArea(); + + // always set the newly created area with defaults + ILaunchConfigurationWorkingCopy wc = getLaunchConfigurationWorkingCopy(); + if (getDynamicTab() == null) { + // remove any debug specfic args from the config + if (wc == null) { + if (getLaunchConfiguration().isWorkingCopy()) { + wc = (ILaunchConfigurationWorkingCopy)getLaunchConfiguration(); + } + } + if (wc != null) { + wc.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP, (Map)null); + } + } else { + if (wc == null) { + try { + if (getLaunchConfiguration().isWorkingCopy()) { + setLaunchConfigurationWorkingCopy((ILaunchConfigurationWorkingCopy)getLaunchConfiguration()); + } else { + setLaunchConfigurationWorkingCopy(getLaunchConfiguration().getWorkingCopy()); + } + wc = getLaunchConfigurationWorkingCopy(); + + } catch (CoreException e) { + return; + } + } + if (initDefaults()) { + getDynamicTab().setDefaults(wc); + } + setInitializeDefault(false); + getDynamicTab().initializeFrom(wc); + } + } + + /** + * Show the contributed piece of UI that was registered for the debugger id + * of the currently selected debugger. + */ + protected void loadDynamicDebugArea() { + // Dispose of any current child widgets in the tab holder area + Control[] children = getDynamicTabHolder().getChildren(); + for (int i = 0; i < children.length; i++) { + children[i].dispose(); + } + + // Retrieve the dynamic UI for the current Debugger + ICDebugConfiguration debugConfig = getConfigForCurrentDebugger(); + if (debugConfig == null) { + setDynamicTab(null); + } else { + ICDebuggerPage tab = null; + try { + tab = CDebugUIPlugin.getDefault().getDebuggerPage(debugConfig.getID()); + } catch (CoreException e) { + GdbUIPlugin.errorDialog(LaunchMessages.getString("AbstractCDebuggerTab.ErrorLoadingDebuggerPage"), e.getStatus()); //$NON-NLS-1$ + } + setDynamicTab(tab); + } + setDebugConfig(debugConfig); + if (getDynamicTab() == null) { + return; + } + // Ask the dynamic UI to create its Control + getDynamicTab().setLaunchConfigurationDialog(getLaunchConfigurationDialog()); + getDynamicTab().createControl(getDynamicTabHolder()); + getDynamicTab().getControl().setVisible(true); + getDynamicTabHolder().layout(true); + contentsChanged(); + } + + /** + * Called whenever the controls within the Debugger tab has changed. + */ + protected void contentsChanged() { + } + + abstract public void createControl(Composite parent); + + @Override + public void activated(ILaunchConfigurationWorkingCopy workingCopy) { + ICDebuggerPage dynamicTab = getDynamicTab(); + if (dynamicTab != null) { + dynamicTab.activated(workingCopy); + } + } + + public void initializeFrom(ILaunchConfiguration config) { + setLaunchConfiguration(config); + ICDebuggerPage dynamicTab = getDynamicTab(); + if (dynamicTab != null) { + dynamicTab.initializeFrom(config); + } + } + + public void performApply(ILaunchConfigurationWorkingCopy config) { + if (getDebugConfig() != null) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, getDebugConfig().getID()); + ICDebuggerPage dynamicTab = getDynamicTab(); + if (dynamicTab == null) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP, (Map)null); + } else { + dynamicTab.performApply(config); + } + } + } + + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + setLaunchConfigurationWorkingCopy(config); + ICDebuggerPage dynamicTab = getDynamicTab(); + if (dynamicTab != null) { + dynamicTab.setDefaults(config); + setInitializeDefault(false); + } + } + + @Override + public boolean isValid(ILaunchConfiguration config) { + setErrorMessage(null); + setMessage(null); + if (getDebugConfig() == null) { + setErrorMessage(LaunchMessages.getString("AbstractCDebuggerTab.No_debugger_available")); //$NON-NLS-1$ + return false; + } + + ICDebuggerPage dynamicTab = getDynamicTab(); + if (dynamicTab != null) { + return dynamicTab.isValid(config); + } + return true; + } + + protected void setInitializeDefault(boolean init) { + fInitDefaults = init; + } + + protected boolean initDefaults() { + return fInitDefaults; + } + + @Override + public Image getImage() { + return LaunchImages.get(LaunchImages.IMG_VIEW_DEBUGGER_TAB); + } + + public String getName() { + return LaunchMessages.getString("AbstractCDebuggerTab.Debugger"); //$NON-NLS-1$ + } + + protected void createDebuggerCombo(Composite parent, int colspan) { + Composite comboComp = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + comboComp.setLayout(layout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = colspan; + comboComp.setLayoutData(gd); + Label dlabel = new Label(comboComp, SWT.NONE); + dlabel.setText(LaunchMessages.getString("Launch.common.DebuggerColon")); //$NON-NLS-1$ + fDCombo = new Combo(comboComp, SWT.READ_ONLY | SWT.DROP_DOWN); + fDCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + fDCombo.addSelectionListener(new SelectionListener() { + public void widgetSelected(SelectionEvent e) { + if (!isInitializing()) { + setInitializeDefault(true); + updateComboFromSelection(); + } + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + + protected void loadDebuggerCombo(ICDebugConfiguration[] debugConfigs, String current) { + fDCombo.removeAll(); + int select = -1; + for (int i = 0; i < debugConfigs.length; i++) { + fDCombo.add(debugConfigs[i].getName()); + fDCombo.setData(Integer.toString(i), debugConfigs[i]); + if (debugConfigs[i].getID().equalsIgnoreCase(current)) { + select = i; + } + } + + fPageUpdated = false; + if (select != -1) { + fDCombo.select(select); + } else { + fDCombo.select(0); + } + //The behaviour is undefined for if the callbacks should be triggered + // for this, + //so force page update if needed. + if (!fPageUpdated) { + updateComboFromSelection(); + } + fPageUpdated = false; + getControl().getParent().layout(true); + + } + + protected void createDebuggerGroup(Composite parent, int colspan) { + Group debuggerGroup = new Group(parent, SWT.SHADOW_ETCHED_IN); + debuggerGroup.setText(LaunchMessages.getString("CDebuggerTab.Debugger_Options")); //$NON-NLS-1$ + setDynamicTabHolder(debuggerGroup); + GridLayout tabHolderLayout = new GridLayout(); + tabHolderLayout.marginHeight = 0; + tabHolderLayout.marginWidth = 0; + tabHolderLayout.numColumns = 1; + getDynamicTabHolder().setLayout(tabHolderLayout); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.horizontalSpan = colspan; + getDynamicTabHolder().setLayoutData(gd); + } + + protected void updateComboFromSelection() { + fPageUpdated = true; + handleDebuggerChanged(); + updateLaunchConfigurationDialog(); + } + + protected boolean isInitializing() { + return fIsInitializing; + } + + protected void setInitializing(boolean isInitializing) { + fIsInitializing = isInitializing; + } + + /** + * Return the class that implements ICDebuggerPage + * that is registered against the debugger id of the currently selected + * debugger. + */ + protected ICDebugConfiguration getConfigForCurrentDebugger() { + int selectedIndex = fDCombo.getSelectionIndex(); + return (ICDebugConfiguration)fDCombo.getData(Integer.toString(selectedIndex)); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CArgumentsTab.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CArgumentsTab.java new file mode 100644 index 00000000000..24c95429e77 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CArgumentsTab.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * IBM Corporation + * Ericsson - Updated for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.StringVariableSelectionDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + + +/** + * A launch configuration tab that displays and edits program arguments, + * and working directory launch configuration attributes. + *

+ * This class may be instantiated. This class is not intended to be subclassed. + *

+ */ +public class CArgumentsTab extends CLaunchConfigurationTab { + + // Program arguments UI widgets + protected Label fPrgmArgumentsLabel; + protected Text fPrgmArgumentsText; + protected Button fArgumentVariablesButton; + + // Working directory + protected WorkingDirectoryBlock fWorkingDirectoryBlock = new WorkingDirectoryBlock(); + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Font font = parent.getFont(); + Composite comp = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(1, true); + comp.setLayout(layout); + comp.setFont(font); + + GridData gd = new GridData(GridData.FILL_BOTH); + comp.setLayoutData(gd); + setControl(comp); + + GdbUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(getControl(), ICDTLaunchHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_ARGUMNETS_TAB); + + createArgumentComponent(comp, 1); + + fWorkingDirectoryBlock.createControl(comp); + } + + protected void createArgumentComponent(Composite comp, int horizontalSpan) { + Font font = comp.getFont(); + Group group = new Group(comp, SWT.NONE); + group.setFont(font); + group.setLayout(new GridLayout()); + GridData gd = new GridData(GridData.FILL_BOTH); + gd.horizontalSpan = horizontalSpan; + group.setLayoutData(gd); + + group.setText(LaunchMessages.getString("CArgumentsTab.C/C++_Program_Arguments")); //$NON-NLS-1$ + fPrgmArgumentsText = new Text(group, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); + fPrgmArgumentsText.getAccessible().addAccessibleListener( + new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + e.result = LaunchMessages.getString("CArgumentsTab.C/C++_Program_Arguments"); //$NON-NLS-1$ + } + } + ); + gd = new GridData(GridData.FILL_BOTH); + gd.heightHint = 40; + gd.widthHint = 100; + fPrgmArgumentsText.setLayoutData(gd); + fPrgmArgumentsText.setFont(font); + fPrgmArgumentsText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent evt) { + updateLaunchConfigurationDialog(); + } + }); + fArgumentVariablesButton= createPushButton(group, LaunchMessages.getString("CArgumentsTab.Variables"), null); //$NON-NLS-1$ + gd = new GridData(GridData.HORIZONTAL_ALIGN_END); + fArgumentVariablesButton.setLayoutData(gd); + fArgumentVariablesButton.addSelectionListener(new SelectionAdapter() { + + /* (non-Javadoc) + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetSelected(SelectionEvent arg0) { + handleVariablesButtonSelected(fPrgmArgumentsText); + } + + }); + addControlAccessibleListener(fArgumentVariablesButton, fArgumentVariablesButton.getText()); // need to strip the mnemonic from buttons + } + + /** + * A variable entry button has been pressed for the given text + * field. Prompt the user for a variable and enter the result + * in the given field. + */ + protected void handleVariablesButtonSelected(Text textField) { + String variable = getVariable(); + if (variable != null) { + textField.append(variable); + } + } + + /** + * Prompts the user to choose and configure a variable and returns + * the resulting string, suitable to be used as an attribute. + */ + private String getVariable() { + StringVariableSelectionDialog dialog = new StringVariableSelectionDialog(getShell()); + dialog.open(); + return dialog.getVariableExpression(); + } + + public void addControlAccessibleListener(Control control, String controlName) { + //strip mnemonic (&) + String[] strs = controlName.split("&"); //$NON-NLS-1$ + StringBuffer stripped = new StringBuffer(); + for (int i = 0; i < strs.length; i++) { + stripped.append(strs[i]); + } + control.getAccessible().addAccessibleListener(new ControlAccessibleListener(stripped.toString())); + } + + private class ControlAccessibleListener extends AccessibleAdapter { + private String controlName; + ControlAccessibleListener(String name) { + controlName = name; + } + @Override + public void getName(AccessibleEvent e) { + e.result = controlName; + } + + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration config) { + return fWorkingDirectoryBlock.isValid(config); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String) null); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration configuration) { + try { + fPrgmArgumentsText.setText(configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, "")); //$NON-NLS-1$ + fWorkingDirectoryBlock.initializeFrom(configuration); + } + catch (CoreException e) { + setErrorMessage(LaunchMessages.getFormattedString("Launch.common.Exception_occurred_reading_configuration_EXCEPTION", e.getStatus().getMessage())); //$NON-NLS-1$ + GdbUIPlugin.log(e); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute( + ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, + getAttributeValueFrom(fPrgmArgumentsText)); + fWorkingDirectoryBlock.performApply(configuration); + } + + /** + * Retuns the string in the text widget, or null if empty. + * + * @return text or null + */ + protected String getAttributeValueFrom(Text text) { + String content = text.getText().trim(); + // bug #131513 - eliminate Windows \r line delimiter + content = content.replaceAll("\r\n", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + if (content.length() > 0) { + return content; + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + public String getName() { + return LaunchMessages.getString("CArgumentsTab.Arguments"); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setLaunchConfigurationDialog(org.eclipse.debug.ui.ILaunchConfigurationDialog) + */ + @Override + public void setLaunchConfigurationDialog(ILaunchConfigurationDialog dialog) { + super.setLaunchConfigurationDialog(dialog); + fWorkingDirectoryBlock.setLaunchConfigurationDialog(dialog); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getErrorMessage() + */ + @Override + public String getErrorMessage() { + String m = super.getErrorMessage(); + if (m == null) { + return fWorkingDirectoryBlock.getErrorMessage(); + } + return m; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getMessage() + */ + @Override + public String getMessage() { + String m = super.getMessage(); + if (m == null) { + return fWorkingDirectoryBlock.getMessage(); + } + return m; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + */ + @Override + public Image getImage() { + return LaunchImages.get(LaunchImages.IMG_VIEW_ARGUMENTS_TAB); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#updateLaunchConfigurationDialog() + */ + @Override + protected void updateLaunchConfigurationDialog() { + super.updateLaunchConfigurationDialog(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CDebuggerTab.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CDebuggerTab.java new file mode 100644 index 00000000000..7660e19b6d7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CDebuggerTab.java @@ -0,0 +1,439 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ken Ryall (Nokia) - https://bugs.eclipse.org/bugs/show_bug.cgi?id=118894 + * IBM Corporation + * Ericsson + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.io.IOException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.ICExtensionReference; +import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.debug.core.CDebugCorePlugin; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.debug.core.ICDebugConfiguration; +import org.eclipse.cdt.debug.core.ICDebugConstants; +import org.eclipse.cdt.debug.ui.ICDebuggerPage; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +public class CDebuggerTab extends AbstractCDebuggerTab { + + private final static String LOCAL_DEBUGGER_ID = "org.eclipse.cdt.dsf.gdb.GdbDebugger";//$NON-NLS-1$ + private final static String REMOTE_DEBUGGER_ID = "org.eclipse.cdt.dsf.gdb.GdbServerDebugger";//$NON-NLS-1$ + + protected boolean fAttachMode = false; + protected boolean fRemoteMode = false; + + protected Button fStopInMain; + protected Text fStopInMainSymbol; + + private ScrolledComposite fContainer; + + private Composite fContents; + + public CDebuggerTab(SessionType sessionType, boolean attach) { + if (sessionType == SessionType.REMOTE) fRemoteMode = true; + fAttachMode = attach; + + ICDebugConfiguration dc = CDebugCorePlugin.getDefault().getDefaultDefaultDebugConfiguration(); + if (dc == null) { + CDebugCorePlugin.getDefault().getPluginPreferences().setDefault(ICDebugConstants.PREF_DEFAULT_DEBUGGER_TYPE, + LOCAL_DEBUGGER_ID); + } + } + + @Override + public void createControl(Composite parent) { + fContainer = new ScrolledComposite(parent, SWT.V_SCROLL | SWT.H_SCROLL); + fContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); + fContainer.setLayout(new FillLayout()); + fContainer.setExpandHorizontal(true); + fContainer.setExpandVertical(true); + + fContents = new Composite(fContainer, SWT.NONE); + setControl(fContainer); + GdbUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(getControl(), + ICDTLaunchHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_DEBBUGER_TAB); + int numberOfColumns = (fAttachMode) ? 2 : 1; + GridLayout layout = new GridLayout(numberOfColumns, false); + fContents.setLayout(layout); + GridData gd = new GridData(GridData.BEGINNING, GridData.CENTER, true, false); + fContents.setLayoutData(gd); + + createDebuggerCombo(fContents, (fAttachMode) ? 1 : 2); + createOptionsComposite(fContents); + createDebuggerGroup(fContents, 2); + + fContainer.setContent(fContents); + } + + protected void loadDebuggerComboBox(ILaunchConfiguration config, String selection) { +// String configPlatform = getPlatform(config); + ICDebugConfiguration[] debugConfigs = CDebugCorePlugin.getDefault().getActiveDebugConfigurations(); + Arrays.sort(debugConfigs, new Comparator() { + public int compare(ICDebugConfiguration c1, ICDebugConfiguration c2) { + return Collator.getInstance().compare(c1.getName(), c2.getName()); + } + }); + List list = new ArrayList(); + if (selection.equals("")) { //$NON-NLS-1$ + ICDebugConfiguration dc = CDebugCorePlugin.getDefault().getDefaultDebugConfiguration(); + if (dc == null) { + CDebugCorePlugin.getDefault().saveDefaultDebugConfiguration(LOCAL_DEBUGGER_ID); + dc = CDebugCorePlugin.getDefault().getDefaultDebugConfiguration(); + } + if (dc != null) + selection = dc.getID(); + } + String defaultSelection = selection; + for (ICDebugConfiguration debugConfig: debugConfigs) { + if (((fRemoteMode || fAttachMode) && debugConfig.getID().equals(REMOTE_DEBUGGER_ID)) || + (!fRemoteMode && debugConfig.getID().equals(LOCAL_DEBUGGER_ID))) { +// String debuggerPlatform = debugConfig.getPlatform(); +// if (validatePlatform(config, debugConfig)) { + list.add(debugConfig); +// // select first exact matching debugger for platform or +// // requested selection +// if ((defaultSelection.equals("") && debuggerPlatform.equalsIgnoreCase(configPlatform))) { //$NON-NLS-1$ +// defaultSelection = debugConfig.getID(); +// } +// } + } + } + // if no selection meaning nothing in config the force initdefault on tab + setInitializeDefault(selection.equals("") ? true : false); //$NON-NLS-1$ + loadDebuggerCombo(list.toArray(new ICDebugConfiguration[list.size()]), defaultSelection); + } + + @Override + protected void updateComboFromSelection() { + super.updateComboFromSelection(); + initializeCommonControls(getLaunchConfiguration()); + } + + @Override + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + super.setDefaults(config); + if (fAttachMode && fRemoteMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH); + } else if (fAttachMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH); + } else if (fRemoteMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE); + } else { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN); + } + + if (!fAttachMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, + ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_DEFAULT); + } + + // Set the default debugger based on the active toolchain on the project (if possible) + String defaultDebugger = null; + try { + String projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, "");//$NON-NLS-1$ + if (projectName.length() > 0) { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + ICProjectDescription projDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription configDesc = projDesc.getActiveConfiguration(); + String configId = configDesc.getId(); + ICDebugConfiguration[] debugConfigs = CDebugCorePlugin.getDefault().getActiveDebugConfigurations(); + outer: for (int i = 0; i < debugConfigs.length; ++i) { + ICDebugConfiguration debugConfig = debugConfigs[i]; + String[] patterns = debugConfig.getSupportedBuildConfigPatterns(); + if (patterns != null) { + for (int j = 0; j < patterns.length; ++j) { + if (configId.matches(patterns[j])) { + defaultDebugger = debugConfig.getID(); + break outer; + } + } + } + } + } + } catch (CoreException e) { + } + + if (defaultDebugger == null) { + ICDebugConfiguration dc = CDebugCorePlugin.getDefault().getDefaultDebugConfiguration(); + if (dc != null) { + defaultDebugger = dc.getID(); + } + } + + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, defaultDebugger); + } + + @Override + public void initializeFrom(ILaunchConfiguration config) { + setInitializing(true); + super.initializeFrom(config); + try { + String id = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_ID, ""); //$NON-NLS-1$ + loadDebuggerComboBox(config, id); + initializeCommonControls(config); + } catch (CoreException e) { + } + setInitializing(false); + } + + @Override + public void performApply(ILaunchConfigurationWorkingCopy config) { + super.performApply(config); + + if (fAttachMode && fRemoteMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH); + } else if (fAttachMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH); + } else if (fRemoteMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE); + } else { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, + ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN); + } + if (!fAttachMode) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, + fStopInMain.getSelection()); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, + fStopInMainSymbol.getText()); + + } + } + + @Override + public boolean isValid(ILaunchConfiguration config) { + if (!validateDebuggerConfig(config)) { + return false; + } +// ICDebugConfiguration debugConfig = getDebugConfig(); +// if (fAttachMode && !debugConfig.supportsMode(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH)) { +// setErrorMessage(MessageFormat.format(LaunchMessages.getString("CDebuggerTab.Mode_not_supported"), //$NON-NLS-1$ +// (Object[]) new String[]{ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH})); +// return false; +// } +// if (fRemoteMode && !debugConfig.supportsMode(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE)) { +// setErrorMessage(MessageFormat.format(LaunchMessages.getString("CDebuggerTab.Mode_not_supported"), //$NON-NLS-1$ +// (Object[]) new String[]{IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE})); +// return false; +// } +// if (!fAttachMode && !fRemoteMode && !debugConfig.supportsMode(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)) { +// setErrorMessage(MessageFormat.format(LaunchMessages.getString("CDebuggerTab.Mode_not_supported"), //$NON-NLS-1$ +// (Object[]) new String[]{ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN})); +// return false; +// } + if (fStopInMain != null && fStopInMainSymbol != null) { + // The "Stop on startup at" field must not be empty + String mainSymbol = fStopInMainSymbol.getText().trim(); + if (fStopInMain.getSelection() && mainSymbol.length() == 0) { + setErrorMessage(LaunchMessages.getString("CDebuggerTab.Stop_on_startup_at_can_not_be_empty")); //$NON-NLS-1$ + return false; + } + } + if (super.isValid(config) == false) { + return false; + } + return true; + } + + protected boolean validatePlatform(ILaunchConfiguration config, ICDebugConfiguration debugConfig) { + String configPlatform = getPlatform(config); + String debuggerPlatform = debugConfig.getPlatform(); + return (debuggerPlatform.equals("*") || debuggerPlatform.equalsIgnoreCase(configPlatform)); //$NON-NLS-1$ + } + + protected IBinaryObject getBinary(ILaunchConfiguration config) throws CoreException { + String programName = null; + String projectName = null; + try { + projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); + programName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, (String)null); + } catch (CoreException e) { + } + if (programName != null) { + IPath exePath = new Path(programName); + if (projectName != null && !projectName.equals("")) { //$NON-NLS-1$ + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if (!project.isAccessible()) { + return null; + } + if (!exePath.isAbsolute()) { + exePath = project.getLocation().append(exePath); + } + ICExtensionReference[] parserRef = CCorePlugin.getDefault().getBinaryParserExtensions(project); + for (int i = 0; i < parserRef.length; i++) { + try { + IBinaryParser parser = (IBinaryParser)parserRef[i].createExtension(); + IBinaryObject exe = (IBinaryObject)parser.getBinary(exePath); + if (exe != null) { + return exe; + } + } catch (ClassCastException e) { + } catch (IOException e) { + } + } + } + IBinaryParser parser = CCorePlugin.getDefault().getDefaultBinaryParser(); + try { + IBinaryObject exe = (IBinaryObject)parser.getBinary(exePath); + return exe; + } catch (ClassCastException e) { + } catch (IOException e) { + } + } + return null; + } + + protected boolean validateDebuggerConfig(ILaunchConfiguration config) { + ICDebugConfiguration debugConfig = getDebugConfig(); + if (debugConfig == null) { + setErrorMessage(LaunchMessages.getString("CDebuggerTab.No_debugger_available")); //$NON-NLS-1$ + return false; + } + // We do not validate platform and CPU compatibility to avoid accidentally disabling + // a valid configuration. It's much better to let an incompatible configuration through + // than to disable a valid one. + return true; + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#updateLaunchConfigurationDialog() + */ + protected void update() { + if (!isInitializing()) { + super.updateLaunchConfigurationDialog(); + } + } + + protected void createOptionsComposite(Composite parent) { + Composite optionsComp = new Composite(parent, SWT.NONE); + int numberOfColumns = (fAttachMode) ? 1 : 3; + GridLayout layout = new GridLayout(numberOfColumns, false); + optionsComp.setLayout(layout); + optionsComp.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, true, false, 1, 1)); + if (!fAttachMode) { + fStopInMain = createCheckButton(optionsComp, LaunchMessages.getString("CDebuggerTab.Stop_at_main_on_startup")); //$NON-NLS-1$ + fStopInMain.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + fStopInMainSymbol.setEnabled(fStopInMain.getSelection()); + update(); + } + }); + fStopInMainSymbol = new Text(optionsComp, SWT.SINGLE | SWT.BORDER); + final GridData gridData = new GridData(GridData.FILL, GridData.CENTER, false, false); + gridData.widthHint = 100; + fStopInMainSymbol.setLayoutData(gridData); + fStopInMainSymbol.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent evt) { + update(); + } + }); + fStopInMainSymbol.getAccessible().addAccessibleListener( + new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + e.result = LaunchMessages.getString("CDebuggerTab.Stop_at_main_on_startup"); //$NON-NLS-1$ + } + } + ); + } + } + + @Override + protected Shell getShell() { + return super.getShell(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose() + */ + @Override + public void dispose() { + ICDebuggerPage debuggerPage = getDynamicTab(); + if (debuggerPage != null) + debuggerPage.dispose(); + super.dispose(); + } + + protected void initializeCommonControls(ILaunchConfiguration config) { + try { + if (!fAttachMode) { + fStopInMain.setSelection(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, + ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_DEFAULT)); + fStopInMainSymbol.setText(config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, + ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT)); + fStopInMainSymbol.setEnabled(fStopInMain.getSelection()); + } else { + // In attach mode, figure out if we are doing a remote connect based on the currently + // chosen debugger + if (getDebugConfig().getID().equals(REMOTE_DEBUGGER_ID)) fRemoteMode = true; + else fRemoteMode = false; + } + } catch (CoreException e) { + } + } + + @Override + protected void setInitializeDefault(boolean init) { + super.setInitializeDefault(init); + } + + @Override + protected void contentsChanged() { + fContainer.setMinSize(fContents.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CLaunchConfigurationTab.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CLaunchConfigurationTab.java new file mode 100644 index 00000000000..665c0b48e4a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CLaunchConfigurationTab.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ken Ryall (Nokia) - bug 178731 + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.ICDescriptor; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; + +public abstract class CLaunchConfigurationTab extends AbstractLaunchConfigurationTab { + + /** + * Returns the current C element context from which to initialize default + * settings, or null if none. Note, if possible we will + * return the IBinary based on config entry as this may be more usefull then + * just the project. + * + * @return C element context. + */ + protected ICElement getContext(ILaunchConfiguration config, String platform) { + String projectName = null; + String programName = null; + IWorkbenchPage page = GdbUIPlugin.getActivePage(); + Object obj = null; + try { + projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); + programName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, (String)null); + } catch (CoreException e) { + } + if (projectName != null && !projectName.equals("")) { //$NON-NLS-1$ + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(project); + if (cProject != null && cProject.exists()) { + obj = cProject; + } + } else { + if (page != null) { + ISelection selection = page.getSelection(); + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection)selection; + if (!ss.isEmpty()) { + obj = ss.getFirstElement(); + } + } + } + } + if (obj instanceof IResource) { + ICElement ce = CoreModel.getDefault().create((IResource)obj); + if (ce == null) { + IProject pro = ((IResource)obj).getProject(); + ce = CoreModel.getDefault().create(pro); + } + obj = ce; + } + if (obj instanceof ICElement) { + if (platform != null && !platform.equals("*")) { //$NON-NLS-1$ + ICDescriptor descriptor; + try { + descriptor = CCorePlugin.getDefault().getCProjectDescription( ((ICElement)obj).getCProject().getProject(), + false); + if (descriptor != null) { + String projectPlatform = descriptor.getPlatform(); + if (!projectPlatform.equals(platform) && !projectPlatform.equals("*")) { //$NON-NLS-1$ + obj = null; + } + } + } catch (CoreException e) { + } + } + if (obj != null) { + if (programName == null || programName.equals("")) { //$NON-NLS-1$ + return (ICElement)obj; + } + ICElement ce = (ICElement)obj; + IProject project; + project = (IProject)ce.getCProject().getResource(); + IPath programFile = project.getFile(programName).getLocation(); + ce = CCorePlugin.getDefault().getCoreModel().create(programFile); + if (ce != null && ce.exists()) { + return ce; + } + return (ICElement)obj; + } + } + IEditorPart part = page.getActiveEditor(); + if (part != null) { + IEditorInput input = part.getEditorInput(); + return (ICElement)input.getAdapter(ICElement.class); + } + return null; + } + + /** + * Set the C project attribute based on the ICElement. + */ + protected void initializeCProject(ICElement cElement, ILaunchConfigurationWorkingCopy config) { + ICProject cProject = cElement.getCProject(); + String name = null; + if (cProject != null && cProject.exists()) { + name = cProject.getElementName(); + config.setMappedResources(new IResource[] {cProject.getProject()}); + + ICProjectDescription projDes = CCorePlugin.getDefault().getProjectDescription(cProject.getProject()); + String buildConfigID = projDes.getActiveConfiguration().getId(); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, buildConfigID); + + } + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, name); + + } + + protected String getPlatform(ILaunchConfiguration config) { + String platform = Platform.getOS(); + try { + return config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PLATFORM, platform); + } catch (CoreException e) { + return platform; + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CMainTab.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CMainTab.java new file mode 100644 index 00000000000..8178ab08caa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/CMainTab.java @@ -0,0 +1,728 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Ken Ryall (Nokia) - bug 178731 + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.io.IOException; +import java.util.ArrayList; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.ICDescriptor; +import org.eclipse.cdt.core.ICExtensionReference; +import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; +import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.IBinary; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.cdt.ui.CElementLabelProvider; +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; +import org.eclipse.ui.dialogs.TwoPaneElementSelector; + +/** + * A launch configuration tab that displays and edits project and main type name launch + * configuration attributes. + *

+ * This class may be instantiated. This class is not intended to be subclassed. + *

+ */ + +public class CMainTab extends CLaunchConfigurationTab { + + // Project UI widgets + protected Label fProjLabel; + protected Text fProjText; + protected Button fProjButton; + + // Main class UI widgets + protected Label fProgLabel; + protected Text fProgText; + protected Button fSearchButton; + + private final boolean fWantsTerminalOption; + protected Button fTerminalButton; + + private final boolean dontCheckProgram; + + protected static final String EMPTY_STRING = ""; //$NON-NLS-1$ + + private String filterPlatform = EMPTY_STRING; + + public static final int WANTS_TERMINAL = 1; + public static final int DONT_CHECK_PROGRAM = 2; + + public CMainTab() { + this(0); + } + + public CMainTab(boolean terminalOption) { + this(terminalOption ? WANTS_TERMINAL : 0); + } + + public CMainTab(int flags) { + fWantsTerminalOption = (flags & WANTS_TERMINAL) != 0; + dontCheckProgram = (flags & DONT_CHECK_PROGRAM) != 0; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + setControl(comp); + + GdbUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(getControl(), ICDTLaunchHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_MAIN_TAB); + + GridLayout topLayout = new GridLayout(); + comp.setLayout(topLayout); + + createVerticalSpacer(comp, 1); + createProjectGroup(comp, 1); + createExeFileGroup(comp, 1); + createVerticalSpacer(comp, 1); + if (wantsTerminalOption() /* && ProcessFactory.supportesTerminal() */) { + createTerminalOption(comp, 1); + } + GdbUIPlugin.setDialogShell(parent.getShell()); + } + + protected void createProjectGroup(Composite parent, int colSpan) { + Composite projComp = new Composite(parent, SWT.NONE); + GridLayout projLayout = new GridLayout(); + projLayout.numColumns = 2; + projLayout.marginHeight = 0; + projLayout.marginWidth = 0; + projComp.setLayout(projLayout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = colSpan; + projComp.setLayoutData(gd); + + fProjLabel = new Label(projComp, SWT.NONE); + fProjLabel.setText(LaunchMessages.getString("CMainTab.&ProjectColon")); //$NON-NLS-1$ + gd = new GridData(); + gd.horizontalSpan = 2; + fProjLabel.setLayoutData(gd); + + fProjText = new Text(projComp, SWT.SINGLE | SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + fProjText.setLayoutData(gd); + fProjText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent evt) { + updateLaunchConfigurationDialog(); + } + }); + + fProjButton = createPushButton(projComp, LaunchMessages.getString("Launch.common.Browse_1"), null); //$NON-NLS-1$ + fProjButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent evt) { + handleProjectButtonSelected(); + updateLaunchConfigurationDialog(); + } + }); + } + + protected void createExeFileGroup(Composite parent, int colSpan) { + Composite mainComp = new Composite(parent, SWT.NONE); + GridLayout mainLayout = new GridLayout(); + mainLayout.numColumns = 3; + mainLayout.marginHeight = 0; + mainLayout.marginWidth = 0; + mainComp.setLayout(mainLayout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = colSpan; + mainComp.setLayoutData(gd); + fProgLabel = new Label(mainComp, SWT.NONE); + fProgLabel.setText(LaunchMessages.getString("CMainTab.C/C++_Application")); //$NON-NLS-1$ + gd = new GridData(); + gd.horizontalSpan = 3; + fProgLabel.setLayoutData(gd); + fProgText = new Text(mainComp, SWT.SINGLE | SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + fProgText.setLayoutData(gd); + fProgText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent evt) { + updateLaunchConfigurationDialog(); + } + }); + + fSearchButton = createPushButton(mainComp, LaunchMessages.getString("CMainTab.Search..."), null); //$NON-NLS-1$ + fSearchButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent evt) { + handleSearchButtonSelected(); + updateLaunchConfigurationDialog(); + } + }); + + Button fBrowseForBinaryButton; + fBrowseForBinaryButton = createPushButton(mainComp, LaunchMessages.getString("Launch.common.Browse_2"), null); //$NON-NLS-1$ + fBrowseForBinaryButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent evt) { + handleBinaryBrowseButtonSelected(); + updateLaunchConfigurationDialog(); + } + }); + } + + protected boolean wantsTerminalOption() { + return fWantsTerminalOption; + } + + protected void createTerminalOption(Composite parent, int colSpan) { + Composite mainComp = new Composite(parent, SWT.NONE); + GridLayout mainLayout = new GridLayout(); + mainLayout.numColumns = 1; + mainLayout.marginHeight = 0; + mainLayout.marginWidth = 0; + mainComp.setLayout(mainLayout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = colSpan; + mainComp.setLayoutData(gd); + + fTerminalButton = createCheckButton(mainComp, LaunchMessages.getString("CMainTab.UseTerminal")); //$NON-NLS-1$ + fTerminalButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent evt) { + updateLaunchConfigurationDialog(); + } + }); + fTerminalButton.setEnabled(PTY.isSupported()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration config) { + filterPlatform = getPlatform(config); + updateProjectFromConfig(config); + updateProgramFromConfig(config); + updateTerminalFromConfig(config); + } + + protected void updateTerminalFromConfig(ILaunchConfiguration config) { + if (fTerminalButton != null) { + boolean useTerminal = true; + try { + useTerminal = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, ICDTLaunchConfigurationConstants.USE_TERMINAL_DEFAULT); + } catch (CoreException e) { + GdbUIPlugin.log(e); + } + fTerminalButton.setSelection(useTerminal); + } + } + + protected void updateProjectFromConfig(ILaunchConfiguration config) { + String projectName = EMPTY_STRING; + try { + projectName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, EMPTY_STRING); + } catch (CoreException ce) { + GdbUIPlugin.log(ce); + } + fProjText.setText(projectName); + } + + protected void updateProgramFromConfig(ILaunchConfiguration config) { + String programName = EMPTY_STRING; + try { + programName = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, EMPTY_STRING); + } catch (CoreException ce) { + GdbUIPlugin.log(ce); + } + fProgText.setText(programName); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy config) { + ICProject cProject = this.getCProject(); + if (cProject != null) + { + config.setMappedResources(new IResource[] { cProject.getProject() }); + try { // Only initialize the build config ID once. + if (config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, "").length() == 0)//$NON-NLS-1$ + { + ICProjectDescription projDes = CCorePlugin.getDefault().getProjectDescription(cProject.getProject()); + if (projDes != null) + { + String buildConfigID = projDes.getActiveConfiguration().getId(); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_BUILD_CONFIG_ID, buildConfigID); + } + } + } catch (CoreException e) { e.printStackTrace(); } + } else { + config.setMappedResources(null); + } + + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, fProjText.getText()); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, fProgText.getText()); + if (fTerminalButton != null) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, fTerminalButton.getSelection()); + } + } + + /** + * Show a dialog that lists all main types + */ + protected void handleSearchButtonSelected() { + + if (getCProject() == null) { + MessageDialog.openInformation(getShell(), LaunchMessages.getString("CMainTab.Project_required"), //$NON-NLS-1$ + LaunchMessages.getString("CMainTab.Enter_project_before_searching_for_program")); //$NON-NLS-1$ + return; + } + + ILabelProvider programLabelProvider = new CElementLabelProvider() { + + @Override + public String getText(Object element) { + if (element instanceof IBinary) { + IBinary bin = (IBinary)element; + StringBuffer name = new StringBuffer(); + name.append(bin.getPath().lastSegment()); + return name.toString(); + } + return super.getText(element); + } + + @Override + public Image getImage(Object element) { + if (! (element instanceof ICElement)) { + return super.getImage(element); + } + ICElement celement = (ICElement)element; + + if (celement.getElementType() == ICElement.C_BINARY) { + IBinary belement = (IBinary)celement; + if (belement.isExecutable()) { + return DebugUITools.getImage(IDebugUIConstants.IMG_ACT_RUN); + } + } + + return super.getImage(element); + } + }; + + ILabelProvider qualifierLabelProvider = new CElementLabelProvider() { + + @Override + public String getText(Object element) { + if (element instanceof IBinary) { + IBinary bin = (IBinary)element; + StringBuffer name = new StringBuffer(); + name.append(bin.getCPU() + (bin.isLittleEndian() ? "le" : "be")); //$NON-NLS-1$ //$NON-NLS-2$ + name.append(" - "); //$NON-NLS-1$ + name.append(bin.getPath().toString()); + return name.toString(); + } + return super.getText(element); + } + }; + + TwoPaneElementSelector dialog = new TwoPaneElementSelector(getShell(), programLabelProvider, qualifierLabelProvider); + dialog.setElements(getBinaryFiles(getCProject())); + dialog.setMessage(LaunchMessages.getString("CMainTab.Choose_program_to_run")); //$NON-NLS-1$ + dialog.setTitle(LaunchMessages.getString("CMainTab.Program_Selection")); //$NON-NLS-1$ + dialog.setUpperListLabel(LaunchMessages.getString("Launch.common.BinariesColon")); //$NON-NLS-1$ + dialog.setLowerListLabel(LaunchMessages.getString("Launch.common.QualifierColon")); //$NON-NLS-1$ + dialog.setMultipleSelection(false); + // dialog.set + if (dialog.open() == Window.OK) { + IBinary binary = (IBinary)dialog.getFirstResult(); + fProgText.setText(binary.getResource().getProjectRelativePath().toString()); + } + + } + + /** + * Show a dialog that lets the user select a project. This in turn provides context for the main + * type, allowing the user to key a main type name, or constraining the search for main types to + * the specified project. + */ + protected void handleBinaryBrowseButtonSelected() { + final ICProject cproject = getCProject(); + if (cproject == null) { + MessageDialog.openInformation(getShell(), LaunchMessages.getString("CMainTab.Project_required"), //$NON-NLS-1$ + LaunchMessages.getString("CMainTab.Enter_project_before_browsing_for_program")); //$NON-NLS-1$ + return; + } + FileDialog fileDialog = new FileDialog(getShell(), SWT.NONE); + fileDialog.setFileName(fProgText.getText()); + String text= fileDialog.open(); + if (text != null) { + fProgText.setText(text); + } + } + + /** + * Iterate through and suck up all of the executable files that we can find. + */ + protected IBinary[] getBinaryFiles(final ICProject cproject) { + final Display display; + if (cproject == null || !cproject.exists()) { + return null; + } + if (getShell() == null) { + display = GdbUIPlugin.getShell().getDisplay(); + } else { + display = getShell().getDisplay(); + } + final Object[] ret = new Object[1]; + BusyIndicator.showWhile(display, new Runnable() { + + public void run() { + try { + ret[0] = cproject.getBinaryContainer().getBinaries(); + } catch (CModelException e) { + GdbUIPlugin.errorDialog("Launch UI internal error", e); //$NON-NLS-1$ + } + } + }); + + return (IBinary[])ret[0]; + } + + /** + * Show a dialog that lets the user select a project. This in turn provides context for the main + * type, allowing the user to key a main type name, or constraining the search for main types to + * the specified project. + */ + protected void handleProjectButtonSelected() { + ICProject project = chooseCProject(); + if (project == null) { + return; + } + + String projectName = project.getElementName(); + fProjText.setText(projectName); + } + + /** + * Realize a C Project selection dialog and return the first selected project, or null if there + * was none. + */ + protected ICProject chooseCProject() { + try { + ICProject[] projects = getCProjects(); + + ILabelProvider labelProvider = new CElementLabelProvider(); + ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), labelProvider); + dialog.setTitle(LaunchMessages.getString("CMainTab.Project_Selection")); //$NON-NLS-1$ + dialog.setMessage(LaunchMessages.getString("CMainTab.Choose_project_to_constrain_search_for_program")); //$NON-NLS-1$ + dialog.setElements(projects); + + ICProject cProject = getCProject(); + if (cProject != null) { + dialog.setInitialSelections(new Object[]{cProject}); + } + if (dialog.open() == Window.OK) { + return (ICProject)dialog.getFirstResult(); + } + } catch (CModelException e) { + GdbUIPlugin.errorDialog("Launch UI internal error", e); //$NON-NLS-1$ + } + return null; + } + + /** + * Return an array a ICProject whose platform match that of the runtime env. + */ + protected ICProject[] getCProjects() throws CModelException { + ICProject cproject[] = CoreModel.getDefault().getCModel().getCProjects(); + ArrayList list = new ArrayList(cproject.length); + + for (int i = 0; i < cproject.length; i++) { + ICDescriptor cdesciptor = null; + try { + cdesciptor = CCorePlugin.getDefault().getCProjectDescription((IProject)cproject[i].getResource(), false); + if (cdesciptor != null) { + String projectPlatform = cdesciptor.getPlatform(); + if (filterPlatform.equals("*") //$NON-NLS-1$ + || projectPlatform.equals("*") //$NON-NLS-1$ + || filterPlatform.equalsIgnoreCase(projectPlatform) == true) { + list.add(cproject[i]); + } + } else { + list.add(cproject[i]); + } + } catch (CoreException e) { + list.add(cproject[i]); + } + } + return list.toArray(new ICProject[list.size()]); + } + + /** + * Return the ICProject corresponding to the project name in the project name text field, or + * null if the text does not match a project name. + */ + protected ICProject getCProject() { + String projectName = fProjText.getText().trim(); + if (projectName.length() < 1) { + return null; + } + return CoreModel.getDefault().getCModel().getCProject(projectName); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration config) { + + setErrorMessage(null); + setMessage(null); + + if (dontCheckProgram) + return true; + + String name = fProjText.getText().trim(); + if (name.length() == 0) { + setErrorMessage(LaunchMessages.getString("CMainTab.Project_not_specified")); //$NON-NLS-1$ + return false; + } + if (!ResourcesPlugin.getWorkspace().getRoot().getProject(name).exists()) { + setErrorMessage(LaunchMessages.getString("Launch.common.Project_does_not_exist")); //$NON-NLS-1$ + return false; + } + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name); + if (!project.isOpen()) { + setErrorMessage(LaunchMessages.getString("CMainTab.Project_must_be_opened")); //$NON-NLS-1$ + return false; + } + + name = fProgText.getText().trim(); + if (name.length() == 0) { + setErrorMessage(LaunchMessages.getString("CMainTab.Program_not_specified")); //$NON-NLS-1$ + return false; + } + if (name.equals(".") || name.equals("..")) { //$NON-NLS-1$ //$NON-NLS-2$ + setErrorMessage(LaunchMessages.getString("CMainTab.Program_does_not_exist")); //$NON-NLS-1$ + return false; + } + IPath exePath = new Path(name); + if (!exePath.isAbsolute()) { + if (!project.getFile(name).exists()) { + setErrorMessage(LaunchMessages.getString("CMainTab.Program_does_not_exist")); //$NON-NLS-1$ + return false; + } + exePath = project.getFile(name).getLocation(); + } else { + if (!exePath.toFile().exists()) { + setErrorMessage(LaunchMessages.getString("CMainTab.Program_does_not_exist")); //$NON-NLS-1$ + return false; + } + } + try { + if (!isBinary(project, exePath)) { + setErrorMessage(LaunchMessages.getString("CMainTab.Program_is_not_a_recongnized_executable")); //$NON-NLS-1$ + return false; + } + } catch (CoreException e) { + GdbUIPlugin.log(e); + setErrorMessage(e.getLocalizedMessage()); + return false; + } + + return true; + } + + /** + * @param project + * @param exePath + * @return + * @throws CoreException + */ + protected boolean isBinary(IProject project, IPath exePath) throws CoreException { + ICExtensionReference[] parserRef = CCorePlugin.getDefault().getBinaryParserExtensions(project); + for (int i = 0; i < parserRef.length; i++) { + try { + IBinaryParser parser = (IBinaryParser)parserRef[i].createExtension(); + IBinaryObject exe = (IBinaryObject)parser.getBinary(exePath); + if (exe != null) { + return true; + } + } catch (ClassCastException e) { + } catch (IOException e) { + } + } + IBinaryParser parser = CCorePlugin.getDefault().getDefaultBinaryParser(); + try { + IBinaryObject exe = (IBinaryObject)parser.getBinary(exePath); + return exe != null; + } catch (ClassCastException e) { + } catch (IOException e) { + } + return false; + } + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + // We set empty attributes for project & program so that when one config + // is + // compared to another, the existence of empty attributes doesn't cause + // an + // incorrect result (the performApply() method can result in empty + // values + // for these attributes being set on a config if there is nothing in the + // corresponding text boxes) + // plus getContext will use this to base context from if set. + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, EMPTY_STRING); + ICElement cElement = null; + cElement = getContext(config, getPlatform(config)); + if (cElement != null) { + initializeCProject(cElement, config); + initializeProgramName(cElement, config); + } + if (wantsTerminalOption()) { + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_USE_TERMINAL, ICDTLaunchConfigurationConstants.USE_TERMINAL_DEFAULT); + } + } + + /** + * Set the program name attributes on the working copy based on the ICElement + */ + protected void initializeProgramName(ICElement cElement, ILaunchConfigurationWorkingCopy config) { + + boolean renamed = false; + + if (!(cElement instanceof IBinary)) + { + cElement = cElement.getCProject(); + } + + if (cElement instanceof ICProject) { + + IProject project = cElement.getCProject().getProject(); + String name = project.getName(); + ICProjectDescription projDes = CCorePlugin.getDefault().getProjectDescription(project); + if (projDes != null) { + String buildConfigName = projDes.getActiveConfiguration().getName(); + name = name + " " + buildConfigName; //$NON-NLS-1$ + } + name = getLaunchConfigurationDialog().generateName(name); + config.rename(name); + renamed = true; + } + + IBinary binary = null; + if (cElement instanceof ICProject) { + IBinary[] bins = getBinaryFiles((ICProject)cElement); + if (bins != null && bins.length == 1) { + binary = bins[0]; + } + } else if (cElement instanceof IBinary) { + binary = (IBinary)cElement; + } + + if (binary != null) { + String path; + path = binary.getResource().getProjectRelativePath().toOSString(); + config.setAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, path); + if (!renamed) + { + String name = binary.getElementName(); + int index = name.lastIndexOf('.'); + if (index > 0) { + name = name.substring(0, index); + } + name = getLaunchConfigurationDialog().generateName(name); + config.rename(name); + renamed = true; + } + } + + if (!renamed) + { + String name = getLaunchConfigurationDialog().generateName(cElement.getCProject().getElementName()); + config.rename(name); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + public String getName() { + return LaunchMessages.getString("CMainTab.Main"); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + */ + @Override + public Image getImage() { + return LaunchImages.get(LaunchImages.IMG_VIEW_MAIN_TAB); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#updateLaunchConfigurationDialog() + */ + @Override + protected void updateLaunchConfigurationDialog() { + super.updateLaunchConfigurationDialog(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GDBSolibBlock.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GDBSolibBlock.java new file mode 100644 index 00000000000..6a5bd6a1023 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GDBSolibBlock.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.util.Map; +import java.util.Observable; +import java.util.Observer; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** + * The content of the Shared Libraries tab of the GDBDebuggerPage. + */ +public class GDBSolibBlock extends Observable implements IMILaunchConfigurationComponent, Observer { + + private IMILaunchConfigurationComponent fSolibSearchPathBlock; + + private Button fAutoSoLibButton; + + private Button fUseSolibForAppButton; + + private Composite fControl; + + private boolean fAutoSolib = false; + + private boolean fUseSolibForApp = true; + + public GDBSolibBlock( IMILaunchConfigurationComponent solibSearchBlock, boolean autoSolib, boolean stopOnSolibEvents ) { + super(); + fSolibSearchPathBlock = solibSearchBlock; + fAutoSolib = autoSolib; + } + + public void createControl( Composite parent ) { + Composite subComp = ControlFactory.createCompositeEx( parent, 1, GridData.FILL_HORIZONTAL ); + ((GridLayout)subComp.getLayout()).makeColumnsEqualWidth = false; + ((GridLayout)subComp.getLayout()).marginHeight = 0; + ((GridLayout)subComp.getLayout()).marginWidth = 0; + if ( fSolibSearchPathBlock != null ) { + fSolibSearchPathBlock.createControl( subComp ); + if ( fSolibSearchPathBlock instanceof Observable ) + ((Observable)fSolibSearchPathBlock).addObserver( this ); + } + if ( fAutoSolib ) { + fAutoSoLibButton = ControlFactory.createCheckBox( subComp, LaunchUIMessages.getString( "GDBSolibBlock.0" ) ); //$NON-NLS-1$ + fAutoSoLibButton.addSelectionListener( new SelectionAdapter() { + + @Override + public void widgetSelected( SelectionEvent e ) { + updateButtons(); + changed(); + } + } ); + } + if ( fUseSolibForApp ) { + fUseSolibForAppButton = ControlFactory.createCheckBox( subComp, LaunchUIMessages.getString( "GDBSolibBlock.2" ) ); //$NON-NLS-1$ + fUseSolibForAppButton.addSelectionListener( new SelectionAdapter() { + + @Override + public void widgetSelected( SelectionEvent e ) { + updateButtons(); + changed(); + } + } ); + } + fControl = subComp; + } + + public void initializeFrom( ILaunchConfiguration configuration ) { + if ( fSolibSearchPathBlock != null ) + fSolibSearchPathBlock.initializeFrom( configuration ); + try { + if ( fAutoSoLibButton != null ) + fAutoSoLibButton.setSelection( configuration.getAttribute( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT ) ); + if ( fUseSolibForAppButton != null ) + fUseSolibForAppButton.setSelection( configuration.getAttribute( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT ) ); + initializeButtons( configuration ); + updateButtons(); + } + catch( CoreException e ) { + } + } + + public void performApply( ILaunchConfigurationWorkingCopy configuration ) { + if ( fSolibSearchPathBlock != null ) + fSolibSearchPathBlock.performApply( configuration ); + try { + @SuppressWarnings("unchecked") + Map attrs = configuration.getAttributes(); + + if ( fAutoSoLibButton != null ) + attrs.put( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, Boolean.valueOf( fAutoSoLibButton.getSelection() ) ); + if ( fUseSolibForAppButton != null ) + attrs.put( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, Boolean.valueOf( fUseSolibForAppButton.getSelection() ) ); + configuration.setAttributes( attrs ); + } + catch( CoreException e ) { + } + } + + public void setDefaults( ILaunchConfigurationWorkingCopy configuration ) { + if ( fSolibSearchPathBlock != null ) + fSolibSearchPathBlock.setDefaults( configuration ); + configuration.setAttribute( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT ); + configuration.setAttribute( IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT ); + } + + protected void updateButtons() { + } + + public void dispose() { + deleteObservers(); + if ( fSolibSearchPathBlock != null ) { + if ( fSolibSearchPathBlock instanceof Observable ) + ((Observable)fSolibSearchPathBlock).deleteObserver( this ); + fSolibSearchPathBlock.dispose(); + } + } + + public void update( Observable o, Object arg ) { + changed(); + } + + protected void changed() { + setChanged(); + notifyObservers(); + } + + protected void initializeButtons( ILaunchConfiguration configuration ) { + try { + boolean enable = !ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE.equals( configuration.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, "" ) ); //$NON-NLS-1$ + if ( fAutoSoLibButton != null ) + fAutoSoLibButton.setEnabled( enable ); + if ( fUseSolibForAppButton != null ) + fUseSolibForAppButton.setEnabled( enable ); + } + catch( CoreException e ) { + } + } + + public Control getControl() { + return fControl; + } + + public boolean isValid( ILaunchConfiguration launchConfig ) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbAttachLaunchConfigurationTabGroup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbAttachLaunchConfigurationTabGroup.java new file mode 100644 index 00000000000..f72f9845179 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbAttachLaunchConfigurationTabGroup.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +public class GdbAttachLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup { + + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTabGroup#createTabs(org.eclipse.debug.ui.ILaunchConfigurationDialog, java.lang.String) + */ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { + new CMainTab(2), // In some case, we don't need to specify an executable + // We don't know yet if we are going to do a remote or local session + new CDebuggerTab(null, true), + new SourceLookupTab(), + new CommonTab() + }; + setTabs(tabs); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java new file mode 100644 index 00000000000..8a6a1478ad8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbDebuggerPage.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.io.File; +import java.util.Observable; +import java.util.Observer; + +import org.eclipse.cdt.debug.ui.AbstractCDebuggerPage; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Text; + +/** + * The dynamic tab for gdb-based debugger implementations. + */ +public class GdbDebuggerPage extends AbstractCDebuggerPage implements Observer { + + protected TabFolder fTabFolder; + protected Text fGDBCommandText; + protected Text fGDBInitText; + protected Button fNonStopCheckBox; + protected Button fVerboseModeButton; + + private IMILaunchConfigurationComponent fSolibBlock; + private boolean fIsInitializing = false; + + public void createControl(Composite parent) { + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(new GridLayout()); + comp.setLayoutData(new GridData(GridData.FILL_BOTH)); + fTabFolder = new TabFolder(comp, SWT.NONE); + fTabFolder.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL)); + createTabs(fTabFolder); + fTabFolder.setSelection(0); + setControl(parent); + } + + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT); + + if (fSolibBlock != null) + fSolibBlock.setDefaults(configuration); + } + + @Override + public boolean isValid(ILaunchConfiguration launchConfig) { + boolean valid = fGDBCommandText.getText().length() != 0; + if (valid) { + setErrorMessage(null); + setMessage(null); + } + else { + setErrorMessage(LaunchUIMessages.getString("GDBDebuggerPage.0")); //$NON-NLS-1$ + setMessage(null); + } + return valid; + } + + public void initializeFrom(ILaunchConfiguration configuration) { + setInitializing(true); + String gdbCommand = IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT; + String gdbInit = IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT; + boolean nonStopMode = IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT; + boolean verboseMode = IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT; + + try { + gdbCommand = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT); + } + catch(CoreException e) { + } + try { + gdbInit = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); + } + catch(CoreException e) { + } + + try { + nonStopMode = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + } + catch(CoreException e) { + } + try { + verboseMode = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT ); + } + catch(CoreException e) { + } + + if (fSolibBlock != null) + fSolibBlock.initializeFrom(configuration); + fGDBCommandText.setText(gdbCommand); + fGDBInitText.setText(gdbInit); + fNonStopCheckBox.setSelection(nonStopMode); + fVerboseModeButton.setSelection(verboseMode); + + setInitializing(false); + } + + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + fGDBCommandText.getText().trim()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, + fGDBInitText.getText().trim()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + fNonStopCheckBox.getSelection()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, + fVerboseModeButton.getSelection() ); + + if (fSolibBlock != null) + fSolibBlock.performApply(configuration); + } + + public String getName() { + return LaunchUIMessages.getString("GDBDebuggerPage.1"); //$NON-NLS-1$ + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getShell() + */ + @Override + protected Shell getShell() { + return super.getShell(); + } + + /** + * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#updateLaunchConfigurationDialog() + */ + @Override + protected void updateLaunchConfigurationDialog() { + super.updateLaunchConfigurationDialog(); + } + + /* + * (non-Javadoc) + * + * @see java.util.Observer#update(java.util.Observable, java.lang.Object) + */ + public void update(Observable o, Object arg) { + if (!isInitializing()) + updateLaunchConfigurationDialog(); + } + + public IMILaunchConfigurationComponent createSolibBlock(Composite parent) { + IMILaunchConfigurationComponent block = new GDBSolibBlock( new SolibSearchPathBlock(), true, true); + block.createControl(parent); + return block; + } + + public void createTabs(TabFolder tabFolder) { + createMainTab(tabFolder); + createSolibTab(tabFolder); + } + + public void createMainTab(TabFolder tabFolder) { + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(LaunchUIMessages.getString("GDBDebuggerPage.2")); //$NON-NLS-1$ + Composite comp = ControlFactory.createCompositeEx(tabFolder, 1, GridData.FILL_BOTH); + ((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false; + comp.setFont(tabFolder.getFont()); + tabItem.setControl(comp); + Composite subComp = ControlFactory.createCompositeEx(comp, 3, GridData.FILL_HORIZONTAL); + ((GridLayout)subComp.getLayout()).makeColumnsEqualWidth = false; + subComp.setFont(tabFolder.getFont()); + Label label = ControlFactory.createLabel(subComp, LaunchUIMessages.getString("GDBDebuggerPage.3")); //$NON-NLS-1$ + GridData gd = new GridData(); + // gd.horizontalSpan = 2; + label.setLayoutData(gd); + fGDBCommandText = ControlFactory.createTextField(subComp, SWT.SINGLE | SWT.BORDER); + fGDBCommandText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent evt) { + if (!isInitializing()) + updateLaunchConfigurationDialog(); + } + }); + Button button = createPushButton(subComp, LaunchUIMessages.getString("GDBDebuggerPage.4"), null); //$NON-NLS-1$ + button.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent evt) { + handleGDBButtonSelected(); + updateLaunchConfigurationDialog(); + } + + private void handleGDBButtonSelected() { + FileDialog dialog = new FileDialog(getShell(), SWT.NONE); + dialog.setText(LaunchUIMessages.getString("GDBDebuggerPage.5")); //$NON-NLS-1$ + String gdbCommand = fGDBCommandText.getText().trim(); + int lastSeparatorIndex = gdbCommand.lastIndexOf(File.separator); + if (lastSeparatorIndex != -1) { + dialog.setFilterPath(gdbCommand.substring(0, lastSeparatorIndex)); + } + String res = dialog.open(); + if (res == null) { + return; + } + fGDBCommandText.setText(res); + } + }); + label = ControlFactory.createLabel(subComp, LaunchUIMessages.getString("GDBDebuggerPage.6")); //$NON-NLS-1$ + gd = new GridData(); + // gd.horizontalSpan = 2; + label.setLayoutData(gd); + fGDBInitText = ControlFactory.createTextField(subComp, SWT.SINGLE | SWT.BORDER); + gd = new GridData(GridData.FILL_HORIZONTAL); + fGDBInitText.setLayoutData(gd); + fGDBInitText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent evt) { + if (!isInitializing()) + updateLaunchConfigurationDialog(); + } + }); + button = createPushButton(subComp, LaunchUIMessages.getString("GDBDebuggerPage.7"), null); //$NON-NLS-1$ + button.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent evt) { + handleGDBInitButtonSelected(); + updateLaunchConfigurationDialog(); + } + + private void handleGDBInitButtonSelected() { + FileDialog dialog = new FileDialog(getShell(), SWT.NONE); + dialog.setText(LaunchUIMessages.getString("GDBDebuggerPage.8")); //$NON-NLS-1$ + String gdbCommand = fGDBInitText.getText().trim(); + int lastSeparatorIndex = gdbCommand.lastIndexOf(File.separator); + if (lastSeparatorIndex != -1) { + dialog.setFilterPath(gdbCommand.substring(0, lastSeparatorIndex)); + } + String res = dialog.open(); + if (res == null) { + return; + } + fGDBInitText.setText(res); + } + }); + + label = ControlFactory.createLabel(subComp, LaunchUIMessages.getString("GDBDebuggerPage.9"), //$NON-NLS-1$ + 200, SWT.DEFAULT, SWT.WRAP); + + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + gd.widthHint = 200; + label.setLayoutData(gd); + + // TODO: Ideally, this field should be disabled if the back-end doesn't support non-stop debugging + // TODO: Find a way to determine if non-stop is supported (i.e. find the GDB version) then grey out the check box if necessary + fNonStopCheckBox = ControlFactory.createCheckBox(subComp, LaunchUIMessages.getString("GDBDebuggerPage.13")); //$NON-NLS-1$ + fNonStopCheckBox.addSelectionListener( new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateLaunchConfigurationDialog(); + } + }); + + fVerboseModeButton = ControlFactory.createCheckBox( subComp, LaunchUIMessages.getString( "StandardGDBDebuggerPage.13" ) ); //$NON-NLS-1$ + fVerboseModeButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + if (!isInitializing()) + updateLaunchConfigurationDialog(); + } + + @Override + public void widgetSelected(SelectionEvent e) { + if (!isInitializing()) + updateLaunchConfigurationDialog(); + } + }); + + // fit options one per line + gd = new GridData(); + gd.horizontalSpan = 3; + fNonStopCheckBox.setLayoutData(gd); + gd = new GridData(); + gd.horizontalSpan = 3; + fVerboseModeButton.setLayoutData(gd); + + // Grayed out until bug 249227 is resolved + // + fVerboseModeButton.setVisible(false); + // + + } + + public void createSolibTab(TabFolder tabFolder) { + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(LaunchUIMessages.getString("GDBDebuggerPage.10")); //$NON-NLS-1$ + Composite comp = ControlFactory.createCompositeEx(fTabFolder, 1, GridData.FILL_BOTH); + comp.setFont(tabFolder.getFont()); + tabItem.setControl(comp); + fSolibBlock = createSolibBlock(comp); + if (fSolibBlock instanceof Observable) + ((Observable)fSolibBlock).addObserver(this); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose() + */ + @Override + public void dispose() { + if (fSolibBlock != null) { + if (fSolibBlock instanceof Observable) + ((Observable)fSolibBlock).deleteObserver(this); + fSolibBlock.dispose(); + } + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#activated(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + @Override + public void activated(ILaunchConfigurationWorkingCopy workingCopy) { + // Override the default behavior + } + + protected boolean isInitializing() { + return fIsInitializing; + } + + private void setInitializing(boolean isInitializing) { + fIsInitializing = isInitializing; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbLocalRunLaunchConfigurationTabGroup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbLocalRunLaunchConfigurationTabGroup.java new file mode 100644 index 00000000000..cfeb8ae04bc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbLocalRunLaunchConfigurationTabGroup.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +public class GdbLocalRunLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup { + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTabGroup#createTabs(org.eclipse.debug.ui.ILaunchConfigurationDialog, java.lang.String) + */ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { + new CMainTab(), + new CArgumentsTab(), + new CDebuggerTab(SessionType.LOCAL, false), + new SourceLookupTab(), +// new EnvironmentTab(), + new CommonTab() + }; + setTabs(tabs); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbRemoteRunLaunchConfigurationTabGroup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbRemoteRunLaunchConfigurationTabGroup.java new file mode 100644 index 00000000000..25f35df62be --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbRemoteRunLaunchConfigurationTabGroup.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.debug.ui.sourcelookup.SourceLookupTab; + +public class GdbRemoteRunLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup { + + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchConfigurationTabGroup#createTabs(org.eclipse.debug.ui.ILaunchConfigurationDialog, java.lang.String) + */ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { + new CMainTab(), + new CDebuggerTab(SessionType.REMOTE, false), + new SourceLookupTab(), + new CommonTab() + }; + setTabs(tabs); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbServerDebuggerPage.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbServerDebuggerPage.java new file mode 100644 index 00000000000..420fc982048 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/GdbServerDebuggerPage.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * IBM Corporation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.debug.internal.ui.dialogfields.ComboDialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.DialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; + +/** + * The dynamic debugger tab for remote launches using gdb server. + */ +public class GdbServerDebuggerPage extends GdbDebuggerPage { + + private final static String CONNECTION_TCP = LaunchUIMessages.getString("GDBServerDebuggerPage.0"); //$NON-NLS-1$ + + private final static String CONNECTION_SERIAL = LaunchUIMessages.getString("GDBServerDebuggerPage.1"); //$NON-NLS-1$ + + private ComboDialogField fConnectionField; + + private String[] fConnections = new String[]{ CONNECTION_TCP, CONNECTION_SERIAL }; + + private TCPSettingsBlock fTCPBlock; + + private SerialPortSettingsBlock fSerialBlock; + + private Composite fConnectionStack; + + private boolean fIsInitializing = false; + + public GdbServerDebuggerPage() { + super(); + fConnectionField = createConnectionField(); + fTCPBlock = new TCPSettingsBlock(); + fSerialBlock = new SerialPortSettingsBlock(); + fTCPBlock.addObserver(this); + fSerialBlock.addObserver(this); + } + + protected void createConnectionTab(TabFolder tabFolder) { + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(LaunchUIMessages.getString("GDBServerDebuggerPage.10")); //$NON-NLS-1$ + Composite comp1 = ControlFactory.createCompositeEx(tabFolder, 1, GridData.FILL_BOTH); + ((GridLayout)comp1.getLayout()).makeColumnsEqualWidth = false; + comp1.setFont(tabFolder.getFont()); + tabItem.setControl(comp1); + Composite comp = ControlFactory.createCompositeEx(comp1, 2, GridData.FILL_BOTH); + ((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false; + comp.setFont(comp1.getFont()); + fConnectionField.doFillIntoGrid(comp, 2); + ((GridData)fConnectionField.getComboControl(null).getLayoutData()).horizontalAlignment = GridData.BEGINNING; + fConnectionStack = ControlFactory.createCompositeEx(comp, 1, GridData.FILL_BOTH); + StackLayout stackLayout = new StackLayout(); + fConnectionStack.setLayout(stackLayout); + ((GridData)fConnectionStack.getLayoutData()).horizontalSpan = 2; + fTCPBlock.createBlock(fConnectionStack); + fSerialBlock.createBlock(fConnectionStack); + } + + private ComboDialogField createConnectionField() { + ComboDialogField field = new ComboDialogField(SWT.DROP_DOWN | SWT.READ_ONLY); + field.setLabelText(LaunchUIMessages.getString("GDBServerDebuggerPage.9")); //$NON-NLS-1$ + field.setItems(fConnections); + field.setDialogFieldListener(new IDialogFieldListener() { + + public void dialogFieldChanged(DialogField f) { + if (!isInitializing()) + connectionTypeChanged(); + } + }); + return field; + } + + protected void connectionTypeChanged() { + connectionTypeChanged0(); + updateLaunchConfigurationDialog(); + } + + private void connectionTypeChanged0() { + ((StackLayout)fConnectionStack.getLayout()).topControl = null; + int index = fConnectionField.getSelectionIndex(); + if (index >= 0 && index < fConnections.length) { + String[] connTypes = fConnectionField.getItems(); + if (CONNECTION_TCP.equals(connTypes[index])) + ((StackLayout)fConnectionStack.getLayout()).topControl = fTCPBlock.getControl(); + else if (CONNECTION_SERIAL.equals(connTypes[index])) + ((StackLayout)fConnectionStack.getLayout()).topControl = fSerialBlock.getControl(); + } + fConnectionStack.layout(); + } + + @Override + public boolean isValid(ILaunchConfiguration launchConfig) { + if (super.isValid(launchConfig)) { + setErrorMessage(null); + setMessage(null); + int index = fConnectionField.getSelectionIndex(); + if (index >= 0 && index < fConnections.length) { + String[] connTypes = fConnectionField.getItems(); + if (CONNECTION_TCP.equals(connTypes[index])) { + if (!fTCPBlock.isValid(launchConfig)) { + setErrorMessage(fTCPBlock.getErrorMessage()); + return false; + } + } + else if (CONNECTION_SERIAL.equals(connTypes[index])) { + if (!fSerialBlock.isValid(launchConfig)) { + setErrorMessage(fSerialBlock.getErrorMessage()); + return false; + } + } + return true; + } + } + return false; + } + + @Override + public void initializeFrom(ILaunchConfiguration configuration) { + setInitializing(true); + super.initializeFrom(configuration); + boolean isTcp = false; + try { + isTcp = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); + } + catch(CoreException e) { + } + fTCPBlock.initializeFrom(configuration); + fSerialBlock.initializeFrom(configuration); + fConnectionField.selectItem((isTcp) ? 0 : 1); + connectionTypeChanged0(); + setInitializing(false); + } + + @Override + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + super.performApply(configuration); + if (fConnectionField != null) + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, fConnectionField.getSelectionIndex() == 0); + fTCPBlock.performApply(configuration); + fSerialBlock.performApply(configuration); + } + + @Override + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + super.setDefaults(configuration); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, false); + fTCPBlock.setDefaults(configuration); + fSerialBlock.setDefaults(configuration); + } + + @Override + protected boolean isInitializing() { + return fIsInitializing; + } + + private void setInitializing(boolean isInitializing) { + fIsInitializing = isInitializing; + } + + @Override + public void createTabs(TabFolder tabFolder) { + super.createTabs(tabFolder); + createConnectionTab(tabFolder); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ICDTLaunchHelpContextIds.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ICDTLaunchHelpContextIds.java new file mode 100644 index 00000000000..8ffc588f17f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ICDTLaunchHelpContextIds.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; + +public interface ICDTLaunchHelpContextIds { + + public static final String PREFIX = GdbUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$ + + // Launch configuration dialog pages + public static final String LAUNCH_CONFIGURATION_DIALOG_MAIN_TAB = PREFIX + "launch_configuration_dialog_main_tab"; //$NON-NLS-1$ + public static final String LAUNCH_CONFIGURATION_DIALOG_ARGUMNETS_TAB = PREFIX + "launch_configuration_dialog_arguments_tab"; //$NON-NLS-1$ + public static final String LAUNCH_CONFIGURATION_DIALOG_ENVIRONMENT_TAB = PREFIX + "launch_configuration_dialog_environment_tab"; //$NON-NLS-1$ + public static final String LAUNCH_CONFIGURATION_DIALOG_DEBBUGER_TAB = PREFIX + "launch_configuration_dialog_debugger_tab"; //$NON-NLS-1$ + public static final String LAUNCH_CONFIGURATION_DIALOG_SOURCELOOKUP_TAB = PREFIX + "launch_configuration_dialog_source_tab"; //$NON-NLS-1$ + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/IMILaunchConfigurationComponent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/IMILaunchConfigurationComponent.java new file mode 100644 index 00000000000..3f7d8df058d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/IMILaunchConfigurationComponent.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +/** + * The common interface for UI components of the launch configuration tabs. + */ +public interface IMILaunchConfigurationComponent { + + /** + * Creates the top level control for this component under the given parent composite. + *

+ * Implementors are responsible for ensuring that the created control can be accessed via getControl + *

+ * + * @param parent the parent composite + */ + public void createControl( Composite parent ); + + /** + * Returns the top level control for this component. + *

+ * May return null if the control has not been created yet. + *

+ * + * @return the top level control or null + */ + public Control getControl(); + + /** + * Initializes the given component with default values. + * This method may be called before this tab's control is created. + * + * @param configuration launch configuration + */ + public void setDefaults( ILaunchConfigurationWorkingCopy configuration ); + + /** + * Initializes this component's controls with values from the given + * launch configuration. + * + * @param configuration launch configuration + */ + public void initializeFrom( ILaunchConfiguration configuration ); + + /** + * Notifies this component that it has been disposed. + * Marks the end of this component's lifecycle, allowing + * to perform any cleanup required. + */ + public void dispose(); + + /** + * Copies values from this component into the given launch configuration. + * + * @param configuration launch configuration + */ + public void performApply( ILaunchConfigurationWorkingCopy configuration ); + + /** + * Returns whether this component is in a valid state in the context + * of the specified launch configuration. + * + * @param launchConfig launch configuration which provides context + * for validating this component. + * This value must not be null. + * + * @return whether this component is in a valid state + */ + public boolean isValid(ILaunchConfiguration launchConfig); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchImages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchImages.java new file mode 100644 index 00000000000..453a4db3a1f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchImages.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Image; + +public class LaunchImages { + private static final String NAME_PREFIX= GdbUIPlugin.PLUGIN_ID + '.'; + private static final int NAME_PREFIX_LENGTH= NAME_PREFIX.length(); + + // The plugin registry + private static ImageRegistry imageRegistry = new ImageRegistry(); + + // Subdirectory (under the package containing this class) where 16 color images are + private static URL fgIconBaseURL; + static { + fgIconBaseURL= Platform.getBundle(GdbUIPlugin.PLUGIN_ID).getEntry("/icons/"); //$NON-NLS-1$ + } + + private static final String T_TABS = "full/view16/"; //$NON-NLS-1$ + private static final String T_OBJS = "full/obj16/"; //$NON-NLS-1$ + + public static String IMG_VIEW_MAIN_TAB = NAME_PREFIX + "main_tab.gif"; //$NON-NLS-1$ + public static String IMG_VIEW_ARGUMENTS_TAB = NAME_PREFIX + "arguments_tab.gif"; //$NON-NLS-1$ + public static String IMG_VIEW_DEBUGGER_TAB = NAME_PREFIX + "debugger_tab.gif"; //$NON-NLS-1$ + + public static final ImageDescriptor DESC_TAB_MAIN= createManaged(T_TABS, IMG_VIEW_MAIN_TAB); + public static final ImageDescriptor DESC_TAB_ARGUMENTS = createManaged(T_TABS, IMG_VIEW_ARGUMENTS_TAB); + public static final ImageDescriptor DESC_TAB_DEBUGGER = createManaged(T_TABS, IMG_VIEW_DEBUGGER_TAB); + + public static String IMG_OBJS_EXEC= NAME_PREFIX + "exec_obj.gif"; //$NON-NLS-1$ + public static final ImageDescriptor DESC_OBJS_EXEC = createManaged(T_OBJS, IMG_OBJS_EXEC); + + public static void initialize() { + } + + private static ImageDescriptor createManaged(String prefix, String name) { + return createManaged(imageRegistry, prefix, name); + } + + private static ImageDescriptor createManaged(ImageRegistry registry, String prefix, String name) { + ImageDescriptor result= ImageDescriptor.createFromURL(makeIconFileURL(prefix, name.substring(NAME_PREFIX_LENGTH))); + registry.put(name, result); + return result; + } + + public static Image get(String key) { + return imageRegistry.get(key); + } + + + private static URL makeIconFileURL(String prefix, String name) { + StringBuffer buffer= new StringBuffer(prefix); + buffer.append(name); + try { + return new URL(fgIconBaseURL, buffer.toString()); + } catch (MalformedURLException e) { + GdbUIPlugin.log(e); + return null; + } + } + + /** + * Helper method to access the image registry from the JavaPlugin class. + */ + static ImageRegistry getImageRegistry() { + return imageRegistry; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.java new file mode 100644 index 00000000000..1b428fbd62f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class LaunchUIMessages { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.gdb.internal.ui.launching.LaunchUIMessages";//$NON-NLS-1$ + + private static ResourceBundle RESOURCE_BUNDLE = null; + + static { + try { + RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + } + catch (MissingResourceException x) { + } + } + + private LaunchUIMessages() {} + + public static String getFormattedString(String key, String arg) { + return MessageFormat.format(getString(key), (Object[])new String[]{arg}); + } + + public static String getFormattedString(String key, String[] args) { + return MessageFormat.format(getString(key), (Object[])args); + } + + public static String getString(String key) { + if (RESOURCE_BUNDLE == null) return '!' + key + '!'; + return RESOURCE_BUNDLE.getString(key); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties new file mode 100644 index 00000000000..d317535bfd3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/LaunchUIMessages.properties @@ -0,0 +1,225 @@ +############################################################################### +# Copyright (c) 2003, 2008 QNX Software Systems 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: +# QNX Software Systems - initial API and implementation +# Ericsson - Updated for DSF +############################################################################### + +CygwinDebuggerPage.0=Cygwin GDB Debugger Options +GDBDebuggerPage.0=Debugger executable must be specified. +GDBDebuggerPage.1=GDB Debugger Options +GDBDebuggerPage.2=Main +GDBDebuggerPage.3=GDB debugger: +GDBDebuggerPage.4=&Browse... +GDBDebuggerPage.5=GDB Debugger +GDBDebuggerPage.6=GDB command file: +GDBDebuggerPage.7=B&rowse... +GDBDebuggerPage.8=GDB Command File +GDBDebuggerPage.9=(Warning: Some commands in this file may interfere with the startup operation of the debugger, for example "run".) +GDBDebuggerPage.10=Shared Libraries +GDBDebuggerPage.11=Protocol: +GDBDebuggerPage.12=Default +GDBDebuggerPage.13=Non-stop mode (Note: Requires non-stop GDB) +StandardGDBDebuggerPage.0=Debugger executable must be specified. +StandardGDBDebuggerPage.1=GDB Debugger Options +StandardGDBDebuggerPage.2=Main +StandardGDBDebuggerPage.3=GDB debugger: +StandardGDBDebuggerPage.4=&Browse... +StandardGDBDebuggerPage.5=GDB Debugger +StandardGDBDebuggerPage.6=GDB command file: +StandardGDBDebuggerPage.7=B&rowse... +StandardGDBDebuggerPage.8=GDB Command File +StandardGDBDebuggerPage.9=(Warning: Some commands in this file may interfere with the startup operation of the debugger, for example "run".) +StandardGDBDebuggerPage.10=Shared Libraries +StandardGDBDebuggerPage.11=Protocol: +StandardGDBDebuggerPage.12=GDB command set: +StandardGDBDebuggerPage.13=Verbose console mode +StandardGDBDebuggerPage.14=Use full file path to set breakpoints +GDBServerDebuggerPage.0=TCP +GDBServerDebuggerPage.1=Serial +GDBServerDebuggerPage.10=Connection +GDBServerDebuggerPage.2=Main +GDBServerDebuggerPage.3=GDB debugger +GDBServerDebuggerPage.4=&Browse... +GDBServerDebuggerPage.5=GDB Debugger +GDBServerDebuggerPage.6=GDB command file: +GDBServerDebuggerPage.7=B&rowse... +GDBServerDebuggerPage.8=GDB Command File +GDBServerDebuggerPage.9=Type: +GDBSolibBlock.0=Load shared library symbols automatically +GDBSolibBlock.1=Stop on shared library events +GDBSolibBlock.2=Use shared library symbols for debugged applications +SerialPortSettingsBlock.0=Device: +SerialPortSettingsBlock.1=Speed: +SerialPortSettingsBlock.2=Device must be specified. +SerialPortSettingsBlock.3=Invalid device. +SerialPortSettingsBlock.4=Speed must be specified. +SolibSearchPathBlock.0=Add... +SolibSearchPathBlock.1=Up +SolibSearchPathBlock.2=Down +SolibSearchPathBlock.3=Remove +SolibSearchPathBlock.4=Directories: +SolibSearchPathBlock.5=Select directory that contains shared library. +SolibSearchPathBlock.6=Select From List +SolibSearchPathBlock.7=Select Libraries +SolibSearchPathBlock.8=Select libraries to load symbols automatically. +SolibSearchPathBlock.9=No libraries found. +SolibSearchPathBlock.Add_Directory=Add Directory +TCPSettingsBlock.0=Host name or IP address: +TCPSettingsBlock.1=Port number: +TCPSettingsBlock.2=Host name or IP address must be specified. +TCPSettingsBlock.3=Invalid host name or IP address. +TCPSettingsBlock.4=Port number must be specified. +TCPSettingsBlock.5=Invalid port number. + +AbstractCLaunchDelegate.Debugger_not_installed=CDT Debugger not installed +AbstractCLaunchDelegate.C_Project_not_specified=C Project not specified +AbstractCLaunchDelegate.Not_a_C_CPP_project=Project is not a C/C++ project +AbstractCLaunchDelegate.Program_file_not_specified=Program file not specified +AbstractCLaunchDelegate.Program_file_does_not_exist=Program file does not exist +AbstractCLaunchDelegate.PROGRAM_PATH_not_found={0} not found +AbstractCLaunchDelegate.Working_directory_does_not_exist=Working directory does not exist +AbstractCLaunchDelegate.WORKINGDIRECTORY_PATH_not_found=The working directory {0} does not exist. +AbstractCLaunchDelegate.Project_NAME_does_not_exist=Project {0} does not exist. Please check that your launch configuration specifies a valid project in your workspace. +AbstractCLaunchDelegate.Project_NAME_is_closed=Project {0} is closed +AbstractCLaunchDelegate.Debugger_Process=Debugger Process +AbstractCLaunchDelegate.building_projects=Building prerequisite project list +AbstractCLaunchDelegate.building=Building +AbstractCLaunchDelegate.searching_for_errors=Searching for compile errors +AbstractCLaunchDelegate.searching_for_errors_in=Searching for compile errors in +AbstractCLaunchDelegate.20=Building prerequisite project list +AbstractCLaunchDelegate.Program_is_not_a_recongnized_executable=Program is not a recognized executable. + +LocalRunLaunchDelegate.Launching_Local_C_Application=Launching Local C/C++ Application +LocalRunLaunchDelegate.Failed_setting_runtime_option_though_debugger=Failed to set program arguments, environment or working directory. +LocalRunLaunchDelegate.Error_starting_process=Error starting process +LocalRunLaunchDelegate.Does_not_support_working_dir=Eclipse runtime does not support working directory + +LocalAttachLaunchDelegate.Attaching_to_Local_C_Application=Attaching to Local C/C++ Application +LocalAttachLaunchDelegate.No_Process_ID_selected=No Process ID selected +LocalAttachLaunchDelegate.Select_Process=Select Process +LocalAttachLaunchDelegate.Platform_cannot_list_processes=Current platform does not support listing processes +LocalAttachLaunchDelegate.Select_Process_to_attach_debugger_to=Select a Process to attach debugger to: +LocalAttachLaunchDelegate.CDT_Launch_Error=CDT Launch Error + +CoreFileLaunchDelegate.Launching_postmortem_debugger=Launching postmortem debugger +CoreFileLaunchDelegate.No_Corefile_selected=No Corefile selected +CoreFileLaunchDelegate.No_Shell_available_in_Launch=No Shell available in Launch +CoreFileLaunchDelegate.Select_Corefile=Select Corefile +CoreFileLaunchDelegate.Corefile_not_accessible=Core file is not accessible. +CoreFileLaunchDelegate.Corefile_not_readable=Core file does not exist or is not readable. +CoreFileLaunchDelegate.postmortem_debugging_failed=Post-mortem debugging failed + +CApplicationLaunchShortcut.Application_Launcher=Application Launcher +CApplicationLaunchShortcut.ChooseConfigToDebug=Choose a debug configuration to debug +CApplicationLaunchShortcut.ChooseConfigToRun=Choose a configuration to run +CApplicationLaunchShortcut.CLocalApplication=C Local Application +CApplicationLaunchShortcut.ChooseLocalAppToDebug=Choose a local application to debug +CApplicationLaunchShortcut.ChooseLocalAppToRun=Choose a local application to run +CApplicationLaunchShortcut.Launch_failed_no_binaries=Launch failed. Binary not found. +CApplicationLaunchShortcut.LaunchFailed=Launch failed +CApplicationLaunchShortcut.LaunchDebugConfigSelection=Launch Debug Configuration Selection +CApplicationLaunchShortcut.LaunchConfigSelection=Launch Configuration Selection +CApplicationLaunchShortcut.Invalid_launch_mode_1=Invalid launch mode +CApplicationLaunchShortcut.Invalid_launch_mode_2=Invalid launch mode. +CApplicationLaunchShortcut.Invalid_launch_mode_3=Invalid launch mode. +CApplicationLaunchShortcut.ChooseLaunchConfigToDebug=Choose a launch configuration to debug +CApplicationLaunchShortcut.ChooseLaunchConfigToRun=Choose a launch configuration to run +CApplicationLaunchShortcut.Launch_failed_no_project_selected=Launch failed no project selected + +AbstractCDebuggerTab.No_debugger_available=No debugger available +AbstractCDebuggerTab.Debugger=Debugger +AbstractCDebuggerTab.ErrorLoadingDebuggerPage=Error Loading Debugger UI Component. + +LaunchUIPlugin.Error=Error + +CMainTab.Project_required=Project required +CMainTab.Enter_project_before_searching_for_program=Project must first be entered before searching for a program +CMainTab.Program_Selection=Program Selection +CMainTab.Enter_project_before_browsing_for_program=Project must first be entered before browsing for a program +CMainTab.Program_selection=Program selection +CMainTab.Selection_must_be_file=Selection must be a file +CMainTab.Selection_must_be_binary_file=Selection must be a binary file +CMainTab.Project_Selection=Project Selection +CMainTab.Choose_project_to_constrain_search_for_program=Choose a &project to constrain the search for a program +CMainTab.Project_not_specified=Project not specified +CMainTab.Program_not_specified=Program not specified +CMainTab.Project_must_be_opened=Project must be opened +CMainTab.Program_does_not_exist=Program does not exist +CMainTab.Main=Main +CMainTab.&ProjectColon=&Project: +CMainTab.C/C++_Application=C/C++ Application: +CMainTab.Search...=Searc&h Project... +CMainTab.Choose_program_to_run=Choose a &program to run: +CMainTab.Choose_program_to_run_from_NAME=Choose a program to run from {0}: +CMainTab.UseTerminal=Connect process input & output to a terminal. +CMainTab.Program_is_not_a_recongnized_executable=Program is not a recognized executable. + +CDebuggerTab.Advanced_Options_Dialog_Title=Advanced Options +CDebuggerTab.Stop_at_main_on_startup=Stop on startup at: +CDebuggerTab.Automatically_track_values_of=Automatically track the values of +CDebuggerTab.Stop_on_startup_at_can_not_be_empty=The "Stop on startup at" field can not be empty. +CDebuggerTab.Debugger_Options=Debugger Options +CDebuggerTab.Mode_not_supported=Mode ''{0}'' is not supported by the selected debugger +CDebuggerTab.Advanced=Advanced... +CDebuggerTab.Variables=Variables +CDebuggerTab.Registers=Registers +CDebuggerTab.No_debugger_available=No debugger available +CDebuggerTab.CPU_is_not_supported=The CPU is not supported by the selected debugger. +CDebuggerTab.Platform_is_not_supported=The project platform is not supported by the selected debugger. + +CoreFileDebuggerTab.No_debugger_available=No debugger available +CoreFileDebuggerTab.platform_is_not_supported=The project platform is not supported by the selected debugger. + +CEnvironmentTab.Edit_Variable=Edit Variable +CEnvironmentTab.New_Variable=New Variable +CEnvironmentTab.NameColon=Name: +CEnvironmentTab.ValueColon=Value: +CEnvironmentTab.Name=Name +CEnvironmentTab.Value=Value +CEnvironmentTab.New...=New... +CEnvironmentTab.Import...=Import... +CEnvironmentTab.Edit...=Edit... +CEnvironmentTab.Remove=Remove +CEnvironmentTab.Environment=Environment +CEnvironmentTab.Existing_Environment_Variable=Existing Environment Variable +CEnvironmentTab.Environment_variable_NAME_exists=Environment variable \" {0} \" exists.\nDo you want to overwrite? + +CArgumentsTab.C/C++_Program_Arguments=Program arguments: +CArgumentsTab.Arguments=Arguments +CArgumentsTab.Variables=Variables... + +WorkingDirectoryBlock.4=Select a &workspace relative working directory: +WorkingDirectoryBlock.7=Select a working directory for the launch configuration: +WorkingDirectoryBlock.0=W&orkspace... +WorkingDirectoryBlock.Working_Directory_8=Working Directory +WorkingDirectoryBlock.Working_directory=Working directory: +WorkingDirectoryBlock.10=Working directory does not exist +WorkingDirectoryBlock.Use_default=Use de&fault +WorkingDirectoryBlock.17=Variabl&es... +WorkingDirectoryBlock.1=File S&ystem... +WorkingDirectoryBlock.Exception_occurred_reading_configuration___15=Exception occurred reading configuration: + +Launch.common.Exception_occurred_reading_configuration_EXCEPTION=Exception occurred reading configuration {0} +Launch.common.DebuggerColon=Debugger: +Launch.common.BinariesColon=Binaries: +Launch.common.QualifierColon=Qualifier: +Launch.common.Browse_1=&Browse... +Launch.common.Browse_2=B&rowse... +Launch.common.Project_does_not_exist=Project does not exist +LocalCDILaunchDelegate.0=Launching Local C/C++ Application +LocalCDILaunchDelegate.1=Launching debugger session +LocalCDILaunchDelegate.2=Debugging local C/C++ application +LocalCDILaunchDelegate.3=Attaching to Local C/C++ Application +LocalCDILaunchDelegate.4=No Process ID selected. +LocalCDILaunchDelegate.5=Launching postmortem debugger session +LocalCDILaunchDelegate.6=No core file selected +LocalCDILaunchDelegate.7=Core file does not exist or is not readable. +LocalCDILaunchDelegate.8=Error starting process. +LocalCDILaunchDelegate.9=Eclipse runtime does not support working directory. +LocalCDILaunchDelegate.10=Failed to set program arguments, environment or working directory. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java new file mode 100644 index 00000000000..7223ac28520 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/ProcessPrompter.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import org.eclipse.cdt.core.IProcessInfo; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.IStatusHandler; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.TwoPaneElementSelector; + +public class ProcessPrompter implements IStatusHandler { + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.IStatusHandler#handleStatus(org.eclipse.core.runtime.IStatus, + * java.lang.Object) + */ + public Object handleStatus(IStatus status, Object processList) throws CoreException { + Shell shell = GdbUIPlugin.getShell(); + if (shell == null) { + IStatus error = new Status(IStatus.ERROR, GdbUIPlugin.getUniqueIdentifier(), + ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR, + LaunchMessages.getString("CoreFileLaunchDelegate.No_Shell_available_in_Launch"), null); //$NON-NLS-1$ + throw new CoreException(error); + } + + IProcessInfo[] plist = (IProcessInfo[])processList; + if (plist == null) { + MessageDialog.openError( + shell, + LaunchMessages.getString("LocalAttachLaunchDelegate.CDT_Launch_Error"), LaunchMessages.getString("LocalAttachLaunchDelegate.Platform_cannot_list_processes")); //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + + if (plist.length == 0) { + // No list available, just let the user put in a pid directly + InputDialog dialog = new InputDialog(shell, + LaunchMessages.getString("LocalAttachLaunchDelegate.Select_Process"), //$NON-NLS-1$ + LaunchMessages.getString("LocalAttachLaunchDelegate.Select_Process_to_attach_debugger_to"), //$NON-NLS-1$ + null, null); + + if (dialog.open() == Window.OK) { + String pidStr = dialog.getValue(); + try { + return Integer.parseInt(pidStr); + } catch (NumberFormatException e) { + } + } + } else { + ILabelProvider provider = new LabelProvider() { + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object) + */ + @Override + public String getText(Object element) { + IProcessInfo info = (IProcessInfo)element; + IPath path = new Path(info.getName()); + return path.lastSegment() + " - " + info.getPid(); //$NON-NLS-1$ + } + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object) + */ + @Override + public Image getImage(Object element) { + return LaunchImages.get(LaunchImages.IMG_OBJS_EXEC); + } + }; + ILabelProvider qprovider = new LabelProvider() { + @Override + public String getText(Object element) { + IProcessInfo info = (IProcessInfo)element; + return info.getName(); + } + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object) + */ + @Override + public Image getImage(Object element) { + return LaunchImages.get(LaunchImages.IMG_OBJS_EXEC); + } + }; + + // Display the list of processes and have the user choose + TwoPaneElementSelector dialog = new TwoPaneElementSelector(shell, provider, qprovider); + dialog.setTitle(LaunchMessages.getString("LocalAttachLaunchDelegate.Select_Process")); //$NON-NLS-1$ + dialog.setMessage(LaunchMessages.getString("LocalAttachLaunchDelegate.Select_Process_to_attach_debugger_to")); //$NON-NLS-1$ + + dialog.setElements(plist); + if (dialog.open() == Window.OK) { + IProcessInfo info = (IProcessInfo)dialog.getFirstResult(); + if (info != null) { + return new Integer(info.getPid()); + } + } + } + + return null; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SerialPortSettingsBlock.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SerialPortSettingsBlock.java new file mode 100644 index 00000000000..4f537d12a5d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SerialPortSettingsBlock.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.util.Observable; + +import org.eclipse.cdt.debug.internal.ui.PixelConverter; +import org.eclipse.cdt.debug.internal.ui.dialogfields.ComboDialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.DialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.debug.internal.ui.dialogfields.LayoutUtil; +import org.eclipse.cdt.debug.internal.ui.dialogfields.StringDialogField; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +public class SerialPortSettingsBlock extends Observable { + + private final static String DEFAULT_ASYNC_DEVICE = "/dev/ttyS0"; //$NON-NLS-1$ + + private final static String DEFAULT_ASYNC_DEVICE_SPEED = "115200"; //$NON-NLS-1$ + + private Shell fShell; + + private StringDialogField fDeviceField; + + private ComboDialogField fSpeedField; + + private String fSpeedChoices[] = { "9600", "19200", "38400", "57600", "115200" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + + private Control fControl; + + private String fErrorMessage = null; + + public SerialPortSettingsBlock() { + super(); + fDeviceField = createDeviceField(); + fSpeedField = createSpeedField(); + } + + public void createBlock(Composite parent) { + fShell = parent.getShell(); + Composite comp = ControlFactory.createCompositeEx(parent, 2, GridData.FILL_BOTH); + ((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false; + ((GridLayout)comp.getLayout()).marginHeight = 0; + ((GridLayout)comp.getLayout()).marginWidth = 0; + comp.setFont(parent.getFont()); + PixelConverter converter = new PixelConverter(comp); + fDeviceField.doFillIntoGrid(comp, 2); + LayoutUtil.setWidthHint(fDeviceField.getTextControl(null), converter.convertWidthInCharsToPixels(20)); + fSpeedField.doFillIntoGrid(comp, 2); + ((GridData)fSpeedField.getComboControl(null).getLayoutData()).horizontalAlignment = GridData.BEGINNING; + setControl(comp); + } + + protected Shell getShell() { + return fShell; + } + + public void dispose() { + deleteObservers(); + } + + public void initializeFrom(ILaunchConfiguration configuration) { + initializeDevice(configuration); + initializeSpeed(configuration); + } + + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV, DEFAULT_ASYNC_DEVICE); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV_SPEED, DEFAULT_ASYNC_DEVICE_SPEED); + } + + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + if (fDeviceField != null) + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV, fDeviceField.getText().trim()); + if (fSpeedField != null) { + int index = fSpeedField.getSelectionIndex(); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV_SPEED, getSpeedItem(index)); + } + } + + private StringDialogField createDeviceField() { + StringDialogField field = new StringDialogField(); + field.setLabelText(LaunchUIMessages.getString("SerialPortSettingsBlock.0")); //$NON-NLS-1$ + field.setDialogFieldListener(new IDialogFieldListener() { + + public void dialogFieldChanged(DialogField f) { + deviceFieldChanged(); + } + }); + return field; + } + + private ComboDialogField createSpeedField() { + ComboDialogField field = new ComboDialogField(SWT.DROP_DOWN | SWT.READ_ONLY); + field.setLabelText(LaunchUIMessages.getString("SerialPortSettingsBlock.1")); //$NON-NLS-1$ + field.setItems(fSpeedChoices); + field.setDialogFieldListener(new IDialogFieldListener() { + + public void dialogFieldChanged(DialogField f) { + speedFieldChanged(); + } + }); + return field; + } + + protected void deviceFieldChanged() { + updateErrorMessage(); + setChanged(); + notifyObservers(); + } + + protected void speedFieldChanged() { + updateErrorMessage(); + setChanged(); + notifyObservers(); + } + + private void initializeDevice(ILaunchConfiguration configuration) { + if (fDeviceField != null) { + try { + fDeviceField.setText(configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV, DEFAULT_ASYNC_DEVICE)); + } + catch(CoreException e) { + } + } + } + + private void initializeSpeed(ILaunchConfiguration configuration) { + if (fSpeedField != null) { + int index = 0; + try { + index = getSpeedItemIndex(configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEV_SPEED, DEFAULT_ASYNC_DEVICE_SPEED)); + } + catch(CoreException e) { + } + fSpeedField.selectItem(index); + } + } + + private String getSpeedItem(int index) { + return (index >= 0 && index < fSpeedChoices.length) ? fSpeedChoices[index] : null; + } + + private int getSpeedItemIndex(String item) { + for(int i = 0; i < fSpeedChoices.length; ++i) + if (fSpeedChoices[i].equals(item)) + return i; + return 0; + } + + public Control getControl() { + return fControl; + } + + protected void setControl(Control control) { + fControl = control; + } + + public boolean isValid(ILaunchConfiguration configuration) { + updateErrorMessage(); + return (getErrorMessage() == null); + } + + private void updateErrorMessage() { + setErrorMessage(null); + if (fDeviceField != null && fSpeedField != null) { + if (fDeviceField.getText().trim().length() == 0) + setErrorMessage(LaunchUIMessages.getString("SerialPortSettingsBlock.2")); //$NON-NLS-1$ + else if (!deviceIsValid(fDeviceField.getText().trim())) + setErrorMessage(LaunchUIMessages.getString("SerialPortSettingsBlock.3")); //$NON-NLS-1$ + else if (fSpeedField.getSelectionIndex() < 0) + setErrorMessage(LaunchUIMessages.getString("SerialPortSettingsBlock.4")); //$NON-NLS-1$ + } + } + + public String getErrorMessage() { + return fErrorMessage; + } + + private void setErrorMessage(String string) { + fErrorMessage = string; + } + + private boolean deviceIsValid(String hostName) { + return true; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SolibSearchPathBlock.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SolibSearchPathBlock.java new file mode 100644 index 00000000000..6faa7b7dfc2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/SolibSearchPathBlock.java @@ -0,0 +1,620 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * IBM Corporation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Observable; +import java.util.Set; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.ICExtensionReference; +import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; +import org.eclipse.cdt.core.IBinaryParser.IBinaryShared; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.debug.internal.ui.PixelConverter; +import org.eclipse.cdt.debug.internal.ui.dialogfields.DialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.debug.internal.ui.dialogfields.IListAdapter; +import org.eclipse.cdt.debug.internal.ui.dialogfields.LayoutUtil; +import org.eclipse.cdt.debug.internal.ui.dialogfields.ListDialogField; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableContext; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerSorter; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog; + +/** + * The UI component to access the shared libraries search path. + */ +public class SolibSearchPathBlock extends Observable implements IMILaunchConfigurationComponent, IDialogFieldListener { + + class AddDirectoryDialog extends Dialog { + + protected Text fText; + + private Button fBrowseButton; + + private IPath fValue; + + /** + * Constructor for AddDirectoryDialog. + */ + public AddDirectoryDialog(Shell parentShell) { + super(parentShell); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite)super.createDialogArea(parent); + + Composite subComp = ControlFactory.createCompositeEx(composite, 2, GridData.FILL_HORIZONTAL); + ((GridLayout)subComp.getLayout()).makeColumnsEqualWidth = false; + GridData data = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); + data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH); + subComp.setLayoutData(data); + subComp.setFont(parent.getFont()); + + fText = new Text(subComp, SWT.SINGLE | SWT.BORDER); + fText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + fText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent e) { + updateOKButton(); + } + }); + + fBrowseButton = ControlFactory.createPushButton(subComp, LaunchUIMessages.getString("GDBServerDebuggerPage.7")); //$NON-NLS-1$ + data = new GridData(); + data.horizontalAlignment = GridData.FILL; + fBrowseButton.setLayoutData(data); + fBrowseButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent evt) { + DirectoryDialog dialog = new DirectoryDialog(AddDirectoryDialog.this.getShell()); + dialog.setMessage(LaunchUIMessages.getString("SolibSearchPathBlock.5")); //$NON-NLS-1$ + String res = dialog.open(); + if (res != null) { + fText.setText(res); + } + } + }); + + applyDialogFont(composite); + return composite; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(LaunchUIMessages.getString("SolibSearchPathBlock.Add_Directory")); //$NON-NLS-1$ + } + + public IPath getValue() { + return fValue; + } + + private void setValue(String value) { + fValue = (value != null) ? new Path(value) : null; + } + + @Override + protected void buttonPressed(int buttonId) { + if (buttonId == IDialogConstants.OK_ID) { + setValue(fText.getText()); + } + else { + setValue(null); + } + super.buttonPressed(buttonId); + } + + protected void updateOKButton() { + Button okButton = getButton(IDialogConstants.OK_ID); + String text = fText.getText(); + okButton.setEnabled(isValid(text)); + } + + protected boolean isValid(String text) { + return (text.trim().length() > 0); + } + + @Override + protected Control createButtonBar(Composite parent) { + Control control = super.createButtonBar(parent); + updateOKButton(); + return control; + } + } + + private Composite fControl; + + public class SolibSearchPathListDialogField extends ListDialogField { + + public SolibSearchPathListDialogField(IListAdapter adapter, String[] buttonLabels, ILabelProvider lprovider) { + super(adapter, buttonLabels, lprovider); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.internal.ui.dialogfields.ListDialogField#managedButtonPressed(int) + */ + @Override + protected boolean managedButtonPressed(int index) { + boolean result = super.managedButtonPressed(index); + if (result) + buttonPressed(index); + return result; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.dialogfields.ListDialogField#getManagedButtonState(org.eclipse.jface.viewers.ISelection, int) + */ + @Override + protected boolean getManagedButtonState(ISelection sel, int index) { + if (index > 3) + return getButtonState(sel, index); + return super.getManagedButtonState(sel, index); + } + } + + private static String[] fgStaticButtonLabels = new String[] { + LaunchUIMessages.getString("SolibSearchPathBlock.0"), //$NON-NLS-1$ + LaunchUIMessages.getString("SolibSearchPathBlock.1"), //$NON-NLS-1$ + LaunchUIMessages.getString("SolibSearchPathBlock.2"), //$NON-NLS-1$ + LaunchUIMessages.getString("SolibSearchPathBlock.3"), //$NON-NLS-1$ + LaunchUIMessages.getString("SolibSearchPathBlock.6"), //$NON-NLS-1$ + null, // separator + }; + + private IProject fProject; + + private Shell fShell; + + private SolibSearchPathListDialogField fDirList; + + private IListAdapter fCustomListAdapter; + + private File[] fAutoSolibs = new File[0]; + + public SolibSearchPathBlock() { + this(new String[0], null); + } + + public SolibSearchPathBlock(String[] customButtonLabels, IListAdapter customListAdapter) { + super(); + fCustomListAdapter = customListAdapter; + int length = fgStaticButtonLabels.length; + if (customButtonLabels.length > 0) + length += customButtonLabels.length; + String[] buttonLabels = new String[length]; + System.arraycopy(fgStaticButtonLabels, 0, buttonLabels, 0, fgStaticButtonLabels.length); + if (length > fgStaticButtonLabels.length) { + for (int i = fgStaticButtonLabels.length; i < length; ++i) + buttonLabels[i] = customButtonLabels[i - fgStaticButtonLabels.length]; + } + IListAdapter listAdapter = new IListAdapter() { + public void customButtonPressed(DialogField field, int index) { + buttonPressed(index); + } + public void selectionChanged(DialogField field) { + } + }; + ILabelProvider lp = new LabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof IPath) + return ((IPath)element).toOSString(); + return super.getText(element); + } + }; + fDirList = new SolibSearchPathListDialogField(listAdapter, buttonLabels, lp); + fDirList.setLabelText(LaunchUIMessages.getString("SolibSearchPathBlock.4")); //$NON-NLS-1$ + fDirList.setUpButtonIndex(1); + fDirList.setDownButtonIndex(2); + fDirList.setRemoveButtonIndex(3); + + fDirList.setDialogFieldListener(this); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + fShell = parent.getShell(); + Composite comp = ControlFactory.createCompositeEx(parent, 2, GridData.FILL_BOTH); + ((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false; + ((GridLayout)comp.getLayout()).marginHeight = 0; + ((GridLayout)comp.getLayout()).marginWidth = 0; + comp.setFont(parent.getFont()); + PixelConverter converter = new PixelConverter(comp); + fDirList.doFillIntoGrid(comp, 3); + LayoutUtil.setHorizontalSpan(fDirList.getLabelControl(null), 2); + LayoutUtil.setWidthHint(fDirList.getLabelControl(null), converter.convertWidthInCharsToPixels(30)); + LayoutUtil.setHorizontalGrabbing(fDirList.getListControl(null)); + fControl = comp; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration configuration) { + IProject project = null; + try { + String projectName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); + if (projectName != null) { + projectName = projectName.trim(); + if (projectName.length() > 0) { + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + } + } + } + catch(CoreException e) { + } + setProject(project); + + if (fDirList != null) { + try { + @SuppressWarnings("unchecked") + List values = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + Collections.EMPTY_LIST); + ArrayList paths = new ArrayList(values.size()); + Iterator it = values.iterator(); + while(it.hasNext()) { + paths.add(new Path(it.next())); + } + fDirList.addElements(paths); + } + catch(CoreException e) { + } + } + + try { + fAutoSolibs = getAutoSolibs(configuration); + } + catch(CoreException e) { + } + } + + public static File[] getAutoSolibs(ILaunchConfiguration configuration) throws CoreException { + @SuppressWarnings("unchecked") + List autoSolibs = configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB_LIST, Collections.EMPTY_LIST ); + + List list = new ArrayList(autoSolibs.size()); + Iterator it = autoSolibs.iterator(); + while(it.hasNext()) { + list.add(new File(it.next())); + } + return list.toArray(new File[list.size()]); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, Collections.EMPTY_LIST); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + if (fDirList != null) { + + @SuppressWarnings("unchecked") + List elements = fDirList.getElements(); + + ArrayList values = new ArrayList(elements.size()); + Iterator it = elements.iterator(); + while(it.hasNext()) { + values.add((it.next()).toOSString()); + } + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, values); + } + ArrayList autoLibs = new ArrayList(fAutoSolibs.length); + for (int i = 0; i < fAutoSolibs.length; ++i) + autoLibs.add(fAutoSolibs[i].getPath()); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB_LIST, autoLibs); + } + + protected void buttonPressed(int index) { + boolean changed = false; + if (index == 0) { // Add button + changed = addDirectory(); + } + else if (index == 4) { //Select from list + changed = selectFromList(); + } + else if (index >= fgStaticButtonLabels.length && fCustomListAdapter != null) { + fCustomListAdapter.customButtonPressed(fDirList, index); + changed = true; + } + if (changed) { + setChanged(); + notifyObservers(); + } + } + + protected boolean getButtonState(ISelection sel, int index) { + if (index == 4) { // select from list + return (!sel.isEmpty()); + } + return true; + } + + protected Shell getShell() { + return fShell; + } + + private boolean addDirectory() { + boolean changed = false; + AddDirectoryDialog dialog = new AddDirectoryDialog(getShell()); + dialog.open(); + IPath result = dialog.getValue(); + if (result != null && !contains(result)) { + fDirList.addElement(result); + changed = true; + } + return changed; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#dispose() + */ + public void dispose() { + deleteObservers(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#getControl() + */ + public Control getControl() { + return fControl; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.internal.ui.IMILaunchConfigurationComponent#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + public boolean isValid(ILaunchConfiguration launchConfig) { + // TODO Auto-generated method stub + return false; + } + + private boolean contains(IPath path) { + @SuppressWarnings("unchecked") + List list = fDirList.getElements(); + + Iterator it = list.iterator(); + while(it.hasNext()) { + IPath p = it.next(); + if (p.toFile().equals(path.toFile())) + return true; + } + return false; + } + + protected IProject getProject() { + return fProject; + } + + private void setProject(IProject project) { + fProject = project; + } + + protected boolean selectFromList() { + boolean changed = false; + + @SuppressWarnings("unchecked") + List dirList = fDirList.getSelectedElements(); + + final HashSet libs = new HashSet(10); + if (generateLibraryList(dirList.toArray(new IPath[dirList.size()]), libs)) { + ITreeContentProvider cp = new ITreeContentProvider() { + + public Object[] getChildren(Object parentElement) { + return getElements(parentElement); + } + + public Object getParent(Object element) { + if (libs.contains(element)) + return libs; + return null; + } + + public boolean hasChildren(Object element) { + return false; + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof Set) { + return ((Set)inputElement).toArray(); + } + return new Object[0]; + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + }; + + LabelProvider lp = new LabelProvider() { + + @Override + public String getText(Object element) { + if (element instanceof File) + return ((File)element).getName(); + return super.getText(element); + } + }; + CheckedTreeSelectionDialog dialog = new CheckedTreeSelectionDialog(getShell(), lp, cp); + dialog.setTitle(LaunchUIMessages.getString("SolibSearchPathBlock.7")); //$NON-NLS-1$ + dialog.setMessage(LaunchUIMessages.getString("SolibSearchPathBlock.8")); //$NON-NLS-1$ + dialog.setEmptyListMessage(LaunchUIMessages.getString("SolibSearchPathBlock.9")); //$NON-NLS-1$ + dialog.setSorter(new ViewerSorter()); + dialog.setInput(libs); + dialog.setInitialElementSelections(Arrays.asList(fAutoSolibs)); + if (dialog.open() == Window.OK) { + Object[] result = dialog.getResult(); + fAutoSolibs = (File[])Arrays.asList(result).toArray(new File[result.length]); + changed = true; + } + } + return changed; + } + + private boolean generateLibraryList(final IPath[] paths, final Set libs) { + boolean result = true; + + IRunnableWithProgress runnable = new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + + for (int i = 0; i < paths.length; ++i) { + File dir = paths[i].toFile(); + if (dir.exists() && dir.isDirectory()) { + File[] all = dir.listFiles(); + for (int j = 0; j < all.length; ++j) { + if (monitor.isCanceled()) { + throw new InterruptedException(); + } + monitor.subTask(all[j].getPath()); + String libName = getSharedLibraryName(all[j]); + if (libName != null) { + libs.add(new File(libName)); + } + } + } + } + } + }; + try { + IRunnableContext context = new ProgressMonitorDialog(getShell()); + context.run(true, true, runnable); + } + catch(InvocationTargetException e) { + } + catch(InterruptedException e) { + result = false; + } + return result; + } + + protected String getSharedLibraryName(File file) { + if (!file.isFile()) + return null; + IProject project = getProject(); + if (project != null) { + IPath fullPath = new Path(file.getPath()); + try { + ICExtensionReference[] binaryParsersExt = CCorePlugin.getDefault().getBinaryParserExtensions(project); + for(int i = 0; i < binaryParsersExt.length; i++) { + IBinaryParser parser = (IBinaryParser)binaryParsersExt[i].createExtension(); + try { + IBinaryFile bin = parser.getBinary(fullPath); + if (bin instanceof IBinaryShared) { + String soname = ((IBinaryShared)bin).getSoName(); + return (soname.length() != 0) ? soname : file.getName(); + } + } + catch(IOException e) { + } + } + } + catch(CoreException e) { + } + return null; + } + // no project: for now + IPath path = new Path(file.getPath()); + String name = path.lastSegment(); + String extension = path.getFileExtension(); + if (extension != null && (extension.compareTo("so") == 0 || extension.compareToIgnoreCase("dll") == 0)) //$NON-NLS-1$ //$NON-NLS-2$ + return name; + return (name.indexOf(".so.") >= 0) ? name : null; //$NON-NLS-1$ + } + + protected boolean isSharedLibrary(File file) { + if (!file.isFile()) + return false; + IProject project = getProject(); + if (project != null) { + IPath fullPath = new Path(file.getPath()); + try { + ICExtensionReference[] binaryParsersExt = CCorePlugin.getDefault().getBinaryParserExtensions(project); + for(int i = 0; i < binaryParsersExt.length; i++) { + IBinaryParser parser = (IBinaryParser)binaryParsersExt[i].createExtension(); + try { + IBinaryFile bin = parser.getBinary(fullPath); + return (bin instanceof IBinaryShared); + } + catch(IOException e) { + } + } + } + catch(CoreException e) { + } + return false; + } + // no project: for now + IPath path = new Path(file.getPath()); + String extension = path.getFileExtension(); + if (extension != null && (extension.compareTo("so") == 0 || extension.compareToIgnoreCase("dll") == 0)) //$NON-NLS-1$ //$NON-NLS-2$ + return true; + String name = path.lastSegment(); + return (name.indexOf(".so.") >= 0); //$NON-NLS-1$ + } + + public void dialogFieldChanged(DialogField field) { + setChanged(); + notifyObservers(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/TCPSettingsBlock.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/TCPSettingsBlock.java new file mode 100644 index 00000000000..63271985042 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/TCPSettingsBlock.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.util.Observable; + +import org.eclipse.cdt.debug.internal.ui.PixelConverter; +import org.eclipse.cdt.debug.internal.ui.dialogfields.DialogField; +import org.eclipse.cdt.debug.internal.ui.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.debug.internal.ui.dialogfields.LayoutUtil; +import org.eclipse.cdt.debug.internal.ui.dialogfields.StringDialogField; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.utils.ui.controls.ControlFactory; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +public class TCPSettingsBlock extends Observable { + + private final static String DEFAULT_HOST_NAME = "localhost"; //$NON-NLS-1$ + + private final static String DEFAULT_PORT_NUMBER = "10000"; //$NON-NLS-1$ + + private Shell fShell; + + private StringDialogField fHostNameField; + + private StringDialogField fPortNumberField; + + private Control fControl; + + private String fErrorMessage = null; + + public TCPSettingsBlock() { + super(); + fHostNameField = createHostNameField(); + fPortNumberField = createPortNumberField(); + } + + public void createBlock(Composite parent) { + fShell = parent.getShell(); + Composite comp = ControlFactory.createCompositeEx(parent, 2, GridData.FILL_BOTH); + ((GridLayout)comp.getLayout()).makeColumnsEqualWidth = false; + ((GridLayout)comp.getLayout()).marginHeight = 0; + ((GridLayout)comp.getLayout()).marginWidth = 0; + comp.setFont(parent.getFont()); + PixelConverter converter = new PixelConverter(comp); + fHostNameField.doFillIntoGrid(comp, 2); + LayoutUtil.setWidthHint(fHostNameField.getTextControl(null), converter.convertWidthInCharsToPixels(20)); + fPortNumberField.doFillIntoGrid(comp, 2); + ((GridData)fPortNumberField.getTextControl(null).getLayoutData()).horizontalAlignment = GridData.BEGINNING; + LayoutUtil.setWidthHint(fPortNumberField.getTextControl(null), converter.convertWidthInCharsToPixels(10)); + setControl(comp); + } + + protected Shell getShell() { + return fShell; + } + + public void dispose() { + deleteObservers(); + } + + public void initializeFrom(ILaunchConfiguration configuration) { + initializeHostName(configuration); + initializePortNumber(configuration); + } + + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, DEFAULT_HOST_NAME); + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, DEFAULT_PORT_NUMBER); + } + + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + if (fHostNameField != null) + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, fHostNameField.getText().trim()); + if (fPortNumberField != null) + configuration.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, fPortNumberField.getText().trim()); + } + + private StringDialogField createHostNameField() { + StringDialogField field = new StringDialogField(); + field.setLabelText(LaunchUIMessages.getString("TCPSettingsBlock.0")); //$NON-NLS-1$ + field.setDialogFieldListener(new IDialogFieldListener() { + + public void dialogFieldChanged(DialogField f) { + hostNameFieldChanged(); + } + }); + return field; + } + + private StringDialogField createPortNumberField() { + StringDialogField field = new StringDialogField(); + field.setLabelText(LaunchUIMessages.getString("TCPSettingsBlock.1")); //$NON-NLS-1$ + field.setDialogFieldListener(new IDialogFieldListener() { + + public void dialogFieldChanged(DialogField f) { + portNumberFieldChanged(); + } + }); + return field; + } + + protected void hostNameFieldChanged() { + updateErrorMessage(); + setChanged(); + notifyObservers(); + } + + protected void portNumberFieldChanged() { + updateErrorMessage(); + setChanged(); + notifyObservers(); + } + + private void initializeHostName(ILaunchConfiguration configuration) { + if (fHostNameField != null) { + try { + fHostNameField.setText(configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST, DEFAULT_HOST_NAME)); + } + catch(CoreException e) { + } + } + } + + private void initializePortNumber(ILaunchConfiguration configuration) { + if (fPortNumberField != null) { + try { + fPortNumberField.setText(configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT, DEFAULT_PORT_NUMBER)); + } + catch(CoreException e) { + } + } + } + + public Control getControl() { + return fControl; + } + + protected void setControl(Control control) { + fControl = control; + } + + public boolean isValid(ILaunchConfiguration configuration) { + updateErrorMessage(); + return (getErrorMessage() == null); + } + + private void updateErrorMessage() { + setErrorMessage(null); + if (fHostNameField != null && fPortNumberField != null) { + if (fHostNameField.getText().trim().length() == 0) + setErrorMessage(LaunchUIMessages.getString("TCPSettingsBlock.2")); //$NON-NLS-1$ + else if (!hostNameIsValid(fHostNameField.getText().trim())) + setErrorMessage(LaunchUIMessages.getString("TCPSettingsBlock.3")); //$NON-NLS-1$ + else if (fPortNumberField.getText().trim().length() == 0) + setErrorMessage(LaunchUIMessages.getString("TCPSettingsBlock.4")); //$NON-NLS-1$ + else if (!portNumberIsValid(fPortNumberField.getText().trim())) + setErrorMessage(LaunchUIMessages.getString("TCPSettingsBlock.5")); //$NON-NLS-1$ + } + } + + public String getErrorMessage() { + return fErrorMessage; + } + + private void setErrorMessage(String string) { + fErrorMessage = string; + } + + private boolean hostNameIsValid(String hostName) { + return true; + } + + private boolean portNumberIsValid(String portNumber) { + try { + int port = Integer.parseInt(portNumber); + return (port > 0 && port <= 0xFFFF); + } + catch(NumberFormatException e) { + return false; + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/WorkingDirectoryBlock.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/WorkingDirectoryBlock.java new file mode 100644 index 00000000000..36c70746f1b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/launching/WorkingDirectoryBlock.java @@ -0,0 +1,394 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Ericsson - Updated for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.launching; + +import java.io.File; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.variables.IStringVariableManager; +import org.eclipse.core.variables.VariablesPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.StringVariableSelectionDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.ContainerSelectionDialog; + + +/** + * A control for setting the working directory associated with a launch + * configuration. + */ +public class WorkingDirectoryBlock extends CLaunchConfigurationTab { + + // Local directory + protected Text fWorkingDirText; + protected Button fWorkspaceButton; + protected Button fFileSystemButton; + protected Button fVariablesButton; + + // use default button + protected Button fUseDefaultWorkingDirButton; + + /** + * The last launch config this tab was initialized from + */ + protected ILaunchConfiguration fLaunchConfiguration; + + /** + * A listener to update for text changes and widget selection + */ + private class WidgetListener extends SelectionAdapter implements ModifyListener { + + public void modifyText(ModifyEvent e) { + updateLaunchConfigurationDialog(); + } + + @Override + public void widgetSelected(SelectionEvent e) { + Object source = e.getSource(); + if (source == fWorkspaceButton) { + handleWorkspaceDirBrowseButtonSelected(); + } else if (source == fFileSystemButton) { + handleWorkingDirBrowseButtonSelected(); + } else if (source == fUseDefaultWorkingDirButton) { + handleUseDefaultWorkingDirButtonSelected(); + } else if (source == fVariablesButton) { + handleWorkingDirVariablesButtonSelected(); + } + } + } + + private WidgetListener fListener = new WidgetListener(); + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) { + Font font = parent.getFont(); + + Group group = new Group(parent, SWT.NONE); + // WorkbenchHelp.setHelp(group, + // IJavaDebugHelpContextIds.WORKING_DIRECTORY_BLOCK); + GridLayout workingDirLayout = new GridLayout(); + workingDirLayout.makeColumnsEqualWidth = false; + group.setLayout(workingDirLayout); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + group.setLayoutData(gd); + group.setFont(font); + setControl(group); + + group.setText(LaunchUIMessages.getString("WorkingDirectoryBlock.Working_directory")); //$NON-NLS-1$ + + fWorkingDirText = new Text(group, SWT.SINGLE | SWT.BORDER); + fWorkingDirText.getAccessible().addAccessibleListener( + new AccessibleAdapter() { + @Override + public void getName(AccessibleEvent e) { + e.result = LaunchUIMessages.getString("WorkingDirectoryBlock.Working_directory"); //$NON-NLS-1$ + } + } + ); + gd = new GridData(GridData.FILL_HORIZONTAL); + fWorkingDirText.setLayoutData(gd); + fWorkingDirText.setFont(font); + fWorkingDirText.addModifyListener(fListener); + + fUseDefaultWorkingDirButton = new Button(group, SWT.CHECK); + fUseDefaultWorkingDirButton.setText(LaunchUIMessages.getString("WorkingDirectoryBlock.Use_default")); //$NON-NLS-1$ + gd = new GridData(GridData.FILL, GridData.BEGINNING, true, false); + fUseDefaultWorkingDirButton.setLayoutData(gd); + fUseDefaultWorkingDirButton.setFont(font); + fUseDefaultWorkingDirButton.addSelectionListener(fListener); + + Composite buttonComp = new Composite(group, SWT.NONE); + GridLayout layout = new GridLayout(3, false); + layout.marginHeight = 0; + layout.marginWidth = 0; + buttonComp.setLayout(layout); + gd = new GridData(GridData.HORIZONTAL_ALIGN_END); + buttonComp.setLayoutData(gd); + buttonComp.setFont(font); + fWorkspaceButton = createPushButton(buttonComp, LaunchUIMessages.getString("WorkingDirectoryBlock.0"), null); //$NON-NLS-1$ + fWorkspaceButton.addSelectionListener(fListener); + + fFileSystemButton = createPushButton(buttonComp, LaunchUIMessages.getString("WorkingDirectoryBlock.1"), null); //$NON-NLS-1$ + fFileSystemButton.addSelectionListener(fListener); + + fVariablesButton = createPushButton(buttonComp, LaunchUIMessages.getString("WorkingDirectoryBlock.17"), null); //$NON-NLS-1$ + fVariablesButton.addSelectionListener(fListener); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose() + */ + @Override + public void dispose() { + } + + /** + * Show a dialog that lets the user select a working directory + */ + protected void handleWorkingDirBrowseButtonSelected() { + DirectoryDialog dialog = new DirectoryDialog(getShell()); + dialog.setMessage(LaunchUIMessages.getString("WorkingDirectoryBlock.7")); //$NON-NLS-1$ + String currentWorkingDir = fWorkingDirText.getText(); + if (!currentWorkingDir.trim().equals("")) { //$NON-NLS-1$ + File path = new File(currentWorkingDir); + if (path.exists()) { + dialog.setFilterPath(currentWorkingDir); + } + } + + String selectedDirectory = dialog.open(); + if (selectedDirectory != null) { + fWorkingDirText.setText(selectedDirectory); + } + } + + /** + * Show a dialog that lets the user select a working directory from the + * workspace + */ + protected void handleWorkspaceDirBrowseButtonSelected() { + ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(), ResourcesPlugin.getWorkspace().getRoot(), false, + LaunchUIMessages.getString("WorkingDirectoryBlock.4")); //$NON-NLS-1$ + + IContainer currentContainer = getContainer(); + if (currentContainer != null) { + IPath path = currentContainer.getFullPath(); + dialog.setInitialSelections(new Object[] { path}); + } + + dialog.showClosedProjects(false); + dialog.open(); + Object[] results = dialog.getResult(); + if ((results != null) && (results.length > 0) && (results[0] instanceof IPath)) { + IPath path = (IPath) results[0]; + String containerName = path.makeRelative().toString(); + fWorkingDirText.setText("${workspace_loc:" + containerName + "}"); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Returns the selected workspace container,or null + */ + protected IContainer getContainer() { + String path = fWorkingDirText.getText().trim(); + if (path.length() > 0) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IResource res = root.findMember(path); + if (res instanceof IContainer) { + return (IContainer) res; + } + } + return null; + } + + /** + * The default working dir check box has been toggled. + */ + protected void handleUseDefaultWorkingDirButtonSelected() { + boolean def = isDefaultWorkingDirectory(); + if (def) { + setDefaultWorkingDir(); + } + fWorkingDirText.setEnabled(!def); + fWorkspaceButton.setEnabled(!def); + fVariablesButton.setEnabled(!def); + fFileSystemButton.setEnabled(!def); + } + + protected void handleWorkingDirVariablesButtonSelected() { + String variableText = getVariable(); + if (variableText != null) { + fWorkingDirText.append(variableText); + } + } + + private String getVariable() { + StringVariableSelectionDialog dialog = new StringVariableSelectionDialog(getShell()); + dialog.open(); + return dialog.getVariableExpression(); + } + + /** + * Sets the default working directory + */ + protected void setDefaultWorkingDir() { + try { + ILaunchConfiguration config = getLaunchConfiguration(); + if (config != null) { + ICProject cProject = LaunchUtils.getCProject(config); + if (cProject != null) { + fWorkingDirText.setText("${workspace_loc:" + cProject.getPath().makeRelative().toOSString() + "}"); //$NON-NLS-1$ //$NON-NLS-2$ + return; + } + } + } catch (CoreException ce) { + } + fWorkingDirText.setText(System.getProperty("user.dir")); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug.core.ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration config) { + + setErrorMessage(null); + setMessage(null); + + // if variables are present, we cannot resolve the directory + String workingDirPath = fWorkingDirText.getText().trim(); + if (workingDirPath.indexOf("${") >= 0) { //$NON-NLS-1$ + IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); + try { + manager.validateStringVariables(workingDirPath); + } catch (CoreException e) { + setErrorMessage(e.getMessage()); + return false; + } + } else if (workingDirPath.length() > 0) { + IContainer container = getContainer(); + if (container == null) { + File dir = new File(workingDirPath); + if (dir.isDirectory()) { + return true; + } + setErrorMessage(LaunchUIMessages.getString("WorkingDirectoryBlock.10")); //$NON-NLS-1$ + return false; + } + } + return true; + } + + /** + * Defaults are empty. + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy config) { + // config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, + // (String)null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration configuration) { + setLaunchConfiguration(configuration); + try { + String wd = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String) null); + fWorkingDirText.setText(""); //$NON-NLS-1$ + if (wd == null) { + fUseDefaultWorkingDirButton.setSelection(true); + } else { + fWorkingDirText.setText(wd); + fUseDefaultWorkingDirButton.setSelection(false); + } + handleUseDefaultWorkingDirButtonSelected(); + } catch (CoreException e) { + setErrorMessage(LaunchUIMessages.getString("WorkingDirectoryBlock.Exception_occurred_reading_configuration___15") + e.getStatus().getMessage()); //$NON-NLS-1$ + GdbUIPlugin.log(e); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy configuration) { + String wd = null; + if (!isDefaultWorkingDirectory()) { + wd = getAttributeValueFrom(fWorkingDirText); + } + configuration.setAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, wd); + } + + /** + * Retuns the string in the text widget, or null if empty. + * + * @return text or null + */ + protected String getAttributeValueFrom(Text text) { + String content = text.getText().trim(); + if (content.length() > 0) { + return content; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + public String getName() { + return LaunchUIMessages.getString("WorkingDirectoryBlock.Working_Directory_8"); //$NON-NLS-1$ + } + + /** + * Returns whether the default working directory is to be used + */ + protected boolean isDefaultWorkingDirectory() { + return fUseDefaultWorkingDirButton.getSelection(); + } + + /** + * Sets the c project currently specified by the given launch config, if + * any. + */ + protected void setLaunchConfiguration(ILaunchConfiguration config) { + fLaunchConfiguration = config; + } + + /** + * Returns the current c project context + */ + protected ILaunchConfiguration getLaunchConfiguration() { + return fLaunchConfiguration; + } + +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbViewModelAdapter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbViewModelAdapter.java new file mode 100644 index 00000000000..5c08a970518 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/GdbViewModelAdapter.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.AbstractDebugVMAdapter; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.SteppingController; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.ExpressionVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.modules.ModulesVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.register.RegisterVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableVMProvider; +import org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch.LaunchVMProvider; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/* + * + */ +@ThreadSafe +@SuppressWarnings("restriction") +public class GdbViewModelAdapter extends AbstractDebugVMAdapter +{ + public GdbViewModelAdapter(DsfSession session, SteppingController controller) { + super(session, controller); + getSession().registerModelAdapter(IColumnPresentationFactory.class, this); + } + + @Override + public void dispose() { + getSession().unregisterModelAdapter(IColumnPresentationFactory.class); + super.dispose(); + } + + @Override + protected AbstractDMVMProvider createViewModelProvider(IPresentationContext context) { + if ( IDebugUIConstants.ID_DEBUG_VIEW.equals(context.getId()) ) { + return new LaunchVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_VARIABLE_VIEW.equals(context.getId()) ) { + return new VariableVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_REGISTER_VIEW.equals(context.getId()) ) { + return new RegisterVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(context.getId()) ) { + return new ExpressionVMProvider(this, context, getSession()); + } else if (IDebugUIConstants.ID_MODULE_VIEW.equals(context.getId()) ) { + return new ModulesVMProvider(this, context, getSession()); + } + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java new file mode 100644 index 00000000000..bff2b7d7dbd --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ContainerVMNode.java @@ -0,0 +1,254 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 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: + * Ericsson - Initial API and implementation + * Wind River Systems - Factored out AbstractContainerVMNode + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; + + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractContainerVMNode; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.ui.IMemento; + + +@SuppressWarnings("restriction") +public class ContainerVMNode extends AbstractContainerVMNode + implements IElementMementoProvider +{ + public ContainerVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session); + } + + @Override + public String toString() { + return "ContainerVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + IProcesses processService = getServicesTracker().getService(IProcesses.class); + ICommandControlService controlService = getServicesTracker().getService(ICommandControlService.class); + if (processService == null || controlService == null) { + handleFailedUpdate(update); + return; + } + + processService.getProcessesBeingDebugged( + controlService.getContext(), + new ViewerDataRequestMonitor(getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + handleFailedUpdate(update); + return; + } + if (getData() != null) fillUpdateWithVMCs(update, getData()); + update.done(); + } + }); + } + + + @Override + protected void updateLabelInSessionThread(final ILabelUpdate update) { + IProcesses processService = getServicesTracker().getService(IProcesses.class); + IRunControl runControl = getServicesTracker().getService(IRunControl.class); + if (processService == null || runControl == null) { + handleFailedUpdate(update); + return; + } + + final IProcessDMContext procDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IProcessDMContext.class); + final IContainerDMContext contDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IContainerDMContext.class); + + String imageKey = null; + if (runControl.isSuspended(contDmc)) { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } else { + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + processService.getExecutionData( + procDmc, + new ViewerDataRequestMonitor(getExecutor(), update) { + @Override + public void handleCompleted() { + if (!isSuccess()) { + update.setLabel("", 0); //$NON-NLS-1$ + update.done(); + return; + } + + // Create Labels of type Name[PID] if the pid is available + final StringBuilder builder = new StringBuilder(); + builder.append(getData().getName()); + if (getData().getId() != null && getData().getId().length() > 0) { + builder.append("[" + getData().getId()+ "]"); //$NON-NLS-1$//$NON-NLS-2$ + } + update.setLabel(builder.toString(), 0); + update.done(); + } + }); + } + + @Override + public int getDeltaFlags(Object e) { + if (e instanceof ICommandControlShutdownDMEvent) { + return IModelDelta.CONTENT; + } + return super.getDeltaFlags(e); + } + + @Override + public void buildDelta(Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + if (e instanceof ICommandControlShutdownDMEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } else { + super.buildDelta(e, parentDelta, nodeOffset, requestMonitor); + return; + } + requestMonitor.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) + */ + private final String MEMENTO_NAME = "CONTAINER_MEMENTO_NAME"; //$NON-NLS-1$ + + public void compareElements(IElementCompareRequest[] requests) { + for (final IElementCompareRequest request : requests) { + + Object element = request.getElement(); + final IMemento memento = request.getMemento(); + final String mementoName = memento.getString(MEMENTO_NAME); + + if (mementoName != null) { + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if (dmc instanceof IContainerDMContext) + { + final IProcessDMContext procDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IProcessDMContext.class); + + if (procDmc != null) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + final IProcesses processService = getServicesTracker().getService(IProcesses.class); + if (processService != null) { + processService.getExecutionData( + procDmc, + new ViewerDataRequestMonitor(processService.getExecutor(), request) { + @Override + protected void handleCompleted() { + if ( getStatus().isOK() ) { + memento.putString(MEMENTO_NAME, "Container." + getData().getName() + getData().getId()); //$NON-NLS-1$ + } + request.done(); + } + }); + } + else { + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + request.done(); + } + + continue; + } + } + } + } + request.done(); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#encodeElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest[]) + */ + public void encodeElements(IElementMementoRequest[] requests) { + for (final IElementMementoRequest request : requests) { + + Object element = request.getElement(); + final IMemento memento = request.getMemento(); + + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if (dmc instanceof IContainerDMContext) + { + final IProcessDMContext procDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(), IProcessDMContext.class); + + if (procDmc != null) { + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + final IProcesses processService = getServicesTracker().getService(IProcesses.class); + if (processService != null) { + processService.getExecutionData( + procDmc, + new ViewerDataRequestMonitor(processService.getExecutor(), request) { + @Override + protected void handleCompleted() { + if ( getStatus().isOK() ) { + memento.putString(MEMENTO_NAME, "Container." + getData().getName() + getData().getId()); //$NON-NLS-1$ + } + request.done(); + } + }); + } else { + request.done(); + } + } + }); + } catch (RejectedExecutionException e) { + request.done(); + } + + continue; + } + } + } + request.done(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMModelProxyStrategy.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMModelProxyStrategy.java new file mode 100644 index 00000000000..d180558738b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMModelProxyStrategy.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.DefaultVMModelProxyStrategy; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.jface.viewers.TreePath; + +/** + * + */ +public class LaunchVMModelProxyStrategy extends DefaultVMModelProxyStrategy { + + final private TreePath fRootPath; + + public LaunchVMModelProxyStrategy(AbstractVMProvider provider, Object rootElement) { + super(provider, rootElement); + fRootPath = new TreePath( new Object[] { rootElement }); + } + + @Override + public Object getViewerInput() { + return DebugPlugin.getDefault().getLaunchManager(); + } + + @Override + public TreePath getRootPath() { + return fRootPath; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMProvider.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMProvider.java new file mode 100644 index 00000000000..b8282dd0206 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/LaunchVMProvider.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for new functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlInitializedDMEvent; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractLaunchVMProvider; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.LaunchRootVMNode; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StandardProcessVMNode; +import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IDebugEventSetListener; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + + +/** + * + */ +@SuppressWarnings("restriction") +public class LaunchVMProvider extends AbstractLaunchVMProvider + implements IDebugEventSetListener, ILaunchesListener2 +{ + @ThreadSafe + public LaunchVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) + { + super(adapter, presentationContext, session); + + IRootVMNode launchNode = new LaunchRootVMNode(this); + setRootNode(launchNode); + + // Container node to contain all processes and threads + IVMNode containerNode = new ContainerVMNode(this, getSession()); + IVMNode processesNode = new StandardProcessVMNode(this); + addChildNodes(launchNode, new IVMNode[] { containerNode, processesNode}); + + IVMNode threadsNode = new ThreadVMNode(this, getSession()); + addChildNodes(containerNode, new IVMNode[] { threadsNode }); + + IVMNode stackFramesNode = new StackFramesVMNode(this, getSession()); + addChildNodes(threadsNode, new IVMNode[] { stackFramesNode }); + + + DebugPlugin.getDefault().addDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this); + } + + @Override + public void dispose() { + DebugPlugin.getDefault().removeDebugEventListener(this); + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this); + super.dispose(); + } + + @Override + protected boolean canSkipHandlingEvent(Object newEvent, Object eventToSkip) { + // Never skip the process lifecycle events. + if (eventToSkip instanceof ICommandControlInitializedDMEvent || + eventToSkip instanceof ICommandControlShutdownDMEvent) + { + return false; + } + return super.canSkipHandlingEvent(newEvent, eventToSkip); + } + + @Override + public void refresh() { + super.refresh(); + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), getSession().getId()); + IProcesses processesService = tracker.getService(IProcesses.class); + if (processesService instanceof ICachingService) { + ((ICachingService)processesService).flushCache(null); + } + IStack stackService = tracker.getService(IStack.class); + if (stackService instanceof ICachingService) { + ((ICachingService)stackService).flushCache(null); + } + IRunControl runControlService = tracker.getService(IRunControl.class); + if (runControlService instanceof ICachingService) { + ((ICachingService)runControlService).flushCache(null); + } + tracker.dispose(); + } + }); + } catch (RejectedExecutionException e) { + // Session disposed, ignore. + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java new file mode 100644 index 00000000000..1942e2e8a63 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb.ui/src/org/eclipse/cdt/dsf/gdb/internal/ui/viewmodel/launch/ThreadVMNode.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for multi threaded functionality + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractThreadVMNode; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.ui.IMemento; + + +@SuppressWarnings("restriction") +public class ThreadVMNode extends AbstractThreadVMNode + implements IElementLabelProvider, IElementMementoProvider +{ + public ThreadVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session); + } + + @Override + public String toString() { + return "ThreadVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected void updateLabelInSessionThread(ILabelUpdate[] updates) { + for (final ILabelUpdate update : updates) { + final IRunControl runControl = getServicesTracker().getService(IRunControl.class); + if (runControl == null) { + handleFailedUpdate(update); + continue; + } + + final IMIExecutionDMContext execDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IMIExecutionDMContext.class); + + String imageKey = null; + final boolean threadSuspended; + if (runControl.isSuspended(execDmc)) { + threadSuspended = true; + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_SUSPENDED; + } else { + threadSuspended = false; + imageKey = IDebugUIConstants.IMG_OBJS_THREAD_RUNNING; + } + update.setImageDescriptor(DebugUITools.getImageDescriptor(imageKey), 0); + + // Find the Reason for the State + runControl.getExecutionData(execDmc, + new ViewerDataRequestMonitor(getSession().getExecutor(), update) { + @Override + public void handleCompleted(){ + if (!isSuccess()) { + update.setLabel("", 0); //$NON-NLS-1$ + update.done(); + return; + } + + final IProcesses procService = getServicesTracker().getService(IProcesses.class); + if ( procService == null ) { + handleFailedUpdate(update); + return; + } + + final StateChangeReason reason = getData().getStateChangeReason(); + + // Retrieve the rest of the thread information + final IThreadDMContext threadDmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), IThreadDMContext.class); + + procService.getExecutionData( + threadDmc, + new ViewerDataRequestMonitor(getSession().getExecutor(), update) { + @Override + public void handleCompleted() { + // We can still generate a good enough label even if this call fails + // so continue and check if we should use getData() or not. + + // Create Labels of type Thread[GDBthreadId]RealThreadID/Name (State: Reason) + // Thread[1] 3457 (Suspended:BREAKPOINT) + final StringBuilder builder = new StringBuilder("Thread["); //$NON-NLS-1$ + builder.append(execDmc.getThreadId()); + builder.append("] "); //$NON-NLS-1$ + if (isSuccess()) { + builder.append(getData().getId()); + builder.append(getData().getName()); + } + if(threadSuspended) + builder.append(" (Suspended"); //$NON-NLS-1$ + else + builder.append(" (Running"); //$NON-NLS-1$ + // Reason will be null before ContainerSuspendEvent is fired + if(reason != null) { + builder.append(" : "); //$NON-NLS-1$ + builder.append(reason); + } + builder.append(")"); //$NON-NLS-1$ + update.setLabel(builder.toString(), 0); + update.done(); + } + }); + } + }); + + } + } + + private String produceThreadElementName(String viewName, IMIExecutionDMContext execCtx) { + return "Thread." + execCtx.getThreadId(); //$NON-NLS-1$ + } + + private static final String MEMENTO_NAME = "THREAD_MEMENTO_NAME"; //$NON-NLS-1$ + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#compareElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest[]) + */ + public void compareElements(IElementCompareRequest[] requests) { + + for ( IElementCompareRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + String mementoName = memento.getString(MEMENTO_NAME); + + if (mementoName != null) { + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IMIExecutionDMContext) { + + String elementName = produceThreadElementName( request.getPresentationContext().getId(), (IMIExecutionDMContext) dmc ); + request.setEqual( elementName.equals( mementoName ) ); + } + } + } + request.done(); + } + } + + /* + * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider#encodeElements(org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest[]) + */ + public void encodeElements(IElementMementoRequest[] requests) { + + for ( IElementMementoRequest request : requests ) { + + Object element = request.getElement(); + IMemento memento = request.getMemento(); + + if (element instanceof IDMVMContext) { + + IDMContext dmc = ((IDMVMContext)element).getDMContext(); + + if ( dmc instanceof IMIExecutionDMContext) { + + String elementName = produceThreadElementName( request.getPresentationContext().getId(), (IMIExecutionDMContext) dmc ); + memento.putString(MEMENTO_NAME, elementName); + } + } + request.done(); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.classpath b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.options b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.options new file mode 100644 index 00000000000..28eeb8b6aa9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.options @@ -0,0 +1 @@ +org.eclipse.cdt.dsf.gdb/debug = false diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.project b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.project new file mode 100644 index 00000000000..01d65c04880 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.project @@ -0,0 +1,34 @@ + + + org.eclipse.cdt.dsf.gdb + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters new file mode 100644 index 00000000000..5d4626f8d7e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/.api_filters @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/org.eclipse.jdt.core.prefs b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..acad5c5fe98 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Tue Jun 24 11:04:03 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..997fe8ae320 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.dsf.gdb;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.dsf.gdb.internal.GdbPlugin +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.cdt.dsf, + org.eclipse.debug.core, + org.eclipse.cdt.core, + org.eclipse.cdt.debug.core, + org.eclipse.core.variables +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Export-Package: org.eclipse.cdt.dsf.gdb, + org.eclipse.cdt.dsf.gdb.actions, + org.eclipse.cdt.dsf.gdb.breakpoints, + org.eclipse.cdt.dsf.gdb.launching, + org.eclipse.cdt.dsf.gdb.service, + org.eclipse.cdt.dsf.gdb.service.command, + org.eclipse.cdt.dsf.mi.service, + org.eclipse.cdt.dsf.mi.service.command, + org.eclipse.cdt.dsf.mi.service.command.commands, + org.eclipse.cdt.dsf.mi.service.command.events, + org.eclipse.cdt.dsf.mi.service.command.output diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/about.html b/dsf-gdb/org.eclipse.cdt.dsf.gdb/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/about.html @@ -0,0 +1,24 @@ + + + + +About +

About This Content

+ +

June 5, 2007

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/build.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/build.properties new file mode 100644 index 00000000000..6c286bf62a9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/build.properties @@ -0,0 +1,17 @@ +############################################################################### +# Copyright (c) 2006, 2008 Wind River Systems 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: +# Wind River Systems - initial API and implementation +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + about.html,\ + plugin.properties diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.properties new file mode 100644 index 00000000000..c3f20315fe2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2006 Wind River Systems 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: +# Wind River Systems - initial API and implementation +############################################################################### +pluginName=GDB DSF Debugger Integration Core +providerName=Eclipse.org + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.xml b/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.xml new file mode 100644 index 00000000000..c49c90e1fb3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/plugin.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java new file mode 100644 index 00000000000..ad9cbb8ec53 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/IGDBLaunchConfigurationConstants.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb; + +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; + + +public class IGDBLaunchConfigurationConstants { + + // + // Taken from org.eclipse.cdt.debug.mi.core.IGDBServerMILaunchConfigurationConstants + // + public static final String ATTR_REMOTE_TCP = GdbPlugin.PLUGIN_ID + ".REMOTE_TCP"; //$NON-NLS-1$ + public static final String ATTR_HOST = GdbPlugin.PLUGIN_ID + ".HOST"; //$NON-NLS-1$ + public static final String ATTR_PORT = GdbPlugin.PLUGIN_ID + ".PORT"; //$NON-NLS-1$ + public static final String ATTR_DEV = GdbPlugin.PLUGIN_ID + ".DEV"; //$NON-NLS-1$ + public static final String ATTR_DEV_SPEED = GdbPlugin.PLUGIN_ID + ".DEV_SPEED"; //$NON-NLS-1$ + // + // + + // + // New to DSF GDB/MI + public static final String DEBUGGER_MODE_REMOTE = "remote"; //$NON-NLS-1$ + public static final String DEBUGGER_MODE_REMOTE_ATTACH = "remote_attach"; //$NON-NLS-1$ + // + // + + // + // Taken from org.eclipse.cdt.debug.mi.core.IMILaunchConfigurationConstants + // + /** + * Launch configuration attribute key. The value is the name of + * the Debuger associated with a C/C++ launch configuration. + */ + public static final String ATTR_DEBUG_NAME = GdbPlugin.PLUGIN_ID + ".DEBUG_NAME"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. Boolean value to set the gdb command file + * Debuger/gdb/MI property. + */ + public static final String ATTR_GDB_INIT = GdbPlugin.PLUGIN_ID + ".GDB_INIT"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. Boolean value to set the non-stop mode + * Debuger/gdb/MI property. + * @since 1.1 + */ + public static final String ATTR_DEBUGGER_NON_STOP = GdbPlugin.PLUGIN_ID + ".NON_STOP"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. Boolean value to set the 'automatically load shared library symbols' flag of the debugger. + */ + public static final String ATTR_DEBUGGER_AUTO_SOLIB = GdbPlugin.PLUGIN_ID + ".AUTO_SOLIB"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. Boolean value to set the 'use shared library symbols for application' flag of the debugger. + * @since 1.1 + */ + public static final String ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP = GdbPlugin.PLUGIN_ID + ".USE_SOLIB_SYMBOLS_FOR_APP"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. The value is a List (array of String) of directories for the search path of shared libraries. + */ + public static final String ATTR_DEBUGGER_SOLIB_PATH = GdbPlugin.PLUGIN_ID + ".SOLIB_PATH"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. The value is a List (array of String) of shared libraries to load symbols automatically. + */ + public static final String ATTR_DEBUGGER_AUTO_SOLIB_LIST = GdbPlugin.PLUGIN_ID + ".AUTO_SOLIB_LIST"; //$NON-NLS-1$ + + /** + * Launch configuration attribute key. The value is a boolean specifying the mode of the gdb console. + * @since 1.1 + */ + public static final String ATTR_DEBUGGER_VERBOSE_MODE = GdbPlugin.PLUGIN_ID + ".verboseMode"; //$NON-NLS-1$ + + /** + * Launch configuration attribute value. The key is ATTR_DEBUG_NAME. + */ + public static final String DEBUGGER_DEBUG_NAME_DEFAULT = "gdb"; //$NON-NLS-1$ + + /** + * Launch configuration attribute value. The key is ATTR_GDB_INIT. + */ + public static final String DEBUGGER_GDB_INIT_DEFAULT = ".gdbinit"; //$NON-NLS-1$ + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_NON_STOP. + * @since 1.1 + */ + public static final boolean DEBUGGER_NON_STOP_DEFAULT = false; + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_AUTO_SOLIB. + */ + public static final boolean DEBUGGER_AUTO_SOLIB_DEFAULT = true; + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP. + * @since 1.1 + */ + public static final boolean DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT = false; + + /** + * Launch configuration attribute value. The key is ATTR_DEBUGGER_VERBOSE_MODE. + * @since 1.1 + */ + public static final boolean DEBUGGER_VERBOSE_MODE_DEFAULT = false; +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/actions/IConnect.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/actions/IConnect.java new file mode 100644 index 00000000000..16fb86f24a0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/actions/IConnect.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.actions; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; + +/** + * @since 1.1 + */ +public interface IConnect { + /** + * Returns whether this element can currently attempt to + * connect to a new process. + */ + public boolean canConnect(); + + /** + * Causes this element to attempt to connect to a new process. + */ + public void connect(RequestMonitor rm); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/breakpoints/CBreakpointGdbThreadsFilterExtension.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/breakpoints/CBreakpointGdbThreadsFilterExtension.java new file mode 100644 index 00000000000..0d68b3f28f8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/breakpoints/CBreakpointGdbThreadsFilterExtension.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.breakpoints; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IDsfBreakpointExtension; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.core.runtime.CoreException; + +/** + * + */ +public class CBreakpointGdbThreadsFilterExtension implements IDsfBreakpointExtension { + + private Map> fFilteredThreadsByTarget = + new HashMap>(1); + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpointExtension#initialize(org.eclipse.cdt.debug.core.model.ICBreakpoint) + */ + public void initialize(ICBreakpoint breakpoint) { + // TODO: Initialize fFilteredThreadsByTarget with current IContainerDMContext[] + // TODO: IRunControl? + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#getTargetFilters() + */ + public IContainerDMContext[] getTargetFilters() throws CoreException { + Set set = fFilteredThreadsByTarget.keySet(); + return set.toArray( new IContainerDMContext[set.size()] ); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#getThreadFilters(org.eclipse.cdt.debug.core.model.ICDebugTarget) + */ + public IExecutionDMContext[] getThreadFilters( IContainerDMContext target ) throws CoreException { + Set set = fFilteredThreadsByTarget.get( target ); + return ( set != null ) ? set.toArray( new IExecutionDMContext[set.size()] ) : null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#removeTargetFilter(org.eclipse.cdt.debug.core.model.ICDebugTarget) + */ + public void removeTargetFilter( IContainerDMContext target ) throws CoreException { + if ( fFilteredThreadsByTarget.containsKey( target ) ) { + fFilteredThreadsByTarget.remove( target ); + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#removeThreadFilters(org.eclipse.cdt.debug.core.model.ICThread[]) + */ + public void removeThreadFilters( IExecutionDMContext[] threads ) throws CoreException { + if ( threads != null && threads.length > 0 ) { + IContainerDMContext target = DMContexts.getAncestorOfType(threads[0], IContainerDMContext.class); + if ( fFilteredThreadsByTarget.containsKey( target ) ) { + Set set = fFilteredThreadsByTarget.get( target ); + if ( set != null ) { + set.removeAll( Arrays.asList( threads ) ); + if ( set.isEmpty() ) { + fFilteredThreadsByTarget.remove( target ); + } + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#setTargetFilter(org.eclipse.cdt.debug.core.model.ICDebugTarget) + */ + public void setTargetFilter( IContainerDMContext target ) throws CoreException { + fFilteredThreadsByTarget.put( target, null ); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICBreakpoint#setThreadFilters(org.eclipse.cdt.debug.core.model.ICThread[]) + */ + public void setThreadFilters( IExecutionDMContext[] threads ) throws CoreException { + if ( threads != null && threads.length > 0 ) { + IContainerDMContext target = DMContexts.getAncestorOfType(threads[0], IContainerDMContext.class); + fFilteredThreadsByTarget.put( target, new HashSet( Arrays.asList( threads ) ) ); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/GdbPlugin.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/GdbPlugin.java new file mode 100644 index 00000000000..f2f1a184714 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/internal/GdbPlugin.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.internal; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class GdbPlugin extends Plugin { + + // Debugging flag + public static boolean DEBUG = false; + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.dsf.gdb"; //$NON-NLS-1$ + + // The shared instance + private static GdbPlugin plugin; + + private static BundleContext fgBundleContext; + + /** + * The constructor + */ + public GdbPlugin() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugins#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + plugin = this; + + DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf.gdb/debug")); //$NON-NLS-1$//$NON-NLS-2$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + shutdownActiveLaunches(); + plugin = null; + super.stop(context); + fgBundleContext = null; + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static GdbPlugin getDefault() { + return plugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + /** + * Shuts down any active launches. We must shutdown any active sessions + * and services associated with this plugin before this plugin is stopped. + * Any attempts to use the plugins {@link BundleContext} after the plugin + * is shut down will result in exceptions. + */ + private void shutdownActiveLaunches() { + for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { + if (launch instanceof GdbLaunch && ((GdbLaunch)launch).getSession().isActive()) { + final GdbLaunch gdbLaunch = (GdbLaunch)launch; + + Query launchShutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + gdbLaunch.shutdownSession(rm); + } + }; + + try { + gdbLaunch.getSession().getExecutor().execute(launchShutdownQuery); + } catch (RejectedExecutionException e) { + // We can get this exception if the session is shutdown concurrently + // to this method running. + break; + } + + // The Query.get() method is a synchronous call which blocks until the + // query completes. + try { + launchShutdownQuery.get(); + } catch (InterruptedException e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "InterruptedException while shutting down PDA debugger launch " + gdbLaunch, e.getCause())); //$NON-NLS-1$ + } catch (ExecutionException e) { + getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, "Exception while shutting down PDA debugger launch " + gdbLaunch, e.getCause())); //$NON-NLS-1$ + } + } + } + } + + public static void debug(String message) { + if (DEBUG) { + System.out.print(message); + } + } + + public static String getDebugTime() { + StringBuilder traceBuilder = new StringBuilder(); + + // Record the time + long time = System.currentTimeMillis(); + long seconds = (time / 1000) % 1000; + if (seconds < 100) traceBuilder.append('0'); + if (seconds < 10) traceBuilder.append('0'); + traceBuilder.append(seconds); + traceBuilder.append(','); + long millis = time % 1000; + if (millis < 100) traceBuilder.append('0'); + if (millis < 10) traceBuilder.append('0'); + traceBuilder.append(millis); + return traceBuilder.toString(); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java new file mode 100644 index 00000000000..c779e58e4c1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/FinalLaunchSequence.java @@ -0,0 +1,472 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.List; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.actions.IConnect; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.CSourceLookup; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLISource; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIEnvironmentCD; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIFileExecAndSymbols; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetArgs; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetAutoSolib; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetNonStop; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBSetSolibSearchPath; +import org.eclipse.cdt.dsf.mi.service.command.commands.MITargetSelect; +import org.eclipse.cdt.dsf.mi.service.command.commands.RawCommand; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class FinalLaunchSequence extends Sequence { + + Step[] fSteps = new Step[] { + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fLaunch.getSession().getId()); + requestMonitor.done(); + } + @Override + public void rollBack(RequestMonitor requestMonitor) { + if (fTracker != null) fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + }}, + + /* + * Fetch the GDBBackend service for later use + */ + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fGDBBackend = fTracker.getService(IGDBBackend.class); + if (fGDBBackend == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain GDBBackend service", null)); //$NON-NLS-1$ + } + + requestMonitor.done(); + }}, + /* + * Fetch the control service for later use + */ + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fCommandControl = fTracker.getService(IGDBControl.class); + if (fCommandControl == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain control service", null)); //$NON-NLS-1$ + } + + requestMonitor.done(); + }}, + /* + * Fetch the process service for later use + */ + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fProcService = fTracker.getService(IMIProcesses.class); + if (fProcService == null) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot obtain process service", null)); //$NON-NLS-1$ + } + + requestMonitor.done(); + }}, + /* + * Source the gdbinit file specified in the launch + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + try { + final String gdbinitFile = fGDBBackend.getGDBInitFile(); + + if (gdbinitFile != null && gdbinitFile.length() > 0) { + fCommandControl.queueCommand( + new CLISource(fCommandControl.getContext(), gdbinitFile), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + // If the gdbinitFile is the default, then it may not exist and we + // should not consider this an error. + // If it is not the default, then the user must have specified it and + // we want to warn the user if we can't find it. + if (!gdbinitFile.equals(IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT )) { + requestMonitor.setStatus(getStatus()); + } + requestMonitor.done(); + } + }); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get gdbinit option", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + }}, + /* + * Specify the executable file to be debugged and read the symbol table. + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + boolean noFileCommand = IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT; + try { + noFileCommand = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP, + IGDBLaunchConfigurationConstants.DEBUGGER_USE_SOLIB_SYMBOLS_FOR_APP_DEFAULT); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot read use solib symbols for app options", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + final IPath execPath = fGDBBackend.getProgramPath(); + if (!noFileCommand && execPath != null && !execPath.isEmpty()) { + fCommandControl.queueCommand( + new MIFileExecAndSymbols(fCommandControl.getContext(), + execPath.toPortableString()), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + }}, + /* + * Specify the arguments to the executable file + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + try { + String args = fGDBBackend.getProgramArguments(); + + if (args != null) { + fCommandControl.queueCommand( + new MIGDBSetArgs(fCommandControl.getContext(), args), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get inferior arguments", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + }}, + /* + * Specify GDB's working directory + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + IPath dir = null; + try { + dir = fGDBBackend.getGDBWorkingDirectory(); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot get working directory", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + if (dir != null) { + fCommandControl.queueCommand( + new MIEnvironmentCD(fCommandControl.getContext(), dir.toPortableString()), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + requestMonitor.done(); + } + }}, + /* + * Enable non-stop mode if necessary + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + boolean isNonStop = false; + try { + isNonStop = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + } catch (CoreException e) { + } + + // GDBs that don't support non-stop don't allow you to set it to false. + // We really should set it to false when GDB supports it though. + // Something to fix later. + if (isNonStop) { + // The raw commands should not be necessary in the official GDB release + fCommandControl.queueCommand( + new RawCommand(fCommandControl.getContext(), "set target-async on"), //$NON-NLS-1$ + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + fCommandControl.queueCommand( + new RawCommand(fCommandControl.getContext(), "set pagination off"), //$NON-NLS-1$ + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + fCommandControl.queueCommand( + new MIGDBSetNonStop(fCommandControl.getContext(), true), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } + }); + } + }); + } else { + requestMonitor.done(); + } + }}, + /* + * Tell GDB to automatically load or not the shared library symbols + */ + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + try { + boolean autolib = fLaunch.getLaunchConfiguration().getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_AUTO_SOLIB, + IGDBLaunchConfigurationConstants.DEBUGGER_AUTO_SOLIB_DEFAULT); + fCommandControl.queueCommand( + new MIGDBSetAutoSolib(fCommandControl.getContext(), autolib), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set shared library option", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + }}, + /* + * Set the shared library paths + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + try { + List p = fGDBBackend.getSharedLibraryPaths(); + + if (p.size() > 0) { + String[] paths = p.toArray(new String[p.size()]); + fCommandControl.queueCommand( + new MIGDBSetSolibSearchPath(fCommandControl.getContext(), paths), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { +// Sysroot is not available in GDB6.6 and will make the launch fail in that case. +// Let's remove it for now + requestMonitor.done(); +// // If we are able to set the solib-search-path, +// // we should disable the sysroot variable, as indicated +// // in the GDB documentation. This is to avoid the sysroot +// // variable finding libraries that were not meant to be found. +// fCommandControl.queueCommand( +// new MIGDBSetSysroot(fCommandControl.getContext()), +// new DataRequestMonitor(getExecutor(), requestMonitor)); + }; + }); + } else { + requestMonitor.done(); + } + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot set share library paths", e)); //$NON-NLS-1$ + requestMonitor.done(); + } + }}, + /* + * Setup the source paths + */ + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + CSourceLookup sourceLookup = fTracker.getService(CSourceLookup.class); + CSourceLookupDirector locator = (CSourceLookupDirector)fLaunch.getSourceLocator(); + ISourceLookupDMContext sourceLookupDmc = (ISourceLookupDMContext)fCommandControl.getContext(); + + sourceLookup.setSourceLookupPath(sourceLookupDmc, locator.getSourceContainers(), requestMonitor); + }}, + /* + * If remote debugging, connect to target. + */ + new Step() { + private boolean fTcpConnection; + private String fRemoteTcpHost; + private String fRemoteTcpPort; + private String fSerialDevice; + + private boolean checkConnectionType(RequestMonitor requestMonitor) { + try { + fTcpConnection = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP, + false); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve connection mode", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getSerialDevice(RequestMonitor requestMonitor) { + try { + fSerialDevice = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_DEV, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve serial device", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getTcpHost(RequestMonitor requestMonitor) { + try { + fRemoteTcpHost = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_HOST, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP host", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + private boolean getTcpPort(RequestMonitor requestMonitor) { + try { + fRemoteTcpPort = fLaunch.getLaunchConfiguration().getAttribute( + IGDBLaunchConfigurationConstants.ATTR_PORT, "invalid"); //$NON-NLS-1$ + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve remote TCP port", e)); //$NON-NLS-1$ + requestMonitor.done(); + return false; + } + return true; + } + + @Override + public void execute(final RequestMonitor requestMonitor) { + if (fSessionType == SessionType.REMOTE) { + if (!checkConnectionType(requestMonitor)) return; + + if (fTcpConnection) { + if (!getTcpHost(requestMonitor)) return; + if (!getTcpPort(requestMonitor)) return; + + fCommandControl.queueCommand( + new MITargetSelect(fCommandControl.getContext(), + fRemoteTcpHost, fRemoteTcpPort, fAttach), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + if (!getSerialDevice(requestMonitor)) return; + + fCommandControl.queueCommand( + new MITargetSelect(fCommandControl.getContext(), + fSerialDevice, fAttach), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } + } else { + requestMonitor.done(); + } + } + }, + /* + * If attach session, perform the attach. + */ + new Step() { + @Override + public void execute(final RequestMonitor requestMonitor) { + if (fAttach) { + // If we are attaching, get the process id. + int pid = -1; + try { + // have we already been given the pid (maybe from a JUnit test launch or something) + pid = fLaunch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1); + } catch (CoreException e) { + // do nothing and fall to below + } + + if (pid != -1) { + fProcService.attachDebuggerToProcess( + fProcService.createProcessContext(fCommandControl.getContext(), Integer.toString(pid)), + new DataRequestMonitor(getExecutor(), requestMonitor)); + } else { + IConnect connectCommand = (IConnect)fLaunch.getSession().getModelAdapter(IConnect.class); + if (connectCommand != null) { + connectCommand.connect(requestMonitor); + } else { + requestMonitor.done(); + } + } + } else { + requestMonitor.done(); + } + } + }, + /* + * Start tracking the breakpoints once we know we are connected to the target (necessary for remote debugging) + */ + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + MIBreakpointsManager bpmService = fTracker.getService(MIBreakpointsManager.class); + IBreakpointsTargetDMContext breakpointDmc = (IBreakpointsTargetDMContext)fCommandControl.getContext(); + + bpmService.startTrackingBreakpoints(breakpointDmc, requestMonitor); + }}, + /* + * Start the program. + */ + new Step() { + @Override + public void execute(final RequestMonitor requestMonitor) { + fCommandControl.start(fLaunch, requestMonitor); + } + }, + /* + * Cleanup + */ + new Step() { + @Override + public void execute(final RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, + }; + + GdbLaunch fLaunch; + SessionType fSessionType; + boolean fAttach; + + private IGDBControl fCommandControl; + private IGDBBackend fGDBBackend; + private IMIProcesses fProcService; + + DsfServicesTracker fTracker; + + public FinalLaunchSequence(DsfExecutor executor, GdbLaunch launch, SessionType sessionType, boolean attach, IProgressMonitor pm) { + super(executor, pm, "Configuring GDB", "Aborting configuring GDB"); + fLaunch = launch; + fSessionType = sessionType; + fAttach = attach; + } + + @Override + public Step[] getSteps() { + return fSteps; + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBDebugger.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBDebugger.java new file mode 100644 index 00000000000..bc4e9ddc460 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBDebugger.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +/* This class simply exists because the extension needs it. + * However, since we only use the extension to re-use some CDT code, + * we don't actually need this class to do anything. + */ +public class GDBDebugger {} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBServerDebugger.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBServerDebugger.java new file mode 100644 index 00000000000..b8f1336b7e2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GDBServerDebugger.java @@ -0,0 +1,18 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for Ericsson + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +/* This class simply exists because the extension needs it. + * However, since we only use the extension to re-use some CDT code, + * we don't actually need this class to do anything. + */ +public class GDBServerDebugger {} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java new file mode 100644 index 00000000000..521426d709f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunch.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlockRetrieval; +import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.Launch; +import org.eclipse.debug.core.model.IDisconnect; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.ISourceLocator; +import org.eclipse.debug.core.model.ITerminate; + +/** + * The only object in the model that implements the traditional interfaces. + */ +@ThreadSafe +public class GdbLaunch extends Launch + implements ITerminate, IDisconnect +{ + private DefaultDsfExecutor fExecutor; + private DsfSession fSession; + private DsfServicesTracker fTracker; + private boolean fInitialized = false; + private boolean fShutDown = false; + + private DsfMemoryBlockRetrieval fMemRetrieval; + private IDsfDebugServicesFactory fServiceFactory; + + public GdbLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); + + // Create the dispatch queue to be used by debugger control and services + // that belong to this launch + final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor(GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + dsfExecutor.prestartCoreThread(); + fExecutor = dsfExecutor; + fSession = DsfSession.startSession(fExecutor, GdbLaunchDelegate.GDB_DEBUG_MODEL_ID); + } + + public DsfExecutor getDsfExecutor() { return fExecutor; } + public IDsfDebugServicesFactory getServiceFactory() { return fServiceFactory; } + + public void initialize() + { + Runnable initRunnable = new DsfRunnable() { + public void run() { + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSession.getId()); + fSession.addServiceEventListener(GdbLaunch.this, null); + + fInitialized = true; + fireChanged(); + } + }; + + // Invoke the execution code and block waiting for the result. + try { + fExecutor.submit(initRunnable).get(); + } catch (InterruptedException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ + } catch (ExecutionException e) { + new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Error initializing launch", e); //$NON-NLS-1$ + } + } + + public void initializeControl() + throws CoreException + { + // Create a memory retrieval and register it with the session + try { + fExecutor.submit( new Callable() { + public Object call() throws CoreException { + ICommandControlService commandControl = fTracker.getService(ICommandControlService.class); + IMIProcesses procService = fTracker.getService(IMIProcesses.class); + if (commandControl != null && procService != null) { + fMemRetrieval = new DsfMemoryBlockRetrieval( + GdbLaunchDelegate.GDB_DEBUG_MODEL_ID, getLaunchConfiguration(), fSession); + fSession.registerModelAdapter(IMemoryBlockRetrieval.class, fMemRetrieval); + + IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + IMemoryDMContext memoryDmc = (IMemoryDMContext)procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + fMemRetrieval.initialize(memoryDmc); + } + return null; + } + }).get(); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException)e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public DsfSession getSession() { return fSession; } + + @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") + public void addInferiorProcess(String label) throws CoreException { + try { + // Add the "inferior" process object to the launch. + MIInferiorProcess inferiorProc = + getDsfExecutor().submit( new Callable() { + public MIInferiorProcess call() throws CoreException { + IGDBControl gdb = fTracker.getService(IGDBControl.class); + if (gdb != null) { + return gdb.getInferiorProcess(); + } + return null; + } + }).get(); + + DebugPlugin.newProcess(this, inferiorProc, label); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException)e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + @ThreadSafeAndProhibitedFromDsfExecutor("getDsfExecutor()") + public void addCLIProcess(String label) throws CoreException { + try { + // Add the CLI process object to the launch. + AbstractCLIProcess cliProc = + getDsfExecutor().submit( new Callable() { + public AbstractCLIProcess call() throws CoreException { + IGDBControl gdb = fTracker.getService(IGDBControl.class); + if (gdb != null) { + return gdb.getCLIProcess(); + } + return null; + } + }).get(); + + DebugPlugin.newProcess(this, cliProc, label); + } catch (InterruptedException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw (CoreException)e.getCause(); + } catch (RejectedExecutionException e) { + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, 0, "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$ + } + } + + public void setServiceFactory(IDsfDebugServicesFactory factory) { + fServiceFactory = factory; + } + + /////////////////////////////////////////////////////////////////////////// + // IServiceEventListener + @DsfServiceEventHandler public void eventDispatched(ICommandControlShutdownDMEvent event) { + shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null)); + } + + /////////////////////////////////////////////////////////////////////////// + // ITerminate + @Override + public boolean canTerminate() { + return super.canTerminate() && fInitialized && !fShutDown; + } + + @Override + public boolean isTerminated() { + return super.isTerminated() || fShutDown; + } + + + @Override + public void terminate() throws DebugException { + if (fShutDown) return; + super.terminate(); + } + // ITerminate + /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////// + // IDisconnect + @Override + public boolean canDisconnect() { + return canTerminate(); + } + + @Override + public boolean isDisconnected() { + return isTerminated(); + } + + @Override + public void disconnect() throws DebugException { + terminate(); + } + // IDisconnect + /////////////////////////////////////////////////////////////////////////// + + /** + * Shuts down the services, the session and the executor associated with + * this launch. + *

+ * Note: The argument request monitor to this method should NOT use the + * executor that belongs to this launch. By the time the shutdown is + * complete, this executor will not be dispatching anymore and the + * request monitor will never be invoked. Instead callers should use + * the {@link ImmediateExecutor}. + *

+ * @param rm The request monitor invoked when the shutdown is complete. + */ + @ConfinedToDsfExecutor("getSession().getExecutor()") + public void shutdownSession(final RequestMonitor rm) { + if (fShutDown) { + rm.done(); + return; + } + fShutDown = true; + + Sequence shutdownSeq = new ShutdownSequence( + getDsfExecutor(), fSession.getId(), + new RequestMonitor(fSession.getExecutor(), rm) { + @Override + public void handleCompleted() { + fSession.removeServiceEventListener(GdbLaunch.this); + if (!isSuccess()) { + GdbPlugin.getDefault().getLog().log(new MultiStatus( + GdbPlugin.PLUGIN_ID, -1, new IStatus[]{getStatus()}, "Session shutdown failed", null)); //$NON-NLS-1$ + } + // Last order of business, shutdown the dispatch queue. + fTracker.dispose(); + fTracker = null; + DsfSession.endSession(fSession); + + // DsfMemoryBlockRetrieval.saveMemoryBlocks(); + fMemRetrieval.saveMemoryBlocks(); + + // endSession takes a full dispatch to distribute the + // session-ended event, finish step only after the dispatch. + fExecutor.shutdown(); + fExecutor = null; + fireTerminate(); + + rm.setStatus(getStatus()); + rm.done(); + } + }); + fExecutor.execute(shutdownSeq); + } + + @SuppressWarnings("unchecked") + @Override + public Object getAdapter(Class adapter) { + // Must force adapters to be loaded. + Platform.getAdapterManager().loadAdapter(this, adapter.getName()); + return super.getAdapter(adapter); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java new file mode 100644 index 00000000000..8a5bdd45f17 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/GdbLaunchDelegate.java @@ -0,0 +1,380 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Windriver and Ericsson - Updated for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.core.model.ICModelMarker; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory; +import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; +import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactoryNS; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2; +import org.eclipse.debug.core.model.ISourceLocator; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; + +/** + * The shared launch configuration delegate for the DSF/GDB debugger. + * This delegate supports all configuration types (local, remote, attach, etc) + */ +@ThreadSafe +public class GdbLaunchDelegate extends LaunchConfigurationDelegate + implements ILaunchConfigurationDelegate2 +{ + public final static String GDB_DEBUG_MODEL_ID = "org.eclipse.cdt.dsf.gdb"; //$NON-NLS-1$ + + private final static String NON_STOP_FIRST_VERSION = "6.8.50"; //$NON-NLS-1$ + private boolean isNonStopSession = false; + + public void launch( ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor ) throws CoreException { + if ( monitor == null ) { + monitor = new NullProgressMonitor(); + } + if ( mode.equals( ILaunchManager.DEBUG_MODE ) ) { + launchDebugger( config, launch, monitor ); + } + } + + private void launchDebugger( ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor ) throws CoreException { + monitor.beginTask("Launching debugger session", 10); + if ( monitor.isCanceled() ) { + return; + } + + try { + launchDebugSession( config, launch, monitor ); + } + finally { + monitor.done(); + } + } + + private void launchDebugSession( final ILaunchConfiguration config, ILaunch l, IProgressMonitor monitor ) throws CoreException { + if ( monitor.isCanceled() ) { + return; + } + + SessionType sessionType = LaunchUtils.getSessionType(config); + boolean attach = LaunchUtils.getIsAttach(config); + + final GdbLaunch launch = (GdbLaunch)l; + + if (sessionType == SessionType.REMOTE) { + monitor.subTask( "Debugging remote C/C++ application" ); + } else { + monitor.subTask( "Debugging local C/C++ application" ); + } + + IPath exePath = new Path(""); //$NON-NLS-1$ + // An attach session does not need to necessarily have an + // executable specified. This is because: + // - In remote multi-process attach, there will be more than one executable + // In this case executables need to be specified differently. + // The current solution is to use the solib-search-path to specify + // the path of any executable we can attach to. + // - In local single process, GDB has the ability to find the executable + // automatically. + // + // An attach session also does not need to necessarily have a project + // specified. This is because we can perform source lookup towards + // code that is outside the workspace. + // See bug 244567 + if (!attach) { + // First verify we are dealing with a proper project. + ICProject project = LaunchUtils.verifyCProject(config); + // Now verify we know the program to debug. + exePath = LaunchUtils.verifyProgramPath(config, project); + // Finally, make sure the program is a proper binary. + LaunchUtils.verifyBinary(config, exePath); + } + + monitor.worked( 1 ); + + String gdbVersion = LaunchUtils.getGDBVersion(config); + + // First make sure non-stop is supported, if the user want to use this mode + if (isNonStopSession && !isNonStopSupported(gdbVersion)) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Non-stop mode is only supported starting with GDB " + NON_STOP_FIRST_VERSION, null)); //$NON-NLS-1$ + } + + launch.setServiceFactory(newServiceFactory(gdbVersion)); + + // Create and invoke the launch sequence to create the debug control and services + IProgressMonitor subMon1 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + final ServicesLaunchSequence servicesLaunchSequence = + new ServicesLaunchSequence(launch.getSession(), launch, subMon1); + + launch.getSession().getExecutor().execute(servicesLaunchSequence); + try { + servicesLaunchSequence.get(); + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in services launch sequence", e1.getCause())); //$NON-NLS-1$ + } + + if (monitor.isCanceled()) + return; + + // The initializeControl method should be called after the ICommandControlService + // be initialized in the ServicesLaunchSequence above. This is because it is that + // service that will trigger the launch cleanup (if we need it during this launch) + // through an ICommandControlShutdownDMEvent + launch.initializeControl(); + + // Add the CLI and "inferior" process objects to the launch. + launch.addCLIProcess("gdb"); //$NON-NLS-1$ + launch.addInferiorProcess(exePath.lastSegment()); + + monitor.worked(1); + + // Create and invoke the final launch sequence to setup GDB + IProgressMonitor subMon2 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + final Sequence finalLaunchSequence = + getFinalLaunchSequence(launch.getSession().getExecutor(), launch, sessionType, attach, subMon2); + + launch.getSession().getExecutor().execute(finalLaunchSequence); + boolean succeed = false; + try { + finalLaunchSequence.get(); + succeed = true; + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in final launch sequence", e1.getCause())); //$NON-NLS-1$ + } finally { + if (!succeed) { + // finalLaunchSequence failed. Shutdown the session so that all started + // services including any GDB process are shutdown. (bug 251486) + // + Query launchShutdownQuery = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + launch.shutdownSession(rm); + } + }; + + launch.getSession().getExecutor().execute(launchShutdownQuery); + + // wait for the shutdown to finish. + // The Query.get() method is a synchronous call which blocks until the + // query completes. + try { + launchShutdownQuery.get(); + } catch (InterruptedException e) { + throw new DebugException( new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "InterruptedException while shutting down debugger launch " + launch, e)); //$NON-NLS-1$ + } catch (ExecutionException e) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in shutting down debugger launch " + launch, e)); //$NON-NLS-1$ + } + } + } + } + + /* + * This method can be overridden by subclasses to allow to change the final launch sequence without + * having to change the entire GdbLaunchDelegate + */ + protected Sequence getFinalLaunchSequence(DsfExecutor executor, GdbLaunch launch, SessionType type, boolean attach, IProgressMonitor pm) { + return new FinalLaunchSequence(executor, launch, type, attach, pm); + } + + + private boolean isNonStopSession(ILaunchConfiguration config) { + try { + boolean nonStopMode = config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, + IGDBLaunchConfigurationConstants.DEBUGGER_NON_STOP_DEFAULT); + return nonStopMode; + } catch (CoreException e) { + } + return false; + } + + + @Override + public boolean preLaunchCheck(ILaunchConfiguration config, String mode, IProgressMonitor monitor) throws CoreException { + // no pre launch check for core file + if (mode.equals(ILaunchManager.DEBUG_MODE) && LaunchUtils.getSessionType(config) == SessionType.CORE) return true; + + return super.preLaunchCheck(config, mode, monitor); + } + + @Override + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { + // Need to configure the source locator before creating the launch + // because once the launch is created and added to launch manager, + // the adapters will be created for the whole session, including + // the source lookup adapter. + + isNonStopSession = isNonStopSession(configuration); + + GdbLaunch launch = new GdbLaunch(configuration, mode, null); + launch.initialize(); + launch.setSourceLocator(getSourceLocator(configuration, launch.getSession())); + return launch; + } + + private ISourceLocator getSourceLocator(ILaunchConfiguration configuration, DsfSession session) throws CoreException { + DsfSourceLookupDirector locator = new DsfSourceLookupDirector(session); + String memento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String)null); + if (memento == null) { + locator.initializeDefaults(configuration); + } else { + locator.initializeFromMemento(memento, configuration); + } + return locator; + } + + /** + * Recursively creates a set of projects referenced by the current project + * + * @param proj + * The current project + * @param referencedProjSet + * A set of referenced projects + * @throws CoreException + * if an error occurs while getting referenced projects from the + * current project + */ + private HashSet getReferencedProjectSet(IProject proj, HashSet referencedProjSet) throws CoreException { + // The top project is a reference too and it must be added at the top to avoid cycles + referencedProjSet.add(proj); + + IProject[] projects = proj.getReferencedProjects(); + for (IProject refProject : projects) { + if (refProject.exists() && !referencedProjSet.contains(refProject)) { + getReferencedProjectSet(refProject, referencedProjSet); + } + } + return referencedProjSet; + } + + /** + * Returns the order list of projects to build before launching. + * Used in buildForLaunch() + */ + @Override + protected IProject[] getBuildOrder(ILaunchConfiguration configuration, String mode) throws CoreException { + IProject[] orderedProjects = null; + ArrayList orderedProjList = null; + + ICProject cProject = LaunchUtils.verifyCProject(configuration); + if (cProject != null) { + HashSet projectSet = getReferencedProjectSet(cProject.getProject(), new HashSet()); + + String[] orderedNames = ResourcesPlugin.getWorkspace().getDescription().getBuildOrder(); + if (orderedNames != null) { + //Projects may not be in the build order but should still be built if selected + ArrayList unorderedProjects = new ArrayList(projectSet.size()); + unorderedProjects.addAll(projectSet); + orderedProjList = new ArrayList(projectSet.size()); + + for (String projectName : orderedNames) { + for (IProject proj : unorderedProjects) { + if (proj.getName().equals(projectName)) { + orderedProjList.add(proj); + unorderedProjects.remove(proj); + break; + } + } + } + + // Add any remaining projects to the end of the list + orderedProjList.addAll(unorderedProjects); + + orderedProjects = orderedProjList.toArray(new IProject[orderedProjList.size()]); + } else { + // Try the project prerequisite order then + IProject[] projects = projectSet.toArray(new IProject[projectSet.size()]); + orderedProjects = ResourcesPlugin.getWorkspace().computeProjectOrder(projects).projects; + } + } + return orderedProjects; + } + + /* Used in finalLaunchCheck() */ + @Override + protected IProject[] getProjectsForProblemSearch(ILaunchConfiguration configuration, String mode) throws CoreException { + return getBuildOrder(configuration, mode); + } + + /** + * Searches for compile errors in the specified project + * Used in finalLaunchCheck() + * @param proj + * The project to search + * @return true if compile errors exist, otherwise false + */ + @Override + protected boolean existsProblems(IProject proj) throws CoreException { + IMarker[] markers = proj.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); + if (markers.length > 0) { + for (IMarker marker : markers) { + Integer severity = (Integer)marker.getAttribute(IMarker.SEVERITY); + if (severity != null) { + return severity.intValue() >= IMarker.SEVERITY_ERROR; + } + } + } + return false; + } + + private boolean isNonStopSupported(String version) { + if (NON_STOP_FIRST_VERSION.compareTo(version) <= 0) { + return true; + } + return false; + } + + // A subclass can override this method and provide its own ServiceFactory. + protected IDsfDebugServicesFactory newServiceFactory(String version) { + + if (isNonStopSession && isNonStopSupported(version)) { + return new GdbDebugServicesFactoryNS(version); + } + + if (version.startsWith("6.6") || //$NON-NLS-1$ + version.startsWith("6.7") || //$NON-NLS-1$ + version.startsWith("6.8")) { //$NON-NLS-1$ + return new GdbDebugServicesFactory(version); + } + + return new GdbDebugServicesFactory(version); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.java new file mode 100644 index 00000000000..449e2df8f16 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - initial API and implementation + * Ericsson - Update for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +public class LaunchMessages { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.gdb.launching.LaunchMessages";//$NON-NLS-1$ + + private static ResourceBundle RESOURCE_BUNDLE = null; + + static { + try { + RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME); + } + catch (MissingResourceException x) { + } + } + + private LaunchMessages() {} + + public static String getFormattedString(String key, String arg) { + return MessageFormat.format(getString(key), (Object[])new String[]{arg}); + } + + public static String getFormattedString(String key, String[] args) { + return MessageFormat.format(getString(key), (Object[])args); + } + + public static String getString(String key) { + if (RESOURCE_BUNDLE == null) return '!' + key + '!'; + return RESOURCE_BUNDLE.getString(key); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties new file mode 100644 index 00000000000..7ee2543bc37 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchMessages.properties @@ -0,0 +1,159 @@ +############################################################################### +# Copyright (c) 2008 QNX Software Systems 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: +# QNX Software Systems - Initial API and implementation +# Monta Vista - Joanne Woo - Bug 87556 +# Nokia - Ken Ryall - Bug 118894 +############################################################################### + +AbstractCLaunchDelegate.Debugger_not_installed=CDT Debugger not installed +AbstractCLaunchDelegate.C_Project_not_specified=C Project not specified +AbstractCLaunchDelegate.Not_a_C_CPP_project=Project is not a C/C++ project +AbstractCLaunchDelegate.Program_file_not_specified=Program file not specified +AbstractCLaunchDelegate.Program_file_does_not_exist=Program file does not exist +AbstractCLaunchDelegate.PROGRAM_PATH_not_found={0} not found +AbstractCLaunchDelegate.Working_directory_does_not_exist=Working directory does not exist +AbstractCLaunchDelegate.WORKINGDIRECTORY_PATH_not_found=The working directory {0} does not exist. +AbstractCLaunchDelegate.Project_NAME_does_not_exist=Project {0} does not exist +AbstractCLaunchDelegate.Project_NAME_is_closed=Project {0} is closed +AbstractCLaunchDelegate.Debugger_Process=Debugger Process +AbstractCLaunchDelegate.building_projects=Building prerequisite project list +AbstractCLaunchDelegate.building=Building +AbstractCLaunchDelegate.searching_for_errors=Searching for compile errors +AbstractCLaunchDelegate.searching_for_errors_in=Searching for compile errors in +AbstractCLaunchDelegate.20=Building prerequisite project list +AbstractCLaunchDelegate.Program_is_not_a_recongnized_executable=Program is not a recognized executable. + +LocalRunLaunchDelegate.Launching_Local_C_Application=Launching Local C/C++ Application +LocalRunLaunchDelegate.Failed_setting_runtime_option_though_debugger=Failed to set program arguments, environment or working directory. +LocalRunLaunchDelegate.Error_starting_process=Error starting process +LocalRunLaunchDelegate.Does_not_support_working_dir=Eclipse runtime does not support working directory + +LocalAttachLaunchDelegate.Attaching_to_Local_C_Application=Attaching to Local C/C++ Application +LocalAttachLaunchDelegate.No_Process_ID_selected=No Process ID selected +LocalAttachLaunchDelegate.Select_Process=Select Process +LocalAttachLaunchDelegate.Platform_cannot_list_processes=Current platform does not support listing processes +LocalAttachLaunchDelegate.Select_Process_to_attach_debugger_to=Select a Process to attach the debugger to: +LocalAttachLaunchDelegate.CDT_Launch_Error=CDT Launch Error + +CoreFileLaunchDelegate.Launching_postmortem_debugger=Launching postmortem debugger +CoreFileLaunchDelegate.No_Corefile_selected=No Corefile selected +CoreFileLaunchDelegate.No_Shell_available_in_Launch=No Shell available in Launch +CoreFileLaunchDelegate.Select_Corefile=Select Corefile +CoreFileLaunchDelegate.Corefile_not_accessible=Core file is not accessible. +CoreFileLaunchDelegate.Corefile_not_readable=Core file does not exist or is not readable. +CoreFileLaunchDelegate.postmortem_debugging_failed=Post-mortem debugging failed + +CApplicationLaunchShortcut.Application_Launcher=Application Launcher +CApplicationLaunchShortcut.ChooseConfigToDebug=Choose a debug configuration to debug +CApplicationLaunchShortcut.ChooseConfigToRun=Choose a configuration to run +CApplicationLaunchShortcut.CLocalApplication=C Local Application +CApplicationLaunchShortcut.ChooseLocalAppToDebug=Choose a local application to debug +CApplicationLaunchShortcut.ChooseLocalAppToRun=Choose a local application to run +CApplicationLaunchShortcut.Launch_failed_no_binaries=Launch failed no binaries +CApplicationLaunchShortcut.LaunchFailed=Launch failed +CApplicationLaunchShortcut.LaunchDebugConfigSelection=Launch Debug Configuration Selection +CApplicationLaunchShortcut.LaunchConfigSelection=Launch Configuration Selection +CApplicationLaunchShortcut.Invalid_launch_mode_1=Invalid launch mode +CApplicationLaunchShortcut.Invalid_launch_mode_2=Invalid launch mode. +CApplicationLaunchShortcut.Invalid_launch_mode_3=Invalid launch mode. +CApplicationLaunchShortcut.ChooseLaunchConfigToDebug=Choose a launch configuration to debug +CApplicationLaunchShortcut.ChooseLaunchConfigToRun=Choose a launch configuration to run +CApplicationLaunchShortcut.Launch_failed_no_project_selected=Launch failed no project selected + +AbstractCDebuggerTab.No_debugger_available=No debugger available +AbstractCDebuggerTab.Debugger=Debugger +AbstractCDebuggerTab.ErrorLoadingDebuggerPage=Error Loading Debugger UI Component. + +LaunchUIPlugin.Error=Error + +CMainTab.Project_required=Project required +CMainTab.Enter_project_before_searching_for_program=Project must first be entered before searching for a program +CMainTab.Program_Selection=Program Selection +CMainTab.Enter_project_before_browsing_for_program=Project must first be entered before browsing for a program +CMainTab.Program_selection=Program selection +CMainTab.Selection_must_be_file=Selection must be a file +CMainTab.Selection_must_be_binary_file=Selection must be a binary file +CMainTab.Project_Selection=Project Selection +CMainTab.Choose_project_to_constrain_search_for_program=Choose a &project to constrain the search for a program +CMainTab.Project_not_specified=Project not specified +CMainTab.Program_not_specified=Program not specified +CMainTab.Project_must_be_opened=Project must be opened +CMainTab.Program_does_not_exist=Program does not exist +CMainTab.Main=Main +CMainTab.&ProjectColon=&Project: +CMainTab.C/C++_Application=C/C++ Application: +CMainTab.Search...=Searc&h Project... +CMainTab.Choose_program_to_run=Choose a &program to run: +CMainTab.Choose_program_to_run_from_NAME=Choose a program to run from {0}: +CMainTab.UseTerminal=Connect process input & output to a terminal. +CMainTab.Program_is_not_a_recongnized_executable=Program is not a recognized executable. + +CDebuggerTab.Advanced_Options_Dialog_Title=Advanced Options +CDebuggerTab.Stop_at_main_on_startup=Stop on startup at: +CDebuggerTab.Automatically_track_values_of=Automatically track the values of +CDebuggerTab.Stop_on_startup_at_can_not_be_empty=The "Stop on startup at" field can not be empty. +CDebuggerTab.Debugger_Options=Debugger Options +CDebuggerTab.Mode_not_supported=Mode ''{0}'' is not supported by selected debugger +CDebuggerTab.Advanced=Advanced... +CDebuggerTab.Variables=Variables +CDebuggerTab.Registers=Registers +CDebuggerTab.No_debugger_available=No debugger available +CDebuggerTab.CPU_is_not_supported=The CPU is not supported by selected debugger. +CDebuggerTab.Platform_is_not_supported=The project platform is not supported by the selected debugger. + +CoreFileDebuggerTab.No_debugger_available=No debugger available +CoreFileDebuggerTab.platform_is_not_supported=The project platform is not supported by the selected debugger. + +CEnvironmentTab.Edit_Variable=Edit Variable +CEnvironmentTab.New_Variable=New Variable +CEnvironmentTab.NameColon=Name: +CEnvironmentTab.ValueColon=Value: +CEnvironmentTab.Name=Name +CEnvironmentTab.Value=Value +CEnvironmentTab.New...=New... +CEnvironmentTab.Import...=Import... +CEnvironmentTab.Edit...=Edit... +CEnvironmentTab.Remove=Remove +CEnvironmentTab.Environment=Environment +CEnvironmentTab.Existing_Environment_Variable=Existing Environment Variable +CEnvironmentTab.Environment_variable_NAME_exists=Environment variable \" {0} \" exists.\nDo you want to overwrite? + +CArgumentsTab.C/C++_Program_Arguments=Program arguments: +CArgumentsTab.Arguments=Arguments +CArgumentsTab.Variables=Variables... + +WorkingDirectoryBlock.4=Select a &workspace relative working directory: +WorkingDirectoryBlock.7=Select a working directory for the launch configuration: +WorkingDirectoryBlock.0=W&orkspace... +WorkingDirectoryBlock.Working_Directory_8=Working Directory +WorkingDirectoryBlock.Working_directory=Working directory: +WorkingDirectoryBlock.10=Working directory does not exist +WorkingDirectoryBlock.Use_default=Use de&fault +WorkingDirectoryBlock.17=Variabl&es... +WorkingDirectoryBlock.1=File S&ystem... +WorkingDirectoryBlock.Exception_occurred_reading_configuration___15=Exception occurred reading configuration: + +Launch.common.Exception_occurred_reading_configuration_EXCEPTION=Exception occurred reading configuration {0} +Launch.common.DebuggerColon=Debugger: +Launch.common.BinariesColon=Binaries: +Launch.common.QualifierColon=Qualifier: +Launch.common.Browse_1=&Browse... +Launch.common.Browse_2=B&rowse... +Launch.common.Project_does_not_exist=Project does not exist +LocalCDILaunchDelegate.0=Launching Local C/C++ Application +LocalCDILaunchDelegate.1=Launching debugger session +LocalCDILaunchDelegate.2=Debugging local C/C++ application +LocalCDILaunchDelegate.3=Attaching to Local C/C++ Application +LocalCDILaunchDelegate.4=No Process ID selected. +LocalCDILaunchDelegate.5=Launching postmortem debugger session +LocalCDILaunchDelegate.6=No core file selected +LocalCDILaunchDelegate.7=Core file does not exist or is not readable. +LocalCDILaunchDelegate.8=Error starting process. +LocalCDILaunchDelegate.9=Eclipse runtime does not support working directory. +LocalCDILaunchDelegate.10=Failed to set program arguments, environment or working directory. diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java new file mode 100644 index 00000000000..6ed13567276 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/LaunchUtils.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.ICExtensionReference; +import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunchConfiguration; + +public class LaunchUtils { + + /** + * Verify the following things about the project: + * - is a valid project name given + * - does the project exist + * - is the project open + * - is the project a C/C++ project + */ + public static ICProject verifyCProject(ILaunchConfiguration configuration) throws CoreException { + String name = getProjectName(configuration); + if (name == null) { + abort(LaunchMessages.getString("AbstractCLaunchDelegate.C_Project_not_specified"), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_UNSPECIFIED_PROJECT); + return null; + } + ICProject cproject = getCProject(configuration); + if (cproject == null && name.length() > 0) { + IProject proj = ResourcesPlugin.getWorkspace().getRoot().getProject(name); + if (!proj.exists()) { + abort( + LaunchMessages.getFormattedString("AbstractCLaunchDelegate.Project_NAME_does_not_exist", name), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT); + } else if (!proj.isOpen()) { + abort(LaunchMessages.getFormattedString("AbstractCLaunchDelegate.Project_NAME_is_closed", name), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT); + } + abort(LaunchMessages.getString("AbstractCLaunchDelegate.Not_a_C_CPP_project"), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT); + } + return cproject; + } + + + /** + * Verify that program name of the configuration can be found as a file. + * + * @return Absolute path of the program location + */ + public static IPath verifyProgramPath(ILaunchConfiguration configuration, ICProject cproject) throws CoreException { + String programName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, (String)null); + if (programName == null) { + abort(LaunchMessages.getString("AbstractCLaunchDelegate.Program_file_not_specified"), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT); + } + + IPath programPath = new Path(programName); + if (programPath.isEmpty()) { + abort(LaunchMessages.getString("AbstractCLaunchDelegate.Program_file_does_not_exist"), null, //$NON-NLS-1$ + ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT); + } + + if (!programPath.isAbsolute() && cproject != null) { + // Find the specified program within the specified project + IFile wsProgramPath = cproject.getProject().getFile(programPath); + programPath = wsProgramPath.getLocation(); + } + + if (!programPath.toFile().exists()) { + abort(LaunchMessages.getString("AbstractCLaunchDelegate.Program_file_does_not_exist"), //$NON-NLS-1$ + new FileNotFoundException( + LaunchMessages.getFormattedString("AbstractCLaunchDelegate.PROGRAM_PATH_not_found", //$NON-NLS-1$ + programPath.toOSString())), + ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST); + } + + return programPath; + } + + + /** + * Verify that the executable path points to a valid binary file. + * + * @return An object representing the binary file. + */ + public static IBinaryObject verifyBinary(ILaunchConfiguration configuration, IPath exePath) throws CoreException { + ICExtensionReference[] parserRefs = CCorePlugin.getDefault().getBinaryParserExtensions(getCProject(configuration).getProject()); + for (ICExtensionReference parserRef : parserRefs) { + try { + IBinaryParser parser = (IBinaryParser)parserRef.createExtension(); + IBinaryObject exe = (IBinaryObject)parser.getBinary(exePath); + if (exe != null) { + return exe; + } + } catch (ClassCastException e) { + } catch (IOException e) { + } + } + + IBinaryParser parser = CCorePlugin.getDefault().getDefaultBinaryParser(); + try { + return (IBinaryObject)parser.getBinary(exePath); + } catch (ClassCastException e) { + } catch (IOException e) { + } + + abort(LaunchMessages.getString("AbstractCLaunchDelegate.Program_is_not_a_recognized_executable"), //$NON-NLS-1$ + new FileNotFoundException( + LaunchMessages.getFormattedString("AbstractCLaunchDelegate.Program_is_not_a_recognized_executable", //$NON-NLS-1$ + exePath.toOSString())), + ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_BINARY); + + return null; + } + + /** + * Throws a core exception with an error status object built from the given + * message, lower level exception, and error code. + * + * @param message + * the status message + * @param exception + * lower level exception associated with the error, or + * null if none + * @param code + * error code + */ + private static void abort(String message, Throwable exception, int code) throws CoreException { + MultiStatus status = new MultiStatus(GdbPlugin.PLUGIN_ID, code, message, exception); + status.add(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, code, + exception == null ? "" : exception.getLocalizedMessage(), //$NON-NLS-1$ + exception)); + throw new CoreException(status); + } + + /** + * Returns an ICProject based on the project name provided in the configuration. + * First look for a project by name, and then confirm it is a C/C++ project. + */ + public static ICProject getCProject(ILaunchConfiguration configuration) throws CoreException { + String projectName = getProjectName(configuration); + if (projectName != null) { + projectName = projectName.trim(); + if (projectName.length() > 0) { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(project); + if (cProject != null && cProject.exists()) { + return cProject; + } + } + } + return null; + } + + private static String getProjectName(ILaunchConfiguration configuration) throws CoreException { + return configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null); + } + + public static IPath getGDBPath(ILaunchConfiguration configuration) { + IPath retVal = new Path(IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT); + try { + retVal = new Path(configuration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, + IGDBLaunchConfigurationConstants.DEBUGGER_DEBUG_NAME_DEFAULT)); + } catch (CoreException e) { + } + return retVal; + } + + public static String getGDBVersion(final ILaunchConfiguration configuration) throws CoreException { + String line, version = "";//$NON-NLS-1$ + Process process = null; + String cmd = getGDBPath(configuration).toOSString() + " --version"; //$NON-NLS-1$ + try { + process = ProcessFactory.getFactory().exec(cmd); + } catch(IOException e) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Error while launching command: " + cmd, e.getCause()));//$NON-NLS-1$ + } + + try { + InputStream stream = process.getInputStream(); + Reader r = new InputStreamReader(stream); + BufferedReader reader = new BufferedReader(r); + + // These are the GDB version patterns I have seen up to now + // The pattern works for all of them extracting the version of 6.8.50.20080730 + // GNU gdb 6.8.50.20080730 + // GNU gdb (GDB) 6.8.50.20080730-cvs + // GNU gdb (Ericsson GDB 1.0-10) 6.8.50.20080730-cvs + Pattern pattern = Pattern.compile(" gdb( \\(.*\\))? (\\d*(\\.\\d*)*)", Pattern.MULTILINE); //$NON-NLS-1$ + + while ((line = reader.readLine()) != null) { + Matcher matcher = pattern.matcher(line); + if (matcher.find()) { + version = matcher.group(2); + } + } + } catch (IOException e) { + throw new DebugException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, + "Error reading GDB STDOUT after sending: " + cmd, e.getCause()));//$NON-NLS-1$ + } + + return version; + } + + public static boolean getIsAttach(ILaunchConfiguration config) { + try { + String debugMode = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ); + if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)) { + return false; + } else if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH)) { + return true; + } else if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE)) { + return false; + } else if (debugMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE)) { + return false; + } else if (debugMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH)) { + return true; + } + } catch (CoreException e) { + } + return false; + } + + public static SessionType getSessionType(ILaunchConfiguration config) { + try { + String debugMode = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ); + if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN)) { + return SessionType.LOCAL; + } else if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_ATTACH)) { + return SessionType.LOCAL; + } else if (debugMode.equals(ICDTLaunchConfigurationConstants.DEBUGGER_MODE_CORE)) { + return SessionType.CORE; + } else if (debugMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE)) { + return SessionType.REMOTE; + } else if (debugMode.equals(IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE_ATTACH)) { + return SessionType.REMOTE; + } + } catch (CoreException e) { + } + return SessionType.LOCAL; + } + + public static boolean getConsoleVerboseMode(ILaunchConfiguration config) { + boolean verboseMode = IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT; + try { + verboseMode = config.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_VERBOSE_MODE, + IGDBLaunchConfigurationConstants.DEBUGGER_VERBOSE_MODE_DEFAULT); + } catch (CoreException e) { + } + return verboseMode; + } + +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ServicesLaunchSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ServicesLaunchSequence.java new file mode 100644 index 00000000000..160fd5150b5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ServicesLaunchSequence.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Nokia - created GDBBackend service. Sep. 2008 + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.CSourceLookup; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IProgressMonitor; + +public class ServicesLaunchSequence extends Sequence { + + Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Create the back end GDB service. + // + fLaunch.getServiceFactory().createService(IMIBackend.class, fSession, fLaunch.getLaunchConfiguration()).initialize(requestMonitor); + } + }, + // Create and initialize the Connection service. + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // + // Create the connection. + // + fCommandControl = fLaunch.getServiceFactory().createService(ICommandControlService.class, fSession, fLaunch.getLaunchConfiguration()); + fCommandControl.initialize(requestMonitor); + } + }, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fProcService = (IMIProcesses)fLaunch.getServiceFactory().createService(IProcesses.class, fSession); + fProcService.initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IRunControl.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IMemory.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IModules.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IStack.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IExpressions.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fSourceLookup = (CSourceLookup)fLaunch.getServiceFactory().createService(ISourceLookup.class, fSession); + fSourceLookup.initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + ISourceLookupDMContext sourceLookupDmc = (ISourceLookupDMContext)fCommandControl.getContext(); + fSourceLookup.setSourceLookupDirector(sourceLookupDmc, (CSourceLookupDirector)fLaunch.getSourceLocator()); + requestMonitor.done(); + }}, + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + // Create the low-level breakpoint service + fLaunch.getServiceFactory().createService(IBreakpoints.class, fSession).initialize(new RequestMonitor(getExecutor(), requestMonitor)); + }}, + new Step() { @Override + public void execute(final RequestMonitor requestMonitor) { + // Create high-level breakpoint service and install breakpoints + // for the GDB debug context. + fLaunch.getServiceFactory().createService(MIBreakpointsManager.class, fSession).initialize(new RequestMonitor(getExecutor(), requestMonitor)); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IRegisters.class, fSession).initialize(requestMonitor); + }}, + new Step() { @Override + public void execute(RequestMonitor requestMonitor) { + fLaunch.getServiceFactory().createService(IDisassembly.class, fSession).initialize(requestMonitor); + }}, + }; + + DsfSession fSession; + GdbLaunch fLaunch; + + ICommandControlService fCommandControl; + IMIProcesses fProcService; + CSourceLookup fSourceLookup; + + public ServicesLaunchSequence(DsfSession session, GdbLaunch launch, IProgressMonitor pm) { + super(session.getExecutor(), pm, "Initializing debugger services", "Aborting debugger services initialization"); + fSession = session; + fLaunch = launch; + } + + @Override + public Step[] getSteps() { + return fSteps; + } + + + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ShutdownSequence.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ShutdownSequence.java new file mode 100644 index 00000000000..e89f4607f9a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/launching/ShutdownSequence.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.launching; + +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class ShutdownSequence extends Sequence { + + String fSessionId; + + String fApplicationName; + + String fDebugModelId; + + DsfServicesTracker fTracker; + + public ShutdownSequence(DsfExecutor executor, String sessionId, RequestMonitor requestMonitor) { + super(executor, requestMonitor); + fSessionId = sessionId; + } + + @Override + public Step[] getSteps() { + return fSteps; + } + + private final Step[] fSteps = new Step[] { new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + assert GdbPlugin.getBundleContext() != null; + fTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fSessionId); + requestMonitor.done(); + } + + @Override + public void rollBack(RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IDisassembly.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IRegisters.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(MIBreakpointsManager.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IBreakpoints.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(ISourceLookup.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IExpressions.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IStack.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IModules.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IMemory.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IRunControl.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IProcesses.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(ICommandControl.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(IMIBackend.class, requestMonitor); + } + }, new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + } }; + + @SuppressWarnings("unchecked") + private void shutdownService(Class clazz, final RequestMonitor requestMonitor) { + IDsfService service = (IDsfService)fTracker.getService(clazz); + if (service != null) { + service.shutdown(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + requestMonitor.done(); + } + }); + } else { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Service '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$//$NON-NLS-2$ + requestMonitor.done(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java new file mode 100644 index 00000000000..e7c08175ae6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBBackend.java @@ -0,0 +1,568 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems, Nokia 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: + * Nokia - initial API and implementation with some code moved from GDBControl. + * Wind River System + * Ericsson + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; +import org.eclipse.cdt.dsf.gdb.service.command.GDBControl.InitializationShutdownStep; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.spawner.ProcessFactory; +import org.eclipse.cdt.utils.spawner.Spawner; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.variables.VariablesPlugin; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.osgi.framework.BundleContext; + +/** + * Implementation of {@link IGDBBackend} for the common case where GDB is launched + * in local file system on host PC where Eclipse runs. This also manages some GDB parameters + * from a given launch configuration.
+ *
+ * You can subclass for you special needs. + * + * @since 1.1 + */ +public class GDBBackend extends AbstractDsfService implements IGDBBackend { + + private ILaunchConfiguration fLaunchConfiguration; + + /* + * Parameters for launching GDB. + */ + private IPath fProgramPath; + private IPath fGDBWorkingDirectory; + private String fGDBInitFile; + private List fSharedLibPaths; + private String fProgramArguments; + + private SessionType fSessionType; + private Boolean fAttach; + + /** + * Unique ID of this service instance. + */ + private final String fBackendId; + private static int fgInstanceCounter = 0; + + /* + * Service state parameters. + */ + private MonitorJob fMonitorJob; + private Process fProcess; + private int fGDBExitValue; + private int fGDBLaunchTimeout = 30; + + public GDBBackend(DsfSession session, ILaunchConfiguration lc) { + super(session); + fBackendId = "gdb[" +Integer.toString(fgInstanceCounter++) + "]"; //$NON-NLS-1$//$NON-NLS-2$ + fLaunchConfiguration = lc; + + try { + // Don't call verifyCProject, because the JUnit tests are not setting a project + ICProject cproject = LaunchUtils.getCProject(lc); + fProgramPath = LaunchUtils.verifyProgramPath(lc, cproject); + } catch (CoreException e) { + fProgramPath = new Path(""); //$NON-NLS-1$ + } + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + public void doInitialize(final RequestMonitor requestMonitor) { + + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new GDBProcessStep(InitializationShutdownStep.Direction.INITIALIZING), + new MonitorJobStep(InitializationShutdownStep.Direction.INITIALIZING), + new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), + }; + + Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return initializeSteps; } + }; + getExecutor().execute(startupSequence); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + final Sequence.Step[] shutdownSteps = new Sequence.Step[] { + new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new MonitorJobStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new GDBProcessStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + }; + Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return shutdownSteps; } + }; + getExecutor().execute(shutdownSequence); + } + + + private IPath getGDBPath() { + return LaunchUtils.getGDBPath(fLaunchConfiguration); + } + + /* + * Options for GDB process. + * Allow subclass to override. + */ + protected String getGDBCommandLine() { + StringBuffer gdbCommandLine = new StringBuffer(getGDBPath().toOSString()); + + // The goal here is to keep options to an absolute minimum. + // All configuration should be done in the launch sequence + // to allow for more flexibility. + gdbCommandLine.append(" --interpreter"); //$NON-NLS-1$ + // We currently work with MI version 2. Don't use just 'mi' because it + // points to the latest MI version, while we want mi2 specifically. + gdbCommandLine.append(" mi2"); //$NON-NLS-1$ + // Don't read the gdbinit file here. It is read explicitly in + // the LaunchSequence to make it easier to customize. + gdbCommandLine.append(" --nx"); //$NON-NLS-1$ + + return gdbCommandLine.toString(); + } + + public String getGDBInitFile() throws CoreException { + if (fGDBInitFile == null) { + fGDBInitFile = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, + IGDBLaunchConfigurationConstants.DEBUGGER_GDB_INIT_DEFAULT); + } + + return fGDBInitFile; + } + + public IPath getGDBWorkingDirectory() throws CoreException { + if (fGDBWorkingDirectory == null) { + + // First try to use the user-specified working directory for the debugged program. + // This is fine only with local debug. + // For remote debug, the working dir of the debugged program will be on remote device + // and hence not applicable. In such case we may just use debugged program path on host + // as the working dir for GDB. + // However, we cannot find a standard/common way to distinguish remote debug from local + // debug. For instance, a local debug may also use gdbserver+gdb. So it's up to each + // debugger implementation to make the distinction. + // + IPath path = null; + String location = fLaunchConfiguration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, (String)null); + + if (location != null) { + String expandedLocation = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(location); + if (expandedLocation.length() > 0) { + path = new Path(expandedLocation); + } + } + + if (path != null) { + // Some validity check. Should have been done by UI code. + if (path.isAbsolute()) { + File dir = new File(path.toPortableString()); + if (! dir.isDirectory()) + path = null; + } else { + IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if (res instanceof IContainer && res.exists()) { + path = res.getLocation(); + } + else + // Relative but not found in workspace. + path = null; + } + } + + if (path == null) { + // default working dir is the project if this config has a project + ICProject cp = LaunchUtils.getCProject(fLaunchConfiguration); + if (cp != null) { + IProject p = cp.getProject(); + path = p.getLocation(); + } + else { + // no meaningful value found. Just return null. + } + } + + fGDBWorkingDirectory = path; + } + + return fGDBWorkingDirectory; + } + + public String getProgramArguments() throws CoreException { + if (fProgramArguments == null) { + fProgramArguments = fLaunchConfiguration.getAttribute( + ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, + (String)null); + + if (fProgramArguments != null) { + fProgramArguments = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(fProgramArguments); + } + } + + return fProgramArguments; + } + + public IPath getProgramPath() { + return fProgramPath; + } + + @SuppressWarnings("unchecked") + public List getSharedLibraryPaths() throws CoreException { + if (fSharedLibPaths == null) { + fSharedLibPaths = fLaunchConfiguration.getAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_SOLIB_PATH, + new ArrayList(0)); + } + + return fSharedLibPaths; + } + + /* + * Launch GDB process. + * Allow subclass to override. + */ + protected Process launchGDBProcess(String commandLine) throws CoreException { + Process proc = null; + try { + proc = ProcessFactory.getFactory().exec(commandLine); + } catch (IOException e) { + String message = "Error while launching command " + commandLine; //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, message, e)); + } + + return proc; + } + + public Process getProcess() { + return fProcess; + } + + public OutputStream getMIOutputStream() { + return fProcess.getOutputStream(); + }; + + public InputStream getMIInputStream() { + return fProcess.getInputStream(); + }; + + public String getId() { + return fBackendId; + } + + public void interrupt() { + if (fProcess instanceof Spawner) { + Spawner gdbSpawner = (Spawner) fProcess; + gdbSpawner.interrupt(); + } + } + + public void destroy() { + // destroy() should be supported even if it's not spawner. + if (getState() == State.STARTED) { + fProcess.destroy(); + } + } + + public State getState() { + if (fMonitorJob == null) { + return State.NOT_INITIALIZED; + } else if (fMonitorJob.fExited) { + return State.TERMINATED; + } else { + return State.STARTED; + } + } + + public int getExitCode() { + return fGDBExitValue; + } + + public SessionType getSessionType() { + if (fSessionType == null) { + fSessionType = LaunchUtils.getSessionType(fLaunchConfiguration); + } + return fSessionType; + } + + public boolean getIsAttachSession() { + if (fAttach == null) { + fAttach = LaunchUtils.getIsAttach(fLaunchConfiguration); + } + return fAttach; + } + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + protected class GDBProcessStep extends InitializationShutdownStep { + GDBProcessStep(Direction direction) { super(direction); } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + class GDBLaunchMonitor { + boolean fLaunched = false; + boolean fTimedOut = false; + } + final GDBLaunchMonitor fGDBLaunchMonitor = new GDBLaunchMonitor(); + + final RequestMonitor gdbLaunchRequestMonitor = new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleCompleted() { + if (!fGDBLaunchMonitor.fTimedOut) { + fGDBLaunchMonitor.fLaunched = true; + if (!isSuccess()) { + requestMonitor.setStatus(getStatus()); + } + requestMonitor.done(); + } + } + }; + + final Job startGdbJob = new Job("Start GDB Process Job") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + if (gdbLaunchRequestMonitor.isCanceled()) { + gdbLaunchRequestMonitor.setStatus(new Status(IStatus.CANCEL, GdbPlugin.PLUGIN_ID, -1, "Canceled starting GDB", null)); //$NON-NLS-1$ + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + String commandLine = getGDBCommandLine(); + + try { + fProcess = launchGDBProcess(commandLine); + } catch(CoreException e) { + gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, e.getMessage(), e)); + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + try { + Reader r = new InputStreamReader(getMIInputStream()); + BufferedReader reader = new BufferedReader(r); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.endsWith("(gdb)")) { //$NON-NLS-1$ + break; + } + } + } catch (IOException e) { + gdbLaunchRequestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error reading GDB STDOUT", e)); //$NON-NLS-1$ + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + + gdbLaunchRequestMonitor.done(); + return Status.OK_STATUS; + } + }; + startGdbJob.schedule(); + + getExecutor().schedule(new Runnable() { + public void run() { + // Only process the event if we have not finished yet (hit the breakpoint). + if (!fGDBLaunchMonitor.fLaunched) { + fGDBLaunchMonitor.fTimedOut = true; + Thread jobThread = startGdbJob.getThread(); + if (jobThread != null) { + jobThread.interrupt(); + } + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, "Timed out trying to launch GDB.", null)); //$NON-NLS-1$ + requestMonitor.done(); + } + }}, + fGDBLaunchTimeout, TimeUnit.SECONDS); + } + + @Override + protected void shutdown(final RequestMonitor requestMonitor) { + new Job("Terminating GDB process.") { //$NON-NLS-1$ + { + setSystem(true); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + destroy(); + + int attempts = 0; + while (attempts < 10) { + try { + // Don't know if we really need the exit value... but what the heck. + fGDBExitValue = fProcess.exitValue(); // throws exception if process not exited + + requestMonitor.done(); + return Status.OK_STATUS; + } catch (IllegalThreadStateException ie) { + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + attempts++; + } + requestMonitor.setStatus(new Status( + IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Process terminate failed", null)); //$NON-NLS-1$ + requestMonitor.done(); + return Status.OK_STATUS; + } + }.schedule(); + } + } + + protected class MonitorJobStep extends InitializationShutdownStep { + MonitorJobStep(Direction direction) { super(direction); } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + fMonitorJob = new MonitorJob( + fProcess, + new DsfRunnable() { + public void run() { + requestMonitor.done(); + } + }); + fMonitorJob.schedule(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + if (!fMonitorJob.fExited) { + fMonitorJob.kill(); + } + requestMonitor.done(); + } + } + + protected class RegisterStep extends InitializationShutdownStep { + RegisterStep(Direction direction) { super(direction); } + @Override + public void initialize(final RequestMonitor requestMonitor) { + register( + new String[]{ IMIBackend.class.getName(), + IGDBBackend.class.getName() }, + new Hashtable()); + + /* + * This event is not consumed by any one at present, instead it's + * the GDBControlInitializedDMEvent that's used to indicate that GDB + * back end is ready for MI commands. But we still fire the event as + * it does no harm and may be needed sometime.... 09/29/2008 + */ + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), IMIBackend.State.STARTED), + getProperties()); + + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + unregister(); + requestMonitor.done(); + } + } + + /** + * Monitors a system process, waiting for it to terminate, and + * then notifies the associated runtime process. + */ + private class MonitorJob extends Job { + boolean fExited = false; + DsfRunnable fMonitorStarted; + Process fMonProcess; + + @Override + protected IStatus run(IProgressMonitor monitor) { + synchronized(fMonProcess) { + getExecutor().submit(fMonitorStarted); + while (!fExited) { + try { + fMonProcess.waitFor(); + fGDBExitValue = fMonProcess.exitValue(); + } catch (InterruptedException ie) { + // clear interrupted state + Thread.interrupted(); + } finally { + fExited = true; + getSession().dispatchEvent( + new BackendStateChangedEvent(getSession().getId(), getId(), IMIBackend.State.TERMINATED), + getProperties()); + } + } + } + return Status.OK_STATUS; + } + + MonitorJob(Process process, DsfRunnable monitorStarted) { + super("GDB process monitor job."); //$NON-NLS-1$ + fMonProcess = process; + fMonitorStarted = monitorStarted; + setSystem(true); + } + + void kill() { + synchronized(fMonProcess) { + if (!fExited) { + getThread().interrupt(); + } + } + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBMemory_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBMemory_7_0.java new file mode 100644 index 00000000000..90ddaddde70 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBMemory_7_0.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.Hashtable; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIMemory; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.model.MemoryByte; + +public class GDBMemory_7_0 extends MIMemory { + + public GDBMemory_7_0(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + register(new String[] { MIMemory.class.getName(), IMemory.class.getName(), GDBMemory_7_0.class.getName()}, + new Hashtable()); + + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + @Override + protected void readMemoryBlock(IDMContext dmc, IAddress address, long offset, + int word_size, int count, DataRequestMonitor drm) + { + IDMContext threadOrMemoryDmc = dmc; + + IMIContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if(containerCtx != null) { + IGDBProcesses procService = getServicesTracker().getService(IGDBProcesses.class); + + if (procService != null) { + IMIExecutionDMContext[] execCtxs = procService.getExecutionContexts(containerCtx); + // Return any thread... let's take the first one. + if (execCtxs != null && execCtxs.length > 0) { + threadOrMemoryDmc = execCtxs[0]; + } + } + } + + super.readMemoryBlock(threadOrMemoryDmc, address, offset, word_size, count, drm); + } + + @Override + protected void writeMemoryBlock(IDMContext dmc, IAddress address, long offset, + int word_size, int count, byte[] buffer, RequestMonitor rm) + { + IDMContext threadOrMemoryDmc = dmc; + + IMIContainerDMContext containerCtx = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if(containerCtx != null) { + IGDBProcesses procService = getServicesTracker().getService(IGDBProcesses.class); + + if (procService != null) { + IMIExecutionDMContext[] execCtxs = procService.getExecutionContexts(containerCtx); + // Return any thread... let's take the first one. + if (execCtxs != null && execCtxs.length > 0) { + threadOrMemoryDmc = execCtxs[0]; + } + } + } + + super.writeMemoryBlock(threadOrMemoryDmc, address, offset, word_size, count, buffer, rm); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java new file mode 100644 index 00000000000..e23b06be894 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBProcesses.java @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.IProcessInfo; +import org.eclipse.cdt.core.IProcessList; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIMonitorListProcesses; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIMonitorListProcessesInfo; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + + +public class GDBProcesses extends MIProcesses { + + private class GDBContainerDMC extends MIContainerDMC + implements IMemoryDMContext + { + public GDBContainerDMC(String sessionId, IProcessDMContext processDmc, String groupId) { + super(sessionId, processDmc, groupId); + } + } + + private IGDBControl fGdb; + + // A map of pid to names. It is filled when we get all the + // processes that are running + private Map fProcessNames = new HashMap(); + + public GDBProcesses(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + /** + * This method initializes this service after our superclass's initialize() + * method succeeds. + * + * @param requestMonitor + * The call-back object to notify when this service's + * initialization is done. + */ + private void doInitialize(RequestMonitor requestMonitor) { + + fGdb = getServicesTracker().getService(IGDBControl.class); + + // Register this service. + register(new String[] { IProcesses.class.getName(), + IMIProcesses.class.getName(), + MIProcesses.class.getName(), + GDBProcesses.class.getName() }, + new Hashtable()); + + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + IProcessDMContext procDmc = createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + IContainerDMContext containerDmc = createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + fGdb.getInferiorProcess().setContainerContext(containerDmc); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + /** + * @return The bundle context of the plug-in to which this service belongs. + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + @Override + public IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, + String groupId) { + return new GDBContainerDMC(getSession().getId(), processDmc, groupId); + } + + @Override + public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IMIProcessDMContext) { + String pidStr = ((IMIProcessDMContext)dmc).getProcId(); + // In our context hierarchy we don't actually use the pid in this version, because in this version, + // we only debug a single process. This means we will not have a proper pid in all cases + // inside the context, so must find it another way. Note that this method is also called to find the name + // of processes to attach to, and in this case, we do have the proper pid. + if (pidStr == null || pidStr.length() == 0) { + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (inferiorProcess != null) { + pidStr = inferiorProcess.getPid(); + } + } + int pid = -1; + try { + pid = Integer.parseInt(pidStr); + } catch (NumberFormatException e) { + } + + String name = fProcessNames.get(pid); + // If we still don't find the name in our list, return the default name of our program + if (name == null) { + IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); + name = backend.getProgramPath().lastSegment(); + } + rm.setData(new MIThreadDMData(name, pidStr)); + rm.done(); + } else { + super.getExecutionData(dmc, rm); + } + } + + @Override + public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor rm) { + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (!fGdb.isConnected() && + inferiorProcess != null && + inferiorProcess.getState() != MIInferiorProcess.State.TERMINATED) { + + rm.setData(true); + } else { + rm.setData(false); + } + rm.done(); + } + + @Override + public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { + super.attachDebuggerToProcess( + procCtx, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + fGdb.setConnected(true); + + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (inferiorProcess != null) { + inferiorProcess.setPid(((IMIProcessDMContext)procCtx).getProcId()); + } + + rm.setData(getData()); + rm.done(); + } + }); + } + + @Override + public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); // don't turn on yet, as we need to generate events to use this properly + rm.done(); + } + + @Override + public void detachDebuggerFromProcess(IDMContext dmc, final RequestMonitor rm) { + super.detachDebuggerFromProcess( + dmc, + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + fGdb.setConnected(false); + + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (inferiorProcess != null) { + inferiorProcess.setPid(null); + } + + rm.done(); + } + }); + } + + @Override + public void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor rm) { + MIInferiorProcess inferiorProcess = fGdb.getInferiorProcess(); + if (fGdb.isConnected() && + inferiorProcess != null && + inferiorProcess.getState() != MIInferiorProcess.State.TERMINATED) { + + final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if (containerDmc == null) { + // This service version only handles a single process to debug, therefore, we can simply + // create the context describing this process ourselves. + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + String groupId = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = createProcessContext(controlDmc, groupId); + IMIContainerDMContext newContainerDmc = createContainerContext(procDmc, groupId); + rm.setData(new IContainerDMContext[] {newContainerDmc}); + rm.done(); + } else { + // List of threads + super.getProcessesBeingDebugged(dmc, rm); + } + } else { + rm.setData(new IDMContext[0]); + rm.done(); + } + } + + @Override + public void getRunningProcesses(IDMContext dmc, final DataRequestMonitor rm) { + final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); + if (backend.getSessionType() == SessionType.LOCAL) { + IProcessList list = null; + try { + list = CCorePlugin.getDefault().getProcessList(); + } catch (CoreException e) { + } + + if (list == null) { + // If the list is null, the prompter will deal with it + fProcessNames.clear(); + rm.setData(null); + } else { + fProcessNames.clear(); + for (IProcessInfo procInfo : list.getProcessList()) { + fProcessNames.put(procInfo.getPid(), procInfo.getName()); + } + rm.setData(makeProcessDMCs(controlDmc, list.getProcessList())); + } + rm.done(); + } else { + // monitor list processes is only for remote session + fGdb.queueCommand( + new CLIMonitorListProcesses(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + for (IProcessInfo procInfo : getData().getProcessList()) { + fProcessNames.put(procInfo.getPid(), procInfo.getName()); + } + rm.setData(makeProcessDMCs(controlDmc, getData().getProcessList())); + } else { + // The monitor list command is not supported. + // Just return an empty list and let the caller deal with it. + fProcessNames.clear(); + rm.setData(new IProcessDMContext[0]); + } + rm.done(); + } + + }); + } + } + + private IProcessDMContext[] makeProcessDMCs(ICommandControlDMContext controlDmc, IProcessInfo[] processes) { + IProcessDMContext[] procDmcs = new IMIProcessDMContext[processes.length]; + for (int i=0; i + * + * @param sessionId Session that this context belongs to. + * @param containerDmc The container that this context belongs to. + * @param threadDmc The thread context parents of this context. + * @param threadId GDB/MI thread identifier. + */ + protected MIExecutionDMC(String sessionId, IContainerDMContext containerDmc, IThreadDMContext threadDmc, String threadId) { + super(sessionId, + containerDmc == null && threadDmc == null ? new IDMContext[0] : + containerDmc == null ? new IDMContext[] { threadDmc } : + threadDmc == null ? new IDMContext[] { containerDmc } : + new IDMContext[] { containerDmc, threadDmc }); + fThreadId = threadId; + } + + /** + * Returns the GDB/MI thread identifier of this context. + * @return + */ + public int getThreadId(){ + try { + return Integer.parseInt(fThreadId); + } catch (NumberFormatException e) { + } + + return 0; + } + + public String getId(){ + return fThreadId; + } + + @Override + public String toString() { return baseToString() + ".thread[" + fThreadId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((MIExecutionDMC)obj).fThreadId.equals(fThreadId); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fThreadId.hashCode(); } + } + + /** + * Context representing a thread group of GDB/MI. + */ + @Immutable + private static class MIContainerDMC extends AbstractDMContext + implements IMIContainerDMContext + { + /** + * String ID that is used to identify the thread group in the GDB/MI protocol. + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createContainerContext + * to create instances of this context based on the group name. + * + * @param sessionId Session that this context belongs to. + * @param processDmc The process context that is the parent of this context. + * @param groupId GDB/MI thread group identifier. + */ + public MIContainerDMC(String sessionId, IProcessDMContext processDmc, String groupId) { + super(sessionId, processDmc == null ? new IDMContext[0] : new IDMContext[] { processDmc }); + fId = groupId; + } + + /** + * Returns the GDB/MI thread group identifier of this context. + */ + public String getGroupId(){ return fId; } + + @Override + public String toString() { return baseToString() + ".threadGroup[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIContainerDMC)obj).fId == null ? fId == null : ((MIContainerDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + private class GDBContainerDMC extends MIContainerDMC + implements IMemoryDMContext + { + public GDBContainerDMC(String sessionId, IProcessDMContext processDmc, String groupId) { + super(sessionId, processDmc, groupId); + } + } + + /** + * Context representing a thread. + */ + @Immutable + private static class MIThreadDMC extends AbstractDMContext + implements IThreadDMContext + { + /** + * ID used by GDB to refer to threads. + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createThreadContext} + * to create instances of this context based on the thread ID. + *

+ * + * @param sessionId Session that this context belongs to. + * @param processDmc The process that this thread belongs to. + * @param id thread identifier. + */ + public MIThreadDMC(String sessionId, IProcessDMContext processDmc, String id) { + super(sessionId, processDmc == null ? new IDMContext[0] : new IDMContext[] { processDmc }); + fId = id; + } + + /** + * Returns the thread identifier of this context. + * @return + */ + public String getId(){ return fId; } + + @Override + public String toString() { return baseToString() + ".OSthread[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIThreadDMC)obj).fId == null ? fId == null : ((MIThreadDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + @Immutable + private static class MIProcessDMC extends AbstractDMContext + implements IMIProcessDMContext + { + /** + * ID given by the OS. + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createProcessContext} + * to create instances of this context based on the PID. + *

+ * + * @param sessionId Session that this context belongs to. + * @param controlDmc The control context parent of this process. + * @param id process identifier. + */ + public MIProcessDMC(String sessionId, ICommandControlDMContext controlDmc, String id) { + super(sessionId, controlDmc == null ? new IDMContext[0] : new IDMContext[] { controlDmc }); + fId = id; + } + + public String getProcId() { return fId; } + + @Override + public String toString() { return baseToString() + ".proc[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIProcessDMC)obj).fId == null ? fId == null : ((MIProcessDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + /** + * The data of a corresponding thread or process. + */ + @Immutable + protected static class MIThreadDMData implements IThreadDMData { + final String fName; + final String fId; + + public MIThreadDMData(String name, String id) { + fName = name; + fId = id; + } + + public String getId() { return fId; } + public String getName() { return fName; } + public boolean isDebuggerAttached() { + return true; + } + } + + /** + * Event indicating that an container (debugged process) has started. This event + * implements the {@link IStartedMDEvent} from the IRunControl service. + */ + public static class ContainerStartedDMEvent extends AbstractDMEvent + implements IStartedDMEvent + { + public ContainerStartedDMEvent(IContainerDMContext context) { + super(context); + } + } + + /** + * Event indicating that an container is no longer being debugged. This event + * implements the {@link IExitedMDEvent} from the IRunControl service. + */ + public static class ContainerExitedDMEvent extends AbstractDMEvent + implements IExitedDMEvent + { + public ContainerExitedDMEvent(IContainerDMContext context) { + super(context); + } + } + + /** + * A map of thread id to thread group id. We use this to find out to which threadGroup a thread belongs. + */ + private Map fThreadToGroupMap = new HashMap(); + + private IGDBControl fCommandControl; + + // A cache for commands about the threadGroups + private CommandCache fContainerCommandCache; + + //A cache for commands about the threads + private CommandCache fThreadCommandCache; + + // A map of process id to process names. It is filled when we get all the processes that are running + private Map fProcessNames = new HashMap(); + + private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$ + + public GDBProcesses_7_0(DsfSession session) { + super(session); + } + + /** + * This method initializes this service. + * + * @param requestMonitor + * The request monitor indicating the operation is finished + */ + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + /** + * This method initializes this service after our superclass's initialize() + * method succeeds. + * + * @param requestMonitor + * The call-back object to notify when this service's + * initialization is done. + */ + private void doInitialize(RequestMonitor requestMonitor) { + + fCommandControl = getServicesTracker().getService(IGDBControl.class); + fContainerCommandCache = new CommandCache(getSession(), fCommandControl); + fContainerCommandCache.setContextAvailable(fCommandControl.getContext(), true); + fThreadCommandCache = new CommandCache(getSession(), fCommandControl); + fThreadCommandCache.setContextAvailable(fCommandControl.getContext(), true); + + getSession().addServiceEventListener(this, null); + fCommandControl.addEventListener(this); + + // Register this service. + register(new String[] { IProcesses.class.getName(), + IMIProcesses.class.getName(), + IGDBProcesses.class.getName(), + GDBProcesses_7_0.class.getName() }, + new Hashtable()); + + requestMonitor.done(); + } + + + /** + * This method shuts down this service. It unregisters the service, stops + * receiving service events, and calls the superclass shutdown() method to + * finish the shutdown process. + * + * @return void + */ + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(this); + fCommandControl.removeEventListener(this); + super.shutdown(requestMonitor); + } + + /** + * @return The bundle context of the plug-in to which this service belongs. + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + public IThreadDMContext createThreadContext(IProcessDMContext processDmc, String threadId) { + return new MIThreadDMC(getSession().getId(), processDmc, threadId); + } + + public IProcessDMContext createProcessContext(ICommandControlDMContext controlDmc, String pid) { + return new MIProcessDMC(getSession().getId(), controlDmc, pid); + } + + public IMIExecutionDMContext createExecutionContext(IContainerDMContext containerDmc, + IThreadDMContext threadDmc, + String threadId) { + return new MIExecutionDMC(getSession().getId(), containerDmc, threadDmc, threadId); + } + + public IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, + String groupId) { + return new GDBContainerDMC(getSession().getId(), processDmc, groupId); + } + + public IMIContainerDMContext createContainerContextFromThreadId(ICommandControlDMContext controlDmc, String threadId) { + String groupId = fThreadToGroupMap.get(threadId); + IProcessDMContext processDmc = createProcessContext(controlDmc, groupId); + return createContainerContext(processDmc, groupId); + } + + public IMIExecutionDMContext[] getExecutionContexts(IMIContainerDMContext containerDmc) { + String groupId = containerDmc.getGroupId(); + List execDmcList = new ArrayList(); + Iterator> iterator = fThreadToGroupMap.entrySet().iterator(); + while (iterator.hasNext()){ + Map.Entry entry = iterator.next(); + if (entry.getValue().equals(groupId)) { + String threadId = entry.getKey(); + IProcessDMContext procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); + IMIExecutionDMContext execDmc = createExecutionContext(containerDmc, + createThreadContext(procDmc, threadId), + threadId); + execDmcList.add(execDmc); + } + } + return execDmcList.toArray(new IMIExecutionDMContext[0]); + } + + /** + * This method obtains the model data for a given IThreadDMContext object + * which can represent a thread or a process. + * + * @param dmc + * The context for which we are requesting the data + * @param rm + * The request monitor that will contain the requested data + */ + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IThreadDMContext) { + getExecutionData((IThreadDMContext) dmc, + (DataRequestMonitor) rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getExecutionData(IThreadDMContext dmc, final DataRequestMonitor rm) { + if (dmc instanceof IMIProcessDMContext) { + String id = ((IMIProcessDMContext)dmc).getProcId(); + String name = fProcessNames.get(id); + if (name == null) name = "Unknown name"; //$NON-NLS-1$ + rm.setData(new MIThreadDMData(name, id)); + rm.done(); + } else if (dmc instanceof MIThreadDMC) { + final MIThreadDMC threadDmc = (MIThreadDMC)dmc; + + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + fThreadCommandCache.execute(new MIThreadInfo(controlDmc, threadDmc.getId()), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IThreadDMData threadData = null; + if (getData().getThreadList().length != 0) { + MIThread thread = getData().getThreadList()[0]; + if (thread.getThreadId().equals(threadDmc.getId())) { + threadData = new MIThreadDMData("", thread.getOsId()); //$NON-NLS-1$ + } + } + + if (threadData != null) { + rm.setData(threadData); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Could not get thread info", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof MIProcessDMC) { + MIProcessDMC procDmc = (MIProcessDMC)dmc; + rm.setData(createContainerContext(procDmc, procDmc.getProcId())); + } else if (dmc instanceof MIThreadDMC) { + MIThreadDMC threadDmc = (MIThreadDMC)dmc; + IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IMIProcessDMContext.class); + IMIContainerDMContext containerDmc = createContainerContext(procDmc, procDmc.getProcId()); + rm.setData(createExecutionContext(containerDmc, threadDmc, threadDmc.getId())); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid thread context.", null)); //$NON-NLS-1$ + } + + rm.done(); + } + + public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor rm) { + IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); + rm.setData(backend.getIsAttachSession()); + rm.done(); + } + + public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { + if (procCtx instanceof IMIProcessDMContext) { + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(procCtx, ICommandControlDMContext.class); + fCommandControl.queueCommand( + new MITargetAttach(controlDmc, ((IMIProcessDMContext)procCtx).getProcId()), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + fCommandControl.setConnected(true); + + IMIContainerDMContext containerDmc = createContainerContext(procCtx, + ((IMIProcessDMContext)procCtx).getProcId()); + rm.setData(containerDmc); + rm.done(); + } + }); + + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { + IGDBBackend backend = getServicesTracker().getService(IGDBBackend.class); + rm.setData(backend.getIsAttachSession() && fCommandControl.isConnected()); + rm.done(); + } + + public void detachDebuggerFromProcess(final IDMContext dmc, final RequestMonitor rm) { + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IMIProcessDMContext.class); + + if (controlDmc != null && procDmc != null) { + fCommandControl.queueCommand( + new MITargetDetach(controlDmc, procDmc.getProcId()), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // only if it is the last detach + fCommandControl.setConnected(false); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void canTerminate(IThreadDMContext thread, DataRequestMonitor rm) { + rm.setData(true); + rm.done(); + } + + public void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); + rm.done(); + } + + public void debugNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void getProcessesBeingDebugged(final IDMContext dmc, final DataRequestMonitor rm) { +// MIInferiorProcess inferiorProcess = fCommandControl.getInferiorProcess(); +// if (fCommandControl.isConnected() && +// inferiorProcess != null && +// inferiorProcess.getState() != MIInferiorProcess.State.TERMINATED) { + + final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if (containerDmc != null) { + fThreadCommandCache.execute( + new MIListThreadGroups(controlDmc, containerDmc.getGroupId()), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(makeExecutionDMCs(containerDmc, getData().getThreadInfo().getThreadList())); + rm.done(); + } + }); + } else { + fContainerCommandCache.execute( + new MIListThreadGroups(controlDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(makeContainerDMCs(controlDmc, getData().getGroupList())); + rm.done(); + } + }); + } +// } else { +// rm.setData(new IDMContext[0]); +// rm.done(); +// } + } + + private IExecutionDMContext[] makeExecutionDMCs(IContainerDMContext containerDmc, MIThread[] threadInfos) { + final IProcessDMContext procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); + + if (threadInfos.length == 0) { + // Main thread always exist even if it is not reported by GDB. + // So create thread-id = 0 when no thread is reported. + // This hack is necessary to prevent AbstractMIControl from issuing a thread-select + // because it doesn't work if the application was not compiled with pthread. + return new IMIExecutionDMContext[]{createExecutionContext(containerDmc, + createThreadContext(procDmc, FAKE_THREAD_ID), + FAKE_THREAD_ID)}; + } else { + IExecutionDMContext[] executionDmcs = new IMIExecutionDMContext[threadInfos.length]; + for (int i = 0; i < threadInfos.length; i++) { + String threadId = threadInfos[i].getThreadId(); + executionDmcs[i] = createExecutionContext(containerDmc, + createThreadContext(procDmc, threadId), + threadId); + } + return executionDmcs; + } + } + + private IMIContainerDMContext[] makeContainerDMCs(ICommandControlDMContext controlDmc, IThreadGroupInfo[] groups) { + IProcessDMContext[] procDmcs = makeProcessDMCs(controlDmc, groups); + + IMIContainerDMContext[] containerDmcs = new IMIContainerDMContext[groups.length]; + for (int i = 0; i < procDmcs.length; i++) { + String groupId = groups[i].getGroupId(); + IProcessDMContext procDmc = createProcessContext(controlDmc, groupId); + containerDmcs[i] = createContainerContext(procDmc, groupId); + } + return containerDmcs; + } + + public void getRunningProcesses(IDMContext dmc, final DataRequestMonitor rm) { + final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + + if (controlDmc != null) { + // Don't cache this command since the list can change at any time. + fCommandControl.queueCommand( + new MIListThreadGroups(controlDmc, true), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + for (IThreadGroupInfo groupInfo : getData().getGroupList()) { + fProcessNames.put(groupInfo.getPid(), groupInfo.getName()); + } + rm.setData(makeProcessDMCs(controlDmc, getData().getGroupList())); + } else { + rm.setData(new IProcessDMContext[0]); + } + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context.", null)); //$NON-NLS-1$ + rm.done(); + } + + } + + private IProcessDMContext[] makeProcessDMCs(ICommandControlDMContext controlDmc, IThreadGroupInfo[] processes) { + IProcessDMContext[] procDmcs = new IMIProcessDMContext[processes.length]; + for (int i=0; i rm) { + rm.setData(false); + rm.done(); + } + + public void runNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void terminate(IThreadDMContext thread, RequestMonitor rm) { + if (thread instanceof IMIProcessDMContext) { + fCommandControl.terminate(rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(final MIThreadGroupCreatedEvent e) { + IProcessDMContext procDmc = e.getDMContext(); + IMIContainerDMContext containerDmc = e.getGroupId() != null ? createContainerContext(procDmc, e.getGroupId()) : null; + getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), getProperties()); + } + + @DsfServiceEventHandler + public void eventDispatched(final MIThreadGroupExitedEvent e) { + IProcessDMContext procDmc = e.getDMContext(); + IMIContainerDMContext containerDmc = e.getGroupId() != null ? createContainerContext(procDmc, e.getGroupId()) : null; + getSession().dispatchEvent(new ContainerExitedDMEvent(containerDmc), getProperties()); + + } + + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + if (e instanceof IContainerResumedDMEvent) { + // This will happen in all-stop mode + fContainerCommandCache.setContextAvailable(e.getDMContext(), false); + fThreadCommandCache.setContextAvailable(e.getDMContext(), false); + } else { + // This will happen in non-stop mode + // Keep target available for Container commands + } + } + + + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + if (e instanceof IContainerSuspendedDMEvent) { + // This will happen in all-stop mode + fContainerCommandCache.setContextAvailable(e.getDMContext(), true); + fThreadCommandCache.setContextAvailable(e.getDMContext(), true); + } else { + // This will happen in non-stop mode + } + } + + // Event handler when a thread or threadGroup starts + @DsfServiceEventHandler + public void eventDispatched(IStartedDMEvent e) { + if (e instanceof ContainerStartedDMEvent) { + fContainerCommandCache.reset(); + } else { + fThreadCommandCache.reset(); + } + } + + // Event handler when a thread or a threadGroup exits + @DsfServiceEventHandler + public void eventDispatched(IExitedDMEvent e) { + if (e instanceof ContainerExitedDMEvent) { + fContainerCommandCache.reset(); + } else { + fThreadCommandCache.reset(); + } + } + + public void flushCache(IDMContext context) { + fContainerCommandCache.reset(context); + fThreadCommandCache.reset(context); + } + + /* + * Catch =thread-created/exited and =thread-group-exited events to update our + * groupId to threadId map. + */ + public void eventReceived(Object output) { + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + if (oobr instanceof MINotifyAsyncOutput) { + MINotifyAsyncOutput exec = (MINotifyAsyncOutput) oobr; + String miEvent = exec.getAsyncClass(); + if ("thread-created".equals(miEvent) || "thread-exited".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ + String threadId = null; + String groupId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("group-id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst) val).getString(); + } + } else if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + threadId = ((MIConst) val).getString(); + } + } + } + + // Until GDB is officially supporting multi-process, we may not get + // a groupId. In this case, we are running single process and we'll + // need its groupId + if (groupId == null) { + groupId = MIProcesses.UNIQUE_GROUP_ID; + } + + if ("thread-created".equals(miEvent)) { //$NON-NLS-1$ + // Update the thread to groupId map with the new groupId + fThreadToGroupMap.put(threadId, groupId); + } else { + fThreadToGroupMap.remove(threadId); + } + } else if ("thread-group-created".equals(miEvent) || "thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ + + String groupId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst) val).getString().trim(); + } + } + } + + if (groupId != null) { + if ("thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ + // Remove any entries for that group from our thread to group map + // When detaching from a group, we won't have received any thread-exited event + // but we don't want to keep those entries. + if (fThreadToGroupMap.containsValue(groupId)) { + Iterator> iterator = fThreadToGroupMap.entrySet().iterator(); + while (iterator.hasNext()){ + if (iterator.next().getValue().equals(groupId)) { + iterator.remove(); + } + } + } + } + } + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java new file mode 100644 index 00000000000..609940e1207 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson AB - Modified for additional functionality + * Nokia - create and use backend service. + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.service; + + +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class GDBRunControl extends MIRunControl { + private IGDBBackend fGdb; + private IMIProcesses fProcService; + + // Record list of execution contexts + private IExecutionDMContext[] fOldExecutionCtxts; + + + public GDBRunControl(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + + fGdb = getServicesTracker().getService(IGDBBackend.class); + fProcService = getServicesTracker().getService(IMIProcesses.class); + + register(new String[]{IRunControl.class.getName(), + MIRunControl.class.getName(), + GDBRunControl.class.getName()}, new Hashtable()); + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + @Override + public IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, int threadId) { + IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class); + + IThreadDMContext threadDmc = null; + if (procDmc != null) { + // For now, reuse the threadId as the OSThreadId + threadDmc = fProcService.createThreadContext(procDmc, Integer.toString(threadId)); + } + + return fProcService.createExecutionContext(container, threadDmc, Integer.toString(threadId)); + } + + @Override + public void suspend(IExecutionDMContext context, final RequestMonitor rm){ + canSuspend( + context, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData()) { + fGdb.interrupt(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + + + /* + * This is a HACK. Remove this method when GDB starts to account exited threads id in -thread-list-id command. + * Exited threads are reported in -thread-list-id command even after an exit event is raised by GDB + * Hence, this method needs a special handling in case of GDB. + * Raises ExitEvent when a thread really exits from the system. This is done by comparing the execution contexts list + * See bug 200615 for details. + */ + @Override + public void getExecutionContexts(IContainerDMContext containerDmc, final DataRequestMonitor rm) { + fProcService.getProcessesBeingDebugged( + containerDmc, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData() instanceof IExecutionDMContext[]) { + IExecutionDMContext[] execDmcs = (IExecutionDMContext[])getData(); + raiseExitEvents(execDmcs); + fOldExecutionCtxts = execDmcs; + rm.setData(fOldExecutionCtxts); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid contexts", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + + private void raiseExitEvents(IExecutionDMContext[] ctxts){ + if(ctxts == null || fOldExecutionCtxts == null) + return; + List list = Arrays.asList(ctxts); + List oldThreadList = Arrays.asList(fOldExecutionCtxts); + Iterator iterator = oldThreadList.iterator(); + while(iterator.hasNext()){ + IExecutionDMContext ctxt = iterator.next(); + if(! list.contains(ctxt)){ + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(ctxt, IContainerDMContext.class); + MIEvent e = new MIThreadExitEvent(containerDmc, Integer.toString(((IMIExecutionDMContext)ctxt).getThreadId())); + // Dispatch DsfMIThreadExitEvent + getSession().dispatchEvent(e, getProperties()); + } + } + } + + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java new file mode 100644 index 00000000000..ee33e80b63b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for additional functionality + * Ericsson - Version 7.0 + * Nokia - create and use backend service. + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.service; + + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class GDBRunControl_7_0 extends MIRunControl { + private IGDBBackend fGdb; + private IMIProcesses fProcService; + + public GDBRunControl_7_0(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + + fGdb = getServicesTracker().getService(IGDBBackend.class); + fProcService = getServicesTracker().getService(IMIProcesses.class); + + register(new String[]{IRunControl.class.getName(), MIRunControl.class.getName()}, + new Hashtable()); + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + @Override + public IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, int threadId) { + IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class); + + IThreadDMContext threadDmc = null; + if (procDmc != null) { + // For now, reuse the threadId as the OSThreadId + threadDmc = fProcService.createThreadContext(procDmc, Integer.toString(threadId)); + } + + return fProcService.createExecutionContext(container, threadDmc, Integer.toString(threadId)); + } + + @Override + public void suspend(IExecutionDMContext context, final RequestMonitor rm){ + canSuspend( + context, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData()) { + fGdb.interrupt(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context cannot be suspended.", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + + @Override + public void getExecutionContexts(IContainerDMContext containerDmc, final DataRequestMonitor rm) { + fProcService.getProcessesBeingDebugged( + containerDmc, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData() instanceof IExecutionDMContext[]) { + IExecutionDMContext[] execDmcs = (IExecutionDMContext[])getData(); + rm.setData(execDmcs); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid contexts", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java new file mode 100644 index 00000000000..8e728b2de03 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GDBRunControl_7_0_NS.java @@ -0,0 +1,781 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson AB - Modified for handling of multiple threads + *******************************************************************************/ + +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.MIStack; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecInterrupt; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNextInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStepInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; +import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIErrorEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISharedLibEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISteppingRangeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * Implementation note: This class implements event handlers for the events that + * are generated by this service itself. When the event is dispatched, these + * handlers will be called first, before any of the clients. These handlers + * update the service's internal state information to make them consistent with + * the events being issued. Doing this in the handlers as opposed to when the + * events are generated, guarantees that the state of the service will always be + * consistent with the events. The purpose of this pattern is to allow clients + * that listen to service events and track service state, to be perfectly in + * sync with the service state. + * @since 1.1 + */ +public class GDBRunControl_7_0_NS extends AbstractDsfService implements IRunControl, ICachingService +{ + @Immutable + private static class ExecutionData implements IExecutionDMData { + private final StateChangeReason fReason; + ExecutionData(StateChangeReason reason) { + fReason = reason; + } + public StateChangeReason getStateChangeReason() { return fReason; } + } + + /** + * Base class for events generated by the MI Run Control service. Most events + * generated by the MI Run Control service are directly caused by some MI event. + * Other services may need access to the extended MI data carried in the event. + * + * @param DMC that this event refers to + * @param MIInfo object that is the direct cause of this event + * @see MIRunControl + */ + @Immutable + private static class RunControlEvent> extends AbstractDMEvent + implements IDMEvent, IMIDMEvent + { + final private T fMIInfo; + public RunControlEvent(V dmc, T miInfo) { + super(dmc); + fMIInfo = miInfo; + } + + public T getMIEvent() { return fMIInfo; } + } + + /** + * Indicates that the given thread has been suspended. + */ + @Immutable + private static class SuspendedEvent extends RunControlEvent + implements ISuspendedDMEvent + { + SuspendedEvent(IExecutionDMContext ctx, MIStoppedEvent miInfo) { + super(ctx, miInfo); + } + + public StateChangeReason getReason() { + if (getMIEvent() instanceof MIBreakpointHitEvent) { + return StateChangeReason.BREAKPOINT; + } else if (getMIEvent() instanceof MISteppingRangeEvent) { + return StateChangeReason.STEP; + } else if (getMIEvent() instanceof MISharedLibEvent) { + return StateChangeReason.SHAREDLIB; + }else if (getMIEvent() instanceof MISignalEvent) { + return StateChangeReason.SIGNAL; + }else if (getMIEvent() instanceof MIWatchpointTriggerEvent) { + return StateChangeReason.WATCHPOINT; + }else if (getMIEvent() instanceof MIErrorEvent) { + return StateChangeReason.ERROR; + }else { + return StateChangeReason.USER_REQUEST; + } + } + } + + @Immutable + private static class ResumedEvent extends RunControlEvent + implements IResumedDMEvent + { + ResumedEvent(IExecutionDMContext ctx, MIRunningEvent miInfo) { + super(ctx, miInfo); + } + + public StateChangeReason getReason() { + switch(getMIEvent().getType()) { + case MIRunningEvent.CONTINUE: + return StateChangeReason.USER_REQUEST; + case MIRunningEvent.NEXT: + case MIRunningEvent.NEXTI: + return StateChangeReason.STEP; + case MIRunningEvent.STEP: + case MIRunningEvent.STEPI: + return StateChangeReason.STEP; + case MIRunningEvent.FINISH: + return StateChangeReason.STEP; + case MIRunningEvent.UNTIL: + case MIRunningEvent.RETURN: + break; + } + return StateChangeReason.UNKNOWN; + } + } + + @Immutable + private static class StartedDMEvent extends RunControlEvent + implements IStartedDMEvent + { + StartedDMEvent(IMIExecutionDMContext executionDmc, MIThreadCreatedEvent miInfo) { + super(executionDmc, miInfo); + } + } + + @Immutable + private static class ExitedDMEvent extends RunControlEvent + implements IExitedDMEvent + { + ExitedDMEvent(IMIExecutionDMContext executionDmc, MIThreadExitEvent miInfo) { + super(executionDmc, miInfo); + } + } + + protected class MIThreadRunState { + // State flags + boolean fSuspended = false; + boolean fResumePending = false; + boolean fStepping = false; + StateChangeReason fStateChangeReason; + } + + /////////////////////////////////////////////////////////////////////////// + // MIRunControlNS + /////////////////////////////////////////////////////////////////////////// + + private ICommandControlService fConnection; + + private boolean fTerminated = false; + + // ThreadStates indexed by the execution context + protected Map fThreadRunStates = new HashMap(); + + /////////////////////////////////////////////////////////////////////////// + // Initialization and shutdown + /////////////////////////////////////////////////////////////////////////// + + public GDBRunControl_7_0_NS(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(final RequestMonitor rm) { + register(new String[]{IRunControl.class.getName()}, new Hashtable()); + fConnection = getServicesTracker().getService(ICommandControlService.class); + getSession().addServiceEventListener(this, null); + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + unregister(); + getSession().removeServiceEventListener(this); + super.shutdown(rm); + } + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IDMService + /////////////////////////////////////////////////////////////////////////// + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IExecutionDMContext) { + getExecutionData((IExecutionDMContext) dmc, (DataRequestMonitor) rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /////////////////////////////////////////////////////////////////////////// + // IRunControl + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // Suspend + // ------------------------------------------------------------------------ + + public boolean isSuspended(IExecutionDMContext context) { + + // Thread case + if (context instanceof IMIExecutionDMContext) { + MIThreadRunState threadState = fThreadRunStates.get(context); + return (threadState == null) ? false : !fTerminated && threadState.fSuspended; + } + + // Container case + if (context instanceof IContainerDMContext) { + boolean isSuspended = false; + for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) { + if (DMContexts.isAncestorOf(threadContext, context)) { + isSuspended |= isSuspended(threadContext); + } + } + return isSuspended; + } + + // Default case + return false; + } + + public void canSuspend(IExecutionDMContext context, DataRequestMonitor rm) { + + // Thread case + if (context instanceof IMIExecutionDMContext) { + rm.setData(doCanSuspend(context)); + rm.done(); + return; + } + + // Container case + if (context instanceof IContainerDMContext) { + boolean canSuspend = false; + for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) { + if (DMContexts.isAncestorOf(threadContext, context)) { + canSuspend |= doCanSuspend(threadContext); + } + } + rm.setData(canSuspend); + rm.done(); + return; + } + + // Default case + rm.setData(false); + rm.done(); + } + + private boolean doCanSuspend(IExecutionDMContext context) { + MIThreadRunState threadState = fThreadRunStates.get(context); + return (threadState == null) ? false : !fTerminated && !threadState.fSuspended; + } + + public void suspend(IExecutionDMContext context, final RequestMonitor rm) { + + assert context != null; + + // Thread case + IMIExecutionDMContext thread = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (thread != null) { + doSuspendThread(thread, rm); + return; + } + + // Container case + IContainerDMContext container = DMContexts.getAncestorOfType(context, IContainerDMContext.class); + if (container != null) { + doSuspendContainer(container, rm); + return; + } + + // Default case + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$ + rm.done(); + } + + private void doSuspendThread(IMIExecutionDMContext context, final RequestMonitor rm) { + + if (!doCanSuspend(context)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + MIExecInterrupt cmd = new MIExecInterrupt(context); + fConnection.queueCommand(cmd, new DataRequestMonitor(getExecutor(), rm)); + } + + private void doSuspendContainer(IContainerDMContext context, final RequestMonitor rm) { + MIExecInterrupt cmd = new MIExecInterrupt(context, true); + fConnection.queueCommand(cmd, new DataRequestMonitor(getExecutor(), rm)); + } + + // ------------------------------------------------------------------------ + // Resume + // ------------------------------------------------------------------------ + + public void canResume(IExecutionDMContext context, DataRequestMonitor rm) { + + // Thread case + if (context instanceof IMIExecutionDMContext) { + rm.setData(doCanResume(context)); + rm.done(); + return; + } + + // Container case + if (context instanceof IContainerDMContext) { + boolean canSuspend = false; + for (IMIExecutionDMContext threadContext : fThreadRunStates.keySet()) { + if (DMContexts.isAncestorOf(threadContext, context)) { + canSuspend |= doCanResume(threadContext); + } + } + rm.setData(canSuspend); + rm.done(); + return; + } + + // Default case + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$ + rm.done(); + } + + private boolean doCanResume(IExecutionDMContext context) { + MIThreadRunState threadState = fThreadRunStates.get(context); + return (threadState == null) ? false : !fTerminated && threadState.fSuspended && !threadState.fResumePending; + } + + public void resume(IExecutionDMContext context, final RequestMonitor rm) { + + assert context != null; + + // Thread case + IMIExecutionDMContext thread = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (thread != null) { + doResumeThread(thread, rm); + return; + } + + // Container case + IContainerDMContext container = DMContexts.getAncestorOfType(context, IContainerDMContext.class); + if (container != null) { + doResumeContainer(container, rm); + return; + } + + // Default case + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid context type.", null)); //$NON-NLS-1$ + rm.done(); + } + + private void doResumeThread(IMIExecutionDMContext context, final RequestMonitor rm) { + + if (!doCanResume(context)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Given context: " + context + ", is already running.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + MIThreadRunState threadState = fThreadRunStates.get(context); + if (threadState == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + threadState.fResumePending = true; + + MIExecContinue cmd = new MIExecContinue(context); + fConnection.queueCommand(cmd, new DataRequestMonitor(getExecutor(), rm)); + } + + private void doResumeContainer(IContainerDMContext context, final RequestMonitor rm) { + MIExecContinue cmd = new MIExecContinue(context, true); + fConnection.queueCommand(cmd, new DataRequestMonitor(getExecutor(), rm)); + } + + // ------------------------------------------------------------------------ + // Step + // ------------------------------------------------------------------------ + + public boolean isStepping(IExecutionDMContext context) { + + // If it's a thread, just look it up + if (context instanceof IMIExecutionDMContext) { + MIThreadRunState threadState = fThreadRunStates.get(context); + return (threadState == null) ? false : !fTerminated && threadState.fStepping; + } + + // Default case + return false; + } + + public void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor rm) { + + // If it's a thread, just look it up + if (context instanceof IMIExecutionDMContext) { + canResume(context, rm); + return; + } + + // If it's a container, then we don't want to step it + rm.setData(false); + rm.done(); + } + + public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + + assert context != null; + + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (!doCanResume(context)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Cannot resume context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + MIThreadRunState threadState = fThreadRunStates.get(context); + if (threadState == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Given context: " + context + " can't be found.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + threadState.fResumePending = true; + threadState.fStepping = true; + + switch (stepType) { + case STEP_INTO: + fConnection.queueCommand(new MIExecStep(dmc), + new DataRequestMonitor(getExecutor(), rm)); + break; + case STEP_OVER: + fConnection.queueCommand(new MIExecNext(dmc), + new DataRequestMonitor(getExecutor(), rm)); + break; + case STEP_RETURN: + // The -exec-finish command operates on the selected stack frame, but here we always + // want it to operate on the stop stack frame. So we manually create a top-frame + // context to use with the MI command. + // We get a local instance of the stack service because the stack service can be shut + // down before the run control service is shut down. So it is possible for the + // getService() request below to return null. + MIStack stackService = getServicesTracker().getService(MIStack.class); + if (stackService != null) { + IFrameDMContext topFrameDmc = stackService.createFrameDMContext(dmc, 0); + fConnection.queueCommand(new MIExecFinish(topFrameDmc), + new DataRequestMonitor(getExecutor(), rm)); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Cannot create context for command, stack service not available.", null)); //$NON-NLS-1$ + rm.done(); + } + break; + case INSTRUCTION_STEP_INTO: + fConnection.queueCommand(new MIExecStepInstruction(dmc), + new DataRequestMonitor(getExecutor(), rm)); + break; + case INSTRUCTION_STEP_OVER: + fConnection.queueCommand(new MIExecNextInstruction(dmc), + new DataRequestMonitor(getExecutor(), rm)); + break; + default: + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + INTERNAL_ERROR, "Given step type not supported", null)); //$NON-NLS-1$ + rm.done(); + } + } + + // ------------------------------------------------------------------------ + // Run to line + // ------------------------------------------------------------------------ + + // Later add support for Address and function. + // skipBreakpoints is not used at the moment. Implement later + public void runToLine(IExecutionDMContext context, String fileName, String lineNo, boolean skipBreakpoints, final DataRequestMonitor rm) { + + assert context != null; + + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (!doCanResume(context)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Cannot resume context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + MIThreadRunState threadState = fThreadRunStates.get(context); + if (threadState == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, + "Given context: " + context + " is not an MI execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + threadState.fResumePending = true; + fConnection.queueCommand(new MIExecUntil(dmc, fileName + ":" + lineNo), //$NON-NLS-1$ + new DataRequestMonitor(getExecutor(), rm)); + } + + // ------------------------------------------------------------------------ + // Support functions + // ------------------------------------------------------------------------ + + public void getExecutionContexts(final IContainerDMContext containerDmc, final DataRequestMonitor rm) { + IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class); + procService.getProcessesBeingDebugged( + containerDmc, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData() instanceof IExecutionDMContext[]) { + rm.setData((IExecutionDMContext[])getData()); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid contexts", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor rm) { + MIThreadRunState threadState = fThreadRunStates.get(dmc); + if (threadState == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,INVALID_HANDLE, + "Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (dmc instanceof IMIExecutionDMContext) { + rm.setData(new ExecutionData(threadState.fSuspended ? threadState.fStateChangeReason : null)); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + } + rm.done(); + } + + + private IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, String threadId) { + IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class); + + IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class); + + IThreadDMContext threadDmc = null; + if (procDmc != null) { + // For now, reuse the threadId as the OSThreadId + threadDmc = procService.createThreadContext(procDmc, threadId); + } + + return procService.createExecutionContext(container, threadDmc, threadId); + } + + private void updateThreadState(IMIExecutionDMContext context, ResumedEvent event) { + StateChangeReason reason = event.getReason(); + boolean isStepping = reason.equals(StateChangeReason.STEP); + MIThreadRunState threadState = fThreadRunStates.get(context); + if (threadState == null) { + threadState = new MIThreadRunState(); + fThreadRunStates.put(context, threadState); + } + threadState.fSuspended = false; + threadState.fResumePending = false; + threadState.fStateChangeReason = reason; + threadState.fStepping = isStepping; + } + + private void updateThreadState(IMIExecutionDMContext context, SuspendedEvent event) { + StateChangeReason reason = event.getReason(); + MIThreadRunState threadState = fThreadRunStates.get(context); + if (threadState == null) { + threadState = new MIThreadRunState(); + fThreadRunStates.put(context, threadState); + } + threadState.fSuspended = true; + threadState.fResumePending = false; + threadState.fStepping = false; + threadState.fStateChangeReason = reason; + } + + /////////////////////////////////////////////////////////////////////////// + // Event handlers + /////////////////////////////////////////////////////////////////////////// + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIRunningEvent e) { + getSession().dispatchEvent(new ResumedEvent(e.getDMContext(), e), getProperties()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIStoppedEvent e) { + getSession().dispatchEvent(new SuspendedEvent(e.getDMContext(), e), getProperties()); + } + + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIThreadCreatedEvent e) { + IContainerDMContext containerDmc = e.getDMContext(); + IMIExecutionDMContext executionCtx = null; + if (e.getStrId() != null) { + executionCtx = createMIExecutionContext(containerDmc, e.getStrId()); + } + getSession().dispatchEvent(new StartedDMEvent(executionCtx, e), getProperties()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIThreadExitEvent e) { + IContainerDMContext containerDmc = e.getDMContext(); + IMIExecutionDMContext executionCtx = null; + if (e.getStrId() != null) { + executionCtx = createMIExecutionContext(containerDmc, e.getStrId()); + } + getSession().dispatchEvent(new ExitedDMEvent(executionCtx, e), getProperties()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ResumedEvent e) { + IExecutionDMContext ctx = e.getDMContext(); + if (ctx instanceof IMIExecutionDMContext) { + updateThreadState((IMIExecutionDMContext)ctx, e); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(SuspendedEvent e) { + IExecutionDMContext ctx = e.getDMContext(); + if (ctx instanceof IMIExecutionDMContext) { + updateThreadState((IMIExecutionDMContext)ctx, e); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(StartedDMEvent e) { + IExecutionDMContext executionCtx = e.getDMContext(); + if (executionCtx instanceof IMIExecutionDMContext) { + if (fThreadRunStates.get(executionCtx) == null) { + fThreadRunStates.put((IMIExecutionDMContext)executionCtx, new MIThreadRunState()); + } + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ExitedDMEvent e) { + fThreadRunStates.remove(e.getDMContext()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + fTerminated = true; + } + + public void flushCache(IDMContext context) { + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java new file mode 100644 index 00000000000..5a7a31c0e7f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactory.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import org.eclipse.cdt.debug.core.CDebugCorePlugin; +import org.eclipse.cdt.dsf.debug.service.AbstractDsfDebugServicesFactory; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.gdb.service.command.GDBControl; +import org.eclipse.cdt.dsf.gdb.service.command.GDBControl_7_0; +import org.eclipse.cdt.dsf.mi.service.CSourceLookup; +import org.eclipse.cdt.dsf.mi.service.ExpressionService; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager; +import org.eclipse.cdt.dsf.mi.service.MIDisassembly; +import org.eclipse.cdt.dsf.mi.service.MIMemory; +import org.eclipse.cdt.dsf.mi.service.MIModules; +import org.eclipse.cdt.dsf.mi.service.MIRegisters; +import org.eclipse.cdt.dsf.mi.service.MIStack; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.debug.core.ILaunchConfiguration; + +public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory { + + private final String fVersion; + + public GdbDebugServicesFactory(String version) { + fVersion = version; + } + + public String getVersion() { return fVersion; } + + @Override + @SuppressWarnings("unchecked") + public V createService(Class clazz, DsfSession session, Object ... optionalArguments) { + if (MIBreakpointsManager.class.isAssignableFrom(clazz)) { + return (V)createBreakpointManagerService(session); + } + else if (ICommandControl.class.isAssignableFrom(clazz)) { + for (Object arg : optionalArguments) { + if (arg instanceof ILaunchConfiguration) { + return (V)createCommandControl(session, (ILaunchConfiguration)arg); + } + } + } + else if (IMIBackend.class.isAssignableFrom(clazz)) { + for (Object arg : optionalArguments) { + if (arg instanceof ILaunchConfiguration) { + return (V)createBackendGDBService(session, (ILaunchConfiguration)arg); + } + } + } + + + return super.createService(clazz, session); + } + + protected MIBreakpointsManager createBreakpointManagerService(DsfSession session) { + return new MIBreakpointsManager(session, CDebugCorePlugin.PLUGIN_ID); + } + + @Override + protected IBreakpoints createBreakpointService(DsfSession session) { + return new MIBreakpoints(session); + } + + protected ICommandControl createCommandControl(DsfSession session, ILaunchConfiguration config) { + if ("6.8".compareTo(fVersion) < 0) { //$NON-NLS-1$ + return new GDBControl_7_0(session, config); + } + return new GDBControl(session, config); + } + + protected IMIBackend createBackendGDBService(DsfSession session, ILaunchConfiguration lc) { + return new GDBBackend(session, lc); + } + + @Override + protected IDisassembly createDisassemblyService(DsfSession session) { + return new MIDisassembly(session); + } + + @Override + protected IExpressions createExpressionService(DsfSession session) { + return new ExpressionService(session); + } + + @Override + protected IMemory createMemoryService(DsfSession session) { + if ("6.8".compareTo(fVersion) < 0) { //$NON-NLS-1$ + return new GDBMemory_7_0(session); + } + + return new MIMemory(session); + } + + @Override + protected IModules createModulesService(DsfSession session) { + return new MIModules(session); + } + + @Override + protected IProcesses createProcessesService(DsfSession session) { + if (fVersion.startsWith("6.8.50.20081118")) { //$NON-NLS-1$ + return new GDBProcesses_7_0(session); + } + return new GDBProcesses(session); + } + + @Override + protected IRegisters createRegistersService(DsfSession session) { + return new MIRegisters(session); + } + + @Override + protected IRunControl createRunControlService(DsfSession session) { + if ("6.8".compareTo(fVersion) < 0) { //$NON-NLS-1$ + return new GDBRunControl_7_0(session); + } + return new GDBRunControl(session); + } + + @Override + protected ISourceLookup createSourceLookupService(DsfSession session) { + return new CSourceLookup(session); + } + + @Override + protected IStack createStackService(DsfSession session) { + return new MIStack(session); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java new file mode 100644 index 00000000000..093e00e56a8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/GdbDebugServicesFactoryNS.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.service.DsfSession; + +public class GdbDebugServicesFactoryNS extends GdbDebugServicesFactory { + + public GdbDebugServicesFactoryNS(String version) { + super(version); + } + + @Override + protected IRunControl createRunControlService(DsfSession session) { + return new GDBRunControl_7_0_NS(session); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java new file mode 100644 index 00000000000..e7eb6061495 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBBackend.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2008 Nokia Corporation. + * 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: + * Nokia - initial version + * Ericsson - Minor cleanup + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import java.util.List; + +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +/** + * Service that manages back end GDB process, such as launching and monitoring + * GDB process, managing certain GDB parameters/options. This service makes it + * easy for debugger implementations to customize the way to start GDB process + * and convert some parameters if needed. See bug 240092 for more.
+ *
+ * A base implementation {@link GDBBackend} is provided that should be + * sufficient for most cases. But if you have special needs, it's recommended to + * subclass the base implementation.
+ *
+ * Here are some special cases:
+ * Example #1: GDB is usually launched on the host machine where Eclipse is + * running, but it can also be launched on a remote machine through, say, SSH.
+ * Example #2: GDB is usually launched in the host file system, but it can also + * be launched in a chroot'ed file system such as Scratchbox (see + * http://www.scratchbox.org)
+ * + * @since 1.1 + */ +public interface IGDBBackend extends IMIBackend { + + /** + * Get path of the debugged program on host. + * + * @return IPath + */ + public IPath getProgramPath(); + + /** + * Get init file for GDB. + * + * @return file name, may have relative or absolute path, or empty string ("") + * indicating an init file is not specified. + * @throws CoreException + * - error in getting the option. + */ + public String getGDBInitFile() throws CoreException; + + /** + * get arguments for the debugged program. + * + * @return String + * @throws CoreException + * - error in getting the option. + */ + public String getProgramArguments() throws CoreException; + + /** + * Get working directory for GDB. + * + * @return IPath - null if no meaningful value found. + * @throws CoreException + * - if any error occurs. + */ + public IPath getGDBWorkingDirectory() throws CoreException; + + /** + * @throws CoreException + * - error in getting the option. + */ + public List getSharedLibraryPaths() throws CoreException; + + + /** + * Sends an interrupt signal to the GDB process. + */ + public void interrupt(); + + /** + * @return The type of the session currently ongoing with the backend + */ + public SessionType getSessionType(); + + /** + * @return true if the ongoing session is attaching to a remote target. + */ + public boolean getIsAttachSession(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java new file mode 100644 index 00000000000..1661011a278 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/IGDBProcesses.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; + +public interface IGDBProcesses extends IMIProcesses { + /** + * Get a list of all execution contexts belonging to a container. This call is synchronous, + * unlike the call to getProcessesBeingDebugged(). However, some services may not be able + * to fulfill this request synchronously and will have to rely on getProcessesBeingDebugged(). + * + * @param containerDmc The container for which we want to get the execution contexts + */ + IMIExecutionDMContext[] getExecutionContexts(IMIContainerDMContext containerDmc); + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/SessionType.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/SessionType.java new file mode 100644 index 00000000000..779892a4fac --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/SessionType.java @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service; + +public enum SessionType { LOCAL, REMOTE, CORE } diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java new file mode 100644 index 00000000000..f861504b82b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl.java @@ -0,0 +1,504 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for additional features in DSF Reference implementation + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service.command; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; +import org.eclipse.cdt.dsf.mi.service.MIProcesses.ContainerStartedDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl; +import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor; +import org.eclipse.cdt.dsf.mi.service.command.MIBackendCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; +import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.osgi.framework.BundleContext; + +/** + * GDB Debugger control implementation. This implementation extends the + * base MI control implementation to provide the GDB-specific debugger + * features. This includes:
+ * - CLI console support,
+ * - inferior process status tracking.
+ */ +public class GDBControl extends AbstractMIControl implements IGDBControl { + + /** + * Event indicating that the back end process has started. + */ + private static class GDBControlInitializedDMEvent extends AbstractDMEvent + implements ICommandControlInitializedDMEvent + { + public GDBControlInitializedDMEvent(ICommandControlDMContext context) { + super(context); + } + } + + /** + * Event indicating that the CommandControl (back end process) has terminated. + */ + private static class GDBControlShutdownDMEvent extends AbstractDMEvent + implements ICommandControlShutdownDMEvent + { + public GDBControlShutdownDMEvent(ICommandControlDMContext context) { + super(context); + } + } + + private GDBControlDMContext fControlDmc; + + private IGDBBackend fMIBackend; + + private boolean fConnected = true; + + private MIRunControlEventProcessor fMIEventProcessor; + private CLIEventProcessor fCLICommandProcessor; + private AbstractCLIProcess fCLIProcess; + private MIInferiorProcess fInferiorProcess = null; + + private PTY fPty; + + public GDBControl(DsfSession session, ILaunchConfiguration config) { + super(session, false); + } + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + public void doInitialize(final RequestMonitor requestMonitor) { + + fMIBackend = getServicesTracker().getService(IGDBBackend.class); + + // getId uses the MIBackend service, which is why we must wait until we + // have it, before we can create this context. + fControlDmc = new GDBControlDMContext(getSession().getId(), getId()); + + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING), + new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING), + new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), + }; + + Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return initializeSteps; } + }; + getExecutor().execute(startupSequence); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + final Sequence.Step[] shutdownSteps = new Sequence.Step[] { + new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + }; + Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return shutdownSteps; } + }; + getExecutor().execute(shutdownSequence); + + } + + public String getId() { + return fMIBackend.getId(); + } + + @Override + public MIControlDMContext getControlDMContext() { + return fControlDmc; + } + + public ICommandControlDMContext getContext() { + return fControlDmc; + } + + public void terminate(final RequestMonitor rm) { + // Schedule a runnable to be executed 2 seconds from now. + // If we don't get a response to the quit command, this + // runnable will kill the task. + final Future quitTimeoutFuture = getExecutor().schedule( + new DsfRunnable() { + public void run() { + fMIBackend.destroy(); + rm.done(); + } + + @Override + protected boolean isExecutionRequired() { + return false; + } + }, + 2, TimeUnit.SECONDS); + + queueCommand( + new MIGDBExit(fControlDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + public void handleCompleted() { + // Cancel the time out runnable (if it hasn't run yet). + if (quitTimeoutFuture.cancel(false)) { + if (!isSuccess()) { + fMIBackend.destroy(); + } + rm.done(); + } + } + } + ); + } + + /* + * This method does the necessary work to setup the input/output streams for the + * inferior process, by either preparing the PTY to be used, to simply leaving + * the PTY null, which indicates that the input/output streams of the CLI should + * be used instead; this decision is based on the type of session. + */ + public void initInferiorInputOutput(final RequestMonitor requestMonitor) { + if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) { + // These types do not use a PTY + fPty = null; + requestMonitor.done(); + } else { + // These types always use a PTY + try { + fPty = new PTY(); + + // Tell GDB to use this PTY + queueCommand( + new MIInferiorTTYSet((ICommandControlDMContext)fControlDmc, fPty.getSlaveName()), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleFailure() { + // We were not able to tell GDB to use the PTY + // so we won't use it at all. + fPty = null; + requestMonitor.done(); + } + }); + } catch (IOException e) { + fPty = null; + requestMonitor.done(); + } + } + } + + + public boolean canRestart() { + if (fMIBackend.getIsAttachSession()) return false; + + // Before GDB6.8, the Linux gdbserver would restart a new + // process when getting a -exec-run but the communication + // with GDB had a bug and everything hung. + // with GDB6.8 the program restarts properly one time, + // but on a second attempt, gdbserver crashes. + // So, lets just turn off the Restart for Remote debugging + if (fMIBackend.getSessionType() == SessionType.REMOTE) return false; + + return true; + } + + /* + * Start the program. + */ + public void start(GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, false, requestMonitor); + } + + /* + * Before restarting the inferior, we must re-initialize its input/output streams + * and create a new inferior process object. Then we can restart the inferior. + */ + public void restart(final GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, true, requestMonitor); + } + + /* + * Insert breakpoint at entry if set, and start or restart the program. + */ + protected void startOrRestart(final GdbLaunch launch, boolean restart, final RequestMonitor requestMonitor) { + if (fMIBackend.getIsAttachSession()) { + // When attaching to a running process, we do not need to set a breakpoint or + // start the program; it is left up to the user. + requestMonitor.done(); + return; + } + + DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); + IMIProcesses procService = servicesTracker.getService(IMIProcesses.class); + servicesTracker.dispose(); + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); + final IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + final MICommand execCommand; + if (fMIBackend.getSessionType() == SessionType.REMOTE) { + // When doing remote debugging, we use -exec-continue instead of -exec-run + execCommand = new MIExecContinue(containerDmc); + } else { + execCommand = new MIExecRun(containerDmc, new String[0]); + } + + boolean stopInMain = false; + try { + stopInMain = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + final DataRequestMonitor execMonitor = new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), getProperties()); + super.handleSuccess(); + } + }; + + if (!stopInMain) { + // Just start the program. + queueCommand(execCommand, execMonitor); + } else { + String stopSymbol = null; + try { + stopSymbol = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + // Insert a breakpoint at the requested stop symbol. + queueCommand( + new MIBreakInsert(fControlDmc, true, false, null, 0, stopSymbol, 0), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // After the break-insert is done, execute the -exec-run or -exec-continue command. + queueCommand(execCommand, execMonitor); + } + }); + } + } + + /* + * This method creates a new inferior process object based on the current Pty or output stream. + */ + public void createInferiorProcess() { + if (fPty == null) { + fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fMIBackend.getMIOutputStream()); + } else { + fInferiorProcess = new GDBInferiorProcess(GDBControl.this, fMIBackend, fPty); + } + } + + public boolean isConnected() { + return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && fConnected; + } + + public void setConnected(boolean connected) { + fConnected = connected; + } + + public AbstractCLIProcess getCLIProcess() { + return fCLIProcess; + } + + public MIInferiorProcess getInferiorProcess() { + return fInferiorProcess; + } + + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + // Handle our "GDB Exited" event and stop processing commands. + stopCommandProcessing(); + } + + @DsfServiceEventHandler + public void eventDispatched(BackendStateChangedEvent e) { + if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) { + // Handle "GDB Exited" event, just relay to following event. + getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties()); + } + } + + public static class InitializationShutdownStep extends Sequence.Step { + public enum Direction { INITIALIZING, SHUTTING_DOWN } + + private Direction fDirection; + public InitializationShutdownStep(Direction direction) { fDirection = direction; } + + @Override + final public void execute(RequestMonitor requestMonitor) { + if (fDirection == Direction.INITIALIZING) { + initialize(requestMonitor); + } else { + shutdown(requestMonitor); + } + } + + @Override + final public void rollBack(RequestMonitor requestMonitor) { + if (fDirection == Direction.INITIALIZING) { + shutdown(requestMonitor); + } else { + super.rollBack(requestMonitor); + } + } + + protected void initialize(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + protected void shutdown(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + } + + protected class CommandMonitoringStep extends InitializationShutdownStep { + CommandMonitoringStep(Direction direction) { super(direction); } + + @Override + protected void initialize(final RequestMonitor requestMonitor) { + startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream()); + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + stopCommandProcessing(); + requestMonitor.done(); + } + } + + protected class InferiorInputOutputInitStep extends InitializationShutdownStep { + InferiorInputOutputInitStep(Direction direction) { super(direction); } + + @Override + protected void initialize(final RequestMonitor requestMonitor) { + initInferiorInputOutput(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + } + + protected class CommandProcessorsStep extends InitializationShutdownStep { + CommandProcessorsStep(Direction direction) { super(direction); } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + try { + fCLIProcess = new MIBackendCLIProcess(GDBControl.this, fMIBackend); + } + catch(IOException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + createInferiorProcess(); + + fCLICommandProcessor = new CLIEventProcessor(GDBControl.this, fControlDmc); + fMIEventProcessor = new MIRunControlEventProcessor(GDBControl.this, fControlDmc); + + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + fCLICommandProcessor.dispose(); + fMIEventProcessor.dispose(); + fCLIProcess.dispose(); + fInferiorProcess.dispose(); + + requestMonitor.done(); + } + } + + protected class RegisterStep extends InitializationShutdownStep { + RegisterStep(Direction direction) { super(direction); } + @Override + public void initialize(final RequestMonitor requestMonitor) { + getSession().addServiceEventListener(GDBControl.this, null); + register( + new String[]{ ICommandControl.class.getName(), + ICommandControlService.class.getName(), + AbstractMIControl.class.getName(), + IGDBControl.class.getName() }, + new Hashtable()); + getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties()); + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(GDBControl.this); + requestMonitor.done(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControlDMContext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControlDMContext.java new file mode 100644 index 00000000000..19657a111e6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControlDMContext.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service.command; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; +import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; +import org.eclipse.cdt.dsf.debug.service.ISignals.ISignalsDMContext; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; + +/** + * + */ +public class GDBControlDMContext extends MIControlDMContext + implements ISymbolDMContext, IBreakpointsTargetDMContext, ISourceLookupDMContext, + ISignalsDMContext, IDisassemblyDMContext +{ + + public GDBControlDMContext(String sessionId, String commandControlId) { + super(sessionId, commandControlId); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java new file mode 100644 index 00000000000..53e651b07ea --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBControl_7_0.java @@ -0,0 +1,496 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for additional features in DSF Reference implementation + * Ericsson - New version for 7_0 + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service.command; + +import java.io.IOException; +import java.util.Hashtable; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; +import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.AbstractMIControl; +import org.eclipse.cdt.dsf.mi.service.command.CLIEventProcessor_7_0; +import org.eclipse.cdt.dsf.mi.service.command.MIBackendCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIRunControlEventProcessor_7_0; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; +import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecRun; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBExit; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIInferiorTTYSet; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.osgi.framework.BundleContext; + +/** + * GDB Debugger control implementation. This implementation extends the + * base MI control implementation to provide the GDB-specific debugger + * features. This includes:
+ * - CLI console support,
+ * - inferior process status tracking.
+ */ +public class GDBControl_7_0 extends AbstractMIControl implements IGDBControl { + + /** + * Event indicating that the back end process has started. + */ + private static class GDBControlInitializedDMEvent extends AbstractDMEvent + implements ICommandControlInitializedDMEvent + { + public GDBControlInitializedDMEvent(ICommandControlDMContext context) { + super(context); + } + } + + /** + * Event indicating that the CommandControl (back end process) has terminated. + */ + private static class GDBControlShutdownDMEvent extends AbstractDMEvent + implements ICommandControlShutdownDMEvent + { + public GDBControlShutdownDMEvent(ICommandControlDMContext context) { + super(context); + } + } + + private GDBControlDMContext fControlDmc; + + private IGDBBackend fMIBackend; + + private boolean fConnected = true; + + private MIRunControlEventProcessor_7_0 fMIEventProcessor; + private CLIEventProcessor_7_0 fCLICommandProcessor; + private AbstractCLIProcess fCLIProcess; + private MIInferiorProcess fInferiorProcess = null; + + private PTY fPty; + + public GDBControl_7_0(DsfSession session, ILaunchConfiguration config) { + super(session, true); + } + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + public void doInitialize(final RequestMonitor requestMonitor) { + fMIBackend = getServicesTracker().getService(IGDBBackend.class); + + // getId uses the MIBackend service, which is why we must wait until we + // have it, before we can create this context. + fControlDmc = new GDBControlDMContext(getSession().getId(), getId()); + + final Sequence.Step[] initializeSteps = new Sequence.Step[] { + new CommandMonitoringStep(InitializationShutdownStep.Direction.INITIALIZING), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.INITIALIZING), + new CommandProcessorsStep(InitializationShutdownStep.Direction.INITIALIZING), + new RegisterStep(InitializationShutdownStep.Direction.INITIALIZING), + }; + + Sequence startupSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return initializeSteps; } + }; + getExecutor().execute(startupSequence); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + final Sequence.Step[] shutdownSteps = new Sequence.Step[] { + new RegisterStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new CommandProcessorsStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new InferiorInputOutputInitStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + new CommandMonitoringStep(InitializationShutdownStep.Direction.SHUTTING_DOWN), + }; + Sequence shutdownSequence = new Sequence(getExecutor(), requestMonitor) { + @Override public Step[] getSteps() { return shutdownSteps; } + }; + getExecutor().execute(shutdownSequence); + + } + + public String getId() { + return fMIBackend.getId(); + } + + @Override + public MIControlDMContext getControlDMContext() { + return fControlDmc; + } + + public ICommandControlDMContext getContext() { + return fControlDmc; + } + + public void terminate(final RequestMonitor rm) { + // Schedule a runnable to be executed 2 seconds from now. + // If we don't get a response to the quit command, this + // runnable will kill the task. + final Future quitTimeoutFuture = getExecutor().schedule( + new DsfRunnable() { + public void run() { + fMIBackend.destroy(); + rm.done(); + } + + @Override + protected boolean isExecutionRequired() { + return false; + } + }, + 2, TimeUnit.SECONDS); + + queueCommand( + new MIGDBExit(fControlDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + public void handleCompleted() { + // Cancel the time out runnable (if it hasn't run yet). + if (quitTimeoutFuture.cancel(false)) { + if (!isSuccess()) { + fMIBackend.destroy(); + } + rm.done(); + } + } + } + ); + } + + /* + * This method does the necessary work to setup the input/output streams for the + * inferior process, by either preparing the PTY to be used, to simply leaving + * the PTY null, which indicates that the input/output streams of the CLI should + * be used instead; this decision is based on the type of session. + */ + public void initInferiorInputOutput(final RequestMonitor requestMonitor) { + if (fMIBackend.getSessionType() == SessionType.REMOTE || fMIBackend.getIsAttachSession()) { + // These types do not use a PTY + fPty = null; + requestMonitor.done(); + } else { + // These types always use a PTY + try { + fPty = new PTY(); + + // Tell GDB to use this PTY + queueCommand( + new MIInferiorTTYSet((ICommandControlDMContext)fControlDmc, fPty.getSlaveName()), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleFailure() { + // We were not able to tell GDB to use the PTY + // so we won't use it at all. + fPty = null; + requestMonitor.done(); + } + }); + } catch (IOException e) { + fPty = null; + requestMonitor.done(); + } + } + } + + + public boolean canRestart() { + if (fMIBackend.getIsAttachSession()) return false; + + // Before GDB6.8, the Linux gdbserver would restart a new + // process when getting a -exec-run but the communication + // with GDB had a bug and everything hung. + // with GDB6.8 the program restarts properly one time, + // but on a second attempt, gdbserver crashes. + // So, lets just turn off the Restart for Remote debugging + if (fMIBackend.getSessionType() == SessionType.REMOTE) return false; + + return true; + } + + /* + * Start the program. + */ + public void start(GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, false, requestMonitor); + } + + /* + * Before restarting the inferior, we must re-initialize its input/output streams + * and create a new inferior process object. Then we can restart the inferior. + */ + public void restart(final GdbLaunch launch, final RequestMonitor requestMonitor) { + startOrRestart(launch, true, requestMonitor); + } + + /* + * Insert breakpoint at entry if set, and start or restart the program. + */ + protected void startOrRestart(final GdbLaunch launch, boolean restart, final RequestMonitor requestMonitor) { + if (fMIBackend.getIsAttachSession()) { + // When attaching to a running process, we do not need to set a breakpoint or + // start the program; it is left up to the user. + requestMonitor.done(); + return; + } + + DsfServicesTracker servicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), getSession().getId()); + IMIProcesses procService = servicesTracker.getService(IMIProcesses.class); + servicesTracker.dispose(); + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, MIProcesses.UNIQUE_GROUP_ID); + final IContainerDMContext containerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + final MICommand execCommand; + if (fMIBackend.getSessionType() == SessionType.REMOTE) { + // When doing remote debugging, we use -exec-continue instead of -exec-run + execCommand = new MIExecContinue(containerDmc); + } else { + execCommand = new MIExecRun(containerDmc, new String[0]); + } + + boolean stopInMain = false; + try { + stopInMain = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, false ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Cannot retrieve stop at entry point boolean", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + final DataRequestMonitor execMonitor = new DataRequestMonitor(getExecutor(), requestMonitor); + + if (!stopInMain) { + // Just start the program. + queueCommand(execCommand, execMonitor); + } else { + String stopSymbol = null; + try { + stopSymbol = launch.getLaunchConfiguration().getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT ); + } catch (CoreException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, DebugException.CONFIGURATION_INVALID, "Cannot retrieve the entry point symbol", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + // Insert a breakpoint at the requested stop symbol. + queueCommand( + new MIBreakInsert(fControlDmc, true, false, null, 0, stopSymbol, 0), + new DataRequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // After the break-insert is done, execute the -exec-run or -exec-continue command. + queueCommand(execCommand, execMonitor); + } + }); + } + } + + /* + * This method creates a new inferior process object based on the current Pty or output stream. + */ + public void createInferiorProcess() { + if (fPty == null) { + fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fMIBackend.getMIOutputStream()); + } else { + fInferiorProcess = new GDBInferiorProcess(GDBControl_7_0.this, fMIBackend, fPty); + } + } + + public boolean isConnected() { + return fInferiorProcess.getState() != MIInferiorProcess.State.TERMINATED && fConnected; + } + + public void setConnected(boolean connected) { + fConnected = connected; + } + + public AbstractCLIProcess getCLIProcess() { + return fCLIProcess; + } + + public MIInferiorProcess getInferiorProcess() { + return fInferiorProcess; + } + + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + // Handle our "GDB Exited" event and stop processing commands. + stopCommandProcessing(); + } + + @DsfServiceEventHandler + public void eventDispatched(BackendStateChangedEvent e) { + if (e.getState() == IMIBackend.State.TERMINATED && e.getBackendId().equals(fMIBackend.getId())) { + // Handle "GDB Exited" event, just relay to following event. + getSession().dispatchEvent(new GDBControlShutdownDMEvent(fControlDmc), getProperties()); + } + } + + public static class InitializationShutdownStep extends Sequence.Step { + public enum Direction { INITIALIZING, SHUTTING_DOWN } + + private Direction fDirection; + public InitializationShutdownStep(Direction direction) { fDirection = direction; } + + @Override + final public void execute(RequestMonitor requestMonitor) { + if (fDirection == Direction.INITIALIZING) { + initialize(requestMonitor); + } else { + shutdown(requestMonitor); + } + } + + @Override + final public void rollBack(RequestMonitor requestMonitor) { + if (fDirection == Direction.INITIALIZING) { + shutdown(requestMonitor); + } else { + super.rollBack(requestMonitor); + } + } + + protected void initialize(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + protected void shutdown(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + } + + protected class CommandMonitoringStep extends InitializationShutdownStep { + CommandMonitoringStep(Direction direction) { super(direction); } + + @Override + protected void initialize(final RequestMonitor requestMonitor) { + startCommandProcessing(fMIBackend.getMIInputStream(), fMIBackend.getMIOutputStream()); + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + stopCommandProcessing(); + requestMonitor.done(); + } + } + + protected class InferiorInputOutputInitStep extends InitializationShutdownStep { + InferiorInputOutputInitStep(Direction direction) { super(direction); } + + @Override + protected void initialize(final RequestMonitor requestMonitor) { + initInferiorInputOutput(requestMonitor); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + requestMonitor.done(); + } + } + + protected class CommandProcessorsStep extends InitializationShutdownStep { + CommandProcessorsStep(Direction direction) { super(direction); } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + try { + fCLIProcess = new MIBackendCLIProcess(GDBControl_7_0.this, fMIBackend); + } + catch(IOException e) { + requestMonitor.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Failed to create CLI Process", e)); //$NON-NLS-1$ + requestMonitor.done(); + return; + } + + createInferiorProcess(); + + fCLICommandProcessor = new CLIEventProcessor_7_0(GDBControl_7_0.this, fControlDmc); + fMIEventProcessor = new MIRunControlEventProcessor_7_0(GDBControl_7_0.this, fControlDmc); + + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + fCLICommandProcessor.dispose(); + fMIEventProcessor.dispose(); + fCLIProcess.dispose(); + fInferiorProcess.dispose(); + + requestMonitor.done(); + } + } + + protected class RegisterStep extends InitializationShutdownStep { + RegisterStep(Direction direction) { super(direction); } + @Override + public void initialize(final RequestMonitor requestMonitor) { + getSession().addServiceEventListener(GDBControl_7_0.this, null); + register( + new String[]{ ICommandControl.class.getName(), + ICommandControlService.class.getName(), + AbstractMIControl.class.getName(), + IGDBControl.class.getName() }, + new Hashtable()); + getSession().dispatchEvent(new GDBControlInitializedDMEvent(fControlDmc), getProperties()); + requestMonitor.done(); + } + + @Override + protected void shutdown(RequestMonitor requestMonitor) { + unregister(); + getSession().removeServiceEventListener(GDBControl_7_0.this); + requestMonitor.done(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBInferiorProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBInferiorProcess.java new file mode 100644 index 00000000000..51c3114646a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/GDBInferiorProcess.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service.command; + +import java.io.OutputStream; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.service.IGDBBackend; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; +import org.eclipse.cdt.utils.pty.PTY; + +/** + * + */ +class GDBInferiorProcess extends MIInferiorProcess { + + private IGDBBackend fBackend; + + public GDBInferiorProcess(ICommandControlService commandControl, IGDBBackend backend, PTY p) { + super(commandControl, p); + fBackend = backend; + } + + public GDBInferiorProcess(ICommandControlService commandControl, IGDBBackend backend, OutputStream gdbOutputStream) { + super(commandControl, gdbOutputStream); + fBackend = backend; + } + + @Override + @ThreadSafeAndProhibitedFromDsfExecutor("getSession#getExecutor") + public void destroy() { + try { + getSession().getExecutor().submit(new DsfRunnable() { + public void run() { + if (isDisposed() || !getSession().isActive()) return; + + // An inferior will be destroy():interrupt and kill if + // - For attach session: + // never (we don't kill an independent process.) + // - For Program session: + // if the inferior is still running. + // - For PostMortem(Core): send event + // else noop + if (fBackend.getIsAttachSession() == false) { + // Try to interrupt the inferior, first. + if (getState() == State.RUNNING) { + fBackend.interrupt(); + } + } + } + }).get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } finally { + super.destroy(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java new file mode 100644 index 00000000000..3027a783581 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/gdb/service/command/IGDBControl.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2008 Ericsson and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.gdb.service.command; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.mi.service.command.AbstractCLIProcess; +import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess; + +public interface IGDBControl extends ICommandControlService { + + void terminate(final RequestMonitor rm); + void initInferiorInputOutput(final RequestMonitor requestMonitor); + + boolean canRestart(); + void start(GdbLaunch launch, final RequestMonitor requestMonitor); + void restart(final GdbLaunch launch, final RequestMonitor requestMonitor); + void createInferiorProcess(); + + boolean isConnected(); + + void setConnected(boolean connected); + + AbstractCLIProcess getCLIProcess(); + + MIInferiorProcess getInferiorProcess(); +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/CSourceLookup.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/CSourceLookup.java new file mode 100644 index 00000000000..66f6298071b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/CSourceLookup.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIEnvironmentDirectory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.FolderSourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer; +import org.osgi.framework.BundleContext; + +/** + * ISourceLookup service implementation based on the CDT CSourceLookupDirector. + */ +public class CSourceLookup extends AbstractDsfService implements ISourceLookup { + + private Map fDirectors = new HashMap(); + + ICommandControl fConnection; + + public CSourceLookup(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + + public void setSourceLookupDirector(ISourceLookupDMContext ctx, CSourceLookupDirector director) { + fDirectors.put(ctx, director); + } + + public void setSourceLookupPath(ISourceLookupDMContext ctx, ISourceContainer[] containers, RequestMonitor rm) { + List pathList = getSourceLookupPath(containers); + String[] paths = pathList.toArray(new String[pathList.size()]); + + fConnection.queueCommand( + new MIEnvironmentDirectory(ctx, paths, false), + new DataRequestMonitor(getExecutor(), rm)); + } + + private List getSourceLookupPath(ISourceContainer[] containers) { + ArrayList list = new ArrayList(containers.length); + + for (int i = 0; i < containers.length; ++i) { + if (containers[i] instanceof ProjectSourceContainer) { + IProject project = ((ProjectSourceContainer)containers[i]).getProject(); + if (project != null && project.exists()) + list.add(project.getLocation().toPortableString()); + } + if (containers[i] instanceof FolderSourceContainer) { + IContainer container = ((FolderSourceContainer)containers[i]).getContainer(); + if (container != null && container.exists()) + list.add(container.getLocation().toPortableString()); + } + if (containers[i] instanceof DirectorySourceContainer) { + File dir = ((DirectorySourceContainer)containers[i]).getDirectory(); + if (dir != null && dir.exists()) { + IPath path = new Path( dir.getAbsolutePath()); + list.add(path.toPortableString()); + } + } + if (containers[i].isComposite()) { + try { + list.addAll(getSourceLookupPath(containers[i].getSourceContainers())); + } catch (CoreException e) { + } + } + } + + return list; + } + + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(final RequestMonitor requestMonitor) { + fConnection = getServicesTracker().getService(ICommandControl.class); + + // Register this service + register(new String[] { CSourceLookup.class.getName(), ISourceLookup.class.getName() }, new Hashtable()); + + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + public void getDebuggerPath(ISourceLookupDMContext sourceLookupCtx, Object source, final DataRequestMonitor rm) + { + if (! (source instanceof String)) { + // In future if needed other elements such as URIs could be supported. + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Only string source element is supported", null)); //$NON-NLS-1$); + rm.done(); + return; + } + final String sourceString = (String) source; + + if (!fDirectors.containsKey(sourceLookupCtx) ){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "No source director configured for given context", null)); //$NON-NLS-1$); + rm.done(); + return; + } + final CSourceLookupDirector director = fDirectors.get(sourceLookupCtx); + + new Job("Lookup Debugger Path") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + IPath debuggerPath = director.getCompilationPath(sourceString); + if (debuggerPath != null) { + rm.setData(debuggerPath.toString()); + } else { + rm.setData(sourceString); + } + rm.done(); + + return Status.OK_STATUS; + } + }.schedule(); + + } + + public void getSource(ISourceLookupDMContext sourceLookupCtx, final String debuggerPath, final DataRequestMonitor rm) + { + if (!fDirectors.containsKey(sourceLookupCtx) ){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "No source director configured for given context", null)); //$NON-NLS-1$); + rm.done(); + return; + } + final CSourceLookupDirector director = fDirectors.get(sourceLookupCtx); + + new Job("Lookup Source") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + Object[] sources; + try { + sources = director.findSourceElements(debuggerPath); + if (sources == null || sources.length == 0) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No sources found", null)); //$NON-NLS-1$); + } else { + rm.setData(sources[0]); + } + } catch (CoreException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Source lookup failed", e)); //$NON-NLS-1$); + } finally { + rm.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ExpressionService.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ExpressionService.java new file mode 100644 index 00000000000..e4b53393621 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ExpressionService.java @@ -0,0 +1,927 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for handling of multiple execution contexts + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataEvaluateExpression; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.Addr32; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * This class implements a debugger expression evaluator as a DSF service. The + * primary interface that clients of this class should use is IExpressions. + */ +public class ExpressionService extends AbstractDsfService implements IExpressions, ICachingService { + + /** + * This class represents the two expressions that characterize an Expression Context. + */ + public static class ExpressionInfo { + private final String fullExpression; + private final String relativeExpression; + + public ExpressionInfo(String full, String relative) { + fullExpression = full; + relativeExpression = relative; + } + + public String getFullExpr() { return fullExpression; } + public String getRelExpr() { return relativeExpression; } + + @Override + public boolean equals(Object other) { + if (other instanceof ExpressionInfo) { + if (fullExpression == null ? ((ExpressionInfo) other).fullExpression == null : + fullExpression.equals(((ExpressionInfo) other).fullExpression)) { + if (relativeExpression == null ? ((ExpressionInfo) other).relativeExpression == null : + relativeExpression.equals(((ExpressionInfo) other).relativeExpression)) { + return true; + } + } + } + return false; + } + + @Override + public int hashCode() { + return (fullExpression == null ? 0 : fullExpression.hashCode()) ^ + (relativeExpression == null ? 0 : relativeExpression.hashCode()); + } + + @Override + public String toString() { + return "[" + fullExpression +", " + relativeExpression + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + } + } + /** + * This class represents an expression. + */ + protected static class MIExpressionDMC extends AbstractDMContext implements IExpressionDMContext { + /** + * This field holds an expression to be evaluated. + */ + private ExpressionInfo exprInfo; + + /** + * ExpressionDMC Constructor for expression to be evaluated in context of + * a stack frame. + * + * @param sessionId + * The session ID in which this context is created. + * @param expression + * The expression to be described by this ExpressionDMC + * @param relExpr + * The relative expression if this expression was created as a child + * @param frameCtx + * The parent stack frame context for this ExpressionDMC. + */ + public MIExpressionDMC(String sessionId, String expression, String relExpr, IFrameDMContext frameCtx) { + this(sessionId, expression, relExpr, (IDMContext)frameCtx); + } + + /** + * ExpressionDMC Constructor for expression to be evaluated in context of + * an thread. + * + * @param sessionId + * The session ID in which this context is created. + * @param expression + * The expression to be described by this ExpressionDMC + * @param relExpr + * The relative expression if this expression was created as a child + * @param execCtx + * The parent thread context for this ExpressionDMC. + */ + public MIExpressionDMC(String sessionId, String expression, String relExpr, IMIExecutionDMContext execCtx) { + this(sessionId, expression, relExpr, (IDMContext)execCtx); + } + + /** + * ExpressionDMC Constructor for expression to be evaluated in context of + * a memory space. + * + * @param sessionId + * The session ID in which this context is created. + * @param expression + * The expression to be described by this ExpressionDMC + * @param relExpr + * The relative expression if this expression was created as a child + * @param memoryCtx + * The parent memory space context for this ExpressionDMC. + */ + public MIExpressionDMC(String sessionId, String expression, String relExpr, IMemoryDMContext memoryCtx) { + this(sessionId, expression, relExpr, (IDMContext)memoryCtx); + } + + private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) { + super(sessionId, new IDMContext[] { parent }); + exprInfo = new ExpressionInfo(expr, relExpr); + } + + /** + * @return True if the two objects are equal, false otherwise. + */ + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && exprInfo.equals(((MIExpressionDMC)other).exprInfo); + } + + /** + * + * @return The hash code of this ExpressionDMC object. + */ + @Override + public int hashCode() { + return super.baseHashCode() + exprInfo.hashCode(); + } + + /** + * + * @return A string representation of this ExpressionDMC (including the + * expression to which it is bound). + */ + @Override + public String toString() { + return baseToString() + ".expr" + exprInfo.toString(); //$NON-NLS-1$ + } + + /** + * @return The full expression string represented by this ExpressionDMC + */ + public String getExpression() { + return exprInfo.getFullExpr(); + } + + /** + * @return The relative expression string represented by this ExpressionDMC + */ + public String getRelativeExpression() { + return exprInfo.getRelExpr(); + } + } + + protected static class InvalidContextExpressionDMC extends AbstractDMContext + implements IExpressionDMContext + { + private final String expression; + + public InvalidContextExpressionDMC(String sessionId, String expr, IDMContext parent) { + super(sessionId, new IDMContext[] { parent }); + expression = expr; + } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && + expression == null ? ((InvalidContextExpressionDMC) other).getExpression() == null : expression.equals(((InvalidContextExpressionDMC) other).getExpression()); + } + + @Override + public int hashCode() { + return expression == null ? super.baseHashCode() : super.baseHashCode() ^ expression.hashCode(); + } + + @Override + public String toString() { + return baseToString() + ".invalid_expr[" + expression + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public String getExpression() { + return expression; + } + } + + + /** + * Contains the address of an expression as well as the size of its type. + */ + protected static class ExpressionDMAddress implements IExpressionDMAddress { + IAddress fAddr; + int fSize; + + public ExpressionDMAddress(IAddress addr, int size) { + fAddr = addr; + fSize = size; + } + + public ExpressionDMAddress(String addrStr, int size) { + fSize = size; + // We must count the "0x" and that + // is why we compare with 10 characters + // instead of 8 + if (addrStr.length() <= 10) { + fAddr = new Addr32(addrStr); + } else { + fAddr = new Addr64(addrStr); + } + } + + public IAddress getAddress() { return fAddr; } + public int getSize() { return fSize; } + + @Override + public boolean equals(Object other) { + if (other instanceof ExpressionDMAddress) { + ExpressionDMAddress otherAddr = (ExpressionDMAddress) other; + return (fSize == otherAddr.getSize()) && + (fAddr == null ? otherAddr.getAddress() == null : fAddr.equals(otherAddr.getAddress())); + } + return false; + } + + @Override + public int hashCode() { + return (fAddr == null ? 0 :fAddr.hashCode()) + fSize; + } + + @Override + public String toString() { + return (fAddr == null ? "null" : "(0x" + fAddr.toString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } + } + + /** + * This class represents the static data referenced by an instance of ExpressionDMC, + * such as its type and number of children; it does not contain the value or format + * of the expression. + */ + protected static class ExpressionDMData implements IExpressionDMData { + // This is the relative expression, such as the name of a field within a structure, + // in contrast to the fully-qualified expression contained in the ExpressionDMC, + // which refers to the full name, including parent structure. + private final String relativeExpression; + private final String exprType; + private final int numChildren; + private final boolean editable; + + /** + * ExpressionDMData constructor. + */ + public ExpressionDMData(String expr, String type, int num, boolean edit) { + relativeExpression = expr; + exprType = type; + numChildren = num; + editable = edit; + } + + public BasicType getBasicType() { + return null; + } + + public String getEncoding() { + return null; + } + + public Map getEnumerations() { + return new HashMap(); + } + + public String getName() { + return relativeExpression; + } + + public IRegisterDMContext getRegister() { + return null; + } + + // See class VariableVMNode for an example of usage of this method + public String getStringValue() { + return null; + } + + public String getTypeId() { + return null; + } + + public String getTypeName() { + return exprType; + } + + public int getNumChildren() { + return numChildren; + } + + public boolean isEditable() { + return editable; + } + + @Override + public boolean equals(Object other) { + if (other instanceof ExpressionDMData) { + ExpressionDMData otherData = (ExpressionDMData) other; + return (getNumChildren() == otherData.getNumChildren()) && + (getTypeName() == null ? otherData.getTypeName() == null : getTypeName().equals(otherData.getTypeName())) && + (getName() == null ? otherData.getName() == null : getName().equals(otherData.getName())); + } + return false; + } + + @Override + public int hashCode() { + return relativeExpression == null ? 0 : relativeExpression.hashCode() + + exprType == null ? 0 : exprType.hashCode() + numChildren; + } + + @Override + public String toString() { + return "relExpr=" + relativeExpression + ", type=" + exprType + ", numchildren=" + numChildren; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ + } + } + + /** + * Event generated every time an expression is changed by the ExpressionService. + * + * A client wishing to receive such events has to register as a service + * event listener and implement the corresponding eventDispatched method. + * + * E.g.: + * + * getSession().addServiceEventListener(listenerObject, null); + * + * @DsfServiceEventHandler + * public void eventDispatched(ExpressionChangedEvent e) { + * IExpressionDMContext context = e.getDMContext(); + * // do something... + * } + */ + protected static class ExpressionChangedEvent extends AbstractDMEvent + implements IExpressionChangedDMEvent { + + public ExpressionChangedEvent(IExpressionDMContext context) { + super(context); + } + } + + private CommandCache fExpressionCache; + private MIVariableManager varManager; + + + public ExpressionService(DsfSession session) { + super(session); + } + + /** + * This method initializes this service. + * + * @param requestMonitor + * The request monitor indicating the operation is finished + */ + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + /** + * This method initializes this service after our superclass's initialize() + * method succeeds. + * + * @param requestMonitor + * The call-back object to notify when this service's + * initialization is done. + */ + private void doInitialize(RequestMonitor requestMonitor) { + + // Register to receive service events for this session. + getSession().addServiceEventListener(this, null); + + // Register this service. + register(new String[] { IExpressions.class.getName(), + ExpressionService.class.getName() }, + new Hashtable()); + + // Create the expressionService-specific CommandControl which is our + // variable object manager. + // It will deal with the meta-commands, before sending real MI commands + // to the back-end, through the MICommandControl service + // It must be created after the ExpressionService is registered + // since it will need to find it. + varManager = new MIVariableManager(getSession(), getServicesTracker()); + + // Create the meta command cache which will use the variable manager + // to actually send MI commands to the back-end + fExpressionCache = new CommandCache(getSession(), varManager); + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + fExpressionCache.setContextAvailable(commandControl.getContext(), true); + + requestMonitor.done(); + } + + /** + * This method shuts down this service. It unregisters the service, stops + * receiving service events, and calls the superclass shutdown() method to + * finish the shutdown process. + * + * @return void + */ + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + varManager.dispose(); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + /** + * @return The bundle context of the plug-in to which this service belongs. + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /** + * Create an expression context with the same full and relative expression + */ + public IExpressionDMContext createExpression(IDMContext ctx, String expression) { + return createExpression(ctx, expression, expression); + } + + /** + * Create an expression context. + */ + public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) { + IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); + if (frameDmc != null) { + return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc); + } + + IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); + if (execCtx != null) { + // If we have a thread context but not a frame context, we give the user + // the expression as per the top-most frame of the specified thread. + // To do this, we create our own frame context. + MIStack stackService = getServicesTracker().getService(MIStack.class); + if (stackService != null) { + frameDmc = stackService.createFrameDMContext(execCtx, 0); + return new MIExpressionDMC(getSession().getId(), expression, relExpr, frameDmc); + } + + return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx); + } + + IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class); + if (memoryCtx != null) { + return new MIExpressionDMC(getSession().getId(), expression, relExpr, memoryCtx); + } + + // Don't care about the relative expression at this point + return new InvalidContextExpressionDMC(getSession().getId(), expression, ctx); + } + + /** + * @see IFormattedValues.getFormattedValueContext(IFormattedDataDMContext, String) + * + * @param dmc + * The context describing the data for which we want to create + * a Formatted context. + * @param formatId + * The format that will be used to create the Formatted context + * + * @return A FormattedValueDMContext that can be used to obtain the value + * of an expression in a specific format. + */ + + public FormattedValueDMContext getFormattedValueContext( + IFormattedDataDMContext dmc, String formatId) { + return new FormattedValueDMContext(this, dmc, formatId); + } + + /** + * @see IFormattedValues.getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor) + * + * @param dmc + * The context describing the data for which we want to know + * which formats are available. + * @param rm + * The data request monitor for this asynchronous operation. + * + */ + + public void getAvailableFormats(IFormattedDataDMContext dmc, + final DataRequestMonitor rm) { + rm.setData(new String[] { IFormattedValues.BINARY_FORMAT, + IFormattedValues.NATURAL_FORMAT, IFormattedValues.HEX_FORMAT, + IFormattedValues.OCTAL_FORMAT, IFormattedValues.DECIMAL_FORMAT }); + rm.done(); + } + + /** + * This method obtains the model data for a given ExpressionDMC object or + * for a FormattedValueDMC, or for this DSF service. + * + * @param dmc + * The context for which we are requesting the data + * @param rm + * The request monitor that will contain the requested data + */ + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof MIExpressionDMC) { + getExpressionData((MIExpressionDMC) dmc, + (DataRequestMonitor) rm); + } else if (dmc instanceof FormattedValueDMContext) { + getFormattedExpressionValue((FormattedValueDMContext) dmc, + (DataRequestMonitor) rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * Obtains the static data of an expression represented + * by an ExpressionDMC object (dmc). + * + * @param dmc + * The ExpressionDMC for the expression to be evaluated. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getExpressionData( + final IExpressionDMContext dmc, + final DataRequestMonitor rm) + { + if (dmc instanceof MIExpressionDMC) { + fExpressionCache.execute( + new ExprMetaGetVar(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(new ExpressionDMData(getData().getExpr(), + getData().getType(), getData().getNumChildren(), getData().getEditable())); + rm.done(); + } + }); + } else if (dmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * Obtains the address of an expression and the size of its type. + * + * @param dmc + * The ExpressionDMC for the expression. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getExpressionAddressData( + IExpressionDMContext dmc, + final DataRequestMonitor rm) { + + // First create an address expression and a size expression + // to be used in back-end calls + final IExpressionDMContext addressDmc = + createExpression( dmc, "&(" + dmc.getExpression() + ")" );//$NON-NLS-1$//$NON-NLS-2$ + final IExpressionDMContext sizeDmc = + createExpression( dmc, "sizeof(" + dmc.getExpression() + ")" ); //$NON-NLS-1$//$NON-NLS-2$ + + if (addressDmc instanceof InvalidContextExpressionDMC || sizeDmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + fExpressionCache.execute( + new MIDataEvaluateExpression(addressDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + String tmpAddrStr = getData().getValue(); + + // Deal with adresses of contents of a char* which is in + // the form of "0x12345678 \"This is a string\"" + int split = tmpAddrStr.indexOf(' '); + if (split != -1) tmpAddrStr = tmpAddrStr.substring(0, split); + final String addrStr = tmpAddrStr; + + fExpressionCache.execute( + new MIDataEvaluateExpression(sizeDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + try { + int size = Integer.parseInt(getData().getValue()); + rm.setData(new ExpressionDMAddress(addrStr, size)); + } catch (NumberFormatException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, + "Unexpected size format from backend: " + getData().getValue(), null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } + }); + } + } + + /** + * Obtains the value of an expression in a specific format. + * + * @param dmc + * The context for the format of the value requested and + * for the expression to be evaluated. The expression context + * should be a parent of the FormattedValueDMContext. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getFormattedExpressionValue( + final FormattedValueDMContext dmc, + final DataRequestMonitor rm) + { + // We need to make sure the FormattedValueDMContext also holds an ExpressionContext, + // or else this method cannot do its work. + // Note that we look for MIExpressionDMC and not IExpressionDMC, because getting + // looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still + // not what we need to have. + if (DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class) == null ) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + fExpressionCache.execute( + new ExprMetaGetValue(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(new FormattedValueDMData(getData().getValue())); + rm.done(); + } + }); + } + } + + /* Not implemented + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IExpressions#getBaseExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getBaseExpressions(IExpressionDMContext exprContext, + DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /** + * Retrieves the children expressions of the specified expression + * + * @param exprCtx + * The context for the expression for which the children + * should be retrieved. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getSubExpressions(final IExpressionDMContext dmc, + final DataRequestMonitor rm) + { + if (dmc instanceof MIExpressionDMC) { + fExpressionCache.execute( + new ExprMetaGetChildren(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + ExpressionInfo[] childrenExpr = getData().getChildrenExpressions(); + IExpressionDMContext[] childArray = new IExpressionDMContext[childrenExpr.length]; + for (int i=0; i rm) { + getSubExpressions( + exprCtx, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData((IExpressionDMContext[])Arrays.asList(getData()).subList(startIndex, startIndex + length).toArray()); + rm.done(); + } + }); + } + + /** + * Retrieves the count of children expressions of the specified expression + * + * @param exprCtx + * The context for the expression for which the children count + * should be retrieved. + * @param rm + * The data request monitor that will contain the requested data + */ + public void getSubExpressionCount(IExpressionDMContext dmc, + final DataRequestMonitor rm) + { + if (dmc instanceof MIExpressionDMC) { + fExpressionCache.execute( + new ExprMetaGetChildCount(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getChildNum()); + rm.done(); + } + }); + } else if (dmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * This method indicates if an expression can be written to. + * + * @param dmc: The data model context representing an expression. + * + * @param rm: Data Request monitor containing True if this expression's value can be edited. False otherwise. + */ + + public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor rm) { + if (dmc instanceof MIExpressionDMC) { + fExpressionCache.execute( + new ExprMetaGetAttributes(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getEditable()); + rm.done(); + } + }); + } else if (dmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + + /** + * Changes the value of the specified expression based on the new value and format. + * + * @param expressionContext + * The context for the expression for which the value + * should be changed. + * @param expressionValue + * The new value for the specified expression + * @param formatId + * The format in which the value is specified + * @param rm + * The request monitor that will indicate the completion of the operation + */ + public void writeExpression(final IExpressionDMContext dmc, String expressionValue, + String formatId, final RequestMonitor rm) { + + if (dmc instanceof MIExpressionDMC) { + // This command must not be cached, since it changes the state of the back-end. + // We must send it directly to the variable manager + varManager.writeValue( + dmc, + expressionValue, + formatId, + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // A value has changed, we should remove any references to that + // value in our cache. Since we don't have such granularity, + // we must clear the entire cache. + // We cannot use the context to do a more-specific reset, because + // the same global variable can be set with different contexts + fExpressionCache.reset(); + + // Issue event that the expression has changed + getSession().dispatchEvent(new ExpressionChangedEvent(dmc), getProperties()); + + rm.done(); + } + }); + } else if (dmc instanceof InvalidContextExpressionDMC) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ + rm.done(); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IResumedDMEvent e) { + fExpressionCache.setContextAvailable(e.getDMContext(), false); + if (e.getReason() != StateChangeReason.STEP) { + fExpressionCache.reset(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + fExpressionCache.setContextAvailable(e.getDMContext(), true); + fExpressionCache.reset(); + } + + @DsfServiceEventHandler + public void eventDispatched(IMemoryChangedEvent e) { + fExpressionCache.reset(); + // MIVariableManager separately traps this event + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fExpressionCache.reset(context); + // We must also mark all variable objects as out-of-date + // to refresh them as well + varManager.markAllOutOfDate(); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIBackend.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIBackend.java new file mode 100644 index 00000000000..eff154637ee --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIBackend.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.service.IDsfService; + +/** + * Service for controlling the back end process. + * @since 1.1 + */ +public interface IMIBackend extends IDsfService { + + public enum State { NOT_INITIALIZED, STARTED, TERMINATED }; + + /** + * Event indicating that the back end process has started. + */ + @Immutable + public static class BackendStateChangedEvent { + final private String fSessionId; + final private String fBackendId; + final private State fState; + + public BackendStateChangedEvent(String sessionId, String backendId, State state) { + fSessionId = sessionId; + fBackendId = backendId; + fState = state; + } + + public String getSessionId() { + return fSessionId; + } + + public String getBackendId() { + return fBackendId; + } + + public State getState() { + return fState; + } + } + + /** + * Returns the identifier of this backend service. It can be used + * to distinguish between multiple instances of this service in a + * single session. + */ + public String getId(); + + /** + * Requests that the backend be immediately terminated. + */ + public void destroy(); + + /** + * Returns the current state of the backed. + * @return + */ + public State getState(); + + /** + * Returns the exit code of the backend. Returns -1 if + * the backend exit code is not available. + * @return + */ + public int getExitCode(); + + /** + * Returns the backend command stream. + */ + public InputStream getMIInputStream(); + + /** + * Returns the backend result and event stream. + * @return + */ + public OutputStream getMIOutputStream(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIContainerDMContext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIContainerDMContext.java new file mode 100644 index 00000000000..b9dd989b3a9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIContainerDMContext.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; + +/** + * An container context object. In the GDB/MI protocol, thread groups + * are used as containers of threads, and are represented by a string + * identifier. These thread groups are the basis for this context. + * @since 1.1 + */ +public interface IMIContainerDMContext extends IContainerDMContext +{ + /** + * Returns the GDB/MI thread group identifier of this context. + */ + public String getGroupId(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExecutionDMContext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExecutionDMContext.java new file mode 100644 index 00000000000..71aafbb1449 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIExecutionDMContext.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + +/** + * An execution context object. In the GDB/MI protocol, threads are represented + * by an integer identifier, which is the basis for this context. The parent of this + * context should always be a container context. + */ +public interface IMIExecutionDMContext extends IExecutionDMContext +{ + /** + * Returns the GDB/MI thread identifier of this context. + * @return + */ + public int getThreadId(); +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcessDMContext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcessDMContext.java new file mode 100644 index 00000000000..55ade079f1b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcessDMContext.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ercisson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; + +/** + * A process context object. In the GDB/MI protocol, processes are represented + * by an string identifier, which is the basis for this context. + * @since 1.1 + */ +public interface IMIProcessDMContext extends IProcessDMContext { + /** + * Returns the GDB/MI process identifier of this context. + * @return + */ + public String getProcId(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcesses.java new file mode 100644 index 00000000000..d0504513ce0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/IMIProcesses.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.debug.service.IProcesses; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; + +/** + * This interface provides a method for creating execution contexts. + * @since 1.1 + */ +public interface IMIProcesses extends IProcesses +{ + /** + * Create a thread context. + * + * @param processDmc The parent process context + * @param threadId The OS Id of the thread + */ + IThreadDMContext createThreadContext(IProcessDMContext processDmc, String threadId); + + /** + * Create a process context. + * + * @param pid The OS Id of the process + */ + IProcessDMContext createProcessContext(ICommandControlDMContext controlDmc, String pid); + + /** + * Create an execution context. + * + * @param containerDmc The parent process debugging context + * @param threadDmc The parent thread context + * @param threadId The thread id of the thread + */ + IMIExecutionDMContext createExecutionContext(IContainerDMContext containerDmc, + IThreadDMContext threadDmc, + String threadId); + + /** + * Create a container context. + * + * @param processDmc The parent process context of this context + * @param groupId The thread group id of the process + */ + IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, + String groupId); + + /** + * Create a container context based on a threadId. This implies knowledge + * of which threads belong to which container. + * + * @param controlDmc The parent command control context of this context + * @param threadId The thread id belonging to the container we want to create + */ + IMIContainerDMContext createContainerContextFromThreadId(ICommandControlDMContext controlDmc, String threadId); +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointDMData.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointDMData.java new file mode 100644 index 00000000000..4a2c5e4e6a9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointDMData.java @@ -0,0 +1,267 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMData; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; +import org.eclipse.cdt.utils.Addr64; + +public class MIBreakpointDMData implements IBreakpointDMData { + + /** + * MI-specific breakpoint attributes markers. + */ + public static final String DSFMIBREAKPOINT = "org.eclipse.cdt.dsf.debug.breakpoint.mi"; //$NON-NLS-1$ + public static final String NUMBER = DSFMIBREAKPOINT + ".number"; //$NON-NLS-1$ + public static final String TYPE = DSFMIBREAKPOINT + ".type"; //$NON-NLS-1$ + public static final String THREAD_ID = DSFMIBREAKPOINT + ".threadId"; //$NON-NLS-1$ + public static final String FULL_NAME = DSFMIBREAKPOINT + ".fullName"; //$NON-NLS-1$ + public static final String HITS = DSFMIBREAKPOINT + ".hits"; //$NON-NLS-1$ + public static final String IS_TEMPORARY = DSFMIBREAKPOINT + ".isTemporary"; //$NON-NLS-1$ + public static final String IS_HARDWARE = DSFMIBREAKPOINT + ".isHardware"; //$NON-NLS-1$ + public static final String LOCATION = DSFMIBREAKPOINT + ".location"; //$NON-NLS-1$ + + // Back-end breakpoint object + private final MIBreakpoint fBreakpoint; + private final Map fProperties; + + // Breakpoint types + public static enum MIBreakpointNature { UNKNOWN, BREAKPOINT, WATCHPOINT, CATCHPOINT }; + private final MIBreakpointNature fNature; + + + /////////////////////////////////////////////////////////////////////////// + // Constructors + /////////////////////////////////////////////////////////////////////////// + + /** + * Copy constructor + * + * @param other + */ + public MIBreakpointDMData(MIBreakpointDMData other) { + + fBreakpoint = new MIBreakpoint(other.fBreakpoint); + fProperties = new HashMap(other.fProperties); + fNature = other.fNature; + } + + /** + * Constructs a DsfMIBreakpoint from a back-end object + * + * @param dsfMIBreakpoint back-end breakpoint + */ + public MIBreakpointDMData(MIBreakpoint dsfMIBreakpoint) { + + // We only support breakpoint and watchpoint (so far) + fBreakpoint = dsfMIBreakpoint; + fNature = dsfMIBreakpoint.isWatchpoint() ? MIBreakpointNature.WATCHPOINT : MIBreakpointNature.BREAKPOINT; + + fProperties = new HashMap(); + switch (fNature) { + + case BREAKPOINT: + { + // Generic breakpoint attributes + fProperties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT); + fProperties.put(MIBreakpoints.FILE_NAME, dsfMIBreakpoint.getFile()); + fProperties.put(MIBreakpoints.LINE_NUMBER, dsfMIBreakpoint.getLine()); + fProperties.put(MIBreakpoints.FUNCTION, dsfMIBreakpoint.getFunction()); + fProperties.put(MIBreakpoints.ADDRESS, dsfMIBreakpoint.getAddress()); + fProperties.put(MIBreakpoints.CONDITION, dsfMIBreakpoint.getCondition()); + fProperties.put(MIBreakpoints.IGNORE_COUNT, dsfMIBreakpoint.getIgnoreCount()); + fProperties.put(MIBreakpoints.IS_ENABLED, new Boolean(dsfMIBreakpoint.isEnabled())); + + // MI-specific breakpoint attributes + fProperties.put(NUMBER, dsfMIBreakpoint.getNumber()); + fProperties.put(TYPE, dsfMIBreakpoint.getType()); + fProperties.put(THREAD_ID, dsfMIBreakpoint.getThreadId()); + fProperties.put(FULL_NAME, dsfMIBreakpoint.getFullName()); + fProperties.put(HITS, dsfMIBreakpoint.getTimes()); + fProperties.put(IS_TEMPORARY, new Boolean(dsfMIBreakpoint.isTemporary())); + fProperties.put(IS_HARDWARE, new Boolean(dsfMIBreakpoint.isHardware())); + fProperties.put(LOCATION, formatLocation()); + break; + } + + case WATCHPOINT: + { + // Generic breakpoint attributes + fProperties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.WATCHPOINT); + fProperties.put(MIBreakpoints.EXPRESSION, dsfMIBreakpoint.getExpression()); + fProperties.put(MIBreakpoints.READ, dsfMIBreakpoint.isAccessWatchpoint() || dsfMIBreakpoint.isReadWatchpoint()); + fProperties.put(MIBreakpoints.WRITE, dsfMIBreakpoint.isAccessWatchpoint() || dsfMIBreakpoint.isWriteWatchpoint()); + + // MI-specific breakpoint attributes + fProperties.put(NUMBER, dsfMIBreakpoint.getNumber()); + break; + } + + // Not reachable + default: + { + fProperties.put(MIBreakpoints.BREAKPOINT_TYPE, null); + break; + } + } + } + + /** + * Formats the LOCATION synthetic property from the existing fields + * + * @return The location string + */ + private String formatLocation() { + + // Unlikely default location + String location = fBreakpoint.getAddress(); + + // Get the relevant parameters + String fileName = fBreakpoint.getFile(); + Integer lineNumber = fBreakpoint.getLine(); + String function = fBreakpoint.getFunction(); + + if (!fileName.equals("")) { //$NON-NLS-1$ + if (lineNumber != -1) { + location = fileName + ":" + lineNumber; //$NON-NLS-1$ + } else { + location = fileName + ":" + function; //$NON-NLS-1$ + } + } + + return location; + } + + /** + * Checks for equality + * + * @param other + * @return + */ + public boolean equals(MIBreakpointDMData other) { + return (fNature == other.fNature) && (fProperties.equals(other.fProperties)); + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointDMData + /////////////////////////////////////////////////////////////////////////// + + public String getBreakpointType() { + return (String) fProperties.get(MIBreakpoints.BREAKPOINT_TYPE); + } + + public int getReference() { + return fBreakpoint.getNumber(); + } + + public IAddress[] getAddresses() { + IAddress[] addresses = new IAddress[1]; + addresses[0] = new Addr64(fBreakpoint.getAddress()); + return addresses; + } + + public String getCondition() { + return fBreakpoint.getCondition(); + } + + public String getExpression() { + return fBreakpoint.getExpression(); + } + + public String getFileName() { + return fBreakpoint.getFile(); + } + + public String getFunctionName() { + return fBreakpoint.getFunction(); + } + + public int getIgnoreCount() { + return fBreakpoint.getIgnoreCount(); + } + + public int getLineNumber() { + return fBreakpoint.getLine(); + } + + public boolean isEnabled() { + return fBreakpoint.isEnabled(); + } + + /////////////////////////////////////////////////////////////////////////// + // MIBreakpointDMData + /////////////////////////////////////////////////////////////////////////// + + public int getNumber() { + return fBreakpoint.getNumber(); + } + + public String getThreadId() { + return fBreakpoint.getThreadId(); + } + + public boolean isTemporary() { + return fBreakpoint.isTemporary(); + } + + public boolean isHardware() { + return fBreakpoint.isHardware(); + } + + public String getLocation() { + return (String) fProperties.get(LOCATION); + } + + public int getHits() { + return fBreakpoint.getTimes(); + } + + public String getFullName() { + return fBreakpoint.getFullName(); + } + + public String getType() { + return fBreakpoint.getType(); + } + + public void setCondition(String condition) { + fBreakpoint.setCondition(condition); + fProperties.put(MIBreakpoints.CONDITION, condition); + } + + public void setIgnoreCount(int ignoreCount) { + fBreakpoint.setIgnoreCount(ignoreCount); + fProperties.put(MIBreakpoints.IGNORE_COUNT, ignoreCount); + } + + public void setEnabled(boolean isEnabled) { + fBreakpoint.setEnabled(isEnabled); + fProperties.put(MIBreakpoints.IS_ENABLED, isEnabled); + } + + public boolean isReadWatchpoint() { + return fBreakpoint.isReadWatchpoint(); + } + + public boolean isWriteWatchpoint() { + return fBreakpoint.isWriteWatchpoint(); + } + + public boolean isAccessWatchpoint() { + return fBreakpoint.isAccessWatchpoint(); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpoints.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpoints.java new file mode 100644 index 00000000000..5f626fa578d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpoints.java @@ -0,0 +1,1009 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakAfter; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakCondition; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakDelete; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakDisable; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakEnable; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakList; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakWatch; +import org.eclipse.cdt.dsf.mi.service.command.events.MIGDBExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * Initial breakpoint service implementation. + * Implements the IBreakpoints interface. + */ +public class MIBreakpoints extends AbstractDsfService implements IBreakpoints +{ + /** + * Breakpoint attributes markers used in the map parameters of insert/updateBreakpoint(). + * All are optional with the possible exception of TYPE. It is the responsibility of the + * service to ensure that the set of attributes provided is sufficient to create/update + * a valid breakpoint on the back-end. + */ + public static final String PREFIX = "org.eclipse.cdt.dsf.debug.breakpoint"; //$NON-NLS-1$ + + // General markers + public static final String BREAKPOINT_TYPE = PREFIX + ".type"; //$NON-NLS-1$ + public static final String BREAKPOINT = "breakpoint"; //$NON-NLS-1$ + public static final String WATCHPOINT = "watchpoint"; //$NON-NLS-1$ + public static final String CATCHPOINT = "catchpoint"; //$NON-NLS-1$ + + // Basic set of breakpoint attribute markers + public static final String FILE_NAME = PREFIX + ".fileName"; //$NON-NLS-1$ + public static final String LINE_NUMBER = PREFIX + ".lineNumber"; //$NON-NLS-1$ + public static final String FUNCTION = PREFIX + ".function"; //$NON-NLS-1$ + public static final String ADDRESS = PREFIX + ".address"; //$NON-NLS-1$ + public static final String CONDITION = PREFIX + ".condition"; //$NON-NLS-1$ + public static final String IGNORE_COUNT = PREFIX + ".ignoreCount"; //$NON-NLS-1$ + public static final String IS_ENABLED = PREFIX + ".isEnabled"; //$NON-NLS-1$ + + // Basic set of watchpoint attribute markers + public static final String EXPRESSION = PREFIX + ".expression"; //$NON-NLS-1$ + public static final String READ = PREFIX + ".read"; //$NON-NLS-1$ + public static final String WRITE = PREFIX + ".write"; //$NON-NLS-1$ + + + // Services + ICommandControl fConnection; + + // Service breakpoints tracking + // The breakpoints are stored per context and keyed on the back-end breakpoint reference + private Map> fBreakpoints = + new HashMap>(); + + // Error messages + final String NULL_STRING = ""; //$NON-NLS-1$ + final String UNKNOWN_EXECUTION_CONTEXT = "Unknown execution context"; //$NON-NLS-1$ + final String UNKNOWN_BREAKPOINT_CONTEXT = "Unknown breakpoint context"; //$NON-NLS-1$ + final String UNKNOWN_BREAKPOINT_TYPE = "Unknown breakpoint type"; //$NON-NLS-1$ + final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; //$NON-NLS-1$ + final String BREAKPOINT_INSERTION_FAILURE = "Breakpoint insertion failure"; //$NON-NLS-1$ + final String WATCHPOINT_INSERTION_FAILURE = "Watchpoint insertion failure"; //$NON-NLS-1$ + final String INVALID_CONDITION = "Invalid condition"; //$NON-NLS-1$ + + /////////////////////////////////////////////////////////////////////////// + // Breakpoint Events + /////////////////////////////////////////////////////////////////////////// + + public class BreakpointsChangedEvent extends AbstractDMEvent implements IBreakpointsChangedEvent { + private IBreakpointDMContext[] fEventBreakpoints; + + public BreakpointsChangedEvent(IBreakpointDMContext bp) { + super(DMContexts.getAncestorOfType(bp, IBreakpointsTargetDMContext.class)); + fEventBreakpoints = new IBreakpointDMContext[] { bp }; + } + public IBreakpointDMContext[] getBreakpoints() { + return fEventBreakpoints; + } + } + + public class BreakpointAddedEvent extends BreakpointsChangedEvent implements IBreakpointsAddedEvent { + public BreakpointAddedEvent(IBreakpointDMContext context) { + super(context); + } + } + + public class BreakpointUpdatedEvent extends BreakpointsChangedEvent implements IBreakpointsUpdatedEvent { + public BreakpointUpdatedEvent(IBreakpointDMContext context) { + super(context); + } + } + + public class BreakpointRemovedEvent extends BreakpointsChangedEvent implements IBreakpointsRemovedEvent { + public BreakpointRemovedEvent(IBreakpointDMContext context) { + super(context); + } + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointDMContext + // Used to hold the back-end breakpoint references. The reference can then + // be used to get the actual DsfMIBreakpoint. + /////////////////////////////////////////////////////////////////////////// + @Immutable + public static final class MIBreakpointDMContext extends AbstractDMContext implements IBreakpointDMContext { + + // The breakpoint reference + private final Integer fReference; + + /** + * @param service the Breakpoint service + * @param parents the parent contexts + * @param reference the DsfMIBreakpoint reference + */ + public MIBreakpointDMContext(MIBreakpoints service, IDMContext[] parents, int reference) { + super(service.getSession().getId(), parents); + fReference = reference; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints.IDsfBreakpointDMContext#getReference() + */ + public int getReference() { + return fReference; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && (fReference == ((MIBreakpointDMContext) obj).fReference); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#hashCode() + */ + @Override + public int hashCode() { + return baseHashCode() + fReference.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return baseToString() + ".reference(" + fReference + ")"; //$NON-NLS-1$//$NON-NLS-2$*/ + } + } + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor + * + * @param session The debugging session + */ + public MIBreakpoints(DsfSession session) { + super(session); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + /* + * Asynchronous service initialization + */ + private void doInitialize(final RequestMonitor rm) { + + // Get the services references + fConnection = getServicesTracker().getService(ICommandControl.class); + + // Register for the useful events + getSession().addServiceEventListener(this, null); + + // Register this service + register(new String[] { IBreakpoints.class.getName(), MIBreakpoints.class.getName() }, + new Hashtable()); + + rm.done(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void shutdown(final RequestMonitor rm) { + unregister(); + getSession().removeServiceEventListener(this); + rm.done(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IServiceEventListener + /////////////////////////////////////////////////////////////////////////// + + /** + * This method is left for API compatibility only. + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(MIWatchpointScopeEvent e) { + // When a watchpoint goes out of scope, it is automatically removed from + // the back-end. To keep our internal state synchronized, we have to + // remove it from our breakpoints map. + IBreakpointsTargetDMContext bpContext = DMContexts.getAncestorOfType(e.getDMContext(), IBreakpointsTargetDMContext.class); + if (bpContext != null) { + Map contextBps = fBreakpoints.get(bpContext); + if (contextBps != null) { + contextBps.remove(e.getNumber()); + } + } + } + + /** + * This method is left for API compatibility only. + * ICommandControlShutdownDMEvent is used instead + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(MIGDBExitEvent e) { + } + + /** + * @since 1.1 + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpoints interface + /////////////////////////////////////////////////////////////////////////// + + //------------------------------------------------------------------------- + // getBreakpoints + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#getBreakpoints(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor drm) + { + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); + drm.done(); + return; + } + + // Select the breakpoints context map + // If it doesn't exist then no breakpoint was ever inserted for this breakpoint space. + // In that case, return an empty list. + final Map breakpointContext = fBreakpoints.get(context); + if (breakpointContext == null) { + drm.setData(new IBreakpointDMContext[0]); + drm.done(); + return; + } + + // Execute the command + fConnection.queueCommand(new MIBreakList(context), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // Refresh the breakpoints map and format the result + breakpointContext.clear(); + MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); + IBreakpointDMContext[] result = new IBreakpointDMContext[breakpoints.length]; + for (int i = 0; i < breakpoints.length; i++) { + MIBreakpointDMData breakpoint = new MIBreakpointDMData(breakpoints[i]); + int reference = breakpoint.getReference(); + result[i] = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference); + breakpointContext.put(reference, breakpoint); + } + drm.setData(result); + drm.done(); + } + }); + } + + //------------------------------------------------------------------------- + // getBreakpointDMData + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#getBreakpointDMData(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IDsfBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getBreakpointDMData(IBreakpointDMContext dmc, DataRequestMonitor drm) + { + // Validate the breakpoint context + if (dmc == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + drm.done(); + return; + } + + // Validate the breakpoint type + MIBreakpointDMContext breakpoint; + if (dmc instanceof MIBreakpointDMContext) { + breakpoint = (MIBreakpointDMContext) dmc; + } + else { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); + drm.done(); + return; + } + + // Validate the target context + IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(breakpoint, IBreakpointsTargetDMContext.class); + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); + drm.done(); + return; + } + + // Select the breakpoints context map + Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + drm.done(); + return; + } + + // No need to go to the back-end for this one + IBreakpointDMData breakpointCopy = new MIBreakpointDMData(contextBreakpoints.get(breakpoint.getReference())); + drm.setData(breakpointCopy); + drm.done(); + } + + //------------------------------------------------------------------------- + // insertBreakpoint + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#insertBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext, java.util.Map, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void insertBreakpoint(IBreakpointsTargetDMContext context, Map attributes, DataRequestMonitor drm) { + + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); + drm.done(); + return; + } + + // Select the breakpoints context map. If it doesn't exist, create it. + Map breakpointContext = fBreakpoints.get(context); + if (breakpointContext == null) { + breakpointContext = new HashMap(); + fBreakpoints.put(context, breakpointContext); + } + + // Validate the breakpoint type + String type = (String) attributes.get(BREAKPOINT_TYPE); + if (type == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); + drm.done(); + return; + } + + // And go... + if (type.equals(BREAKPOINT)) { + addBreakpoint(context, attributes, drm); + } + else if (type.equals(WATCHPOINT)) { + addWatchpoint(context, attributes, drm); + } + else { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); + drm.done(); + } + } + + /** + * @param map + * @param key + * @param defaultValue + * @return + */ + public Object getProperty(Map map, String key, Object defaultValue) { + return (map.containsKey(key) && (map.get(key) != null)) ? map.get(key) : defaultValue; + } + + /** + * @param attributes + * @return + */ + private String formatLocation(Map attributes) { + + // Unlikely default location + String location = (String) getProperty(attributes, ADDRESS, NULL_STRING); + + // Get the relevant parameters + String fileName = (String) getProperty(attributes, FILE_NAME, NULL_STRING); + Integer lineNumber = (Integer) getProperty(attributes, LINE_NUMBER, -1); + String function = (String) getProperty(attributes, FUNCTION, NULL_STRING); + + if (!fileName.equals(NULL_STRING)) { + if (lineNumber != -1) { + location = fileName + ":" + lineNumber; //$NON-NLS-1$ + } else { + location = fileName + ":" + function; //$NON-NLS-1$ + } + } else if (!function.equals(NULL_STRING)) { + // function location without source + location = function; + } else if (location.length() > 0) { + // address location + if (Character.isDigit(location.charAt(0))) { + // numeric address needs '*' prefix + location = '*' + location; + } + } + + return location; + } + + /** + * Add a breakpoint of type BREAKPOINT + * + * @param context + * @param breakpoint + * @param drm + */ + private void addBreakpoint(final IBreakpointsTargetDMContext context, final Map attributes, final DataRequestMonitor drm) + { + // Select the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + drm.done(); + return; + } + + // Extract the relevant parameters (providing default values to avoid potential NPEs) + String location = formatLocation(attributes); + + if (location.equals(NULL_STRING)) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + drm.done(); + return; + } + + Boolean isTemporary = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_TEMPORARY, false); + Boolean isHardware = (Boolean) getProperty(attributes, MIBreakpointDMData.IS_HARDWARE, false); + final String condition = (String) getProperty(attributes, CONDITION, NULL_STRING); + Integer ignoreCount = (Integer) getProperty(attributes, IGNORE_COUNT, 0 ); + String threadId = (String) getProperty(attributes, MIBreakpointDMData.THREAD_ID, "0"); //$NON-NLS-1$ + int tid = Integer.parseInt(threadId); + + // The DataRequestMonitor for the add request + DataRequestMonitor addBreakpointDRM = + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + + // With MI, an invalid location won't generate an error + if (getData().getMIBreakpoints().length == 0) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); + drm.done(); + return; + } + + // Create a breakpoint object and store it in the map + final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]); + int reference = newBreakpoint.getNumber(); + if (reference == -1) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); + drm.done(); + return; + } + contextBreakpoints.put(reference, newBreakpoint); + + // Format the return value + MIBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference); + drm.setData(dmc); + + // Flag the event + getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); + + // By default the breakpoint is enabled at creation + // If it wasn't supposed to be, then disable it right away + Map delta = new HashMap(); + delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true)); + modifyBreakpoint(dmc, delta, drm, false); + } + + @Override + protected void handleError() { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, BREAKPOINT_INSERTION_FAILURE, null)); + drm.done(); + } + }; + + // Execute the command + fConnection.queueCommand( + new MIBreakInsert(context, isTemporary, isHardware, condition, ignoreCount, location, tid), addBreakpointDRM); + } + + /** + * Add a breakpoint of type WATCHPOINT + * + * @param context + * @param watchpoint + * @param drm + */ + private void addWatchpoint(final IBreakpointsTargetDMContext context, final Map attributes, final DataRequestMonitor drm) + { + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + drm.done(); + return; + } + + // Extract the relevant parameters (providing default values to avoid potential NPEs) + String expression = (String) getProperty(attributes, EXPRESSION, NULL_STRING); + boolean isRead = (Boolean) getProperty(attributes, READ, false); + boolean isWrite = (Boolean) getProperty(attributes, WRITE, false); + + // The DataRequestMonitor for the add request + DataRequestMonitor addWatchpointDRM = + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + + // With MI, an invalid location won't generate an error + if (getData().getMIBreakpoints().length == 0) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, WATCHPOINT_INSERTION_FAILURE, null)); + drm.done(); + return; + } + + // Create a breakpoint object and store it in the map + final MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(getData().getMIBreakpoints()[0]); + int reference = newBreakpoint.getNumber(); + if (reference == -1) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, WATCHPOINT_INSERTION_FAILURE, null)); + drm.done(); + return; + } + contextBreakpoints.put(reference, newBreakpoint); + + // Format the return value + IBreakpointDMContext dmc = new MIBreakpointDMContext(MIBreakpoints.this, new IDMContext[] { context }, reference); + drm.setData(dmc); + + // Flag the event + getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); + + // Condition, ignore count and state can not be specified at watchpoint creation time. + // Therefore, we have to update the watchpoint if any of these is present + Map delta = new HashMap(); + delta.put(CONDITION, getProperty(attributes, CONDITION, NULL_STRING)); + delta.put(IGNORE_COUNT, getProperty(attributes, IGNORE_COUNT, 0 )); + delta.put(IS_ENABLED, getProperty(attributes, IS_ENABLED, true)); + modifyBreakpoint(dmc, delta, drm, false); + } + + @Override + protected void handleError() { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, WATCHPOINT_INSERTION_FAILURE, null)); + drm.done(); + } + }; + + // Execute the command + fConnection.queueCommand(new MIBreakWatch(context, isRead, isWrite, expression), addWatchpointDRM); + } + + //------------------------------------------------------------------------- + // removeBreakpoint + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#removeBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void removeBreakpoint(final IBreakpointDMContext dmc, final RequestMonitor rm) { + + // Validate the breakpoint context + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Validate the breakpoint type + MIBreakpointDMContext breakpointCtx; + if (dmc instanceof MIBreakpointDMContext) { + breakpointCtx = (MIBreakpointDMContext) dmc; + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); + rm.done(); + return; + } + + // Validate the target context + IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); + if (context == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); + rm.done(); + return; + } + + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + + // Validate the breakpoint + final int reference = breakpointCtx.getReference(); + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + + // Queue the command + fConnection.queueCommand( + new MIBreakDelete(context, new int[] { reference }), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + getSession().dispatchEvent(new BreakpointRemovedEvent(dmc), getProperties()); + contextBreakpoints.remove(reference); + } + rm.done(); + } + }); + } + + // ------------------------------------------------------------------------- + // updateBreakpoint + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IBreakpoints#updateBreakpoint(org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext, java.util.Map, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void updateBreakpoint(IBreakpointDMContext dmc, Map properties, RequestMonitor rm) + { + // Validate the breakpoint context + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Validate the breakpoint type + MIBreakpointDMContext breakpointCtx; + if (dmc instanceof MIBreakpointDMContext) { + breakpointCtx = (MIBreakpointDMContext) dmc; + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE, null)); + rm.done(); + return; + } + + // Validate the context + IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); + if (context == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT, null)); + rm.done(); + return; + } + + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + + // Validate the breakpoint + final int reference = breakpointCtx.getReference(); + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + + modifyBreakpoint(dmc, properties, rm, true); + } + + /** + * @param dmc + * @param properties + * @param rm + * @param generateUpdateEvent + */ + private void modifyBreakpoint(final IBreakpointDMContext dmc, Map attributes, final RequestMonitor rm, final boolean generateUpdateEvent) + { + // Use a working copy of the attributes since we are going to tamper happily with them + Map properties = new HashMap(attributes); + + // Retrieve the breakpoint parameters + // At this point, we know their are OK so there is no need to re-validate + MIBreakpointDMContext breakpointCtx = (MIBreakpointDMContext) dmc; + IBreakpointsTargetDMContext context = DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class); + final Map contextBreakpoints = fBreakpoints.get(context); + final int reference = breakpointCtx.getReference(); + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + + // Track the number of change requests + int numberOfChanges = 0; + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (generateUpdateEvent) + getSession().dispatchEvent(new BreakpointUpdatedEvent(dmc), getProperties()); + rm.done(); + } + }; + + // Determine if the breakpoint condition changed + String conditionAttribute = CONDITION; + if (properties.containsKey(conditionAttribute)) { + String oldValue = breakpoint.getCondition(); + String newValue = (String) properties.get(conditionAttribute); + if (newValue == null) newValue = NULL_STRING; + if (!oldValue.equals(newValue)) { + changeCondition(context, reference, newValue, countingRm); + numberOfChanges++; + } + properties.remove(conditionAttribute); + } + + // Determine if the breakpoint ignore count changed + String ignoreCountAttribute = IGNORE_COUNT; + if (properties.containsKey(ignoreCountAttribute)) { + Integer oldValue = breakpoint.getIgnoreCount(); + Integer newValue = (Integer) properties.get(ignoreCountAttribute); + if (newValue == null) newValue = 0; + if (!oldValue.equals(newValue)) { + changeIgnoreCount(context, reference, newValue, countingRm); + numberOfChanges++; + } + properties.remove(ignoreCountAttribute); + } + + // Determine if the breakpoint state changed + String enableAttribute = IS_ENABLED; + if (properties.containsKey(enableAttribute)) { + Boolean oldValue = breakpoint.isEnabled(); + Boolean newValue = (Boolean) properties.get(enableAttribute); + if (newValue == null) newValue = false; + if (!oldValue.equals(newValue)) { + numberOfChanges++; + if (newValue) + enableBreakpoint(context, reference, countingRm); + else + disableBreakpoint(context, reference, countingRm); + } + properties.remove(enableAttribute); + } + + // Set the number of completions required + countingRm.setDoneCount(numberOfChanges); + } + + /** + * Update the breakpoint condition + * + * @param context + * @param dmc + * @param condition + * @param rm + */ + private void changeCondition(final IBreakpointsTargetDMContext context, + final int reference, final String condition, final RequestMonitor rm) + { + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Queue the command + fConnection.queueCommand( + new MIBreakCondition(context, reference, condition), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + breakpoint.setCondition(condition); + rm.done(); + } + + // In case of error (new condition could not be installed for whatever reason), + // GDB "offers" different behaviors depending on its version: it can either keep + // the original condition (the right thing to do) or keep the invalid condition. + // Our sole option is to remove the condition in case of error and rely on the + // upper layer to re-install the right condition. + @Override + protected void handleError() { + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + // Remove invalid condition from the back-end breakpoint + breakpoint.setCondition(NULL_STRING); + fConnection.queueCommand( + new MIBreakCondition(context, reference, NULL_STRING), + new DataRequestMonitor(getExecutor(), rm) { + @Override + // The report the initial problem + protected void handleCompleted() { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONDITION, null)); + rm.done(); + } + }); + } + }); + } + + + /** + * Update the breakpoint ignoreCount + * + * @param context + * @param reference + * @param ignoreCount + * @param rm + */ + private void changeIgnoreCount(IBreakpointsTargetDMContext context, + final int reference, final int ignoreCount, final RequestMonitor rm) + { + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Queue the command + fConnection.queueCommand( + new MIBreakAfter(context, reference, ignoreCount), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + breakpoint.setIgnoreCount(ignoreCount); + rm.done(); + } + }); + } + + /** + * Enable the breakpoint + * + * @param context + * @param reference + * @param rm + */ + private void enableBreakpoint(IBreakpointsTargetDMContext context, + final int reference, final RequestMonitor rm) + { + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Queue the command + fConnection.queueCommand( + new MIBreakEnable(context, new int[] { reference }), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + breakpoint.setEnabled(true); + rm.done(); + } + }); + } + + /** + * Disable the breakpoint + * + * @param context + * @param dmc + * @param rm + */ + private void disableBreakpoint(IBreakpointsTargetDMContext context, + final int reference, final RequestMonitor rm) + { + // Pick the context breakpoints map + final Map contextBreakpoints = fBreakpoints.get(context); + if (contextBreakpoints == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_CONTEXT, null)); + rm.done(); + return; + } + + // Queue the command + fConnection.queueCommand( + new MIBreakDisable(context, new int[] { reference }), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIBreakpointDMData breakpoint = contextBreakpoints.get(reference); + if (breakpoint == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT, null)); + rm.done(); + return; + } + breakpoint.setEnabled(false); + rm.done(); + } + }); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointsManager.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointsManager.java new file mode 100644 index 00000000000..58c4586481f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIBreakpointsManager.java @@ -0,0 +1,1658 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River 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: + * Wind River - Initial API and implementation + * Ericsson - High-level breakpoints integration + * Ericsson - Added breakpoint filter support + * Ericsson - Re-factored the service and put a few comments + * Ericsson - Added Action support + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.debug.core.CDebugCorePlugin; +import org.eclipse.cdt.debug.core.breakpointactions.BreakpointActionManager; +import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint; +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.debug.core.model.ICBreakpointExtension; +import org.eclipse.cdt.debug.core.model.ICLineBreakpoint; +import org.eclipse.cdt.debug.core.model.ICWatchpoint; +import org.eclipse.cdt.debug.internal.core.breakpoints.BreakpointProblems; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.IDsfBreakpointExtension; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointAddedEvent; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointRemovedEvent; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.BreakpointUpdatedEvent; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext; +import org.eclipse.cdt.dsf.mi.service.breakpoint.actions.BreakpointActionAdapter; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIGDBExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.MultiRule; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointListener; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.IBreakpointManagerListener; +import org.eclipse.debug.core.model.IBreakpoint; +import org.osgi.framework.BundleContext; + +/** + * Breakpoint service interface. The breakpoint service tracks CDT breakpoint + * objects, and based on those, it manages breakpoints in the debugger back end. + * + * It relies on MIBreakpoints for the actual back-end interface. + */ +public class MIBreakpointsManager extends AbstractDsfService implements IBreakpointManagerListener, IBreakpointListener +{ + // Note: Find a way to import this (careful of circular dependencies) + public final static String GDB_DEBUG_MODEL_ID = "org.eclipse.cdt.dsf.gdb"; //$NON-NLS-1$ + + // Extra breakpoint attributes + private static final String ATTR_DEBUGGER_PATH = GdbPlugin.PLUGIN_ID + ".debuggerPath"; //$NON-NLS-1$ + private static final String ATTR_THREAD_FILTER = GdbPlugin.PLUGIN_ID + ".threadFilter"; //$NON-NLS-1$ + private static final String ATTR_THREAD_ID = GdbPlugin.PLUGIN_ID + ".threadID"; //$NON-NLS-1$ + + // Services + ICommandControl fConnection; + IRunControl fRunControl; + ISourceLookup fSourceLookup; + IBreakpoints fBreakpoints; + IBreakpointManager fBreakpointManager; // Platform breakpoint manager (not this!) + BreakpointActionManager fBreakpointActionManager; + + /////////////////////////////////////////////////////////////////////////// + // Breakpoints tracking + /////////////////////////////////////////////////////////////////////////// + + private String fDebugModelId; + + // Holds the set of platform breakpoints with their corresponding back-end + // breakpoint attributes, per context (i.e. each platform breakpoint is + // replicated for each execution context). + // - Context entry added/removed on start/stopTrackingBreakpoints() + // - Augmented on breakpointAdded() + // - Modified on breakpointChanged() + // - Diminished on breakpointRemoved() + private Map>> fPlatformBPs = + new HashMap>>(); + + // Holds the set of target breakpoints, per execution context, and their + // mapping to the corresponding platform breakpoint. In a given execution + // context there can only be one platform breakpoint per target breakpoint. + // Acts as a mapping from target (low-level) BP to the corresponding platform + // (high-level) BP. + // Updated when: + // - We start/stop tracking an execution context + // - A platform breakpoint is added/removed + // - A thread filter is applied/removed + private Map> fTargetBPs = + new HashMap>(); + + // Holds the mapping from platform breakpoint to the corresponding target + // breakpoint(s), per context. There can be multiple back-end BPs for a + // single platform BP in the case of [1] multiple target contexts, and/or + // [2] thread filtering. + // Updated when: + // - We start/stop tracking an execution context + // - A platform breakpoint is added/removed + // - A thread filter is applied/removed + private Map>> fBreakpointIDs = + new HashMap>>(); + + // Holds the mapping from platform breakpoint to the corresponding target + // breakpoint threads, per context. + // Updated when: + // - We start/stop tracking an execution context + // - A platform breakpoint is added/removed + // - A thread filter is applied/removed + private Map>> fBreakpointThreads = + new HashMap>>(); + + // Due to the very asynchronous nature of DSF, a new breakpoint request can + // pop up at any time before an ongoing one is completed. The following set + // is used to store requests until the ongoing operation completes. + private Set fPendingRequests = new HashSet(); + private Set fPendingBreakpoints = new HashSet(); + + private Map fBreakpointMarkerProblems = + new HashMap(); + + /////////////////////////////////////////////////////////////////////////// + // String constants + /////////////////////////////////////////////////////////////////////////// + + private static final String NULL_STRING = ""; //$NON-NLS-1$ + + static final String CONTEXT_ALREADY_INITIALIZED = "Context already initialized"; //$NON-NLS-1$ + static final String INVALID_CONTEXT_TYPE = "Invalid context type"; //$NON-NLS-1$ + static final String INVALID_CONTEXT = "Invalid context"; //$NON-NLS-1$ + + static final String UNABLE_TO_READ_BREAKPOINT = "Unable to read initial breakpoint attributes"; //$NON-NLS-1$ + static final String BREAKPOINT_NOT_INSTALLED = "Breakpoints not installed for given context"; //$NON-NLS-1$ + static final String BREAKPOINT_ALREADY_INSTALLED = "Breakpoint already installed"; //$NON-NLS-1$ + static final String BREAKPOINT_ALREADY_REMOVED = "Breakpoint already removed"; //$NON-NLS-1$ + + static final String INVALID_BREAKPOINT = "Invalid breakpoint"; //$NON-NLS-1$ + static final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; //$NON-NLS-1$ + static final String INVALID_PARAMETER = "Invalid breakpoint parameter(s)"; //$NON-NLS-1$ + + static final String NO_DEBUGGER_PATH = "No debugger path for breakpoint"; //$NON-NLS-1$ + static final String NO_MARKER_FOR_BREAKPOINT = "No marker associated with breakpoint"; //$NON-NLS-1$ + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor. + * Performs basic instantiation (method initialize() performs the real + * service initialization asynchronously). + * + * @param session the debugging session + * @param debugModelId the debugging model + */ + public MIBreakpointsManager(DsfSession session, String debugModelId) { + super(session); + fDebugModelId = debugModelId; + } + + //------------------------------------------------------------------------- + // initialize + //------------------------------------------------------------------------- + // - Collect references for the services we interact with + // - Register to interesting events + // - Obtain the list of platform breakpoints + // - Register the service for interested parties + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + /** + * @param rm + */ + private void doInitialize(RequestMonitor rm) { + + // Get the required services references from central repository + fConnection = getServicesTracker().getService(ICommandControl.class); + fRunControl = getServicesTracker().getService(IRunControl.class); + fSourceLookup = getServicesTracker().getService(ISourceLookup.class); + fBreakpoints = getServicesTracker().getService(IBreakpoints.class); + fBreakpointManager = DebugPlugin.getDefault().getBreakpointManager(); + fBreakpointActionManager = CDebugCorePlugin.getDefault().getBreakpointActionManager(); + + // Register to the useful events + getSession().addServiceEventListener(this, null); + fBreakpointManager.addBreakpointListener(this); + fBreakpointManager.addBreakpointManagerListener(this); + + // And register this service + register(new String[] { MIBreakpointsManager.class.getName() }, + new Hashtable()); + rm.done(); + } + + //------------------------------------------------------------------------- + // shutdown + //------------------------------------------------------------------------- + // - Un-register the service + // - Stop listening to events + // - Remove the breakpoints installed by this service + // + // Since we are shutting down, there is no overwhelming need + // to keep the maps coherent... + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void shutdown(final RequestMonitor rm) { + + // Stop accepting requests and events + unregister(); + getSession().removeServiceEventListener(this); + fBreakpointManager.removeBreakpointListener(this); + fBreakpointManager.removeBreakpointManagerListener(this); + + // Cleanup the breakpoints that are still installed by the service. + // Use a counting monitor which will call mom to complete the shutdown + // after the breakpoints are un-installed (successfully or not). + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + MIBreakpointsManager.super.shutdown(rm); + } + }; + + List targetBPKeys = new ArrayList(fTargetBPs.size()); + targetBPKeys.addAll(0, fTargetBPs.keySet()); + for (IBreakpointsTargetDMContext dmc : targetBPKeys) { + stopTrackingBreakpoints(dmc, countingRm); + } + countingRm.setDoneCount(targetBPKeys.size()); + } + + //------------------------------------------------------------------------- + // getBundleContext + //------------------------------------------------------------------------- + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointsManager + /////////////////////////////////////////////////////////////////////////// + + //------------------------------------------------------------------------- + // startTrackingBreakpoints + //------------------------------------------------------------------------- + // - Augment the maps with the new execution context + // - Install the platform breakpoints on the selected target + //------------------------------------------------------------------------- + + /** + * Install and begin tracking breakpoints for given context. The service + * will keep installing new breakpoints that appear in the IDE for this + * context until {@link #uninstallBreakpoints(IDMContext)} is called for that + * context. + * @param dmc Context to start tracking breakpoints for. + * @param rm Completion callback. + */ + public void startTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + + // Validate the execution context + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_CONTEXT, null)); + rm.done(); + return; + } + + // Make sure a mapping for this execution context does not already exist + Map> platformBPs = fPlatformBPs.get(dmc); + Map> breakpointIDs = fBreakpointIDs.get(dmc); + Map targetIDs = fTargetBPs.get(dmc); + Map> threadIDs = fBreakpointThreads.get(dmc); + if ((platformBPs != null) || (breakpointIDs != null) || (targetIDs != null) || (threadIDs != null)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, CONTEXT_ALREADY_INITIALIZED, null)); + rm.done(); + return; + } + + // Create entries in the breakpoint tables for the new context. These entries should only + // be removed when this service stops tracking breakpoints for the given context. + fPlatformBPs.put(dmc, new HashMap>()); + fBreakpointIDs.put(dmc, new HashMap>()); + fTargetBPs.put(dmc, new HashMap()); + fBreakpointThreads.put(dmc, new HashMap>()); + + // Install the platform breakpoints (stored in fPlatformBPs) on the target. + new Job("DSF BreakpointsManager: Install initial breakpoints on target") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + // Submit the runnable to plant the breakpoints on dispatch thread. + getExecutor().submit(new Runnable() { + public void run() { + installInitialBreakpoints(dmc, rm); + } + }); + + return Status.OK_STATUS; + } + }.schedule(); + } + + /** + * Installs the breakpoints that existed prior to the activation of this + * execution context. + * + * @param dmc + * @param initialPlatformBPs + * @param rm + */ + private void installInitialBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) + { + // Retrieve the set of platform breakpoints for this context + final Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); + rm.done(); + return; + } + + // Read current breakpoints from platform and copy their augmented + // attributes into the local reference map + try { + IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(fDebugModelId); + for (IBreakpoint breakpoint : breakpoints) { + if (supportsBreakpoint(breakpoint)) { + @SuppressWarnings("unchecked") + Map attributes = breakpoint.getMarker().getAttributes(); + attributes.put(ATTR_DEBUGGER_PATH, NULL_STRING); + attributes.put(ATTR_THREAD_FILTER, extractThreads(dmc, (ICBreakpoint) breakpoint)); + attributes.put(ATTR_THREAD_ID, NULL_STRING); + platformBPs.put((ICBreakpoint) breakpoint, attributes); + } + } + } catch (CoreException e) { + IStatus status = new Status( + IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, UNABLE_TO_READ_BREAKPOINT, e); + rm.setStatus(status); + rm.done(); + } + + // Install the individual breakpoints on the dispatcher thread + // Requires a counting monitor to know when we are done + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm); + countingRm.setDoneCount(platformBPs.size()); + + for (final ICBreakpoint breakpoint : platformBPs.keySet()) { + final Map attributes = platformBPs.get(breakpoint); + // Upon determining the debuggerPath, the breakpoint is installed + determineDebuggerPath(dmc, attributes, new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + installBreakpoint(dmc, breakpoint, attributes, new RequestMonitor(getExecutor(), countingRm)); + } + }); + } + } + + //------------------------------------------------------------------------- + // stopTrackingBreakpoints + //------------------------------------------------------------------------- + // - Remove the target breakpoints for the given execution context + // - Update the maps + //------------------------------------------------------------------------- + + /** + * Uninstall and stop tracking breakpoints for the given context. + * @param dmc Context to start tracking breakpoints for. + * @param rm Completion callback. + */ + public void stopTrackingBreakpoints(final IBreakpointsTargetDMContext dmc, final RequestMonitor rm) { + + // Validate the context + if (dmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); + rm.done(); + return; + } + + // Retrieve the set of platform breakpoints for this context + final Map> platformBPs = fPlatformBPs.get(dmc); + if (platformBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, INVALID_CONTEXT, null)); + rm.done(); + return; + } + + // Un-install the individual breakpoints on the dispatcher thread + // (requires a counting monitor to know when we are done). + // On completion (success or failure), update the maps. + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + fPlatformBPs.remove(dmc); + fBreakpointIDs.remove(dmc); + fTargetBPs.remove(dmc); + fBreakpointThreads.remove(dmc); + rm.done(); + } + }; + countingRm.setDoneCount(platformBPs.size()); + + for (final ICBreakpoint breakpoint : platformBPs.keySet()) { + uninstallBreakpoint(dmc, breakpoint, + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleCompleted() { + countingRm.done(); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // Back-end interface functions + /////////////////////////////////////////////////////////////////////////// + + //------------------------------------------------------------------------- + // installBreakpoint + //------------------------------------------------------------------------- + + /** + * Install a platform breakpoint on the back-end. For a given context, a + * platform breakpoint can resolve into multiple back-end breakpoints when + * threads are taken into account. + * + * @param dmc + * @param breakpoint + * @param attributes + * @param rm + */ + private void installBreakpoint(IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, + final Map attributes, final RequestMonitor rm) + { + // Retrieve the breakpoint maps + final Map> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + final Map> breakpointIDs = fBreakpointIDs.get(dmc); + assert breakpointIDs != null; + + final Map targetBPs = fTargetBPs.get(dmc); + assert targetBPs != null; + + final Map> threadsIDs = fBreakpointThreads.get(dmc); + assert threadsIDs != null; + + // Minimal validation + if (breakpointIDs.containsKey(breakpoint) || targetBPs.containsValue(breakpoint)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, BREAKPOINT_ALREADY_INSTALLED, null)); + rm.done(); + return; + } + + // Ensure the breakpoint has a valid debugger source path + if (breakpoint instanceof ICLineBreakpoint && !(breakpoint instanceof ICAddressBreakpoint)) { + String debuggerPath = (String) attributes.get(ATTR_DEBUGGER_PATH); + if (debuggerPath == null || debuggerPath == NULL_STRING) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, NO_DEBUGGER_PATH, null)); + rm.done(); + return; + } + } + + // A back-end breakpoint needs to be installed for each specified thread + // Note: This is a bit academic since [1] thread info is not kept by the + // BreakpointManager (so it can not possibly be restored when a target is + // started), and [2] the standard GUI doesn't allow to specify thread at + // breakpoint creation. However, it is conceivable that an enhanced Editor + // would permit it. + final Set threads = getThreads(attributes); + + // Update the breakpoint state when all back-end breakpoints have been installed + final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleCompleted() { + // Store the platform breakpoint + platformBPs.put(breakpoint, attributes); + rm.done(); + } + }; + installRM.setDoneCount(threads.size()); + + // Install the back-end breakpoint(s) + for (final String thread : threads) { + DataRequestMonitor drm = + new DataRequestMonitor(getExecutor(), installRM) { + @Override + protected void handleSuccess() { + // Add the new back-end breakpoint to the map + Vector list = breakpointIDs.get(breakpoint); + if (list == null) + list = new Vector(); + IBreakpointDMContext targetBP = getData(); + list.add(targetBP); + breakpointIDs.put(breakpoint, list); + + // Add the reverse mapping + targetBPs.put(targetBP, breakpoint); + + // And update the corresponding thread list + Set thrds = threadsIDs.get(breakpoint); + if (thrds == null) + thrds = new HashSet(); + thrds.add(thread); + threadsIDs.put(breakpoint, thrds); + + // Finally, update the platform breakpoint + attributes.remove(ATTR_THREAD_ID); + try { + breakpoint.incrementInstallCount(); + } catch (CoreException e) { + } + installRM.done(); + } + + @Override + protected void handleError() { + addBreakpointProblemMarker(breakpoint, "Breakpoint attribute problem: installation failed", IMarker.SEVERITY_WARNING); //$NON-NLS-1$ + installRM.done(); + } + }; + + // Convert the breakpoint attributes for the back-end + attributes.put(ATTR_THREAD_ID, thread); + Map targetAttributes = convertToTargetBreakpoint(breakpoint, attributes); + fBreakpoints.insertBreakpoint(dmc, targetAttributes, drm); + } + } + + private void addBreakpointProblemMarker(final ICBreakpoint breakpoint, final String description, final int severity) { + + new Job("Add Breakpoint Problem Marker") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + + if (breakpoint instanceof ICLineBreakpoint) { + ICLineBreakpoint lineBreakpoint = (ICLineBreakpoint) breakpoint; + try { + // Locate the workspace resource via the breakpoint marker + IMarker breakpoint_marker = lineBreakpoint.getMarker(); + IResource resource = breakpoint_marker.getResource(); + + // Add a problem marker to the resource + IMarker problem_marker = resource.createMarker(BreakpointProblems.BREAKPOINT_PROBLEM_MARKER_ID); + int line_number = lineBreakpoint.getLineNumber(); + problem_marker.setAttribute(IMarker.LOCATION, String.valueOf(line_number)); + problem_marker.setAttribute(IMarker.MESSAGE, description); + problem_marker.setAttribute(IMarker.SEVERITY, severity); + problem_marker.setAttribute(IMarker.LINE_NUMBER, line_number); + + // And save the baby + fBreakpointMarkerProblems.put(breakpoint, problem_marker); + } catch (CoreException e) { + } + } + return Status.OK_STATUS; + } + }.schedule(); + } + + private void removeBreakpointProblemMarker(final ICBreakpoint breakpoint) { + + new Job("Remove Breakpoint Problem Marker") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + + IMarker marker = fBreakpointMarkerProblems.remove(breakpoint); + if (marker != null) { + try { + marker.delete(); + } catch (CoreException e) { + } + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + //------------------------------------------------------------------------- + // uninstallBreakpoint + //------------------------------------------------------------------------- + + /** + * Un-install an individual breakpoint on the back-end. For one platform + * breakpoint in a given execution context, there could be multiple + * corresponding back-end breakpoints (one per thread). + * + * @param dmc + * @param breakpoint + * @param rm + */ + private void uninstallBreakpoint(final IBreakpointsTargetDMContext dmc, + final ICBreakpoint breakpoint, final RequestMonitor rm) + { + // Retrieve the breakpoint maps + final Map> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + final Map> breakpointIDs = fBreakpointIDs.get(dmc); + assert breakpointIDs != null; + + final Map targetBPs = fTargetBPs.get(dmc); + assert targetBPs != null; + + final Map> threadsIDs = fBreakpointThreads.get(dmc); + assert threadsIDs != null; + + // Remove breakpoint problem marker (if any) + removeBreakpointProblemMarker(breakpoint); + + // Minimal validation + if (!platformBPs.containsKey(breakpoint) || !breakpointIDs.containsKey(breakpoint) || !targetBPs.containsValue(breakpoint)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, BREAKPOINT_ALREADY_REMOVED, null)); + rm.done(); + return; + } + + // Remove completion monitor + // Upon completion, update the mappings + CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Update the mappings + platformBPs.remove(breakpoint); + threadsIDs.remove(breakpoint); + + Vector contexts = breakpointIDs.get(breakpoint); + if (contexts != null) { + for (IBreakpointDMContext context : contexts) + targetBPs.remove(context); + } + + breakpointIDs.get(breakpoint).clear(); + breakpointIDs.remove(breakpoint); + + fPendingRequests.remove(breakpoint); + + rm.done(); + } + }; + + // Remove the back-end breakpoints + Vector list = breakpointIDs.get(breakpoint); + int count = 0; + if (list != null) { + for (IBreakpointDMContext bp : list) { + fBreakpoints.removeBreakpoint(bp, removeRM); + try { + breakpoint.decrementInstallCount(); + } catch (CoreException e) { + } + } + count = list.size(); + } + removeRM.setDoneCount(count); + } + + //------------------------------------------------------------------------- + // modifyBreakpoint + //------------------------------------------------------------------------- + + /** + * Modify a platform breakpoint which can translate to quite a few updates + * on the target... + * + * @param dmc + * @param breakpoint + * @param attributes + * @param oldValues + * @param rm + */ + private void modifyBreakpoint(final IBreakpointsTargetDMContext dmc, final ICBreakpoint breakpoint, + final Map attributes, final IMarkerDelta oldValues, final RequestMonitor rm) + { + // Retrieve the breakpoint maps + final Map> platformBPs = fPlatformBPs.get(dmc); + assert platformBPs != null; + + final Map> breakpointIDs = fBreakpointIDs.get(dmc); + assert breakpointIDs != null; + + final Map targetBPs = fTargetBPs.get(dmc); + assert targetBPs != null; + + final Map> threadsIDs = fBreakpointThreads.get(dmc); + assert threadsIDs != null; + + // Minimal validation + if (!platformBPs.containsKey(breakpoint) || !breakpointIDs.containsKey(breakpoint) || !targetBPs.containsValue(breakpoint)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, BREAKPOINT_NOT_INSTALLED, null)); + rm.done(); + return; + } + + // Get the original breakpoint attributes + final Map original_attributes = platformBPs.get(breakpoint); + if (original_attributes == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, INVALID_BREAKPOINT, null)); + rm.done(); + return; + } + + // Determine the attributes delta + final Map oldAttributes = new HashMap(original_attributes); + oldAttributes.put(ATTR_THREAD_FILTER, threadsIDs.get(breakpoint)); + + final Set newThreads = extractThreads(dmc, breakpoint); + Map newAttributes = new HashMap(attributes); + newAttributes.put(ATTR_THREAD_FILTER, newThreads); + + final Map attributesDelta = determineAttributesDelta(oldAttributes, newAttributes); + + // Get the list of back-end breakpoints + final Vector oldTargetBPs = new Vector(breakpointIDs.get(breakpoint)); + if (oldTargetBPs == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, INVALID_BREAKPOINT, null)); + rm.done(); + return; + } + + // We're all set for the breakpoint update. + // + // The path for a simple update is straightforward: + // - For each back-end BP corresponding to a platform BP + // - Send an update command to the back-end + // - If the operation succeeded, update the data structures + // - If the operation failed, try to roll-back + // + // In cases where the the back-end breakpoint cannot be + // simply updated (e.g. thread filter modification), the old + // breakpoint has to be removed and new one(s) inserted. + // + // The path for such an update is: + // - Install the updated breakpoint + // - In the operation succeeded + // - Remove the old breakpoint(s) + // - perform any pending update + + // Update completion monitor + final CountingRequestMonitor updateRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Success: simply store the new attributes + platformBPs.put(breakpoint, attributes); + rm.done(); + } + + @Override + protected void handleError() { + // Reset the breakpoint attributes. This will trigger a + // breakpoint change event and the correct delta will be + // computed, resulting in a correctly restored breakpoint + // at the back-end. + rollbackAttributes(breakpoint, oldValues); + platformBPs.put(breakpoint, attributes); + + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_PARAMETER, null)); + rm.done(); + } + }; + + // Everything OK: remove the old back-end breakpoints + final Vector newTargetBPs = new Vector(); + final CountingRequestMonitor removeRM = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // All right! Save the new list and perform the final update + Map> breakpointIDs = fBreakpointIDs.get(dmc); + if (breakpointIDs == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_BREAKPOINT, null)); + rm.done(); + return; + } + breakpointIDs.put(breakpoint, newTargetBPs); + for (IBreakpointDMContext ref : newTargetBPs) { + fBreakpoints.updateBreakpoint(ref, attributesDelta, updateRM); + } + updateRM.setDoneCount(newTargetBPs.size()); + }}; + + // New back-end breakpoints insertion monitor + // Holds the list of new back-end breakpoint contexts of the platform breakpoint + final DataRequestMonitor> insertRM = + new DataRequestMonitor>(getExecutor(), null) { + + @Override + // In theory, we could have had a partial success and the original threads + // list would be invalid. We think it is highly unlikely so we assume that + // either everything went fine or else everything failed. + protected void handleSuccess() { + // Get the list of new back-end breakpoints contexts + newTargetBPs.addAll(getData()); + threadsIDs.put(breakpoint, newThreads); + for (IBreakpointDMContext ref : oldTargetBPs) { + fBreakpoints.removeBreakpoint(ref, removeRM); + try { + breakpoint.decrementInstallCount(); // A tad early but it should work... + } catch (CoreException e) { + } + } + removeRM.setDoneCount(oldTargetBPs.size()); + } + + @Override + protected void handleError() { + // Keep the old threads list and reset the attributes + // (bad attributes are the likely cause of failure) + updateRM.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_PARAMETER, null)); + updateRM.setDoneCount(0); + } + }; + + // If the changes in the breakpoint attributes justify it, install a + // new set of back-end breakpoint(s) and then update them + if (needsResinstallation(attributesDelta)) { + reinstallBreakpoint(dmc, breakpoint, attributes, newThreads, insertRM); + } + else { + // Update the back-end breakpoint(s) state + for (IBreakpointDMContext ref : oldTargetBPs) { + fBreakpoints.updateBreakpoint(ref, attributesDelta, updateRM); + } + updateRM.setDoneCount(oldTargetBPs.size()); + } + } + + /** + * Re-install the back-end breakpoints + * + * @param context the target context + * @param breakpoint the platform breakpoint + * @param attributes breakpoint augmented attributes + * @param threads list of threads where breakpoint is to be installed + * @param drm will contain the list of successfully installed back-end breakpoints + */ + private void reinstallBreakpoint(final IBreakpointsTargetDMContext context, final ICBreakpoint breakpoint, + final Map attributes, Set threads, final DataRequestMonitor> drm) + { + // Our new list of back-end breakpoints. Built as we go. + final Vector breakpointList = new Vector(); + + // Counting monitor for the new back-end breakpoints to install + // Once we're done, return the new list of back-end breakpoints contexts + final CountingRequestMonitor installRM = new CountingRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // Report whatever we have managed to install + // It is very likely installation either succeeded or failed for all + drm.setData(breakpointList); + drm.done(); + } + }; + installRM.setDoneCount(threads.size()); + + // And install the new back-end breakpoints + for (String thread : threads) { + // Convert the breakpoint attributes for the back-end + // Refresh the set of attributes at each iteration just in case... + Map attrs = convertToTargetBreakpoint(breakpoint, attributes); + if (!fBreakpointManager.isEnabled()) { + attrs.put(MIBreakpoints.IS_ENABLED, false); + } + // Add the secret ingredient.. + attrs.put(MIBreakpointDMData.THREAD_ID, thread); + + // Then install the spiked breakpoint + fBreakpoints.insertBreakpoint(context, attrs, + new DataRequestMonitor(getExecutor(), installRM) { + @Override + protected void handleSuccess() { + // Add the new back-end breakpoint context to the list + breakpointList.add(getData()); + try { + breakpoint.incrementInstallCount(); + } catch (CoreException e) { + } + installRM.done(); + } + + @Override + protected void handleError() { + // Add the new back-end breakpoint context to the list + installRM.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, INVALID_PARAMETER, null)); + installRM.done(); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointManagerListener implementation + /////////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IBreakpointManagerListener#breakpointManagerEnablementChanged(boolean) + */ + public void breakpointManagerEnablementChanged(boolean enabled) { + + // Only modify enabled breakpoints + for (IBreakpointsTargetDMContext context : fBreakpointIDs.keySet()) { + for (ICBreakpoint breakpoint : fBreakpointIDs.get(context).keySet()) { + try { + if (breakpoint.isEnabled()) { + for (IBreakpointDMContext ref : fBreakpointIDs.get(context).get(breakpoint)) { + Map delta = new HashMap(); + delta.put(MIBreakpoints.IS_ENABLED, enabled); + fBreakpoints.updateBreakpoint(ref, delta, new RequestMonitor(getExecutor(), null)); + } + } + } catch (CoreException e) { + } + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // IBreakpointListener implementation + /////////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) + */ + @ThreadSafe + public void breakpointAdded(final IBreakpoint breakpoint) { + + if (supportsBreakpoint(breakpoint)) { + try { + // Retrieve the breakpoint attributes + @SuppressWarnings("unchecked") + final Map attrs = breakpoint.getMarker().getAttributes(); + + getExecutor().execute(new DsfRunnable() { + public void run() { + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Install the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + determineDebuggerPath(dmc, attrs, + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + installBreakpoint(dmc, (ICBreakpoint) breakpoint, + attrs, new RequestMonitor(getExecutor(), countingRm)); + } + }); + } + } + }); + } catch (CoreException e) { + } catch (RejectedExecutionException e) { + } + } + } + + /** + * @param bp + * @return + * @throws CoreException + */ + private IDsfBreakpointExtension getFilterExtension(ICBreakpoint bp) throws CoreException { + return (IDsfBreakpointExtension) bp.getExtension(GDB_DEBUG_MODEL_ID, ICBreakpointExtension.class); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointChanged(final IBreakpoint breakpoint, final IMarkerDelta delta) { + + if (supportsBreakpoint(breakpoint)) { + + try { + // Retrieve the breakpoint attributes + @SuppressWarnings("unchecked") + final Map attrs = breakpoint.getMarker().getAttributes(); + if (!fBreakpointManager.isEnabled()) { + attrs.put(ICBreakpoint.ENABLED, false); + } + + // Modify the breakpoint in all the target contexts + getExecutor().execute( new DsfRunnable() { + public void run() { + + // If the breakpoint is currently being updated, queue the request and exit + if (fPendingRequests.contains(breakpoint)) { + fPendingBreakpoints.add(breakpoint); + return; + } + + // Keep track of the updates + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleCompleted() { + + if (!isSuccess()) { + if (getStatus().getSeverity() == IStatus.ERROR) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + } + + // Indicate that the pending request has completed + fPendingRequests.remove(breakpoint); + + // Process the next pending update for this breakpoint + if (fPendingBreakpoints.contains(breakpoint)) { + fPendingBreakpoints.remove(breakpoint); + breakpointChanged(breakpoint, delta); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Mark the breakpoint as being updated and go + fPendingRequests.add(breakpoint); + + // Modify the breakpoint in all the execution contexts + for (final IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + determineDebuggerPath(dmc, attrs, + new RequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + modifyBreakpoint(dmc, (ICBreakpoint) breakpoint, attrs, delta, new RequestMonitor(getExecutor(), countingRm)); + } + }); + } + } + }); + } catch (CoreException e) { + } catch (RejectedExecutionException e) { + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointRemoved(final IBreakpoint breakpoint, IMarkerDelta delta) { + + if (supportsBreakpoint(breakpoint)) { + try { + getExecutor().execute(new DsfRunnable() { + public void run() { + CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleError() { + if (getStatus().getSeverity() == IStatus.ERROR) { + GdbPlugin.getDefault().getLog().log(getStatus()); + } + } + }; + countingRm.setDoneCount(fPlatformBPs.size()); + + // Remove the breakpoint in all the execution contexts + for (IBreakpointsTargetDMContext dmc : fPlatformBPs.keySet()) { + if (fPlatformBPs.get(dmc).containsKey(breakpoint)) { + uninstallBreakpoint(dmc, (ICBreakpoint) breakpoint, countingRm); + } + } + } + }); + } catch (RejectedExecutionException e) { + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // IServiceEventListener + /////////////////////////////////////////////////////////////////////////// + + //------------------------------------------------------------------------- + // Breakpoints + //------------------------------------------------------------------------- + + @DsfServiceEventHandler + public void eventDispatched(BreakpointAddedEvent e) { + // Nothing to do - already handled by breakpointAdded() + } + + @DsfServiceEventHandler + public void eventDispatched(BreakpointUpdatedEvent e) { + // Nothing to do - already handled by breakpointChanged() + } + + @DsfServiceEventHandler + public void eventDispatched(BreakpointRemovedEvent e) { + // Nothing to do - already handled by breakpointRemoved() + } + + /* + * When a watchpoint goes out of scope, it is automatically removed from + * the back-end. To keep our internal state synchronized, we have to + * remove it from our breakpoints maps. + * Unfortunately, GDB doesn't generate the correct event... + */ + @DsfServiceEventHandler + public void eventDispatched(MIWatchpointScopeEvent e) { + } + + //------------------------------------------------------------------------- + // Breakpoint actions + //------------------------------------------------------------------------- + + @DsfServiceEventHandler + public void eventDispatched(MIBreakpointHitEvent e) { + performBreakpointAction(e.getNumber()); + } + + @DsfServiceEventHandler + public void eventDispatched(MIWatchpointTriggerEvent e) { + performBreakpointAction(e.getNumber()); + } + + private void performBreakpointAction(int number) { + // Identify the platform breakpoint + final ICBreakpoint breakpoint = findPlatformBreakpoint(number); + + // FIXME: (Bug228703) Temporary hack to have a context + Object[] contexts = fTargetBPs.keySet().toArray(); + final IBreakpointsTargetDMContext context = (IBreakpointsTargetDMContext) contexts[0]; + + // Perform the actions asynchronously (otherwise we can have a deadlock...) + new Job("Breakpoint action") { //$NON-NLS-1$ + { setSystem(true); } + @Override + protected IStatus run(IProgressMonitor monitor) { + fBreakpointActionManager.executeActions(breakpoint, new BreakpointActionAdapter(getExecutor(), getServicesTracker(), context)); + return Status.OK_STATUS; + }; + }.schedule(); + } + + // Helper function to locate the platform breakpoint corresponding + // to the target breakpoint/watchpoint that was just hit + + // FIXME: (Bug228703) Need a way to identify the correct context where the BP was hit + private ICBreakpoint findPlatformBreakpoint(int targetBreakpointID) { + Set targets = fTargetBPs.keySet(); + for (IBreakpointsTargetDMContext target : targets) { + Map bps = fTargetBPs.get(target); + Set contexts = bps.keySet(); + for (IBreakpointDMContext context : contexts) { + if (context instanceof MIBreakpointDMContext) { + MIBreakpointDMContext ctx = (MIBreakpointDMContext) context; + if (ctx.getReference() == targetBreakpointID) { + return bps.get(context); + } + } + } + } + return null; + } + + //------------------------------------------------------------------------- + // Session exit + //------------------------------------------------------------------------- + + // Not used, kept for API compatibility. + /** + * This method is left for API compatibility only. + * ICommandControlShutdownDMEvent is used instead. + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(MIGDBExitEvent e) { + terminated(); + } + + /** + * @since 1.1 + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + // bug 243899: The call to terminate results in an exception, + // need to investigate. + //terminated(); + } + + private void terminated() { + clearBreakpointStatus(fPlatformBPs.keySet().toArray(new ICBreakpoint[fPlatformBPs.size()])); + fPlatformBPs.clear(); + } + + /////////////////////////////////////////////////////////////////////////// + // Breakpoint status handling functions + /////////////////////////////////////////////////////////////////////////// + + /** + * @param bps + */ + private void clearBreakpointStatus(final ICBreakpoint[] bps) + { + new Job("Clear Breakpoints Status") { //$NON-NLS-1$ + @Override + protected IStatus run(IProgressMonitor monitor) { + IWorkspaceRunnable wr = new IWorkspaceRunnable() { + public void run(IProgressMonitor monitor) throws CoreException { + for (ICBreakpoint breakpoint : bps) { + breakpoint.resetInstallCount(); + } + } + }; + + // Create the scheduling rule to clear all bp planted. + ISchedulingRule rule = null; + List markerRules = new ArrayList(); + for (ICBreakpoint bp : bps) { + IMarker marker = bp.getMarker(); + if (marker != null) { + ISchedulingRule markerRule = + ResourcesPlugin.getWorkspace().getRuleFactory().markerRule( + marker.getResource()); + if (markerRule == null) { + markerRules = null; + break; + } else { + markerRules.add(markerRule); + } + } + } + if (markerRules != null) { + rule = MultiRule.combine(markerRules.toArray(new ISchedulingRule[markerRules.size()])); + } + + try { + ResourcesPlugin.getWorkspace().run(wr, rule, 0, null); + } catch (CoreException e) { + return e.getStatus(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + /////////////////////////////////////////////////////////////////////////// + // Support functions + /////////////////////////////////////////////////////////////////////////// + + /** + * supportsBreakpoint + * + * Indicates if it is breakpoint we can deal with. For now, it boils down + * to a CDI breakpoint... + * + * @param bp + * @return + */ + private boolean supportsBreakpoint(IBreakpoint bp) { + if (bp instanceof ICBreakpoint && bp.getModelIdentifier().equals(fDebugModelId)) { + IMarker marker = bp.getMarker(); + if (marker != null) { + return true; + } + } + return false; + } + + /** + * determineDebuggerPath + * + * Adds the path to the source file to the set of attributes + * (for the debugger). + * + * @param dmc + * @param attributes + * @param rm + */ + private void determineDebuggerPath(IBreakpointsTargetDMContext dmc, + final Map attributes, final RequestMonitor rm) + { + String hostPath = (String) attributes.get(ICBreakpoint.SOURCE_HANDLE); + + if (hostPath != null) { + + ISourceLookupDMContext srcDmc = DMContexts.getAncestorOfType(dmc, ISourceLookupDMContext.class); + if (srcDmc != null) { + fSourceLookup.getDebuggerPath(srcDmc, hostPath, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + attributes.put(ATTR_DEBUGGER_PATH, adjustDebuggerPath(getData())); + rm.done(); + } + }); + } else { + // Source lookup not available for given context, use the host + // path for the debugger path. + attributes.put(ATTR_DEBUGGER_PATH, adjustDebuggerPath(hostPath)); + rm.done(); + } + } else { + // Some types of breakpoints do not require a path + // (e.g. watchpoints) + rm.done(); + } + } + + /** + * See bug232415 + * + * @param path the absolute path to the source file + * @return + */ + private String adjustDebuggerPath(String path) { + String result = path; + // Make it MinGW-specific + if (Platform.getOS().startsWith("win")) { //$NON-NLS-1$ + if (!path.startsWith("/")) { //$NON-NLS-1$ + result = path.substring(path.lastIndexOf('\\') + 1); + } + } + return result; + } + + /** + * Determine the set of modified attributes. + * Elementary set operations in full action :-) + * + * @param oldAttributes + * @param newAttributes + * @return + */ + private Map determineAttributesDelta(Map oldAttributes, Map newAttributes) { + + Map delta = new HashMap(); + + Set oldKeySet = oldAttributes.keySet(); + Set newKeySet = newAttributes.keySet(); + + Set commonKeys = new HashSet(newKeySet); commonKeys.retainAll(oldKeySet); + Set addedKeys = new HashSet(newKeySet); addedKeys.removeAll(oldKeySet); + Set removedKeys = new HashSet(oldKeySet); removedKeys.removeAll(newKeySet); + + // Add the modified attributes + for (String key : commonKeys) { + if (!(oldAttributes.get(key).equals(newAttributes.get(key)))) + delta.put(key, newAttributes.get(key)); + } + + // Add the new attributes + for (String key : addedKeys) { + delta.put(key, newAttributes.get(key)); + } + + // Remove the deleted attributes + for (String key : removedKeys) { + delta.put(key, null); + } + + return convertedAttributes(delta); + } + + /** + * Converts ICBreakpoint attributes to IBreakpoints attributes. + * + * @param cdt_attributes + * @return + */ + private Map convertedAttributes(Map cdt_attributes) { + + Map result = new HashMap(); + + // IBreakpoint attributes + if (cdt_attributes.containsKey(ATTR_DEBUGGER_PATH)) + result.put(MIBreakpoints.FILE_NAME, cdt_attributes.get(ATTR_DEBUGGER_PATH)); + + if (cdt_attributes.containsKey(IMarker.LINE_NUMBER)) + result.put(MIBreakpoints.LINE_NUMBER, cdt_attributes.get(IMarker.LINE_NUMBER)); + + // ICLineBreakpoint attributes + if (cdt_attributes.containsKey(ICLineBreakpoint.FUNCTION)) + result.put(MIBreakpoints.FUNCTION, cdt_attributes.get(ICLineBreakpoint.FUNCTION)); + + if (cdt_attributes.containsKey(ICLineBreakpoint.ADDRESS)) + result.put(MIBreakpoints.ADDRESS, cdt_attributes.get(ICLineBreakpoint.ADDRESS)); + + // ICBreakpoint attributes + if (cdt_attributes.containsKey(ICBreakpoint.CONDITION)) + result.put(MIBreakpoints.CONDITION, cdt_attributes.get(ICBreakpoint.CONDITION)); + + if (cdt_attributes.containsKey(ICBreakpoint.IGNORE_COUNT)) + result.put(MIBreakpoints.IGNORE_COUNT, cdt_attributes.get(ICBreakpoint.IGNORE_COUNT)); + + if (cdt_attributes.containsKey(ICBreakpoint.ENABLED)) + result.put(MIBreakpoints.IS_ENABLED, cdt_attributes.get(ICBreakpoint.ENABLED)); + + // ICWatchpoint attributes + if (cdt_attributes.containsKey(ICWatchpoint.EXPRESSION)) + result.put(MIBreakpoints.EXPRESSION, cdt_attributes.get(ICWatchpoint.EXPRESSION)); + + if (cdt_attributes.containsKey(ICWatchpoint.READ)) + result.put(MIBreakpoints.READ, cdt_attributes.get(ICWatchpoint.READ)); + + if (cdt_attributes.containsKey(ICWatchpoint.WRITE)) + result.put(MIBreakpoints.WRITE, cdt_attributes.get(ICWatchpoint.WRITE)); + + // Threads + if (cdt_attributes.containsKey(ATTR_THREAD_FILTER)) + result.put(ATTR_THREAD_FILTER, cdt_attributes.get(ATTR_THREAD_FILTER)); + + return result; + } + + /** + * Figure out the corresponding number of back-end breakpoints + * Even though the thread IDs are usually integers, they are + * stored as strings in CBreakpoints. + * + * @param attributes + * @return + */ + @SuppressWarnings("unchecked") + private Set getThreads(Map attributes) { + Set threads = (Set) attributes.get(ATTR_THREAD_FILTER); + if (threads == null) { + threads = new HashSet(); + threads.add("0"); // Thread 0 means all threads //$NON-NLS-1$ + } + return threads; + } + + /** + * Get the list of threads from the platform breakpoint attributes + * + * @param breakpoint + * @return + */ + private Set extractThreads(IBreakpointsTargetDMContext context, ICBreakpoint breakpoint) { + Set results = new HashSet(); + + // Find the ancestor + List threads = new ArrayList(1); + + try { + // Retrieve the targets + IDsfBreakpointExtension filterExtension = getFilterExtension(breakpoint); + IContainerDMContext[] targets = filterExtension.getTargetFilters(); + + // If no target is present, breakpoint applies to all. + if (targets.length == 0) { + results.add("0"); //$NON-NLS-1$ + return results; + } + + // Extract the thread IDs (if there is none, we are covered) + for (IContainerDMContext ctxt : targets) { + if (DMContexts.isAncestorOf(ctxt, context)) { + threads.add(filterExtension.getThreadFilters(ctxt)); + } + } + } catch (CoreException e1) { + } + + if (supportsThreads(breakpoint)) { + for (IExecutionDMContext[] targetThreads : threads) { + if (targetThreads != null) { + for (IExecutionDMContext thread : targetThreads) { + if (thread instanceof IMIExecutionDMContext) { + IMIExecutionDMContext dmc = (IMIExecutionDMContext) thread; + results.add(((Integer) dmc.getThreadId()).toString()); + } + } + } else { + results.add("0"); //$NON-NLS-1$ + break; + } + } + } else { + results.add("0"); //$NON-NLS-1$ + } + + return results; + } + + /////////////////////////////////////////////////////////////////////////// + // Non-generic (MI-specific) functions + /////////////////////////////////////////////////////////////////////////// + + /** + * Create a target breakpoint from an ICBreakpoint + * + * @param breakpoint + * @param attributes + * @return + */ + protected Map convertToTargetBreakpoint(ICBreakpoint breakpoint, Map attributes) { + + Map properties = new HashMap(); + + if (breakpoint instanceof ICWatchpoint) { + // Convert the CDI watchpoint to an IBreakpoint + properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.WATCHPOINT); + properties.put(MIBreakpoints.EXPRESSION, attributes.get(ICWatchpoint.EXPRESSION)); + properties.put(MIBreakpoints.READ, attributes.get(ICWatchpoint.READ)); + properties.put(MIBreakpoints.WRITE, attributes.get(ICWatchpoint.WRITE)); + } + else if (breakpoint instanceof ICLineBreakpoint) { + // Convert the CDI breakpoint to an IBreakpoint + properties.put(MIBreakpoints.BREAKPOINT_TYPE, MIBreakpoints.BREAKPOINT); + properties.put(MIBreakpoints.FILE_NAME, attributes.get(ATTR_DEBUGGER_PATH)); + properties.put(MIBreakpoints.LINE_NUMBER, attributes.get(IMarker.LINE_NUMBER)); + properties.put(MIBreakpoints.FUNCTION, attributes.get(ICLineBreakpoint.FUNCTION)); + properties.put(MIBreakpoints.ADDRESS, attributes.get(ICLineBreakpoint.ADDRESS)); + } else { + // catchpoint? + } + + // Common fields + properties.put(MIBreakpoints.CONDITION, attributes.get(ICBreakpoint.CONDITION)); + properties.put(MIBreakpoints.IGNORE_COUNT, attributes.get(ICBreakpoint.IGNORE_COUNT)); + properties.put(MIBreakpoints.IS_ENABLED, attributes.get(ICBreakpoint.ENABLED)); + properties.put(MIBreakpointDMData.THREAD_ID, attributes.get(ATTR_THREAD_ID)); + + // Adjust for "skip-all" + if (!fBreakpointManager.isEnabled()) { + properties.put(MIBreakpoints.IS_ENABLED, false); + } + + return properties; + } + + /** + * Determine if the modified attributes necessitate + * a breakpoint removal/re-installation + * + * @param delta + * @return + */ + protected boolean needsResinstallation(Map delta) { + + // Check if there is any modified attribute + if (delta == null) + return false; + + // Check the "critical" attributes + if (delta.containsKey(ATTR_DEBUGGER_PATH) // File name + || delta.containsKey(IMarker.LINE_NUMBER) // Line number + || delta.containsKey(ICLineBreakpoint.FUNCTION) // Function name + || delta.containsKey(ICLineBreakpoint.ADDRESS) // Absolute address + || delta.containsKey(ATTR_THREAD_FILTER) // Thread ID + || delta.containsKey(ICWatchpoint.EXPRESSION) // Watchpoint expression + || delta.containsKey(ICWatchpoint.READ) // Watchpoint type + || delta.containsKey(ICWatchpoint.WRITE)) { // Watchpoint type + return true; + } + + return false; + } + + /** + * @param breakpoint + * @param oldValues + */ + protected void rollbackAttributes(ICBreakpoint breakpoint, IMarkerDelta oldValues) { + + try { + String new_condition = breakpoint.getCondition(); + if (new_condition == null) + new_condition = NULL_STRING; + String old_condition = (oldValues != null) ? oldValues.getAttribute(ICBreakpoint.CONDITION, NULL_STRING) : NULL_STRING; + if (!old_condition.equals(new_condition)) { + breakpoint.setCondition(old_condition); + } + else { + breakpoint.setCondition(NULL_STRING); + } + } catch (CoreException e) { + } + } + + /** + * Indicates if the back-end supports multiple threads for + * this type of breakpoint + * + * @param breakpoint + */ + protected boolean supportsThreads(ICBreakpoint breakpoint) { + + return !(breakpoint instanceof ICWatchpoint); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIDisassembly.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIDisassembly.java new file mode 100644 index 00000000000..ca31a085f8c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIDisassembly.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service; + +import java.math.BigInteger; +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IInstruction; +import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataDisassemble; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataDisassembleInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +public class MIDisassembly extends AbstractDsfService implements IDisassembly { + + // Services + ICommandControl fConnection; + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService + /////////////////////////////////////////////////////////////////////////// + + /** + * The service constructor + * + * @param session The debugging session + */ + public MIDisassembly(DsfSession session) { + super(session); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void initialize(final RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(final RequestMonitor rm) { + fConnection = getServicesTracker().getService(ICommandControl.class); +// getSession().addServiceEventListener(this, null); + register(new String[] { IDisassembly.class.getName(), MIDisassembly.class.getName() }, + new Hashtable()); + rm.done(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void shutdown(RequestMonitor rm) { + unregister(); +// getSession().removeServiceEventListener(this); + rm.done(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IDisassembly + /////////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getInstructions(IDisassemblyDMContext context, + BigInteger startAddress, BigInteger endAddress, + final DataRequestMonitor drm) + { + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + drm.done(); + return; + } + + // Go for it + String start = (startAddress != null) ? startAddress.toString() : "$pc"; //$NON-NLS-1$ + String end = (endAddress != null) ? endAddress.toString() : "$pc + 100"; //$NON-NLS-1$ + fConnection.queueCommand(new MIDataDisassemble(context, start, end, false), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + IInstruction[] result = getData().getMIAssemblyCode(); + drm.setData(result); + drm.done(); + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getInstructions(IDisassemblyDMContext context, String filename, + int linenum, int lines, final DataRequestMonitor drm) + { + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + drm.done(); + return; + } + + // Go for it + fConnection.queueCommand(new MIDataDisassemble(context, filename, linenum, lines, false), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + IInstruction[] result = getData().getMIAssemblyCode(); + drm.setData(result); + drm.done(); + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getMixedInstructions(IDisassemblyDMContext context, + BigInteger startAddress, BigInteger endAddress, + final DataRequestMonitor drm) + { + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + drm.done(); + return; + } + + // Go for it + String start = (startAddress != null) ? startAddress.toString() : "$pc"; //$NON-NLS-1$ + String end = (endAddress != null) ? endAddress.toString() : "$pc + 100"; //$NON-NLS-1$ + fConnection.queueCommand(new MIDataDisassemble(context, start, end, true), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + IMixedInstruction[] result = getData().getMIMixedCode(); + drm.setData(result); + drm.done(); + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getMixedInstructions(IDisassemblyDMContext context, + String filename, int linenum, int lines, + final DataRequestMonitor drm) + { + // Validate the context + if (context == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + drm.done(); + return; + } + + // Go for it + fConnection.queueCommand(new MIDataDisassemble(context, filename, linenum, lines, true), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + IMixedInstruction[] result = getData().getMIMixedCode(); + drm.setData(result); + drm.done(); + } + }); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIFormat.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIFormat.java new file mode 100644 index 00000000000..ab38919653e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIFormat.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service; + +import java.math.BigInteger; + +/** + * Help class to specify formats. + */ +public final class MIFormat { + public final static int HEXADECIMAL = 0; + public final static int OCTAL = 1; + public final static int BINARY = 2; + public final static int DECIMAL = 3; + public final static int RAW = 4; + public final static int NATURAL = 5; + + public final static int FLOAT = 10; + public final static int ADDRESS = 11; + public final static int INSTRUCTION = 12; + public final static int CHAR = 13; + public final static int STRING = 14; + public final static int UNSIGNED = 15; + + // no instanciation. + private MIFormat() { + } + + /** + * We are assuming that GDB will print the address in hex format + * like: + * 0xbfffe5f0 "hello" + * (int *) 0xbfffe2b8 + * + * @param buffer + * @return + */ + public static BigInteger decodeAdress(String buffer) { + int radix = 10; + int cursor = 0; + int offset = 0; + int len = buffer.length(); + + if ((offset = buffer.indexOf("0x")) != -1 || //$NON-NLS-1$ + (offset = buffer.indexOf("0X")) != -1) { //$NON-NLS-1$ + radix = 16; + cursor = offset + 2; + } + + while (cursor < len && Character.digit(buffer.charAt(cursor), radix) != -1) { + cursor++; + } + + String s = buffer.substring(offset, cursor); + return getBigInteger(s); + } + + public static BigInteger getBigInteger(String address) { + int index = 0; + int radix = 10; + boolean negative = false; + + // Handle zero length + address = address.trim(); + if (address.length() == 0) { + return BigInteger.ZERO; + } + + // Handle minus sign, if present + if (address.startsWith("-")) { //$NON-NLS-1$ + negative = true; + index++; + } + if (address.startsWith("0x", index) || address.startsWith("0X", index)) { //$NON-NLS-1$ //$NON-NLS-2$ + index += 2; + radix = 16; + } else if (address.startsWith("#", index)) { //$NON-NLS-1$ + index ++; + radix = 16; + } else if (address.startsWith("0", index) && address.length() > 1 + index) { //$NON-NLS-1$ + index ++; + radix = 8; + } + + if (index > 0) { + address = address.substring(index); + } + if (negative) { + address = "-" + address; //$NON-NLS-1$ + } + try { + return new BigInteger(address, radix); + } catch (NumberFormatException e) { + // ... + // What can we do ??? + } + return BigInteger.ZERO; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIMemory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIMemory.java new file mode 100644 index 00000000000..06b2da91d93 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIMemory.java @@ -0,0 +1,958 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson AB - expanded from initial stub + * Ericsson AB - added support for event handling + * Ericsson AB - added memory cache + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.ExpressionService.ExpressionChangedEvent; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataReadMemory; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataWriteMemory; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataReadMemoryInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataWriteMemoryInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.model.MemoryByte; +import org.osgi.framework.BundleContext; + +/** + * Memory service implementation + */ +public class MIMemory extends AbstractDsfService implements IMemory, ICachingService { + + public class MemoryChangedEvent extends AbstractDMEvent + implements IMemoryChangedEvent + { + IAddress[] fAddresses; + IDMContext fContext; + + public MemoryChangedEvent(IMemoryDMContext context, IAddress[] addresses) { + super(context); + fAddresses = addresses; + } + + public IAddress[] getAddresses() { + return fAddresses; + } + } + + // Back-end commands cache + private CommandCache fCommandCache; + // Map of memory caches + private Map fMemoryCaches; + + private MIMemoryCache getMemoryCache(IMemoryDMContext memoryDMC) { + MIMemoryCache cache = fMemoryCaches.get(memoryDMC); + if (cache == null) { + cache = new MIMemoryCache(); + fMemoryCaches.put(memoryDMC, cache); + } + return cache; + } + + /** + * Constructor + */ + public MIMemory(DsfSession session) { + super(session); + } + + /////////////////////////////////////////////////////////////////////////// + // AbstractDsfService overrides + /////////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + * + * This function is called during the launch sequence (where the service is + * instantiated). See LaunchSequence.java. + */ + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + /* + * Initialization function: + * - Register the service + * - Create the command cache + * - Register self to service events + * + * @param requestMonitor + */ + private void doInitialize(final RequestMonitor requestMonitor) { + // Create the command cache + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + fCommandCache = new CommandCache(getSession(), commandControl); + fCommandCache.setContextAvailable(commandControl.getContext(), true); + + // Register this service + register(new String[] { MIMemory.class.getName(), IMemory.class.getName() }, new Hashtable()); + + // Create the memory requests cache + fMemoryCaches = new HashMap(); + + // Register as service event listener + getSession().addServiceEventListener(this, null); + + // Done + requestMonitor.done(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#shutdown(org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + @Override + public void shutdown(final RequestMonitor requestMonitor) { + + // Unregister this service + unregister(); + + // Remove event listener + getSession().removeServiceEventListener(this); + + // Complete the shutdown + super.shutdown(requestMonitor); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext() + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IMemory + /////////////////////////////////////////////////////////////////////////// + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IMemory#getMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, + int word_size, int count, DataRequestMonitor drm) + { + // Validate the context + if (memoryDMC == null) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + drm.done(); + return; + } + + // Validate the word size + // NOTE: We only accept 1 byte words for this implementation + if (word_size != 1) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ + drm.done(); + return; + } + + // Validate the byte count + if (count < 0) { + drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid word count (< 0)", null)); //$NON-NLS-1$ + drm.done(); + return; + } + + // All is clear: go for it + getMemoryCache(memoryDMC).getMemory(memoryDMC, address.add(offset), word_size, count, drm); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IMemory#setMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void setMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, + int word_size, int count, byte[] buffer, RequestMonitor rm) + { + // Validate the context + if (memoryDMC == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + rm.done(); + return; + } + + // Validate the word size + // NOTE: We only accept 1 byte words for this implementation + if (word_size != 1) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Validate the byte count + if (count < 0) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid word count (< 0)", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Validate the buffer size + if (buffer.length < count) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Buffer too short", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // All is clear: go for it + getMemoryCache(memoryDMC).setMemory(memoryDMC, address, offset, word_size, count, buffer, rm); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IMemory#fillMemory(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.core.IAddress, long, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void fillMemory(IMemoryDMContext memoryDMC, IAddress address, long offset, + int word_size, int count, byte[] pattern, RequestMonitor rm) + { + // Validate the context + if (memoryDMC == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown context type", null)); //$NON-NLS-1$); + rm.done(); + return; + } + + // Validate the word size + // NOTE: We only accept 1 byte words for this implementation + if (word_size != 1) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Word size not supported (!= 1)", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Validate the repeat count + if (count < 0) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Invalid repeat count (< 0)", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Validate the pattern + if (pattern.length < 1) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Empty pattern", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create an aggregate buffer so we can write in 1 shot + int length = pattern.length; + byte[] buffer = new byte[count * length]; + for (int i = 0; i < count; i++) { + System.arraycopy(pattern, 0, buffer, i * length, length); + } + + // All is clear: go for it + getMemoryCache(memoryDMC).setMemory(memoryDMC, address, offset, word_size, count * length, buffer, rm); + } + + /////////////////////////////////////////////////////////////////////// + // Back-end functions + /////////////////////////////////////////////////////////////////////// + + /** + * @param memoryDMC + * @param address + * @param offset + * @param word_size + * @param count + * @param drm + * + * @since 1.1 + */ + protected void readMemoryBlock(IDMContext dmc, IAddress address, final long offset, + final int word_size, final int count, final DataRequestMonitor drm) + { + /* To simplify the parsing of the MI result, we request the output to + * be on 1 row of [count] columns, no char interpretation. + */ + int mode = MIFormat.HEXADECIMAL; + int nb_rows = 1; + int nb_cols = count; + Character asChar = null; + + fCommandCache.execute( + new MIDataReadMemory(dmc, offset, address.toString(), mode, word_size, nb_rows, nb_cols, asChar), + new DataRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // Retrieve the memory block + drm.setData(getData().getMIMemoryBlock()); + drm.done(); + } + @Override + protected void handleFailure() { + // Bug234289: If memory read fails, return a block marked as invalid + MemoryByte[] block = new MemoryByte[word_size * count]; + for (int i = 0; i < block.length; i++) + block[i] = new MemoryByte((byte) 0, (byte) 0); + drm.setData(block); + drm.done(); + } + } + ); + } + + /** + * @param memoryDMC + * @param address + * @param offset + * @param word_size + * @param count + * @param buffer + * @param rm + * + * @since 1.1 + */ + protected void writeMemoryBlock(final IDMContext dmc, final IAddress address, final long offset, + final int word_size, final int count, final byte[] buffer, final RequestMonitor rm) + { + // Each byte is written individually (GDB power...) + // so we need to keep track of the count + final CountingRequestMonitor countingRM = new CountingRequestMonitor(getExecutor(), rm); + countingRM.setDoneCount(count); + + // We will format the individual bytes in decimal + int format = MIFormat.DECIMAL; + String baseAddress = address.toString(); + + // Issue an MI request for each byte to write + for (int i = 0; i < count; i++) { + String value = new Byte(buffer[i]).toString(); + fCommandCache.execute( + new MIDataWriteMemory(dmc, offset + i, baseAddress, format, word_size, value), + new DataRequestMonitor(getExecutor(), countingRM) + ); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Event handlers + ////////////////////////////////////////////////////////////////////////// + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + if (e instanceof IContainerResumedDMEvent) { + fCommandCache.setContextAvailable(e.getDMContext(), false); + } + + if (e.getReason() != StateChangeReason.STEP) { + fCommandCache.reset(e.getDMContext()); + IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(e.getDMContext(), IMemoryDMContext.class); + if (fMemoryCaches.containsKey(memoryDMC)) { + // We do not want to use the call to getMemoryCache() here. + // This is because: + // 1- if there is not an entry already , we do not want to automatically + // create one, just to call reset() on it. + // 2- if memoryDMC == null, we do not want to create a cache + // entry for which the key is 'null' + fMemoryCaches.get(memoryDMC).reset(); + } + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + if (e instanceof IContainerSuspendedDMEvent) { + fCommandCache.setContextAvailable(e.getDMContext(), true); + } + + fCommandCache.reset(e.getDMContext()); + IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(e.getDMContext(), IMemoryDMContext.class); + if (fMemoryCaches.containsKey(memoryDMC)) { + // We do not want to use the call to getMemoryCache() here. + // This is because: + // 1- if there is not an entry already , we do not want to automatically + // create one, just to call reset() on it. + // 2- if memoryDMC == null, we do not want to create a cache + // entry for which the key is 'null' + fMemoryCaches.get(memoryDMC).reset(); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ExpressionChangedEvent e) { + + // Get the context and expression service handle + final IExpressionDMContext context = e.getDMContext(); + IExpressions expressionService = getServicesTracker().getService(IExpressions.class); + + // Get the variable information and update the corresponding memory locations + if (expressionService != null) { + expressionService.getExpressionAddressData(context, + new DataRequestMonitor(getExecutor(), null) { + @Override + protected void handleSuccess() { + // Figure out which memory area was modified + IExpressionDMAddress expression = getData(); + final int count = expression.getSize(); + IAddress expAddress = expression.getAddress(); + final Addr64 address; + if (expAddress instanceof Addr64) + address = (Addr64) expAddress; + else + address = new Addr64(expAddress.getValue()); + + final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class); + getMemoryCache(memoryDMC).refreshMemory(memoryDMC, address, 0, 1, count, + new RequestMonitor(getExecutor(), null)); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////////// + // SortedLinkedlist + /////////////////////////////////////////////////////////////////////////// + + // This class is really the equivalent of a C struct (old habits die hard...) + // For simplicity, everything is public. + private class MemoryBlock { + public IAddress fAddress; + public long fLength; + public MemoryByte[] fBlock; + public MemoryBlock(IAddress address, long length, MemoryByte[] block) { + fAddress = address; + fLength = length; + fBlock = block; + } + } + + // Address-ordered data structure to cache the memory blocks. + // Contiguous blocks are merged if possible. + @SuppressWarnings("serial") + private class SortedMemoryBlockList extends LinkedList { + + public SortedMemoryBlockList() { + super(); + } + + // Insert the block in the sorted linked list and merge contiguous + // blocks if necessary + @Override + public boolean add(MemoryBlock block) { + + // If the list is empty, just store the block + if (isEmpty()) { + addFirst(block); + return true; + } + + // Insert the block at the correct location and then + // merge the blocks if possible + ListIterator it = listIterator(); + while (it.hasNext()) { + int index = it.nextIndex(); + MemoryBlock item = it.next(); + if (block.fAddress.compareTo(item.fAddress) < 0) { + add(index, block); + compact(index); + return true; + } + } + + // Put at the end of the list and merge if necessary + addLast(block); + compact(size() - 1); + return true; + } + + // Merge this block with its contiguous neighbors (if any) + // Note: Merge is not performed if resulting block size would exceed MAXINT + private void compact(int index) { + + MemoryBlock newBlock = get(index); + + // Case where the block is to be merged with the previous block + if (index > 0) { + MemoryBlock prevBlock = get(index - 1); + IAddress endOfPreviousBlock = prevBlock.fAddress.add(prevBlock.fLength); + if (endOfPreviousBlock.distanceTo(newBlock.fAddress).longValue() == 0) { + long newLength = prevBlock.fLength + newBlock.fLength; + if (newLength <= Integer.MAX_VALUE) { + MemoryByte[] block = new MemoryByte[(int) newLength] ; + System.arraycopy(prevBlock.fBlock, 0, block, 0, (int) prevBlock.fLength); + System.arraycopy(newBlock.fBlock, 0, block, (int) prevBlock.fLength, (int) newBlock.fLength); + newBlock = new MemoryBlock(prevBlock.fAddress, newLength, block); + remove(index); + index -= 1; + set(index, newBlock); + } + } + } + + // Case where the block is to be merged with the following block + int lastIndex = size() - 1; + if (index < lastIndex) { + MemoryBlock nextBlock = get(index + 1); + IAddress endOfNewBlock = newBlock.fAddress.add(newBlock.fLength); + if (endOfNewBlock.distanceTo(nextBlock.fAddress).longValue() == 0) { + long newLength = newBlock.fLength + nextBlock.fLength; + if (newLength <= Integer.MAX_VALUE) { + MemoryByte[] block = new MemoryByte[(int) newLength] ; + System.arraycopy(newBlock.fBlock, 0, block, 0, (int) newBlock.fLength); + System.arraycopy(nextBlock.fBlock, 0, block, (int) newBlock.fLength, (int) nextBlock.fLength); + newBlock = new MemoryBlock(newBlock.fAddress, newLength, block); + set(index, newBlock); + remove(index + 1); + } + } + } + } + } + + /////////////////////////////////////////////////////////////////////////// + // MIMemoryCache + /////////////////////////////////////////////////////////////////////////// + + private class MIMemoryCache { + // The memory cache data structure + private SortedMemoryBlockList fMemoryBlockList; + + public MIMemoryCache() { + // Create the memory block cache + fMemoryBlockList = new SortedMemoryBlockList(); + } + + public void reset() { + // Clear the memory cache + fMemoryBlockList.clear(); + } + + /** + * This function walks the address-sorted memory block list to identify + * the 'missing' blocks (i.e. the holes) that need to be fetched on the target. + * + * The idea is fairly simple but an illustration could perhaps help. + * Assume the cache holds a number of cached memory blocks with gaps i.e. + * there is un-cached memory areas between blocks A, B and C: + * + * +---------+ +---------+ +---------+ + * + A + + B + + C + + * +---------+ +---------+ +---------+ + * : : : : : : + * [a] : : [b] : : [c] : : [d] + * : : : : : : + * [e---+--] : [f--+---------+--] : : + * [g---+---------+------+---------+------+---------+----] + * : : : : : : + * : [h] : : [i----+--] : : + * + * + * We have the following cases to consider.The requested block [a-i] either: + * + * [1] Fits entirely before A, in one of the gaps, or after C + * with no overlap and no contiguousness (e.g. [a], [b], [c] and [d]) + * -> Add the requested block to the list of blocks to fetch + * + * [2] Starts before an existing block but overlaps part of it, possibly + * spilling in the gap following the cached block (e.g. [e], [f] and [g]) + * -> Determine the length of the missing part (< count) + * -> Add a request to fill the gap before the existing block + * -> Update the requested block for the next iteration: + * - Start address to point just after the end of the cached block + * - Count reduced by cached block length (possibly becoming negative, e.g. [e]) + * At this point, the updated requested block starts just beyond the cached block + * for the next iteration. + * + * [3] Starts at or into an existing block and overlaps part of it ([h] and [i]) + * -> Update the requested block for the next iteration: + * - Start address to point just after the end of the cached block + * - Count reduced by length to end of cached block (possibly becoming negative, e.g. [h]) + * At this point, the updated requested block starts just beyond the cached block + * for the next iteration. + * + * We iterate over the cached blocks list until there is no entry left or until + * the remaining requested block count is <= 0, meaning the result list contains + * only the sub-blocks needed to fill the gap(s), if any. + * + * (As is often the case, it takes much more typing to explain it than to just do it :-) + * + * What is missing is a parameter that indicates the minimal block size that is worth fetching. + * This is target-specific and straight in the realm of the coalescing function... + * + * @param reqBlockStart The address of the requested block + * @param count Its length + * @return A list of the sub-blocks to fetch in order to fill enough gaps in the memory cache + * to service the request + */ + private LinkedList getListOfMissingBlocks(IAddress reqBlockStart, int count) { + + LinkedList list = new LinkedList(); + ListIterator it = fMemoryBlockList.listIterator(); + + // Look for holes in the list of memory blocks + while (it.hasNext() && count > 0) { + MemoryBlock cachedBlock = it.next(); + IAddress cachedBlockStart = cachedBlock.fAddress; + IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); + + // Case where we miss a block before the cached block + if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0) { + int length = (int) Math.min(reqBlockStart.distanceTo(cachedBlockStart).longValue(), count); + // If both blocks start at the same location, no need to create a new cached block + if (length > 0) { + MemoryBlock newBlock = new MemoryBlock(reqBlockStart, length, new MemoryByte[0]); + list.add(newBlock); + } + // Adjust request block start and length for the next iteration + reqBlockStart = cachedBlockEnd; + count -= length + cachedBlock.fLength; + } + + // Case where the requested block starts somewhere in the cached block + else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() > 0 + && reqBlockStart.distanceTo(cachedBlockEnd).longValue() >= 0) + { + // Start of the requested block already in cache + // Adjust request block start and length for the next iteration + count -= reqBlockStart.distanceTo(cachedBlockEnd).longValue(); + reqBlockStart = cachedBlockEnd; + } + } + + // Case where we miss a block at the end of the cache + if (count > 0) { + MemoryBlock newBlock = new MemoryBlock(reqBlockStart, count, new MemoryByte[0]); + list.add(newBlock); + } + + return list; + } + + /** + * This function walks the address-sorted memory block list to get the + * cached memory bytes (possibly from multiple contiguous blocks). + * This function is called *after* the missing blocks have been read from + * the back end i.e. the requested memory is all cached. + * + * Again, this is fairly simple. As we loop over the address-ordered list, + * There are really only 2 cases: + * + * [1] The requested block fits entirely in the cached block ([a] or [b]) + * [2] The requested block starts in a cached block and ends in the + * following (contiguous) one ([c]) in which case it is treated + * as 2 contiguous requests ([c'] and [c"]) + * + * +--------------+--------------+ + * + A + B + + * +--------------+--------------+ + * : [a----] : [b-----] : + * : : : + * : [c-----+------] : + * : [c'---]+[c"---] : + * + * @param reqBlockStart The address of the requested block + * @param count Its length + * @return The cached memory content + */ + private MemoryByte[] getMemoryBlockFromCache(IAddress reqBlockStart, int count) { + + IAddress reqBlockEnd = reqBlockStart.add(count); + MemoryByte[] resultBlock = new MemoryByte[count]; + ListIterator iter = fMemoryBlockList.listIterator(); + + while (iter.hasNext()) { + MemoryBlock cachedBlock = iter.next(); + IAddress cachedBlockStart = cachedBlock.fAddress; + IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); + + // Case where the cached block overlaps completely the requested memory block + if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0 + && reqBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) + { + int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue(); + System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, count); + } + + // Case where the beginning of the cached block is within the requested memory block + else if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0 + && cachedBlockStart.distanceTo(reqBlockEnd).longValue() > 0) + { + int pos = (int) reqBlockStart.distanceTo(cachedBlockStart).longValue(); + int length = (int) Math.min(cachedBlock.fLength, count - pos); + System.arraycopy(cachedBlock.fBlock, 0, resultBlock, pos, length); + } + + // Case where the end of the cached block is within the requested memory block + else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0 + && reqBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) + { + int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue(); + int length = (int) Math.min(cachedBlock.fLength - pos, count); + System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, length); + } + } + return resultBlock; + } + + /** + * This function walks the address-sorted memory block list and updates + * the content with the actual memory just read from the target. + * + * @param modBlockStart + * @param count + * @param modBlock + */ + private void updateMemoryCache(IAddress modBlockStart, int count, MemoryByte[] modBlock) { + + IAddress modBlockEnd = modBlockStart.add(count); + ListIterator iter = fMemoryBlockList.listIterator(); + + while (iter.hasNext()) { + MemoryBlock cachedBlock = iter.next(); + IAddress cachedBlockStart = cachedBlock.fAddress; + IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength); + + // For now, we only bother to update bytes already cached. + // Note: In a better implementation (v1.1), we would augment + // the cache with the missing memory blocks since we went + // through the pains of reading them in the first place. + // (this is left as an exercise to the reader :-) + + // Case where the modified block is completely included in the cached block + if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0 + && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) + { + int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue(); + System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, count); + } + + // Case where the beginning of the modified block is within the cached block + else if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0 + && modBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) + { + int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue(); + int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue(); + System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, length); + } + + // Case where the end of the modified block is within the cached block + else if (cachedBlockStart.distanceTo(modBlockEnd).longValue() > 0 + && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) + { + int pos = (int) modBlockStart.distanceTo(cachedBlockStart).longValue(); + int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue(); + System.arraycopy(modBlock, pos, cachedBlock.fBlock, 0, length); + } + } + return; + } + + /** + * @param memoryDMC + * @param address the memory block address (on the target) + * @param word_size the size, in bytes, of an addressable item + * @param count the number of bytes to read + * @param drm the asynchronous data request monitor + */ + public void getMemory(IMemoryDMContext memoryDMC, final IAddress address, final int word_size, + final int count, final DataRequestMonitor drm) + { + // Determine the number of read requests to issue + LinkedList missingBlocks = getListOfMissingBlocks(address, count); + int numberOfRequests = missingBlocks.size(); + + // A read request will be issued for each block needed + // so we need to keep track of the count + final CountingRequestMonitor countingRM = + new CountingRequestMonitor(getExecutor(), drm) { + @Override + protected void handleSuccess() { + // We received everything so read the result from the memory cache + drm.setData(getMemoryBlockFromCache(address, count)); + drm.done(); + } + }; + countingRM.setDoneCount(numberOfRequests); + + // Issue the read requests + for (int i = 0; i < numberOfRequests; i++) { + MemoryBlock block = missingBlocks.get(i); + final IAddress startAddress = block.fAddress; + final int length = (int) block.fLength; + readMemoryBlock(memoryDMC, startAddress, 0, word_size, length, + new DataRequestMonitor(getSession().getExecutor(), drm) { + @Override + protected void handleSuccess() { + MemoryByte[] block = new MemoryByte[count]; + block = getData(); + MemoryBlock memoryBlock = new MemoryBlock(startAddress, length, block); + fMemoryBlockList.add(memoryBlock); + countingRM.done(); + } + }); + } + } + + /** + * @param memoryDMC + * @param address the memory block address (on the target) + * @param offset the offset from the start address + * @param word_size the size, in bytes, of an addressable item + * @param count the number of bytes to write + * @param buffer the source buffer + * @param rm the asynchronous request monitor + */ + public void setMemory(final IMemoryDMContext memoryDMC, final IAddress address, + final long offset, final int word_size, final int count, final byte[] buffer, + final RequestMonitor rm) + { + writeMemoryBlock( + memoryDMC, address, offset, word_size, count, buffer, + new RequestMonitor(getSession().getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Clear the command cache (otherwise we can't guarantee + // that the subsequent memory read will be correct) + fCommandCache.reset(); + + // Re-read the modified memory block to asynchronously update of the memory cache + readMemoryBlock(memoryDMC, address, offset, word_size, count, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + updateMemoryCache(address.add(offset), count, getData()); + // Send the MemoryChangedEvent + IAddress[] addresses = new IAddress[count]; + for (int i = 0; i < count; i++) { + addresses[i] = address.add(offset + i); + } + getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties()); + // Finally... + rm.done(); + } + }); + } + }); + } + + /** + * @param memoryDMC + * @param address + * @param offset + * @param word_size + * @param count + * @param rm + */ + public void refreshMemory(final IMemoryDMContext memoryDMC, final IAddress address, + final long offset, final int word_size, final int count, final RequestMonitor rm) + { + // Check if we already cache part of this memory area (which means it + // is used by a memory service client that will have to be updated) + LinkedList list = getListOfMissingBlocks(address, count); + int sizeToRead = 0; + for (MemoryBlock block : list) { + sizeToRead += block.fLength; + } + + // If none of the requested memory is in cache, just get out + if (sizeToRead == count) { + rm.done(); + return; + } + + // Prepare the data for the MemoryChangedEvent + final IAddress[] addresses = new IAddress[count]; + for (int i = 0; i < count; i++) { + addresses[i] = address.add(i); + } + + // Read the corresponding memory block + fCommandCache.reset(); + readMemoryBlock(memoryDMC, address, 0, 1, count, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + MemoryByte[] oldBlock = getMemoryBlockFromCache(address, count); + MemoryByte[] newBlock = getData(); + boolean blocksDiffer = false; + for (int i = 0; i < oldBlock.length; i++) { + if (oldBlock[i].getValue() != newBlock[i].getValue()) { + blocksDiffer = true; + break; + } + } + if (blocksDiffer) { + updateMemoryCache(address, count, newBlock); + getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties()); + } + rm.done(); + } + }); + } + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fCommandCache.reset(context); + + IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class); + if (fMemoryCaches.containsKey(memoryDMC)) { + // We do not want to use the call to getMemoryCache() here. + // This is because: + // 1- if there is not an entry already , we do not want to automatically + // create one, just to call reset() on it. + // 2- if memoryDMC == null, we do not want to create a cache + // entry for which the key is 'null' + fMemoryCaches.get(memoryDMC).reset(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIModules.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIModules.java new file mode 100644 index 00000000000..498f0b56513 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIModules.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson AB - Modules implementation for GDB + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.math.BigInteger; +import java.util.Hashtable; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IModules; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoSharedLibrary; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoSharedLibraryInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoSharedLibraryInfo.DsfMISharedInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * + */ +public class MIModules extends AbstractDsfService implements IModules, ICachingService { + private CommandCache fModulesCache; + + public MIModules(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + // Cache for holding Modules data + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + fModulesCache = new CommandCache(getSession(), commandControl); + fModulesCache.setContextAvailable(commandControl.getContext(), true); + /* + * Make ourselves known so clients can use us. + */ + register(new String[]{IModules.class.getName(), MIModules.class.getName()}, new Hashtable()); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + static class ModuleDMContext extends AbstractDMContext implements IModuleDMContext { + private final String fFile; + ModuleDMContext(MIModules service, IDMContext[] parents, String file) { + super(service, parents); + fFile = file; + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && fFile.equals(((ModuleDMContext)obj).fFile); + } + + @Override + public int hashCode() { + return baseHashCode() + fFile.hashCode(); + } + } + + static class ModuleDMData implements IModuleDMData { + private final String fFile; + private final String fFromAddress; + private final String fToAddress; + private final boolean fIsSymbolsRead; + + public ModuleDMData(ModuleDMContext dmc) { + fFile = dmc.fFile; + fFromAddress = null; + fToAddress = null; + fIsSymbolsRead = false; + } + + public ModuleDMData(String fileName, String fromAddress, String toAddress, boolean isSymsRead){ + fFile = fileName; + fFromAddress = fromAddress; + fToAddress = toAddress; + fIsSymbolsRead = isSymsRead; + } + + public String getFile() { + return fFile; + } + + public String getName() { + return fFile; + } + + public long getTimeStamp() { + return 0; + } + + public String getBaseAddress() { + return fFromAddress; + } + + public String getToAddress() { + return fToAddress; + } + + public boolean isSymbolsLoaded() { + return fIsSymbolsRead; + } + + public long getSize() { + long result = 0; + if(getBaseAddress() == null || getToAddress() == null) + return result; + BigInteger start = MIFormat.getBigInteger(getBaseAddress()); + BigInteger end = MIFormat.getBigInteger(getToAddress()); + if ( end.compareTo( start ) > 0 ) + result = end.subtract( start ).longValue(); + return result; + } + + } + + public void getModules(final ISymbolDMContext symCtx, final DataRequestMonitor rm) { + if(symCtx != null){ + fModulesCache.execute(new CLIInfoSharedLibrary(symCtx), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(makeModuleContexts(symCtx, getData())); + rm.done(); + } + }); + } + else{ + rm.setData(new IModuleDMContext[] { new ModuleDMContext(this, DMContexts.EMPTY_CONTEXTS_ARRAY, "example module 1"), new ModuleDMContext(this, DMContexts.EMPTY_CONTEXTS_ARRAY, "example module 2") }); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + } + } + + private IModuleDMContext[] makeModuleContexts(IDMContext symCtxt, CLIInfoSharedLibraryInfo info){ + + DsfMISharedInfo[] sharedInfos = info.getMIShared(); + ModuleDMContext[] modules = new ModuleDMContext[sharedInfos.length]; + int i = 0; + for(DsfMISharedInfo shared : sharedInfos){ + modules[i++] = new ModuleDMContext(this, new IDMContext[]{symCtxt}, shared.getName()); + } + return modules; + } + + public void getModuleData(final IModuleDMContext dmc, final DataRequestMonitor rm) { + assert dmc != null; + if (dmc instanceof ModuleDMContext) { + fModulesCache.execute(new CLIInfoSharedLibrary(dmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData( createSharedLibInfo((ModuleDMContext)dmc, getData()) ); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DM Context", null)); //$NON-NLS-1$ + rm.done(); + } + } + + private IModuleDMData createSharedLibInfo(ModuleDMContext dmc, CLIInfoSharedLibraryInfo info){ + for (CLIInfoSharedLibraryInfo.DsfMISharedInfo shared : info.getMIShared()) { + if(shared.getName().equals(dmc.fFile)){ + return new ModuleDMData(shared.getName(), shared.getFrom(), shared.getTo(), shared.isRead()); + } + } + return new ModuleDMData("","", "", false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } + + public void calcAddressInfo(ISymbolDMContext symCtx, String file, int line, int col, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Functionality not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void calcLineInfo(ISymbolDMContext symCtx, IAddress address, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Functionality not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fModulesCache.reset(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java new file mode 100644 index 00000000000..49d8f1c0144 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIProcesses.java @@ -0,0 +1,677 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIAttach; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIDetach; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIInfoThreads; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIThreadListIds; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoThreadsInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadListIdsInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + + +/** + * @since 1.1 + */ +public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICachingService { + + // Below is the context hierarchy that is implemented between the + // MIProcesses service and the MIRunControl service for the MI + // implementation of DSF: + // + // MIControlDMContext + // | + // MIProcessDMC (IProcess) + // MIContainerDMC ______/ | + // (IContainer) | + // | MIThreadDMC (IThread) + // MIExecutionDMC _____/ + // (IExecution) + // + + /** + * Context representing a thread in GDB/MI + */ + @Immutable + private static class MIExecutionDMC extends AbstractDMContext + implements IMIExecutionDMContext + { + /** + * String ID that is used to identify the thread in the GDB/MI protocol. + */ + private final String fThreadId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createExecutionContext()} + * to create instances of this context based on the thread ID. + *

+ * + * @param sessionId Session that this context belongs to. + * @param containerDmc The container that this context belongs to. + * @param threadDmc The thread context parents of this context. + * @param threadId GDB/MI thread identifier. + */ + protected MIExecutionDMC(String sessionId, IContainerDMContext containerDmc, IThreadDMContext threadDmc, String threadId) { + super(sessionId, + containerDmc == null && threadDmc == null ? new IDMContext[0] : + containerDmc == null ? new IDMContext[] { threadDmc } : + threadDmc == null ? new IDMContext[] { containerDmc } : + new IDMContext[] { containerDmc, threadDmc }); + fThreadId = threadId; + } + + /** + * Returns the GDB/MI thread identifier of this context. + * @return + */ + public int getThreadId(){ + try { + return Integer.parseInt(fThreadId); + } catch (NumberFormatException e) { + } + + return 0; + } + + public String getId(){ + return fThreadId; + } + + @Override + public String toString() { return baseToString() + ".thread[" + fThreadId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((MIExecutionDMC)obj).fThreadId.equals(fThreadId); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fThreadId.hashCode(); } + } + + /** + * Context representing a thread group of GDB/MI. + */ + @Immutable + protected static class MIContainerDMC extends AbstractDMContext + implements IMIContainerDMContext + { + /** + * String ID that is used to identify the thread group in the GDB/MI protocol. + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createContainerContext + * to create instances of this context based on the group name. + * + * @param sessionId Session that this context belongs to. + * @param processDmc The process context that is the parent of this context. + * @param groupId GDB/MI thread group identifier. + */ + public MIContainerDMC(String sessionId, IProcessDMContext processDmc, String groupId) { + super(sessionId, processDmc == null ? new IDMContext[0] : new IDMContext[] { processDmc }); + fId = groupId; + } + + /** + * Returns the GDB/MI thread group identifier of this context. + */ + public String getGroupId(){ return fId; } + + @Override + public String toString() { return baseToString() + ".threadGroup[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIContainerDMC)obj).fId == null ? fId == null : ((MIContainerDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + /** + * Context representing a thread. + */ + @Immutable + private static class MIThreadDMC extends AbstractDMContext + implements IThreadDMContext + { + /** + * ID used by GDB to refer to threads. + * We use the same id as the one used in {@link MIProcesses#MIExecutionDMC} + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createThreadContext} + * to create instances of this context based on the thread ID. + *

+ * + * @param sessionId Session that this context belongs to. + * @param processDmc The process that this thread belongs to. + * @param id thread identifier. + */ + public MIThreadDMC(String sessionId, IProcessDMContext processDmc, String id) { + super(sessionId, processDmc == null ? new IDMContext[0] : new IDMContext[] { processDmc }); + fId = id; + } + + /** + * Returns the thread identifier of this context. + * @return + */ + public String getId(){ return fId; } + + @Override + public String toString() { return baseToString() + ".OSthread[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIThreadDMC)obj).fId == null ? fId == null : ((MIThreadDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + @Immutable + private static class MIProcessDMC extends AbstractDMContext + implements IMIProcessDMContext + { + /** + * ID given by the OS. + * For practicality, we use the same id as the one used in {@link MIProcesses#MIContainerDMC} + */ + private final String fId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link IMIProcesses#createProcessContext} + * to create instances of this context based on the PID. + *

+ * + * @param sessionId Session that this context belongs to. + * @param controlDmc The control context parent of this process. + * @param id process identifier. + */ + public MIProcessDMC(String sessionId, ICommandControlDMContext controlDmc, String id) { + super(sessionId, controlDmc == null ? new IDMContext[0] : new IDMContext[] { controlDmc }); + fId = id; + } + + public String getProcId() { return fId; } + + @Override + public String toString() { return baseToString() + ".proc[" + fId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && + (((MIProcessDMC)obj).fId == null ? fId == null : ((MIProcessDMC)obj).fId.equals(fId)); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ (fId == null ? 0 : fId.hashCode()); } + } + + /* + * The data of a corresponding thread or process. + */ + @Immutable + protected static class MIThreadDMData implements IThreadDMData { + final String fName; + final String fId; + + public MIThreadDMData(String name, String id) { + fName = name; + fId = id; + } + + public String getId() { return fId; } + public String getName() { return fName; } + public boolean isDebuggerAttached() { + return true; + } + } + + /** + * Event indicating that an execution group (debugged process) has started. This event + * implements the {@link IStartedMDEvent} from the IRunControl service. + */ + public static class ContainerStartedDMEvent extends AbstractDMEvent + implements IStartedDMEvent + { + public ContainerStartedDMEvent(IContainerDMContext context) { + super(context); + } + } + + /** + * Event indicating that an execution group is no longer being debugged. This event + * implements the {@link IExitedMDEvent} from the IRunControl service. + */ + public static class ContainerExitedDMEvent extends AbstractDMEvent + implements IExitedDMEvent + { + public ContainerExitedDMEvent(IContainerDMContext context) { + super(context); + } + } + + private ICommandControlService fCommandControl; + private CommandCache fContainerCommandCache; + + private static final String FAKE_THREAD_ID = "0"; //$NON-NLS-1$ + // The unique id should be an empty string so that the views know not to display the fake id + public static final String UNIQUE_GROUP_ID = ""; //$NON-NLS-1$ + + public MIProcesses(DsfSession session) { + super(session); + } + + /** + * This method initializes this service. + * + * @param requestMonitor + * The request monitor indicating the operation is finished + */ + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize(new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + } + }); + } + + /** + * This method initializes this service after our superclass's initialize() + * method succeeds. + * + * @param requestMonitor + * The call-back object to notify when this service's + * initialization is done. + */ + private void doInitialize(RequestMonitor requestMonitor) { + +// // Register this service. +// register(new String[] { IProcesses.class.getName(), +// MIProcesses.class.getName() }, +// new Hashtable()); + + fCommandControl = getServicesTracker().getService(ICommandControlService.class); + fContainerCommandCache = new CommandCache(getSession(), fCommandControl); + fContainerCommandCache.setContextAvailable(fCommandControl.getContext(), true); + getSession().addServiceEventListener(this, null); + + requestMonitor.done(); + } + + /** + * This method shuts down this service. It unregisters the service, stops + * receiving service events, and calls the superclass shutdown() method to + * finish the shutdown process. + * + * @return void + */ + @Override + public void shutdown(RequestMonitor requestMonitor) { +// unregister(); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + /** + * @return The bundle context of the plug-in to which this service belongs. + */ + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + public IThreadDMContext createThreadContext(IProcessDMContext processDmc, String threadId) { + return new MIThreadDMC(getSession().getId(), processDmc, threadId); + } + + public IProcessDMContext createProcessContext(ICommandControlDMContext controlDmc, String pid) { + return new MIProcessDMC(getSession().getId(), controlDmc, pid); + } + + public IMIExecutionDMContext createExecutionContext(IContainerDMContext containerDmc, + IThreadDMContext threadDmc, + String threadId) { + return new MIExecutionDMC(getSession().getId(), containerDmc, threadDmc, threadId); + } + + public IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, + String groupId) { + return new MIContainerDMC(getSession().getId(), processDmc, groupId); + } + + public IMIContainerDMContext createContainerContextFromThreadId(ICommandControlDMContext controlDmc, String threadId) { + String groupId = UNIQUE_GROUP_ID; + IProcessDMContext processDmc = createProcessContext(controlDmc, groupId); + return createContainerContext(processDmc, groupId); + } + + /** + * This method obtains the model data for a given IThreadDMContext object + * which can represent a thread or a process. + * + * @param dmc + * The context for which we are requesting the data + * @param rm + * The request monitor that will contain the requested data + */ + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IThreadDMContext) { + getExecutionData((IThreadDMContext) dmc, + (DataRequestMonitor) rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getExecutionData(IThreadDMContext dmc, final DataRequestMonitor rm) { + if (dmc instanceof MIProcessDMC) { + rm.setData(new MIThreadDMData("", ((MIProcessDMC)dmc).getProcId())); //$NON-NLS-1$ + rm.done(); + } else if (dmc instanceof MIThreadDMC) { + final MIThreadDMC threadDmc = (MIThreadDMC)dmc; + + IProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IProcessDMContext.class); + getDebuggingContext(procDmc, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData() instanceof IMIContainerDMContext) { + IMIContainerDMContext contDmc = (IMIContainerDMContext)getData(); + fContainerCommandCache.execute(new CLIInfoThreads(contDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IThreadDMData threadData = null; + for (CLIInfoThreadsInfo.ThreadInfo thread : getData().getThreadInfo()) { + if (thread.getId().equals(threadDmc.getId())) { + threadData = new MIThreadDMData(thread.getName(), thread.getOsId()); + break; + } + } + if (threadData != null) { + rm.setData(threadData); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Could not get thread info", null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + }); + } + } + + public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof MIProcessDMC) { + MIProcessDMC procDmc = (MIProcessDMC)dmc; + rm.setData(createContainerContext(procDmc, procDmc.getProcId())); + } else if (dmc instanceof MIThreadDMC) { + MIThreadDMC threadDmc = (MIThreadDMC)dmc; + IMIProcessDMContext procDmc = DMContexts.getAncestorOfType(dmc, IMIProcessDMContext.class); + IMIContainerDMContext containerDmc = createContainerContext(procDmc, procDmc.getProcId()); + rm.setData(createExecutionContext(containerDmc, threadDmc, threadDmc.getId())); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid thread context.", null)); //$NON-NLS-1$ + } + + rm.done(); + } + + public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); + rm.done(); + } + + public void attachDebuggerToProcess(final IProcessDMContext procCtx, final DataRequestMonitor rm) { + if (procCtx instanceof IMIProcessDMContext) { + + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(procCtx, ICommandControlDMContext.class); + fCommandControl.queueCommand( + new CLIAttach(controlDmc, ((IMIProcessDMContext)procCtx).getProcId()), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IMIContainerDMContext containerDmc = createContainerContext(procCtx, + ((IMIProcessDMContext)procCtx).getProcId()); + getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), + getProperties()); + rm.setData(containerDmc); + rm.done(); + } + }); + + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); + rm.done(); + } + + public void detachDebuggerFromProcess(final IDMContext dmc, final RequestMonitor rm) { + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + + if (controlDmc != null) { + // This service version cannot use -target-detach because it didn't exist + // in versions of GDB up to and including GDB 6.8 + fCommandControl.queueCommand( + new CLIDetach(controlDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); + if (containerDmc != null) { + getSession().dispatchEvent(new ContainerExitedDMEvent(containerDmc), + getProperties()); + } + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid context.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void canTerminate(IThreadDMContext thread, DataRequestMonitor rm) { + rm.setData(true); + rm.done(); + } + + public void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); + rm.done(); + } + + public void debugNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void getProcessesBeingDebugged(IDMContext dmc, final DataRequestMonitor rm) { + final IMIContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IMIContainerDMContext.class); + if (containerDmc != null) { + fContainerCommandCache.execute( + new MIThreadListIds(containerDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(makeExecutionDMCs(containerDmc, getData())); + rm.done(); + } + }); + } else { + // This service version only handles a single process to debug, therefore, we can simply + // create the context describing this process ourselves. + ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); + String groupId = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = createProcessContext(controlDmc, groupId); + IMIContainerDMContext newContainerDmc = createContainerContext(procDmc, groupId); + rm.setData(new IContainerDMContext[] {newContainerDmc}); + rm.done(); + } + } + + private IExecutionDMContext[] makeExecutionDMCs(IContainerDMContext containerDmc, MIThreadListIdsInfo info) { + final IProcessDMContext procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); + + if (info.getStrThreadIds().length == 0) { + // Main thread always exist even if it is not reported by GDB. + // So create thread-id = 0 when no thread is reported. + // This hack is necessary to prevent AbstractMIControl from issuing a thread-select + // because it doesn't work if the application was not compiled with pthread. + return new IMIExecutionDMContext[]{createExecutionContext(containerDmc, + createThreadContext(procDmc, FAKE_THREAD_ID), + FAKE_THREAD_ID)}; + } else { + IExecutionDMContext[] executionDmcs = new IMIExecutionDMContext[info.getStrThreadIds().length]; + for (int i = 0; i < info.getStrThreadIds().length; i++) { + String threadId = info.getStrThreadIds()[i]; + executionDmcs[i] = createExecutionContext(containerDmc, + createThreadContext(procDmc, threadId), + threadId); + } + return executionDmcs; + } + } + + public void getRunningProcesses(IDMContext dmc, final DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void isRunNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.setData(false); + rm.done(); + } + public void runNewProcess(IDMContext dmc, String file, + Map attributes, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + public void terminate(IThreadDMContext thread, RequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, + NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + if (e instanceof IContainerResumedDMEvent) { + // This will happen in all-stop mode + fContainerCommandCache.setContextAvailable(e.getDMContext(), false); + } else { + // This will happen in non-stop mode + // Keep target available for Container commands + } + } + + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + if (e instanceof IContainerSuspendedDMEvent) { + // This will happen in all-stop mode + fContainerCommandCache.setContextAvailable(e.getDMContext(), true); + } else { + // This will happen in non-stop mode + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IStartedDMEvent e) { + fContainerCommandCache.reset(); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IExitedDMEvent e) { + fContainerCommandCache.reset(); + } + + public void flushCache(IDMContext context) { + fContainerCommandCache.reset(context); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java new file mode 100644 index 00000000000..40a0716d078 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRegisters.java @@ -0,0 +1,646 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for additional features in DSF Reference Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataListRegisterNames; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataListRegisterValues; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterNamesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterValuesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIRegisterValue; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +/** + * + *

+ * Implementation note: + * This class implements event handlers for the events that are generated by + * this service itself. When the event is dispatched, these handlers will + * be called first, before any of the clients. These handlers update the + * service's internal state information to make them consistent with the + * events being issued. Doing this in the handlers as opposed to when + * the events are generated, guarantees that the state of the service will + * always be consistent with the events. + */ + +public class MIRegisters extends AbstractDsfService implements IRegisters, ICachingService { + private static final String BLANK_STRING = ""; //$NON-NLS-1$ + /* + * Support class used to construct Register Group DMCs. + */ + + public static class MIRegisterGroupDMC extends AbstractDMContext implements IRegisterGroupDMContext { + private int fGroupNo; + private String fGroupName; + + public MIRegisterGroupDMC(MIRegisters service, IContainerDMContext contDmc, int groupNo, String groupName) { + super(service.getSession().getId(), new IDMContext[] { contDmc }); + fGroupNo = groupNo; + fGroupName = groupName; + } + + public int getGroupNo() { return fGroupNo; } + public String getName() { return fGroupName; } + + @Override + public boolean equals(Object other) { + return ((super.baseEquals(other)) && (((MIRegisterGroupDMC) other).fGroupNo == fGroupNo) && + (((MIRegisterGroupDMC) other).fGroupName.equals(fGroupName))); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fGroupNo; } + @Override + public String toString() { return baseToString() + ".group[" + fGroupNo + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + } + + /* + * Support class used to construct Register DMCs. + */ + + public static class MIRegisterDMC extends AbstractDMContext implements IRegisterDMContext { + private int fRegNo; + private String fRegName; + + public MIRegisterDMC(MIRegisters service, MIRegisterGroupDMC group, int regNo, String regName) { + super(service.getSession().getId(), + new IDMContext[] { group }); + fRegNo = regNo; + fRegName = regName; + } + + public MIRegisterDMC(MIRegisters service, MIRegisterGroupDMC group, IMIExecutionDMContext execDmc, int regNo, String regName) { + super(service.getSession().getId(), + new IDMContext[] { execDmc, group }); + fRegNo = regNo; + fRegName = regName; + } + + public int getRegNo() { return fRegNo; } + public String getName() { return fRegName; } + + @Override + public boolean equals(Object other) { + return ((super.baseEquals(other)) && (((MIRegisterDMC) other).fRegNo == fRegNo) && + (((MIRegisterDMC) other).fRegName.equals(fRegName))); + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fRegNo; } + @Override + public String toString() { return baseToString() + ".register[" + fRegNo + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + } + + /* + * Event class to notify register value is changed + */ + public static class RegisterChangedDMEvent implements IRegisters.IRegisterChangedDMEvent { + + private final IRegisterDMContext fRegisterDmc; + + RegisterChangedDMEvent(IRegisterDMContext registerDMC) { + fRegisterDmc = registerDMC; + } + + public IRegisterDMContext getDMContext() { + return fRegisterDmc; + } + } + + /* + * Internal control variables. + */ + + private MIRegisterGroupDMC fGeneralRegistersGroupDMC; + private CommandCache fRegisterNameCache; // Cache for holding the Register Names in the single Group + private CommandCache fRegisterValueCache; // Cache for holding the Register Values + + public MIRegisters(DsfSession session) + { + super(session); + } + + @Override + protected BundleContext getBundleContext() + { + return GdbPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + /* + * Create the lower level register cache. + */ + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + fRegisterValueCache = new CommandCache(getSession(), commandControl); + fRegisterValueCache.setContextAvailable(commandControl.getContext(), true); + fRegisterNameCache = new CommandCache(getSession(), commandControl); + fRegisterNameCache.setContextAvailable(commandControl.getContext(), true); + + /* + * Sign up so we see events. We use these events to decide how to manage + * any local caches we are providing as well as the lower level register + * cache we create to get/set registers on the target. + */ + getSession().addServiceEventListener(this, null); + + /* + * Make ourselves known so clients can use us. + */ + register(new String[]{IRegisters.class.getName(), MIRegisters.class.getName()}, new Hashtable()); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) + { + unregister(); + getSession().removeServiceEventListener(this); + super.shutdown(requestMonitor); + } + + public boolean isValid() { return true; } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + /* + * This is the method which is called when actual results need to be returned. We + * can be called either with a service DMC for which we return ourselves or we can + * be called with the DMC's we have handed out. If the latter is the case then we + * data mine by talking to the Debug Engine. + */ + + if (dmc instanceof MIRegisterGroupDMC) { + getRegisterGroupData((MIRegisterGroupDMC)dmc, (DataRequestMonitor)rm); + } else if (dmc instanceof MIRegisterDMC) { + getRegisterData((MIRegisterDMC)dmc, (DataRequestMonitor)rm); + } else if (dmc instanceof FormattedValueDMContext) { + getFormattedExpressionValue((FormattedValueDMContext)dmc, (DataRequestMonitor)rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor rm) { + if (dmc.getParents().length == 1 && dmc.getParents()[0] instanceof MIRegisterDMC) { + getRegisterDataValue( (MIRegisterDMC) dmc.getParents()[0], dmc.getFormatID(), rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getRegisterGroupData(IRegisterGroupDMContext regGroupDmc, DataRequestMonitor rm) { + /** + * For the GDB GDBMI implementation there is only on group. The GPR and FPU registers are grouped into + * one set. We are going to hard wire this set as the "General Registers". + */ + class RegisterGroupData implements IRegisterGroupDMData { + public String getName() { return "General Registers"; } //$NON-NLS-1$ + public String getDescription() { return "General Purpose and FPU Register Group"; } //$NON-NLS-1$ + } + + rm.setData( new RegisterGroupData() ) ; + rm.done(); + } + + public void getBitFieldData(IBitFieldDMContext dmc, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Bit fields not yet supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /** + * For the GDB GDBMI implementation there is only on group. We represent + * this group as a single list we maintain within this service. So we + * need to search this list to see if we have a current value. + */ + public void getRegisterData(IRegisterDMContext regDmc , final DataRequestMonitor rm) { + if (regDmc instanceof MIRegisterDMC) { + final MIRegisterDMC miRegDmc = (MIRegisterDMC)regDmc; + IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(regDmc, IMIExecutionDMContext.class); + // Create register DMC with name if execution DMC is not present. + if(execDmc == null){ + rm.setData(new RegisterData(miRegDmc.getName(), BLANK_STRING, false)); + rm.done(); + return; + } + + int[] regnos = {miRegDmc.getRegNo()}; + fRegisterValueCache.execute( + new MIDataListRegisterValues(execDmc, MIFormat.HEXADECIMAL, regnos), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Retrieve the register value. + MIRegisterValue[] regValue = getData().getMIRegisterValues(); + + // If the list is empty just return empty handed. + if (regValue.length == 0) { + assert false : "Backend protocol error"; //$NON-NLS-1$ + //done.setStatus(new Status(IStatus.ERROR, IDsfStatusConstants.INTERNAL_ERROR ,)); + rm.done(); + return; + } + + // We can determine if the register is floating point because + // GDB returns this additional information as part of the value. + MIRegisterValue reg = regValue[0]; + boolean isFloat = false; + + if ( reg.getValue().contains("float")) { //$NON-NLS-1$ + isFloat = true; + } + + // Return the new register attributes. + rm.setData(new RegisterData(miRegDmc.getName(), BLANK_STRING, isFloat)); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + private void getRegisterDataValue( final MIRegisterDMC regDmc, final String formatId, final DataRequestMonitor rm) { + IMIExecutionDMContext miExecDmc = DMContexts.getAncestorOfType(regDmc, IMIExecutionDMContext.class); + if(miExecDmc == null){ + // Set value to blank if execution dmc is not present + rm.setData( new FormattedValueDMData( BLANK_STRING ) ); + rm.done(); + return; + } + + // Select the format to be shown + int NumberFormat = MIFormat.HEXADECIMAL; + + if ( HEX_FORMAT.equals ( formatId ) ) { NumberFormat = MIFormat.HEXADECIMAL; } + if ( OCTAL_FORMAT.equals ( formatId ) ) { NumberFormat = MIFormat.OCTAL; } + if ( NATURAL_FORMAT.equals( formatId ) ) { NumberFormat = MIFormat.NATURAL; } + if ( BINARY_FORMAT.equals ( formatId ) ) { NumberFormat = MIFormat.BINARY; } + if ( DECIMAL_FORMAT.equals( formatId ) ) { NumberFormat = MIFormat.DECIMAL; } + + int[] regnos = {regDmc.getRegNo()}; + fRegisterValueCache.execute( + new MIDataListRegisterValues(miExecDmc, NumberFormat, regnos), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Retrieve the register value. + MIRegisterValue[] regValue = getData().getMIRegisterValues(); + + // If the list is empty just return empty handed. + if (regValue.length == 0) { + assert false : "Backend protocol error"; //$NON-NLS-1$ + //done.setStatus(new Status(IStatus.ERROR, IDsfStatusConstants.INTERNAL_ERROR ,)); + rm.done(); + return; + } + + MIRegisterValue reg = regValue[0]; + + // Return the new register value. + rm.setData( new FormattedValueDMData( reg.getValue() ) ); + rm.done(); + } + }); + } + + static class RegisterData implements IRegisterDMData { + + final private String fRegName; + final private String fRegDesc; + final private boolean fIsFloat; + + public RegisterData(String regName, String regDesc, boolean isFloat ) { + + fRegName = regName; + fRegDesc = regDesc; + fIsFloat = isFloat; + } + + public boolean isReadable() { return true; } + public boolean isReadOnce() { return false; } + public boolean isWriteable() { return true; } + public boolean isWriteOnce() { return false; } + public boolean hasSideEffects() { return false; } + public boolean isVolatile() { return true; } + + public boolean isFloat() { return fIsFloat; } + public String getName() { return fRegName; } + public String getDescription() { return fRegDesc; } + } + + // Wraps a list of registers in DMContexts. + private MIRegisterDMC[] makeRegisterDMCs(MIRegisterGroupDMC groupDmc, String[] regNames) { + return makeRegisterDMCs(groupDmc, null, regNames); + } + + // Wraps a list of registers in DMContexts. + private MIRegisterDMC[] makeRegisterDMCs(MIRegisterGroupDMC groupDmc, IMIExecutionDMContext execDmc, String[] regNames) { + MIRegisterDMC[] regDmcList = new MIRegisterDMC[regNames.length]; + int regNo = 0 ; + for (String regName : regNames) { + if(execDmc != null) + regDmcList[regNo] = new MIRegisterDMC(this, groupDmc, execDmc, regNo, regName); + else + regDmcList[regNo] = new MIRegisterDMC(this, groupDmc, regNo, regName); + regNo++; + } + + return regDmcList; + } + + /* + * Event handling section. These event handlers control the caching state of the + * register caches. This service creates several cache objects. Not all of which + * need to be flushed. These handlers maintain the state of the caches. + */ + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IResumedDMEvent e) { + fRegisterValueCache.setContextAvailable(e.getDMContext(), false); + if (e.getReason() != StateChangeReason.STEP) { + fRegisterValueCache.reset(); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched( + IRunControl.ISuspendedDMEvent e) { + fRegisterValueCache.setContextAvailable(e.getDMContext(), true); + fRegisterValueCache.reset(); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final IRegisters.IRegisterChangedDMEvent e) { + fRegisterValueCache.reset(); + } + + private void generateRegisterChangedEvent(IRegisterDMContext dmc ) { + getSession().dispatchEvent(new RegisterChangedDMEvent(dmc), getProperties()); + } + + /* + * These are the public interfaces for this service. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisterGroups(org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext, org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getRegisterGroups(IDMContext ctx, DataRequestMonitor rm ) { + IContainerDMContext contDmc = DMContexts.getAncestorOfType(ctx, IContainerDMContext.class); + if (contDmc == null) { + rm.setStatus( new Status( IStatus.ERROR , GdbPlugin.PLUGIN_ID , INVALID_HANDLE , "Container context not found", null ) ) ; //$NON-NLS-1$ + rm.done(); + return; + } + + if (fGeneralRegistersGroupDMC == null) { + fGeneralRegistersGroupDMC = new MIRegisterGroupDMC( this , contDmc, 0 , "General Registers" ) ; //$NON-NLS-1$ + } + MIRegisterGroupDMC[] groupDMCs = new MIRegisterGroupDMC[] { fGeneralRegistersGroupDMC }; + rm.setData(groupDMCs) ; + rm.done() ; + } + + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisters(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getRegisters(final IDMContext dmc, final DataRequestMonitor rm) { + final MIRegisterGroupDMC groupDmc = DMContexts.getAncestorOfType(dmc, MIRegisterGroupDMC.class); + if ( groupDmc == null ) { + rm.setStatus( new Status( IStatus.ERROR , GdbPlugin.PLUGIN_ID , INVALID_HANDLE , "RegisterGroup context not found", null ) ) ; //$NON-NLS-1$ + rm.done(); + return; + } + + final IContainerDMContext containerDmc = DMContexts.getAncestorOfType(dmc, IContainerDMContext.class); + if ( containerDmc == null ) { + rm.setStatus( new Status( IStatus.ERROR , GdbPlugin.PLUGIN_ID , INVALID_HANDLE , "Container context not found" , null ) ) ; //$NON-NLS-1$ + rm.done(); + return; + } + + // There is only one group and its number must be 0. + if ( groupDmc.getGroupNo() == 0 ) { + final IMIExecutionDMContext executionDmc = DMContexts.getAncestorOfType(dmc, IMIExecutionDMContext.class); + fRegisterNameCache.execute( + new MIDataListRegisterNames(containerDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Retrieve the register names. + String[] regNames = getData().getRegisterNames() ; + + // If the list is empty just return empty handed. + if ( regNames.length == 0 ) { + rm.done(); + return; + } + // Create DMContexts for each of the register names. + if(executionDmc == null) + rm.setData(makeRegisterDMCs(groupDmc, regNames)); + else + rm.setData(makeRegisterDMCs(groupDmc, executionDmc, regNames)); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR , GdbPlugin.PLUGIN_ID , INTERNAL_ERROR , "Invalid group = " + groupDmc , null)); //$NON-NLS-1$ + rm.done(); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getBitFields(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getBitFields( IDMContext regDmc , DataRequestMonitor rm ) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "BitField not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeRegister(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext, java.lang.String, java.lang.String, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void writeRegister(IRegisterDMContext regCtx, final String regValue, final String formatId, final RequestMonitor rm) { + MIRegisterGroupDMC grpDmc = DMContexts.getAncestorOfType(regCtx, MIRegisterGroupDMC.class); + if ( grpDmc == null ) { + rm.setStatus( new Status( IStatus.ERROR , GdbPlugin.PLUGIN_ID , INVALID_HANDLE , "RegisterGroup context not found" , null ) ) ; //$NON-NLS-1$ + rm.done(); + return; + } + + final MIRegisterDMC regDmc = (MIRegisterDMC)regCtx; + // There is only one group and its number must be 0. + if ( grpDmc.getGroupNo() == 0 ) { + final ExpressionService exprService = getServicesTracker().getService(ExpressionService.class); + String regName = regDmc.getName(); + final IExpressionDMContext exprCtxt = exprService.createExpression(regCtx, "$" + regName); //$NON-NLS-1$ + exprService.getModelData(exprCtxt, new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Evaluate the expression - request HEX since it works in every case + final FormattedValueDMContext valueDmc = exprService.getFormattedValueContext(exprCtxt, formatId); + exprService.getModelData( + valueDmc, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + if(! regValue.equals(getData().getFormattedValue()) || ! valueDmc.getFormatID().equals(formatId)){ + exprService.writeExpression(exprCtxt, regValue, formatId, new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + generateRegisterChangedEvent(regDmc); + rm.done(); + } + }); + }//if + else { + rm.done(); + } + }//handleOK + } + ); + } + }); + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Invalid group = " + grpDmc, null)); //$NON-NLS-1$ + rm.done(); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeBitField(org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext, java.lang.String, java.lang.String, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void writeBitField(IBitFieldDMContext bitFieldCtx, String bitFieldValue, String formatId, RequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Writing bit field not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeBitField(org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext, org.eclipse.cdt.dsf.debug.service.IRegisters.IMnemonic, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + public void writeBitField(IBitFieldDMContext bitFieldCtx, IMnemonic mnemonic, RequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Writing bit field not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues#getAvailableFormats(org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor rm) { + + rm.setData(new String[] { HEX_FORMAT, DECIMAL_FORMAT, OCTAL_FORMAT, BINARY_FORMAT, NATURAL_FORMAT }); + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues#getFormattedValueContext(org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext, java.lang.String) + */ + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) { + if ( dmc instanceof MIRegisterDMC ) { + MIRegisterDMC regDmc = (MIRegisterDMC) dmc; + return( new FormattedValueDMContext( this, regDmc, formatId)); + } + return null; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findRegisterGroup(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void findRegisterGroup(IDMContext ctx, String name, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Finding a Register Group context not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findRegister(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void findRegister(IDMContext ctx, String name, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Finding a Register context not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findBitField(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) + */ + public void findBitField(IDMContext ctx, String name, DataRequestMonitor rm) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Finding a Register Group context not supported", null)); //$NON-NLS-1$ + rm.done(); + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fRegisterNameCache.reset(context); + fRegisterValueCache.reset(context); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java new file mode 100644 index 00000000000..247ea764052 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIRunControl.java @@ -0,0 +1,719 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson AB - Modified for handling of multiple threads + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.datamodel.IDMEvent; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecInterrupt; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNextInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStepInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIThreadListIds; +import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIErrorEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIGDBExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISharedLibEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISteppingRangeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadListIdsInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + + +/** + * + *

+ * Implementation note: + * This class implements event handlers for the events that are generated by + * this service itself. When the event is dispatched, these handlers will + * be called first, before any of the clients. These handlers update the + * service's internal state information to make them consistent with the + * events being issued. Doing this in the handlers as opposed to when + * the events are generated, guarantees that the state of the service will + * always be consistent with the events. + * The purpose of this pattern is to allow clients that listen to service + * events and track service state, to be perfectly in sync with the service + * state. + */ +public class MIRunControl extends AbstractDsfService implements IRunControl, ICachingService +{ + @Deprecated + protected class MIExecutionDMC extends AbstractDMContext implements IMIExecutionDMContext + { + /** + * Integer ID that is used to identify the thread in the GDB/MI protocol. + */ + private final int fThreadId; + + /** + * Constructor for the context. It should not be called directly by clients. + * Instead clients should call {@link MIRunControl#createMIExecutionContext(IContainerDMContext, int)} + * to create instances of this context based on the thread ID. + *

+ * Classes extending {@link MIRunControl} may also extend this class to include + * additional information in the context. + * + * @param sessionId Session that this context belongs to. + * @param containerDmc The container that this context belongs to. + * @param threadId GDB/MI thread identifier. + */ + protected MIExecutionDMC(String sessionId, IContainerDMContext containerDmc, int threadId) { + super(sessionId, containerDmc != null ? new IDMContext[] { containerDmc } : new IDMContext[0]); + fThreadId = threadId; + } + + /** + * Returns the GDB/MI thread identifier of this context. + * @return + */ + public int getThreadId(){ + return fThreadId; + } + + @Override + public String toString() { return baseToString() + ".thread[" + fThreadId + "]"; } //$NON-NLS-1$ //$NON-NLS-2$ + + @Override + public boolean equals(Object obj) { + return super.baseEquals(obj) && ((MIExecutionDMC)obj).fThreadId == fThreadId; + } + + @Override + public int hashCode() { return super.baseHashCode() ^ fThreadId; } + } + + @Immutable + private static class ExecutionData implements IExecutionDMData { + private final StateChangeReason fReason; + ExecutionData(StateChangeReason reason) { + fReason = reason; + } + public StateChangeReason getStateChangeReason() { return fReason; } + } + + /** + * Base class for events generated by the MI Run Control service. Most events + * generated by the MI Run Control service are directly caused by some MI event. + * Other services may need access to the extended MI data carried in the event. + * + * @param DMC that this event refers to + * @param MIInfo object that is the direct cause of this event + * @see MIRunControl + */ + @Immutable + protected static class RunControlEvent> extends AbstractDMEvent + implements IDMEvent, IMIDMEvent + { + final private T fMIInfo; + public RunControlEvent(V dmc, T miInfo) { + super(dmc); + fMIInfo = miInfo; + } + + public T getMIEvent() { return fMIInfo; } + } + + /** + * Indicates that the given thread has been suspended. + */ + @Immutable + protected static class SuspendedEvent extends RunControlEvent + implements ISuspendedDMEvent + { + SuspendedEvent(IExecutionDMContext ctx, MIStoppedEvent miInfo) { + super(ctx, miInfo); + } + + public StateChangeReason getReason() { + if (getMIEvent() instanceof MIBreakpointHitEvent) { + return StateChangeReason.BREAKPOINT; + } else if (getMIEvent() instanceof MISteppingRangeEvent) { + return StateChangeReason.STEP; + } else if (getMIEvent() instanceof MISharedLibEvent) { + return StateChangeReason.SHAREDLIB; + }else if (getMIEvent() instanceof MISignalEvent) { + return StateChangeReason.SIGNAL; + }else if (getMIEvent() instanceof MIWatchpointTriggerEvent) { + return StateChangeReason.WATCHPOINT; + }else if (getMIEvent() instanceof MIErrorEvent) { + return StateChangeReason.ERROR; + }else { + return StateChangeReason.USER_REQUEST; + } + } + } + + @Immutable + protected static class ContainerSuspendedEvent extends SuspendedEvent + implements IContainerSuspendedDMEvent + { + final IExecutionDMContext[] triggeringDmcs; + ContainerSuspendedEvent(IContainerDMContext containerDmc, MIStoppedEvent miInfo, IExecutionDMContext triggeringDmc) { + super(containerDmc, miInfo); + this.triggeringDmcs = triggeringDmc != null + ? new IExecutionDMContext[] { triggeringDmc } : new IExecutionDMContext[0]; + } + + public IExecutionDMContext[] getTriggeringContexts() { + return triggeringDmcs; + } + } + + @Immutable + protected static class ResumedEvent extends RunControlEvent + implements IResumedDMEvent + { + ResumedEvent(IExecutionDMContext ctx, MIRunningEvent miInfo) { + super(ctx, miInfo); + } + + public StateChangeReason getReason() { + switch(getMIEvent().getType()) { + case MIRunningEvent.CONTINUE: + return StateChangeReason.USER_REQUEST; + case MIRunningEvent.NEXT: + case MIRunningEvent.NEXTI: + return StateChangeReason.STEP; + case MIRunningEvent.STEP: + case MIRunningEvent.STEPI: + return StateChangeReason.STEP; + case MIRunningEvent.FINISH: + return StateChangeReason.STEP; + case MIRunningEvent.UNTIL: + case MIRunningEvent.RETURN: + break; + } + return StateChangeReason.UNKNOWN; + } + } + + @Immutable + protected static class ContainerResumedEvent extends ResumedEvent + implements IContainerResumedDMEvent + { + final IExecutionDMContext[] triggeringDmcs; + + ContainerResumedEvent(IContainerDMContext containerDmc, MIRunningEvent miInfo, IExecutionDMContext triggeringDmc) { + super(containerDmc, miInfo); + this.triggeringDmcs = triggeringDmc != null + ? new IExecutionDMContext[] { triggeringDmc } : new IExecutionDMContext[0]; + } + + public IExecutionDMContext[] getTriggeringContexts() { + return triggeringDmcs; + } + } + + @Immutable + protected static class StartedDMEvent extends RunControlEvent + implements IStartedDMEvent + { + StartedDMEvent(IMIExecutionDMContext executionDmc, MIThreadCreatedEvent miInfo) { + super(executionDmc, miInfo); + } + } + + @Immutable + protected static class ExitedDMEvent extends RunControlEvent + implements IExitedDMEvent + { + ExitedDMEvent(IMIExecutionDMContext executionDmc, MIThreadExitEvent miInfo) { + super(executionDmc, miInfo); + } + } + + private ICommandControlService fConnection; + private CommandCache fMICommandCache; + + // State flags + private boolean fSuspended = true; + private boolean fResumePending = false; + private boolean fStepping = false; + private boolean fTerminated = false; + + private StateChangeReason fStateChangeReason; + private IExecutionDMContext fStateChangeTriggeringContext; + + private static final int FAKE_THREAD_ID = 0; + + public MIRunControl(DsfSession session) { + super(session); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + }}); + } + + private void doInitialize(final RequestMonitor rm) { + fConnection = getServicesTracker().getService(ICommandControlService.class); + fMICommandCache = new CommandCache(getSession(), fConnection); + fMICommandCache.setContextAvailable(fConnection.getContext(), true); + getSession().addServiceEventListener(this, null); + rm.done(); + } + + @Override + public void shutdown(final RequestMonitor rm) { + getSession().removeServiceEventListener(this); + fMICommandCache.reset(); + super.shutdown(rm); + } + + public boolean isValid() { return true; } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IExecutionDMContext) { + getExecutionData((IExecutionDMContext)dmc, (DataRequestMonitor)rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public CommandCache getCache() { return fMICommandCache; } + + public IMIExecutionDMContext createMIExecutionContext(IContainerDMContext container, int threadId) { + return new MIExecutionDMC(getSession().getId(), container, threadId); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIRunningEvent e) { + IDMEvent event = null; + // Find the container context, which is used in multi-threaded debugging. + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class); + if (containerDmc != null) { + // Set the triggering context only if it's different than the container context. + IExecutionDMContext triggeringCtx = !e.getDMContext().equals(containerDmc) ? e.getDMContext() : null; + event = new ContainerResumedEvent(containerDmc, e, triggeringCtx); + } else { + event = new ResumedEvent(e.getDMContext(), e); + } + getSession().dispatchEvent(event, getProperties()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIStoppedEvent e) { + IDMEvent event = null; + // Find the container context, which is used in multi-threaded debugging. + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(e.getDMContext(), IContainerDMContext.class); + if (containerDmc != null) { + // Set the triggering context only if it's different than the container context. + IExecutionDMContext triggeringCtx = !e.getDMContext().equals(containerDmc) ? e.getDMContext() : null; + event = new ContainerSuspendedEvent(containerDmc, e, triggeringCtx); + } else { + event = new SuspendedEvent(e.getDMContext(), e); + } + getSession().dispatchEvent(event, getProperties()); + } + + /** + * Thread Created event handling + * When a new thread is created - OOB Event fired ~"[New Thread 1077300144 (LWP 7973)]\n" + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIThreadCreatedEvent e) { + IContainerDMContext containerDmc = e.getDMContext(); + IMIExecutionDMContext executionCtx = e.getStrId() != null ? createMIExecutionContext(containerDmc, e.getId()) : null; + getSession().dispatchEvent(new StartedDMEvent(executionCtx, e), getProperties()); + } + + /** + * Thread exit event handling + * When a new thread is destroyed - OOB Event fired " + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(final MIThreadExitEvent e) { + IContainerDMContext containerDmc = e.getDMContext(); + IMIExecutionDMContext executionCtx = e.getStrId() != null ? createMIExecutionContext(containerDmc, e.getId()) : null; + getSession().dispatchEvent(new ExitedDMEvent(executionCtx, e), getProperties()); + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ContainerResumedEvent e) { + fSuspended = false; + fResumePending = false; + fStateChangeReason = e.getReason(); + fMICommandCache.setContextAvailable(e.getDMContext(), false); + //fStateChangeTriggeringContext = e.getTriggeringContext(); + if (e.getReason().equals(StateChangeReason.STEP)) { + fStepping = true; + } else { + fMICommandCache.reset(); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ContainerSuspendedEvent e) { + fMICommandCache.setContextAvailable(e.getDMContext(), true); + fMICommandCache.reset(); + fStateChangeReason = e.getReason(); + fStateChangeTriggeringContext = e.getTriggeringContexts().length != 0 + ? e.getTriggeringContexts()[0] : null; + fSuspended = true; + fStepping = false; + } + + /** + * Not used, kept for API compatibility. ICommandControlShutdownDMEvent is used instead + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(MIGDBExitEvent e) { + fTerminated = true; + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * @since 1.1 + */ + @DsfServiceEventHandler + public void eventDispatched(ICommandControlShutdownDMEvent e) { + fTerminated = true; + } + + + /** + * Event handler when New thread is created + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(StartedDMEvent e) { + + } + + /** + * Event handler when a thread is destroyed + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(ExitedDMEvent e) { + fMICommandCache.reset(e.getDMContext()); + } + + /////////////////////////////////////////////////////////////////////////// + // AbstractService + @Override + protected BundleContext getBundleContext() { + return GdbPlugin.getBundleContext(); + } + + /////////////////////////////////////////////////////////////////////////// + // IRunControl + public void canResume(IExecutionDMContext context, DataRequestMonitor rm) { + rm.setData(doCanResume(context)); + rm.done(); + } + + private boolean doCanResume(IExecutionDMContext context) { + return !fTerminated && isSuspended(context) && !fResumePending; + } + + public void canSuspend(IExecutionDMContext context, DataRequestMonitor rm) { + rm.setData(doCanSuspend(context)); + rm.done(); + } + + private boolean doCanSuspend(IExecutionDMContext context) { + return !fTerminated && !isSuspended(context); + } + + public boolean isSuspended(IExecutionDMContext context) { + return !fTerminated && fSuspended; + } + + public boolean isStepping(IExecutionDMContext context) { + return !fTerminated && fStepping; + } + + public void resume(IExecutionDMContext context, final RequestMonitor rm) { + assert context != null; + + if (doCanResume(context)) { + fResumePending = true; + // Cygwin GDB will accept commands and execute them after the step + // which is not what we want, so mark the target as unavailable + // as soon as we send a resume command. + fMICommandCache.setContextAvailable(context, false); + MIExecContinue cmd = null; + if(context instanceof IContainerDMContext) + cmd = new MIExecContinue(context); + else{ + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + cmd = new MIExecContinue(dmc);//, new String[0]); + } + fConnection.queueCommand( + cmd, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.done(); + } + } + ); + }else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Given context: " + context + ", is already running.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + } + } + + public void suspend(IExecutionDMContext context, final RequestMonitor rm){ + assert context != null; + + if (doCanSuspend(context)) { + MIExecInterrupt cmd = null; + if(context instanceof IContainerDMContext){ + cmd = new MIExecInterrupt(context); + } + else { + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + cmd = new MIExecInterrupt(dmc); + } + fConnection.queueCommand( + cmd, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.done(); + } + } + ); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + ", is already suspended.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + } + } + + public void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor rm) { + if (context instanceof IContainerDMContext) { + rm.setData(false); + rm.done(); + return; + } + canResume(context, rm); + } + + public void step(IExecutionDMContext context, StepType stepType, final RequestMonitor rm) { + assert context != null; + + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (!doCanResume(context)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot resume context", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + fResumePending = true; + fStepping = true; + fMICommandCache.setContextAvailable(context, false); + switch(stepType) { + case STEP_INTO: + fConnection.queueCommand( + new MIExecStep(dmc, 1), new DataRequestMonitor(getExecutor(), rm) {} + ); + break; + case STEP_OVER: + fConnection.queueCommand( + new MIExecNext(dmc), new DataRequestMonitor(getExecutor(), rm) {}); + break; + case STEP_RETURN: + // The -exec-finish command operates on the selected stack frame, but here we always + // want it to operate on the stop stack frame. So we manually create a top-frame + // context to use with the MI command. + // We get a local instance of the stack service because the stack service can be shut + // down before the run control service is shut down. So it is possible for the + // getService() reqeust below to return null. + MIStack stackService = getServicesTracker().getService(MIStack.class); + if (stackService != null) { + IFrameDMContext topFrameDmc = stackService.createFrameDMContext(dmc, 0); + fConnection.queueCommand( + new MIExecFinish(topFrameDmc), new DataRequestMonitor(getExecutor(), rm) {}); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Cannot create context for command, stack service not available.", null)); //$NON-NLS-1$ + rm.done(); + } + break; + case INSTRUCTION_STEP_INTO: + fConnection.queueCommand( + new MIExecStepInstruction(dmc, 1), new DataRequestMonitor(getExecutor(), rm) {} + ); + break; + case INSTRUCTION_STEP_OVER: + fConnection.queueCommand( + new MIExecNextInstruction(dmc, 1), new DataRequestMonitor(getExecutor(), rm) {} + ); + break; + default: + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Given step type not supported", null)); //$NON-NLS-1$ + rm.done(); + } + } + + public void getExecutionContexts(final IContainerDMContext containerDmc, final DataRequestMonitor rm) { + fMICommandCache.execute( + new MIThreadListIds(containerDmc), + new DataRequestMonitor( + getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(makeExecutionDMCs(containerDmc, getData())); + rm.done(); + } + }); + } + + + private IExecutionDMContext[] makeExecutionDMCs(IContainerDMContext containerCtx, MIThreadListIdsInfo info) { + if (info.getThreadIds().length == 0) { + // Main thread always exist even if it is not reported by GDB. + // So create thread-id = 0 when no thread is reported. + // This hack is necessary to prevent AbstractMIControl from issuing a thread-select + // because it doesn't work if the application was not compiled with pthread. + return new IMIExecutionDMContext[]{createMIExecutionContext(containerCtx, FAKE_THREAD_ID)}; + } else { + IExecutionDMContext[] executionDmcs = new IMIExecutionDMContext[info.getThreadIds().length]; + for (int i = 0; i < info.getThreadIds().length; i++) { + executionDmcs[i] = createMIExecutionContext(containerCtx, info.getThreadIds()[i]); + } + return executionDmcs; + } + } + + public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor rm){ + if (dmc instanceof IContainerDMContext) { + rm.setData( new ExecutionData(fStateChangeReason) ); + } else if (dmc instanceof IMIExecutionDMContext) { + StateChangeReason reason = dmc.equals(fStateChangeTriggeringContext) ? fStateChangeReason : StateChangeReason.CONTAINER; + rm.setData(new ExecutionData(reason)); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Given context: " + dmc + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + } + rm.done(); + } + + /** + * Run selected execution thread to a given line number. + */ + public void runToLine(IExecutionDMContext context, String fileName, String lineNo, boolean skipBreakpoints, final DataRequestMonitor rm){ + // Later add support for Address and function. + // skipBreakpoints is not used at the moment. Implement later + + assert context != null; + + IMIExecutionDMContext dmc = DMContexts.getAncestorOfType(context, IMIExecutionDMContext.class); + if (dmc == null){ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Given context: " + context + " is not an execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$ + rm.done(); + return; + } + + if (doCanResume(context)) { + fResumePending = true; + fMICommandCache.setContextAvailable(context, false); + fConnection.queueCommand(new MIExecUntil(dmc, fileName + ":" + lineNo), //$NON-NLS-1$ + new DataRequestMonitor( + getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, + "Cannot resume given DMC.", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fMICommandCache.reset(context); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java new file mode 100644 index 00000000000..755fdebaa8d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIStack.java @@ -0,0 +1,698 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for handling of multiple execution contexts + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.ICachingService; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IStack2; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.command.CommandCache; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackInfoDepth; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackListArguments; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackListFrames; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackListLocals; +import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIArg; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackInfoDepthInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListArgumentsInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListFramesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListLocalsInfo; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.Addr32; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.BundleContext; + +public class MIStack extends AbstractDsfService + implements IStack2, ICachingService +{ + protected static class MIFrameDMC extends AbstractDMContext + implements IFrameDMContext + { + private final int fLevel; + // public MIFrameDMC(MIStack service, int level) { + public MIFrameDMC(String sessionId, IExecutionDMContext execDmc, int level) { + super(sessionId, new IDMContext[] { execDmc }); + fLevel = level; + } + + public int getLevel() { return fLevel; } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && ((MIFrameDMC)other).fLevel == fLevel; + } + + @Override + public int hashCode() { + return super.baseHashCode() ^ fLevel; + } + + @Override + public String toString() { + return baseToString() + ".frame[" + fLevel + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + protected static class MIVariableDMC extends AbstractDMContext + implements IVariableDMContext + { + public enum Type { ARGUMENT, LOCAL } + final private Type fType; + final private int fIndex; + + public MIVariableDMC(MIStack service, IFrameDMContext frame, Type type, int index) { + super(service, new IDMContext[] { frame }); + fIndex = index; + fType = type; + } + + public int getIndex() { return fIndex; } + public Type getType() { return fType; } + + @Override + public boolean equals(Object other) { + return super.baseEquals(other) && + ((MIVariableDMC)other).fType == fType && + ((MIVariableDMC)other).fIndex == fIndex; + } + + @Override + public int hashCode() { + int typeFactor = 0; + if (fType == Type.LOCAL) typeFactor = 2; + else if (fType == Type.ARGUMENT) typeFactor = 3; + return super.baseHashCode() ^ typeFactor ^ fIndex; + } + + @Override + public String toString() { + return baseToString() + ".variable(" + fType + ")[" + fIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + private CommandCache fMICommandCache; + private MIStoppedEvent fCachedStoppedEvent; + private IRunControl fRunControl; + + public MIStack(DsfSession session) + { + super(session); + } + + @Override + protected BundleContext getBundleContext() + { + return GdbPlugin.getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor rm) { + super.initialize( + new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + doInitialize(rm); + } + }); + } + + private void doInitialize(RequestMonitor rm) { + ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); + fMICommandCache = new CommandCache(getSession(), commandControl); + fMICommandCache.setContextAvailable(commandControl.getContext(), true); + fRunControl = getServicesTracker().getService(IRunControl.class); + + getSession().addServiceEventListener(this, null); + register(new String[]{IStack.class.getName(), MIStack.class.getName()}, new Hashtable()); + rm.done(); + } + + @Override + public void shutdown(RequestMonitor rm) + { + unregister(); + getSession().removeServiceEventListener(this); + fMICommandCache.reset(); + super.shutdown(rm); + } + + @SuppressWarnings("unchecked") + public void getModelData(IDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof MIFrameDMC) { + getFrameData((MIFrameDMC)dmc, (DataRequestMonitor)rm); + // getFrameData invokes rm + } else if (dmc instanceof MIVariableDMC) { + getVariableData((MIVariableDMC)dmc, (DataRequestMonitor)rm); + // getVariablesData invokes rm + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * Creates a frame context. This method is intended to be used by other MI + * services and sub-classes which need to create a frame context directly. + *

+ * Sub-classes can override this method to provide custom stack frame + * context implementation. + *

+ * @param execDmc Execution context that this frame is to be a child of. + * @param level Level of the new context. + * @return A new frame context. + */ + public IFrameDMContext createFrameDMContext(IExecutionDMContext execDmc, int level) { + return new MIFrameDMC(getSession().getId(), execDmc, level); + } + + public void getFrames(final IDMContext ctx, final DataRequestMonitor rm) { + getFrames(ctx, 0, ALL_FRAMES, rm); + } + + public void getFrames(final IDMContext ctx, final int startIndex, final int endIndex, final DataRequestMonitor rm) { + + if (startIndex < 0 || endIndex > 0 && endIndex < startIndex) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid stack frame range [" + startIndex + ',' + endIndex + ']', null)); //$NON-NLS-1$ + rm.done(); + return; + } + + final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); + + if (execDmc == null) { + //rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "No frame context found in " + ctx, null)); //$NON-NLS-1$ + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context " + ctx, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Make sure the thread is stopped + if (!fRunControl.isSuspended(execDmc)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Context is running: " + ctx, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + if (startIndex == 0 && endIndex == 0) { + // Try to retrieve the top stack frame from the cached stopped event. + if (fCachedStoppedEvent != null && + fCachedStoppedEvent.getFrame() != null && + execDmc.equals(fCachedStoppedEvent.getDMContext())) + { + rm.setData(new IFrameDMContext[] { createFrameDMContext(execDmc, fCachedStoppedEvent.getFrame().getLevel()) }); + rm.done(); + return; + } + } + + final MIStackListFrames miStackListCmd; + // firstIndex is the first index retrieved + final int firstIndex; + if (endIndex >= 0) { + miStackListCmd = new MIStackListFrames(execDmc, startIndex, endIndex); + firstIndex = startIndex; + } else { + miStackListCmd = new MIStackListFrames(execDmc); + firstIndex = 0; + } + fMICommandCache.execute( + miStackListCmd, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getFrames(execDmc, getData(), firstIndex, endIndex, startIndex)); + rm.done(); + } + }); + } + + public void getTopFrame(final IDMContext ctx, final DataRequestMonitor rm) { + final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); + if (execDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + ctx, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Try to retrieve the top stack frame from the cached stopped event. + if (fCachedStoppedEvent != null && + fCachedStoppedEvent.getFrame() != null && + execDmc.equals(fCachedStoppedEvent.getDMContext())) + { + rm.setData(createFrameDMContext(execDmc, fCachedStoppedEvent.getFrame().getLevel())); + rm.done(); + return; + } + + // If stopped event is not available or doesn't contain frame info, + // query top stack frame + getFrames( + ctx, + 0, + 0, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData()[0]); + rm.done(); + } + }); + } + + //private MIFrameDMC[] getFrames(DsfMIStackListFramesInfo info) { + private IFrameDMContext[] getFrames(IMIExecutionDMContext execDmc, MIStackListFramesInfo info, int firstIndex, int lastIndex, int startIndex) { + int length = info.getMIFrames().length; + if (lastIndex > 0) { + int limit= lastIndex - startIndex + 1; + if (limit < length) { + length = limit; + } + } + IFrameDMContext[] frameDMCs = new MIFrameDMC[length]; + for (int i = 0; i < length; i++) { + //frameDMCs[i] = new MIFrameDMC(this, info.getMIFrames()[i].getLevel()); + final MIFrame frame= info.getMIFrames()[i + startIndex - firstIndex]; + assert startIndex + i == frame.getLevel(); + frameDMCs[i] = createFrameDMContext(execDmc, frame.getLevel()); + } + return frameDMCs; + } + + + + public void getFrameData(final IFrameDMContext frameDmc, final DataRequestMonitor rm) { + if (!(frameDmc instanceof MIFrameDMC)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context type " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + final MIFrameDMC miFrameDmc = (MIFrameDMC)frameDmc; + + IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(frameDmc, IMIExecutionDMContext.class); + if (execDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "No execution context found in " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /** + * Base class for the IFrameDMData object that uses an MIFrame object to + * provide the data. Sub-classes must provide the MIFrame object + */ + abstract class FrameData implements IFrameDMData + { + abstract protected MIFrame getMIFrame(); + + public IAddress getAddress() { + String addr = getMIFrame().getAddress(); + if (addr.startsWith("0x")) { //$NON-NLS-1$ + addr = addr.substring(2); + } + if (addr.length() <= 8) { + return new Addr32(getMIFrame().getAddress()); + } else { + return new Addr64(getMIFrame().getAddress()); + } + } + + public int getColumn() { return 0; } + + public String getFile() { return getMIFrame().getFile(); } + public int getLine() { return getMIFrame().getLine(); } + public String getFunction() { return getMIFrame().getFunction(); } + + @Override + public String toString() { return getMIFrame().toString(); } + } + + // If requested frame is the top stack frame, try to retrieve it from + // the stopped event data. + class FrameDataFromStoppedEvent extends FrameData { + private final MIStoppedEvent fEvent; + FrameDataFromStoppedEvent(MIStoppedEvent event) { fEvent = event; } + @Override + protected MIFrame getMIFrame() { return fEvent.getFrame(); } + } + + // Retrieve the top stack frame from the stopped event only if the selected thread is the one on which stopped event + // is raised + if (fCachedStoppedEvent != null && + execDmc.equals(fCachedStoppedEvent.getDMContext()) && + miFrameDmc.fLevel == 0 && + fCachedStoppedEvent.getFrame() != null) + { + rm.setData(new FrameDataFromStoppedEvent(fCachedStoppedEvent)); + rm.done(); + return; + } + + // If not, retrieve the full list of frame data. + class FrameDataFromMIStackFrameListInfo extends FrameData { + private MIStackListFramesInfo fFrameDataCacheInfo; + private int fFrameIndex; + + FrameDataFromMIStackFrameListInfo(MIStackListFramesInfo info, int index) { + fFrameDataCacheInfo = info; + fFrameIndex = index; + } + + @Override + protected MIFrame getMIFrame() { return fFrameDataCacheInfo.getMIFrames()[fFrameIndex]; } + } + + fMICommandCache.execute( + new MIStackListFrames(execDmc), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Find the index to the correct MI frame object. + int idx = findFrameIndex(getData().getMIFrames(), miFrameDmc.fLevel); + if (idx == -1) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid frame " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create the data object. + rm.setData(new FrameDataFromMIStackFrameListInfo(getData(), idx)); + rm.done(); + } + }); + } + + public void getArguments(final IFrameDMContext frameDmc, final DataRequestMonitor rm) { + final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(frameDmc, IMIExecutionDMContext.class); + if (execDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "No execution context found in " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + + // If requested frame is the top stack frame, try to retrieve it from + // the stopped event data. + if (frameDmc.getLevel() == 0 && + fCachedStoppedEvent != null && + fCachedStoppedEvent.getFrame() != null && + execDmc.equals(fCachedStoppedEvent.getDMContext()) && + fCachedStoppedEvent.getFrame().getArgs() != null) + { + rm.setData(makeVariableDMCs( + frameDmc, MIVariableDMC.Type.ARGUMENT, fCachedStoppedEvent.getFrame().getArgs().length)); + rm.done(); + return; + } + + // If not, retrieve the full list of frame data. + fMICommandCache.execute( + new MIStackListArguments(execDmc, true), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Find the index to the correct MI frame object. + // Note: this is a short-cut, but it won't work once we implement retrieving + // partial lists of stack frames. + int idx = frameDmc.getLevel(); + if (idx == -1 || idx >= getData().getMIFrames().length) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid frame " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create the variable array out of MIArg array. + MIArg[] args = getData().getMIFrames()[idx].getArgs(); + if (args == null) args = new MIArg[0]; + rm.setData(makeVariableDMCs(frameDmc, MIVariableDMC.Type.ARGUMENT, args.length)); + rm.done(); + } + }); + } + + public void getVariableData(IVariableDMContext variableDmc, final DataRequestMonitor rm) { + if (!(variableDmc instanceof MIVariableDMC)) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context type " + variableDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + final MIVariableDMC miVariableDmc = (MIVariableDMC)variableDmc; + + // Extract the frame DMC from the variable DMC. + final MIFrameDMC frameDmc = DMContexts.getAncestorOfType(variableDmc, MIFrameDMC.class); + if (frameDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "No frame context found in " + variableDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + final IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(frameDmc, IMIExecutionDMContext.class); + if (execDmc == null) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "No execution context found in " + frameDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + /** + * Same as with frame objects, this is a base class for the IVariableDMData object that uses an MIArg object to + * provide the data. Sub-classes must supply the MIArg object. + */ + class VariableData implements IVariableDMData { + private MIArg dsfMIArg; + VariableData(MIArg arg){ + dsfMIArg = arg; + } + public String getName() { return dsfMIArg.getName(); } + public String getValue() { return dsfMIArg.getValue(); } + @Override + public String toString() { return dsfMIArg.toString(); } + } + + // Check if the stopped event can be used to extract the variable value. + if (execDmc != null && miVariableDmc.fType == MIVariableDMC.Type.ARGUMENT && + frameDmc.fLevel == 0 && fCachedStoppedEvent != null && fCachedStoppedEvent.getFrame() != null && + execDmc.equals(fCachedStoppedEvent.getDMContext()) && + fCachedStoppedEvent.getFrame().getArgs() != null) + { + if (miVariableDmc.fIndex >= fCachedStoppedEvent.getFrame().getArgs().length) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Invalid variable " + miVariableDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + rm.setData(new VariableData(fCachedStoppedEvent.getFrame().getArgs()[miVariableDmc.fIndex])); + rm.done(); + return; + } + + if (miVariableDmc.fType == MIVariableDMC.Type.ARGUMENT){ + fMICommandCache.execute( + new MIStackListArguments(execDmc, true), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Find the correct frame and argument + if ( frameDmc.fLevel >= getData().getMIFrames().length || + miVariableDmc.fIndex >= getData().getMIFrames()[frameDmc.fLevel].getArgs().length ) + { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid variable " + miVariableDmc, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // Create the data object. + rm.setData(new VariableData(getData().getMIFrames()[frameDmc.fLevel].getArgs()[miVariableDmc.fIndex])); + rm.done(); + }}); + }//if + if (miVariableDmc.fType == MIVariableDMC.Type.LOCAL){ + fMICommandCache.execute( + new MIStackListLocals(frameDmc, true), + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + + // Create the data object. + MIArg[] locals = getData().getLocals(); + if (locals.length > miVariableDmc.fIndex) { + rm.setData(new VariableData(locals[miVariableDmc.fIndex])); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid variable " + miVariableDmc, null)); //$NON-NLS-1$ + } + rm.done(); + } + }); + + }//if + + } + + private MIVariableDMC[] makeVariableDMCs(IFrameDMContext frame, MIVariableDMC.Type type, int count) { + MIVariableDMC[] variables = new MIVariableDMC[count]; + for (int i = 0; i < count; i++) { + variables[i]= new MIVariableDMC(this, frame, type, i); + } + return variables; + } + + private int findFrameIndex(MIFrame[] frames, int level) { + for (int idx = 0; idx < frames.length; idx++) { + if (frames[idx].getLevel() == level) { + return idx; + } + } + return -1; + } + + + public void getLocals(final IFrameDMContext frameDmc, final DataRequestMonitor rm) { + + final List localsList = new ArrayList(); + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData( localsList.toArray(new IVariableDMContext[localsList.size()]) ); + rm.done(); + } + }; + countingRm.setDoneCount(2); + + getArguments( + frameDmc, + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList(getData()) ); + countingRm.done(); + } + }); + + fMICommandCache.execute( + new MIStackListLocals(frameDmc, true), + new DataRequestMonitor(getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + localsList.addAll( Arrays.asList( + makeVariableDMCs(frameDmc, MIVariableDMC.Type.LOCAL, getData().getLocals().length)) ); + countingRm.done(); + } + }); + } + + public void getStackDepth(IDMContext dmc, final int maxDepth, final DataRequestMonitor rm) { + IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(dmc, IMIExecutionDMContext.class); + if (execDmc != null) { + // Make sure the thread is stopped + if (!fRunControl.isSuspended(execDmc)) { + rm.setData(0); + rm.done(); + return; + } + + MIStackInfoDepth depthCommand = null; + if (maxDepth > 0) depthCommand = new MIStackInfoDepth(execDmc, maxDepth); + else depthCommand = new MIStackInfoDepth(execDmc); + + fMICommandCache.execute( + depthCommand, + new DataRequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getDepth()); + rm.done(); + } + }); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$ + rm.done(); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(IResumedDMEvent e) { + fMICommandCache.setContextAvailable(e.getDMContext(), false); + if (e.getReason() != StateChangeReason.STEP) { + fCachedStoppedEvent = null; + fMICommandCache.reset(); + } + } + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * @since 1.1 + */ + @DsfServiceEventHandler + public void eventDispatched(ISuspendedDMEvent e) { + fMICommandCache.setContextAvailable(e.getDMContext(), true); + fMICommandCache.reset(); + } + + + /** + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + * @since 1.1 + */ + @DsfServiceEventHandler + public void eventDispatched(IMIDMEvent e) { + if (e.getMIEvent() instanceof MIStoppedEvent) { + fCachedStoppedEvent = (MIStoppedEvent)e.getMIEvent(); + } + } + + /** + * This method is left for API compatibility only. + * @nooverride This method is not intended to be re-implemented or extended by clients. + * @noreference This method is not intended to be referenced by clients. + */ + @DsfServiceEventHandler + public void eventDispatched(MIRunControl.ContainerSuspendedEvent e) { + } + + /** + * {@inheritDoc} + * @since 1.1 + */ + public void flushCache(IDMContext context) { + fMICommandCache.reset(context); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java new file mode 100644 index 00000000000..c0ffa7774d6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/MIVariableManager.java @@ -0,0 +1,1713 @@ +/******************************************************************************* + * Copyright (c) 2008 Monta Vista 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: + * Monta Vista - initial API and implementation + * Ericsson - Modified for handling of multiple execution contexts + * Ericsson - Major updates for GDB/MI implementation + * Ericsson - Major re-factoring to deal with children + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.ExpressionService.ExpressionInfo; +import org.eclipse.cdt.dsf.mi.service.ExpressionService.MIExpressionDMC; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue; +import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataEvaluateExpression; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarAssign; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarCreate; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarDelete; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarEvaluateExpression; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarInfoPathExpression; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarListChildren; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarSetFormat; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarShowAttributes; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIVarUpdate; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVar; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarAssignInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarChange; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarCreateInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarDeleteInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarEvaluateExpressionInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoPathExpressionInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarListChildrenInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarSetFormatInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowAttributesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarUpdateInfo; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Manages a list of variable objects as created through GDB/MI commands. + * + * This class is passed expression-meta-commands which have their own cache. + * Therefore, we don't use the standard MICommandCache in this class. + * In fact, we can't even use it, because many variableObject MI commands, + * should not be cached as they alter the state of the back-end. + * + * Design details: + * ============== + * + * GDB variable object information + * ------------------------------- + * o Variable objects are recursively hierarchical, where children can be created through + * the parent. + * o A varObject created with -var-create is a ROOT + * o A varObject created with -var-list-children, is not a root + * o Only varObject with no children or varObjects that are pointers can change values + * and therefore + * those objects can be used with -var-assign + * o After a program stops, a varObject must be 'updated' before used + * o Only root varObject can be updated with -var-update, which will trigger all + * of the root's descendants to be updated. + * o Once updated, a varObject need not be updated until the program resumes again; + * this is true even after -var-assign is used (because it does an implicit -var-update) + * o -var-update will return the list of all modifiable descendants of the udpated root which + * have changed + * o -var-update will indicate if a root is out-of-scope (which implies that all + * its descendants are out-of-scope) + * o if a varObject is out-of-scope, another varObject may be valid for the same + * expression as the out-of-scope varObject + * o deleting a varObject will delete all its descendants, therefore, it is only + * necessary to delete roots + * + * + * Class details + * ------------- + * - We have an MIVariableObject class which represents a variable object in GDB + * + * - MIVariableObject includes a buffered value for each allowed format. + * + * - We have an MIRootVariableObject class inheriting from MIVariableObject to describe + * root varObjects created with -var-create. Objects created with -var-list-children + * are MIVariableObjects only. The root class will keep track of if the root object + * needs to be updated, if the root object is out-of-scope, and of a list of all + * modifiable descendants of this root. The list of modifiable descendants is + * accessed using the gdb-given name to allow quick updates from the -var-update + * result (see below.) + * + * - we do not use -var-list-children for arrays, but create them manually + * + * - when the program stops, we should mark all roots as needing to be updated. + * To achieve this efficiently, we have a dedicated list of roots that are updated. + * When the program stops, we go through this list, remove each element and mark it + * as needing to be updated. + * + * - when a varObject is accessed, if its root must be updated, the var-update + * command shall be used. The result of that command will indicate all + * modifiable descendants that have changed. We also use --all-values with -var-update + * to get the new value (in the current format) for each modified descendant. Using the list of modifiable + * descendants of the root, we can quickly update the changed ones to invalidate their buffered + * values and store the new current format value. + * + * - all values of non-modifiable varObjects (except arrays) will be set to {...} + * without going to the back-end + * + * - requesting the value of an array varObject will trigger the creation of a new + * varObject for the array's address. Note that we must still use a variable + * object and not the command -data-evaluate-expression, because we still need to get + * the array address in multiple formats. + * + * - we keep an LRU (Least Recently Used) structure of all variable objects. This LRU + * will be bounded to a maximum allowed number of variable objects. Whenever we get an + * object from the LRU cleanup will be done if the maximum size has been reached. + * The LRU will not delete a parent varObject until all its children are deleted; this is + * achieved by touching each of the parents of an object whenever that object is put or get + * + * - It may happen that when accessing a varObject we find its root to be + * out-of-scope. The expression for which we are trying to access a varObject + * could still be valid, and therefore we should try to create a new varObject for + * that expression. This can happen for example if two methods use the same name + * for a variable. In the case when we find that a varObject is out-of-scope (when + * its root is out-of-scope) the following should be done: + * - replace the varObject in the LRU with a newly created one in GDB + * - if the old object was a root, delete it in GDB. + * + * - In GDB, -var-update will only report a change if -var-evaluate-expression has + * changed -- in the current format--. This means that situations like + * double z = 1.2; + * z = 1.4; + * Will not report a change if the format is anything else than natural. + * This is because 1.2 and 1.4 are both printed as 1, 0x1, etc + * Since we cache the values of every format, we must know if the value has + * change in -any- format, not just the current one. + * To solve this, we always keep the display format of variable objects (and their + * children) to the natural format; we believe that if the value changes in any + * format, it guarantees that it will change in the natural format. + * The simplest way to do this is that whenever we change the format + * of a variable object, we immediately set it back to natural with a second + * var-set-format command. + * Note that versions of GDB after 6.7 will allows to issue -var-evaluate-expression + * with a specified format, therefore allowing us to never use -var-set-format, and + * consequently, to easily keep the display format of all variable objects to natural. + */ +public class MIVariableManager implements ICommandControl { + + + /** + * Utility class to track the progress and information of MI variable objects + */ + public class MIVariableObject { + + // Don't use an enumeration to allow subclasses to extend this + protected static final int STATE_READY = 0; + protected static final int STATE_UPDATING = 1; + + protected int currentState; + + // This is the lock used when we must run multiple + // operations at once. This lock should be independent of the + // UPDATING state, which is why we don't make it part of the state + private boolean locked = false; + + // This id is the one used to search for this object in our hash-map + private final VariableObjectId internalId; + // This is the name of the variable object, as given by GDB (e.g., var1 or var1.public.x) + private String gdbName = null; + // The current format of this variable object, within GDB + private String format = IFormattedValues.NATURAL_FORMAT; + + // The full expression that can be used to characterize this object + private String fullExp = null; + private String type = null; + private int numChildren = 0; + private Boolean editable = null; + + // The current values of the expression for each format. (null if not known yet) + private Map valueMap = null; + + // A queue of request monitors waiting for this object to be ready + private LinkedList operationsPending; + + // A queue of request monitors that requested an update + protected LinkedList> updatesPending; + + // The relative expressions of the children of this variable, if any. + // Null means we didn't fetch them yet, while an empty array means no children + private ExpressionInfo[] children = null; + + // The parent of this variable object within GDB. Null if this object has no parent + private MIVariableObject parent = null; + + // The root parent that must be used to issue -var-update. + // If this object is a root, then the rootToUpdate is itself + private MIRootVariableObject rootToUpdate = null; + + protected boolean outOfScope = false; + + public MIVariableObject(VariableObjectId id, MIVariableObject parentObj) { + currentState = STATE_READY; + + operationsPending = new LinkedList(); + updatesPending = new LinkedList>(); + + internalId = id; + setParent(parentObj); + + // No values are available yet + valueMap = new HashMap(); + resetValues(); + } + + public VariableObjectId getInternalId() { return internalId; } + public String getGdbName() { return gdbName; } + public String getCurrentFormat() { return format; } + public MIVariableObject getParent() { return parent; } + public MIRootVariableObject getRootToUpdate() { return rootToUpdate; } + + public String getExpression() { return fullExp; } + public String getType() { return type; } + public int getNumChildren() { return numChildren; } + public String getValue(String format) { return valueMap.get(format); } + + public ExpressionInfo[] getChildren() { return children; } + + + //FIX replace these methods with CDT's GDBTypeParser (see bug 200897 comment #5) + // int(*)[5] is a pointer to an array (so it is a pointer but not an array) (e.g., &b where int b[5]) + // int *[5] is an array of pointers (so it is an array but not a pointer) (e.g., int *b[5]) + public boolean isArray() { return (getType() == null) ? false : getType().endsWith("]") && !getType().contains("(*)") ; }//$NON-NLS-1$//$NON-NLS-2$ + public boolean isPointer() { return (getType() == null) ? false : getType().contains("*")&& !isArray(); }//$NON-NLS-1$ + public boolean isMethod() { return (getType() == null) ? false : getType().contains("()"); }//$NON-NLS-1$ + // A complex variable is one with children. However, it must not be a pointer since a pointer has one child + // according to GDB, but is still a 'simple' variable + public boolean isComplex() { return (getType() == null) ? false : getNumChildren() > 0 && !isPointer(); } + + public void setGdbName(String n) { gdbName = n; } + public void setCurrentFormat(String f) { format = f; } + + public void setExpressionData(String fullExpression, String t, int num) { + fullExp = fullExpression; + type = t; + numChildren = num; + } + + public void setValue(String format, String val) { valueMap.put(format, val); } + + public void resetValues(String valueInCurrentFormat) { + resetValues(); + setValue(getCurrentFormat(), valueInCurrentFormat); + } + + public void resetValues() { + valueMap.put(IFormattedValues.NATURAL_FORMAT, null); + valueMap.put(IFormattedValues.BINARY_FORMAT, null); + valueMap.put(IFormattedValues.HEX_FORMAT, null); + valueMap.put(IFormattedValues.OCTAL_FORMAT, null); + valueMap.put(IFormattedValues.DECIMAL_FORMAT, null); + } + + public void setChildren(ExpressionInfo[] c) { children = c; } + public void setParent(MIVariableObject p) { + parent = p; + rootToUpdate = (p == null ? (MIRootVariableObject)this : p.getRootToUpdate()); + } + + public void executeWhenNotUpdating(RequestMonitor rm) { + getRootToUpdate().executeWhenNotUpdating(rm); + } + + private void lock() { + locked = true; + } + + private void unlock() { + locked = false; + + while (operationsPending.size() > 0) { + operationsPending.poll().done(); + } + } + + public boolean isOutOfScope() { return outOfScope; } + + /** + * This method updates the variable object. + * Updating a variable object is done by updating its root. + */ + public void update(final DataRequestMonitor rm) { + + // We check to see if we are already out-of-scope + // We must do this to avoid the risk of re-creating this object + // twice, due to race conditions + if (isOutOfScope()) { + rm.setData(false); + rm.done(); + } else if (currentState == STATE_UPDATING) { + // If we were already updating, we just queue the request monitor + // until the on-going update finishes. + updatesPending.add(rm); + } else { + currentState = STATE_UPDATING; + getRootToUpdate().update(new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + currentState = STATE_READY; + + if (isSuccess()) { + outOfScope = getRootToUpdate().isOutOfScope(); + // This request monitor is the one that should re-create + // the variable object if the old one was out-of-scope + rm.setData(outOfScope); + rm.done(); + + // All the other request monitors must be notified but must + // not re-create the object, even if it is out-of-scope + while (updatesPending.size() > 0) { + DataRequestMonitor pendingRm = updatesPending.poll(); + pendingRm.setData(false); + pendingRm.done(); + } + } else { + rm.setStatus(getStatus()); + rm.done(); + + while (updatesPending.size() > 0) { + DataRequestMonitor pendingRm = updatesPending.poll(); + pendingRm.setStatus(getStatus()); + pendingRm.done(); + } + } + } + }); + } + } + + /** + * Variable objects need not be deleted unless they are root. + * This method is specialized in the MIRootVariableObject class. + */ + public void deleteInGdb() {} + + /** + * This method returns the value of the variable object attributes by + * using -var-show-attributes. + * Currently, the only attribute available is 'editable'. + * + * @param rm + * The data request monitor that will hold the value returned + */ + private void getAttributes(final DataRequestMonitor rm) { + if (editable != null) { + rm.setData(editable); + rm.done(); + } else if (isComplex()) { + editable = false; + rm.setData(editable); + rm.done(); + } else { + fCommandControl.queueCommand( + new MIVarShowAttributes(getRootToUpdate().getControlDMContext(), getGdbName()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + editable = getData().isEditable(); + + rm.setData(editable); + rm.done(); + } + }); + } + } + + /** + * This method returns the value of the variable object. + * This operation translates to multiple MI commands which affect the state of the + * variable object in the back-end; therefore, we must make sure the object is not + * locked doing another operation, and we must lock the object once it is our turn + * to use it. + * + * @param dmc + * The context containing the format to be used for the evaluation + * @param rm + * The data request monitor that will hold the value returned + */ + private void getValue(final FormattedValueDMContext dmc, + final DataRequestMonitor rm) { + + // We might already know the value + String value = getValue(dmc.getFormatID()); + if (value != null) { + rm.setData(new FormattedValueDMData(value)); + rm.done(); + return; + } + + // If the variable is a complex structure, there is no need to ask the back-end for a value, + // we can give it the {...} ourselves + // Unless we are dealing with an array, in which case, we want to get the address of it + if (isComplex()) { + if (isArray()) { + // Figure out the address + IExpressionDMContext exprCxt = DMContexts.getAncestorOfType(dmc, IExpressionDMContext.class); + IExpressionDMContext addrCxt = fExpressionService.createExpression(exprCxt, "&(" + exprCxt.getExpression() + ")"); //$NON-NLS-1$//$NON-NLS-2$ + + final FormattedValueDMContext formatCxt = new FormattedValueDMContext( + fSession.getId(), + addrCxt, + dmc.getFormatID() + ); + + getVariable( + addrCxt, + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + getData().getValue(formatCxt, rm); + + } + }); + } else { + // Other complex structure + String complexValue = "{...}"; //$NON-NLS-1$ + setValue(dmc.getFormatID(), complexValue); + rm.setData(new FormattedValueDMData(complexValue)); + rm.done(); + } + + return; + } + + if (locked) { + operationsPending.add(new RequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + getValue(dmc, rm); + } + }); + } else { + lock(); + + // If the format is already the one set for this variable object, + // we don't need to set it again + if (dmc.getFormatID().equals(getCurrentFormat())) { + evaluate(rm); + } else { + // We must first set the new format and then evaluate the variable + fCommandControl.queueCommand( + new MIVarSetFormat(getRootToUpdate().getControlDMContext(), getGdbName(), dmc.getFormatID()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + setCurrentFormat(dmc.getFormatID()); + + // If set-format returned the value, no need to evaluate + // This is only valid after GDB 6.7 + if (getData().getValue() != null) { + setValue(dmc.getFormatID(), getData().getValue()); + rm.setData(new FormattedValueDMData(getData().getValue())); + rm.done(); + + // Unlock is done within this method + resetFormatToNatural(); + } else { + evaluate(rm); + } + } else { + rm.setStatus(getStatus()); + rm.done(); + + unlock(); + } + } + }); + } + } + } + + /** + * This method evaluates a variable object + */ + private void evaluate(final DataRequestMonitor rm) { + fCommandControl.queueCommand( + new MIVarEvaluateExpression(getRootToUpdate().getControlDMContext(), getGdbName()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + setValue(getCurrentFormat(), getData().getValue()); + rm.setData(new FormattedValueDMData(getData().getValue())); + rm.done(); + } else { + rm.setStatus(getStatus()); + rm.done(); + } + + // Unlock is done within this method + resetFormatToNatural(); + } + }); + } + + // In GDB, var-update will only report a change if -var-evaluate-expression has + // changed -- in the current format--. This means that situations like + // double z = 1.2; + // z = 1.4; + // Will not report a change if the format is anything else than natural. + // This is because 1.2 and 1.4 are both printed as 1, 0x1, etc + // Since we cache the values of every format, we must know if -any- format has + // changed, not just the current one. + // To solve this, we always do an update in the natural format; I am not aware + // of any case where the natural format would stay the same, but another format + // would change. However, since a var-update update all children as well, + // we must make sure these children are also in the natural format + // The simplest way to do this is that whenever we change the format + // of a variable object, we immediately set it back to natural with a second + // var-set-format command. + private void resetFormatToNatural() { + if (!getCurrentFormat().equals(IFormattedValues.NATURAL_FORMAT)) { + fCommandControl.queueCommand( + new MIVarSetFormat(getRootToUpdate().getControlDMContext(), getGdbName(), IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + setCurrentFormat(IFormattedValues.NATURAL_FORMAT); + } + unlock(); + } + }); + } else { + unlock(); + } + } + + /** + * This method returns the list of children of the variable object passed as a parameter. + * + * @param rm + * The data request monitor that will hold the children returned + */ + private void getChildren(final MIExpressionDMC exprDmc, final DataRequestMonitor rm) { + + // If we already know the children, no need to go to the back-end + ExpressionInfo[] childrenArray = getChildren(); + if (childrenArray != null) { + rm.setData(childrenArray); + rm.done(); + return; + } + + // If the variable does not have children, we can return an empty list right away + if (getNumChildren() == 0) { + // First store the empty list, for the next time + setChildren(new ExpressionInfo[0]); + rm.setData(getChildren()); + rm.done(); + return; + } + + // For arrays (which could be very large), we create the children ourselves. This is + // to avoid creating an enormous amount of children variable objects that the view may + // never need. Using -var-list-children will create a variable object for every child + // immediately, that is why won't don't want to use it for arrays. + if (isArray()) { + ExpressionInfo[] childrenOfArray = new ExpressionInfo[getNumChildren()]; + for (int i= 0; i < childrenOfArray.length; i++) { + String indexStr = "[" + i + "]";//$NON-NLS-1$//$NON-NLS-2$ + String fullExpr = exprDmc.getExpression() + indexStr; + String relExpr = exprDmc.getRelativeExpression() + indexStr; + + childrenOfArray[i] = new ExpressionInfo(fullExpr, relExpr); + } + + // First store these children, for the next time + setChildren(childrenOfArray); + rm.setData(getChildren()); + rm.done(); + return; + } + + // No need to wait for the object to be ready since listing children can be performed + // at any time, as long as the object is created, which we know it is, since we can only + // be called here with a fully created object. + // Also no need to lock the object, since getting the children won't affect other operations + fCommandControl.queueCommand( + new MIVarListChildren(getRootToUpdate().getControlDMContext(), getGdbName()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + MIVar[] children = getData().getMIVars(); + final List realChildren = new ArrayList(); + + final CountingRequestMonitor countingRm = new CountingRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + // Store the children in our variable object cache + setChildren(realChildren.toArray(new ExpressionInfo[realChildren.size()])); + rm.setData(getChildren()); + rm.done(); + } + }; + + int numSubRequests = 0; + for (final MIVar child : children) { + // These children get created automatically as variable objects in GDB, so we should + // add them to the LRU. + // Note that if this variable object already exists, we can be in three scenarios: + // 1- the existing object is the same variable object in GDB. In this case, + // the existing and new one are identical so we can keep either one. + // 2- the existing object is out-of-scope and should be replaced by the new one. + // This can happen if a root was found to be out-of-scope, but this child + // had not been accessed and therefore had not been removed. + // 3- the existing object is an in-scope root object representing the same expression. + // In this case, either object can be kept and the other one can be deleted. + // The existing root could currently be in-use by another operation and may + // not be deleted; but since we can only have one entry in the LRU, we are + // forced to keep the existing root. Note that we need not worry about + // the newly created child since it will automatically be deleted when + // its root is deleted. + + numSubRequests++; + + final DataRequestMonitor childPathRm = + new DataRequestMonitor(fSession.getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + String childFullExpression = getData(); + + // For children that do not map to a real expression (such as f.public) + // GDB returns an empty string. In this case, we can use another unique + // name, such as the variable name + boolean fakeChild = false; + if (childFullExpression.length() == 0) { + fakeChild = true; + childFullExpression = child.getVarName(); + } + + // Now try to see if we already have this variable object in our Map + // Since our map names use the expression, and not the GDB given + // name, we must determine the correct map name from the varName + VariableObjectId childId = new VariableObjectId(); + childId.generateId(childFullExpression, getInternalId()); + MIVariableObject childVar = lruVariableList.get(childId); + + // Note that we must check the root to know if it is out-of-scope. + // We cannot check the child as it has not be updated and its + // outOfScope variable is not updated either. + if (childVar != null && childVar.getRootToUpdate().isOutOfScope()) { + childVar.deleteInGdb(); + childVar = null; + } + + if (childVar == null) { + childVar = new MIVariableObject(childId, MIVariableObject.this); + childVar.setGdbName(child.getVarName()); + childVar.setExpressionData( + childFullExpression, + child.getType(), + child.getNumChild()); + + // This will replace any existing entry + lruVariableList.put(childId, childVar); + + // Is this new child a modifiable descendant of the root? + if (childVar.isModifiable()) { + getRootToUpdate().addModifiableDescendant(child.getVarName(), childVar); + } + } + + if (fakeChild) { + // This is just a qualifier level of C++, and we must + // get the children of this child to get the real children + childVar.getChildren( + exprDmc, + new DataRequestMonitor(fSession.getExecutor(), countingRm) { + @Override + protected void handleSuccess() { + ExpressionInfo[] vars = getData(); + for (ExpressionInfo realChild : vars) { + realChildren.add(realChild); + } + countingRm.done(); + } + }); + } else { + // This is a real child + realChildren.add(new ExpressionInfo(childFullExpression, child.getExp())); + countingRm.done(); + } + } + }; + + + if (isAccessQualifier(child.getExp())) { + // This is just a qualifier level of C++, so we don't need + // to call -var-info-path-expression for real, but just pretend we did. + childPathRm.setData(""); //$NON-NLS-1$ + childPathRm.done(); + } else { + // To build the child id, we need the fully qualified expression which we + // can get from -var-info-path-expression starting from GDB 6.7 + fCommandControl.queueCommand( + new MIVarInfoPathExpression(getRootToUpdate().getControlDMContext(), child.getVarName()), + new DataRequestMonitor(fSession.getExecutor(), childPathRm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + childPathRm.setData(getData().getFullExpression()); + } else { + // If we don't have var-info-path-expression + // build the expression ourselves + // Note that this does not work well yet + childPathRm.setData(buildChildExpression(exprDmc.getExpression(), child.getExp())); + } + childPathRm.done(); + } + }); + } + } + + countingRm.setDoneCount(numSubRequests); + } + }); + } + + /** + * This method builds a child expression based on its parent's expression. + * It is a fallback solution for when GDB doesn't support the var-info-path-expression. + * + * Currently, this does not support inherited class such as + * class foo : bar { + * ... + * } + * because we'll create foo.bar instead of (bar)foo. + */ + private String buildChildExpression(String parentExp, String childExp) { + // For pointers, the child expression is already contained in the parent, + // so we must simply prefix with * + // See Bug219179 for more information. + if (isPointer()) { + return "*("+parentExp+")"; //$NON-NLS-1$//$NON-NLS-2$ + } + + return parentExp + "." + childExp; //$NON-NLS-1$ + // No need for a special case for arrays since we deal with arrays differently + // and don't call this method for them + } + + /** + * This method returns the count of children of the variable object passed as a parameter. + * + * @param rm + * The data request monitor that will hold the count of children returned + */ + private void getChildrenCount(final DataRequestMonitor rm) { + // No need to lock the object or wait for it to be ready since this operation does not + // affect other operations + rm.setData(getNumChildren()); + rm.done(); + } + + + + /** + * This method request the back-end to change the value of the variable object. + * + * @param value + * The new value. + * @param formatId + * The format the new value is specified in. + * @param rm + * The request monitor to indicate the operation is finished + */ + private void writeValue(String value, String formatId, final RequestMonitor rm) { + + // If the variable is a complex structure (including an array), then we cannot write to it + if (isComplex()) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, + "Cannot change the value of a complex expression", null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // First deal with the format. For GDB, the way to specify a format is to prefix the value with + // 0x for hex, 0 for octal etc So we need to make sure that 'value' has this prefix. + // Note that there is no way to specify a binary format for GDB up to and including + // GDB 6.7.1, so we convert 'value' into a decimal format. + // If the formatId is NATURAL, we do nothing for now because it is more complicated. + // For example for a bool, a value of "true" is correct and should be left as is, + // but for a pointer a value of 16 should be sent to GDB as 0x16. To figure this out, + // we need to know the type of the variable, which we don't have yet. + + if (formatId.equals(IFormattedValues.HEX_FORMAT)) { + if (!value.startsWith("0x")) value = "0x" + value; //$NON-NLS-1$ //$NON-NLS-2$ + } + else if (formatId.equals(IFormattedValues.OCTAL_FORMAT)) { + if (!value.startsWith("0")) value = "0" + value; //$NON-NLS-1$ //$NON-NLS-2$ + } + else if (formatId.equals(IFormattedValues.BINARY_FORMAT)) { + // convert from binary to decimal + if (value.startsWith("0b")) value = value.substring(2, value.length()); //$NON-NLS-1$ + try { + value = Integer.toString(Integer.parseInt(value, 2)); + } catch (NumberFormatException e) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Invalid binary number: " + value, e)); //$NON-NLS-1$ + rm.done(); + return; + } + + formatId = IFormattedValues.DECIMAL_FORMAT; + } + else if (formatId.equals(IFormattedValues.DECIMAL_FORMAT)) { + // nothing to do + } + else if (formatId.equals(IFormattedValues.NATURAL_FORMAT)) { + // we do nothing for now and let the user have put in the proper value + } + else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Unknown format: " + formatId, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // If the value has not changed, no need to set it. + // Return a warning status so that handleSuccess is not called and we don't send + // an ExpressionChanged event + if (value.equals(getValue(formatId))) { + rm.setStatus(new Status(IStatus.WARNING, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, + "Setting to the same value of: " + value, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + // No need to be in ready state or to lock the object + fCommandControl.queueCommand( + new MIVarAssign(getRootToUpdate().getControlDMContext(), getGdbName(), value), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + // We must also mark all variable objects + // as out-of-date. This is because some variable objects may be affected + // by this one having changed. + // e.g., + // int i; + // int* pi = &i; + // Here, if 'i' is changed by the user, then 'pi' will also change + // Since there is no way to know this unless we keep track of all addresses, + // we must mark everything as out-of-date. See bug 213061 + markAllOutOfDate(); + + // Useless since we just marked everything as out-of-date + // resetValues(getData().getValue()); + + rm.done(); + } + }); + } + + private boolean isAccessQualifier(String str) { + return str.equals("private") || str.equals("public") || str.equals("protected"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public boolean isModifiable() { + if (!isComplex()) return true; + return false; + } + } + + private class MIRootVariableObject extends MIVariableObject { + + // Only root variables go through the GDB creation process + protected static final int STATE_NOT_CREATED = 10; + protected static final int STATE_CREATING = 11; + + // The control context within which this variable object was created + // It only needs to be stored in the Root VarObj since any children + // will have the same control context + private ICommandControlDMContext fControlContext = null; + + private boolean outOfDate = false; + + // Modifiable descendants are any variable object that is a descendant or itself for + // which the value can change. + private Map modifiableDescendants; + + public MIRootVariableObject(VariableObjectId id) { + super(id, null); + currentState = STATE_NOT_CREATED; + modifiableDescendants = new HashMap(); + } + + public ICommandControlDMContext getControlDMContext() { return fControlContext; } + + public boolean isUpdating() { return currentState == STATE_UPDATING; } + + public void markAsOutOfDate() { outOfDate = true; } + + // Remember that we must add ourself as a modifiable descendant if our value can change + public void addModifiableDescendant(String gdbName, MIVariableObject descendant) { + modifiableDescendants.put(gdbName, descendant); + } + + public void processChanges(MIVarChange[] updates) { + for (MIVarChange update : updates) { + MIVariableObject descendant = modifiableDescendants.get(update.getVarName()); + // Descendant should never be null, but just to be safe + if (descendant != null) descendant.resetValues(update.getValue()); + } + } + + public void create(final IExpressionDMContext exprCtx, + final RequestMonitor rm) { + + if (currentState == STATE_NOT_CREATED) { + + currentState = STATE_CREATING; + fControlContext = DMContexts.getAncestorOfType(exprCtx, ICommandControlDMContext.class); + + fCommandControl.queueCommand( + new MIVarCreate(exprCtx, exprCtx.getExpression()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + setGdbName(getData().getName()); + setExpressionData( + exprCtx.getExpression(), + getData().getType(), + getData().getNumChildren()); + + // Store the value returned at create (available in GDB 6.7) + // Don't store if it is an array, since we want to show + // the address of an array as its value + if (getData().getValue() != null && !isArray()) { + setValue(getCurrentFormat(), getData().getValue()); + } + + // If we are modifiable, we should be in our modifiable list + if (isModifiable()) { + addModifiableDescendant(getData().getName(), MIRootVariableObject.this); + } + } else { + rm.setStatus(getStatus()); + } + + rm.done(); + } + }); + } else { + assert false; + } + } + + private void creationCompleted(boolean success) { + // A creation completed we must be up-to-date, so we + // can tell any pending monitors that updates are done + if (success) { + currentState = STATE_READY; + while (updatesPending.size() > 0) { + DataRequestMonitor rm = updatesPending.poll(); + // Nothing to be re-created + rm.setData(false); + rm.done(); + } + } else { + currentState = STATE_NOT_CREATED; + + // Creation failed, inform anyone waiting. + while (updatesPending.size() > 0) { + RequestMonitor rm = updatesPending.poll(); + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Unable to create variable object", null)); //$NON-NLS-1$ + rm.done(); + } + } + } + + @Override + public void update(final DataRequestMonitor rm) { + + if (isOutOfScope()) { + rm.setData(false); + rm.done(); + } else if (currentState != STATE_READY) { + // Object is not fully created or is being updated + // so add RequestMonitor to pending queue + updatesPending.add(rm); + } else if (outOfDate == false) { + rm.setData(false); + rm.done(); + } else { + // Object needs to be updated in the back-end + currentState = STATE_UPDATING; + + // In GDB, var-update will only report a change if -var-evaluate-expression has + // changed -- in the current format--. This means that situations like + // double z = 1.2; + // z = 1.4; + // Will not report a change if the format is anything else than natural. + // This is because 1.2 and 1.4 are both printed as 1, 0x1, etc + // Since we cache the values of every format, we must know if -any- format has + // changed, not just the current one. + // To solve this, we always do an update in the natural format; I am not aware + // of any case where the natural format would stay the same, but another format + // would change. However, since a var-update update all children as well, + // we must make sure these children are also in the natural format + // The simplest way to do this is that whenever we change the format + // of a variable object, we immediately set it back to natural with a second + // var-set-format command. This is done in the getValue() method + fCommandControl.queueCommand( + new MIVarUpdate(getRootToUpdate().getControlDMContext(), getGdbName()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + currentState = STATE_READY; + + if (isSuccess()) { + outOfDate = false; + + MIVarChange[] changes = getData().getMIVarChanges(); + if (changes.length > 0 && changes[0].isInScope() == false) { + // Object is out-of-scope + outOfScope = true; + + // We can delete this root in GDB right away. This is safe, even + // if the root has children, because they are also out-of-scope. + // We -must- also remove this entry from our LRU. If we don't + // we can end-up with a race condition that create this object + // twice, or have an infinite loop while never re-creating the object. + // The can happen if we update a child first then we request + // the root later, + lruVariableList.remove(getInternalId()); + + rm.setData(true); + rm.done(); + } else { + // The root object is now up-to-date, we must parse the changes, if any. + processChanges(changes); + + // We only mark this root as updated in our list if it is in-scope. + // For out-of-scope object, we don't ever need to re-update them so + // we don't need to add them to this list. + updatedRootList.add(MIRootVariableObject.this); + + rm.setData(false); + rm.done(); + } + + while (updatesPending.size() > 0) { + DataRequestMonitor pendingRm = updatesPending.poll(); + pendingRm.setData(false); + pendingRm.done(); + } + } else { + // We were not able to update for some reason + rm.setData(false); + rm.done(); + + while (updatesPending.size() > 0) { + DataRequestMonitor pendingRm = updatesPending.poll(); + pendingRm.setStatus(getStatus()); + pendingRm.done(); + } + } + } + }); + } + } + + /** + * This method request the back-end to delete a variable object. + * We check if the GDB name has been filled to confirm that this object + * was actually successfully created on the back-end. + * Only root variable objects are deleted, while children are left in GDB + * to be deleted automatically when their root is deleted. + */ + @Override + public void deleteInGdb() { + if (getGdbName() != null) { + fCommandControl.queueCommand( + new MIVarDelete(getRootToUpdate().getControlDMContext(), getGdbName()), + new DataRequestMonitor(fSession.getExecutor(), null)); + // Nothing to do in the requestMonitor, since the object was already + // removed from our list before calling this method. + + // Set the GDB name to null to make sure we don't attempt to delete + // this variable a second time. This can happen if the LRU triggers + // an automatic delete. + setGdbName(null); + } else { + // Variable was never created or was already deleted, no need to do anything. + } + } + + + } + + /** + * This class represents an unique identifier for a variable object. + * + * The following must be considered to obtain a unique name: + * - the expression itself + * - the execution context + * - relative depth of frame based on the frame context and the total depth of the stack + * + * Note that if no frameContext is specified (only Execution, or even only Container), which can + * characterize a global variable for example, we will only use the available information. + */ + private class VariableObjectId { + // We don't use the expression context because it is not safe to compare them + // See bug 187718. So we store the expression itself, and it's parent execution context. + String fExpression = null; + IExecutionDMContext fExecContext = null; + // We need the depth of the frame. The frame level is not sufficient because + // the same frame will have a different level based on the current depth of the stack + Integer fFrameId = null; + + @Override + public boolean equals(Object other) { + if (other instanceof VariableObjectId) { + VariableObjectId otherId = (VariableObjectId) other; + return (fExpression == null ? otherId.fExpression == null : fExpression.equals(otherId.fExpression)) && + (fExecContext == null ? otherId.fExecContext == null : fExecContext.equals(otherId.fExecContext)) && + (fFrameId == null ? otherId.fFrameId == null : fFrameId.equals(otherId.fFrameId)); + } + return false; + } + + @Override + public int hashCode() { + return (fExpression == null ? 0 : fExpression.hashCode()) + + (fExecContext == null ? 0 : fExecContext.hashCode()) + + (fFrameId == null ? 0 : fFrameId.hashCode()); + } + + public void generateId(IExpressionDMContext exprCtx, final RequestMonitor rm) { + fExpression = exprCtx.getExpression(); + + fExecContext = DMContexts.getAncestorOfType(exprCtx, IExecutionDMContext.class); + if (fExecContext == null) { + rm.done(); + return; + } + + final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); + if (frameCtx == null) { + rm.done(); + return; + } + + // We need the current stack depth to be able to make a unique and reproducible name + // for this expression. This is pretty efficient since the stackDepth will be retrieved + // from the StackService command cache after the first time. + fStackService.getStackDepth( + fExecContext, 0, + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + fFrameId = new Integer(getData() - frameCtx.getLevel()); + rm.done(); + } + }); + } + + public void generateId(String childFullExp, VariableObjectId parentId) { + // The execution context and the frame depth are the same as the parent + fExecContext = parentId.fExecContext; + fFrameId = parentId.fFrameId; + // The expression here must be the one that is part of IExpressionContext for this child + // This will allow us to find a variable object directly + fExpression = childFullExp; + } + } + + /** + * This is the real work horse of managing our objects. Not only must every + * value be unique to get inserted, this also creates an LRU (least recently + * used). When we hit our size limitation, the LRUsed will be removed to + * make space. Removing means that a GDB request to delete the object is + * generated. We must also take into consideration the fact that GDB will + * automatically delete children of a variable object, when deleting the parent + * variable object. Our solution to that is to tweak the LRU to make sure that + * children are always older than their parents, to guarantee the children will + * always be delete before their parents. + * + */ + private class LRUVariableCache extends LinkedHashMap { + public static final long serialVersionUID = 0; + + // Maximum allowed concurrent variables + private static final int MAX_VARIABLE_LIST = 1000; + + public LRUVariableCache() { + super(0, // Initial load capacity + 0.75f, // Load factor as defined in JAVA 1.5 + true); // Order is dictated by access, not insertion + } + + // We never remove doing put operations. Instead, we rely on our get() operations + // to trigger the remove. See bug 200897 + @Override + public boolean removeEldestEntry(Map.Entry eldest) { + return false; + } + + @Override + public MIVariableObject get(Object key) { + MIVariableObject varObj = super.get(key); + touchAncestors(varObj); + + // If we're over our max size, attempt to remove eldest entry. + if (size() > MAX_VARIABLE_LIST) { + Map.Entry eldest = entrySet().iterator().next(); + // First make sure we are not deleting ourselves! + if (!eldest.getValue().equals(varObj)) { + if (eldest.getValue().currentState == MIVariableObject.STATE_READY) { + remove(eldest.getKey()); + } + } + } + return varObj; + } + + private void touchAncestors(MIVariableObject varObj) { + while (varObj != null) { + varObj = varObj.getParent(); + // If there is a parent, touch it + if (varObj != null) super.get(varObj.getInternalId()); + } + } + + @Override + public MIVariableObject put(VariableObjectId key, MIVariableObject varObj) { + MIVariableObject retVal = super.put(key, varObj); + + // Touch all parents of this element so as + // to guarantee they are not deleted before their children. + touchAncestors(varObj); + + return retVal; + } + + @Override + public MIVariableObject remove(Object key) { + MIVariableObject varObj = super.remove(key); + varObj.deleteInGdb(); + return varObj; + } + } + + private final DsfSession fSession; + + /** Provides access to the GDB/MI back-end */ + private final ICommandControl fCommandControl; + // The stack service needs to be used to get information such + // as the stack depth to differentiate between expressions that have the + // same name but refer to a different context + private final IStack fStackService; + private IExpressions fExpressionService; + + // Typically, there will only be one listener, since only the ExpressionService will use this class + private final List fCommandProcessors = new ArrayList(); + + /** Our least recently used cache */ + private final LRUVariableCache lruVariableList; + + /** The list of root variable objects that have been updated */ + private final LinkedList updatedRootList = new LinkedList(); + + /** + * MIVariableManager constructor + * + * @param session + * The session we are working with + * @param tracker + * The service tracker that can be used to find other services + */ + public MIVariableManager(DsfSession session, DsfServicesTracker tracker) { + fSession = session; + lruVariableList = new LRUVariableCache(); + fCommandControl = tracker.getService(ICommandControl.class); + fStackService = tracker.getService(IStack.class); + fExpressionService = tracker.getService(IExpressions.class); + + // Register to receive service events for this session. + fSession.addServiceEventListener(this, null); + } + + public void dispose() { + fSession.removeServiceEventListener(this); + } + + /** + * This method returns a variable object based on the specified + * ExpressionDMC, creating it in GDB if it was not created already. + * The method guarantees that the variable is finished creating and that + * is it not out-of-scope. + * + * @param exprCtx + * The expression context to which the variable object is applied to. + * + * @param rm + * The data request monitor that will contain the requested variable object + */ + private void getVariable(final IExpressionDMContext exprCtx, + final DataRequestMonitor rm) { + // Generate an id for this expression so that we can determine if we already + // have a variable object tracking it. If we don't we'll need to create one. + final VariableObjectId id = new VariableObjectId(); + id.generateId( + exprCtx, + new RequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + getVariable(id, exprCtx, rm); + } + }); + } + + private void getVariable(final VariableObjectId id, + final IExpressionDMContext exprCtx, + final DataRequestMonitor rm) { + + final MIVariableObject varObj = lruVariableList.get(id); + + if (varObj == null) { + // We do not have this varObject, so we create it + createVariable(id, exprCtx, rm); + } else { + // We have found a varObject, but it may not be updated yet. + // Updating the object will also tell us if it is out-of-scope + // and if we should re-create it. + varObj.update(new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + + boolean shouldCreateNew = getData().booleanValue(); + + if (varObj.isOutOfScope()) { + // The variable object is out-of-scope and we + // should not use it. + if (shouldCreateNew) { + /** + * It may happen that when accessing a varObject we find it to be + * out-of-scope. The expression for which we are trying to access a varObject + * could still be valid, and therefore we should try to create a new varObject for + * that expression. This can happen for example if two methods use the same name + * for a variable. In the case when we find that a varObject is out-of-scope (when + * its root is out-of-scope) the following should be done: + * + * - create a new varObject for the expression (as a root varObject) and insert it + * in the LRU. Make sure that when creating children of this new varObject, they + * will replace any old children with the same name in the LRU (this is ok since the + * children being replaced are also out-of-scope). + */ + + createVariable(id, exprCtx, rm); + } else { + // Just request the variable object again + // We must use this call to handle the fact that + // the new object might be in the middle of being + // created. + getVariable(id, exprCtx, rm); + } + } else { + // The variable object is up-to-date and valid + rm.setData(varObj); + rm.done(); + } + } + }); + } + } + + + + /** + * This method creates a variable object in GDB. + */ + private void createVariable(final VariableObjectId id, + final IExpressionDMContext exprCtx, + final DataRequestMonitor rm) { + + // Variable objects that are created directly like this, are considered ROOT variable objects + // in comparison to variable objects that are children of other variable objects. + final MIRootVariableObject newVarObj = new MIRootVariableObject(id); + + // We must put this object in our map right away, in case it is + // requested again, before it completes its creation. + // Note that this will replace any old entry with the same id. + lruVariableList.put(id, newVarObj); + + newVarObj.create(exprCtx, new RequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + // Also store the object as a varObj that is up-to-date + updatedRootList.add(newVarObj); + // VarObj can now be used by others + newVarObj.creationCompleted(true); + + rm.setData(newVarObj); + rm.done(); + } else { + // Object was not created, remove it from our list + lruVariableList.remove(id); + // Tell any waiting monitors that creation failed. + // It is important to do this call after we have removed the id + // from our LRU; this is to avoid the following: + // The same varObj is requested before it was removed from the LRU + // but after we called creationCompleted(). + // In this case, the request for this varObj would be queued, but + // since creationCompleted() already sent the notifications + // the newly queue request will never get serviced. + // We avoid this race condition by sending the notifications _after_ removing + // the object from the LRU, to avoid any new requests being queue. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=231655 + newVarObj.creationCompleted(false); + + rm.setStatus(getStatus()); + rm.done(); + } + } + }); + } + + /** + * This method requests the back-end to change the value of an expression. + * + * @param expressionContext + * The context of the expression we want to change + * @param expressionValue + * The new value of the expression + * @param formatId + * The format in which the expressionValue is specified in + * @param rm + * The request monitor to indicate the operation is finished + */ + // This method can be called directly from the ExpressionService, since it cannot be cached + public void writeValue(final IExpressionDMContext ctx, final String expressionValue, + final String formatId, final RequestMonitor rm) { + + getVariable( + ctx, + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + getData().writeValue(expressionValue, formatId, rm); + } + }); + } + + public ICommandToken queueCommand(final ICommand command, DataRequestMonitor rm) { + + final ICommandToken token = new ICommandToken() { + public ICommand getCommand() { + return command; + } + }; + + // The MIVariableManager does not buffer commands itself, but sends them directly to the real + // MICommandControl service. Therefore, we must immediately tell our calling cache that the command + // has been sent, since we can never cancel it. Note that this removes any option of coalescing, + // but coalescing was not applicable to variableObjects anyway. + processCommandSent(token); + + if (command instanceof ExprMetaGetVar) { + @SuppressWarnings("unchecked") + final DataRequestMonitor drm = (DataRequestMonitor)rm; + final MIExpressionDMC exprCtx = (MIExpressionDMC)(command.getContext()); + + getVariable( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData( + new ExprMetaGetVarInfo( + exprCtx.getRelativeExpression(), + getData().getNumChildren(), + getData().getType(), + !getData().isComplex())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } else if (command instanceof ExprMetaGetAttributes) { + @SuppressWarnings("unchecked") + final DataRequestMonitor drm = (DataRequestMonitor)rm; + final IExpressionDMContext exprCtx = (IExpressionDMContext)(command.getContext()); + + getVariable( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + getData().getAttributes( + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData(new ExprMetaGetAttributesInfo(getData())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } + }); + + + } else if (command instanceof ExprMetaGetValue) { + @SuppressWarnings("unchecked") + final DataRequestMonitor drm = (DataRequestMonitor)rm; + final FormattedValueDMContext valueCtx = (FormattedValueDMContext)(command.getContext()); + final IExpressionDMContext exprCtx = DMContexts.getAncestorOfType(valueCtx, IExpressionDMContext.class); + + getVariable( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + getData().getValue( + valueCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData( + new ExprMetaGetValueInfo(getData().getFormattedValue())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } + }); + + } else if (command instanceof ExprMetaGetChildren) { + @SuppressWarnings("unchecked") + final DataRequestMonitor drm = (DataRequestMonitor)rm; + final MIExpressionDMC exprCtx = (MIExpressionDMC)(command.getContext()); + + getVariable( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + getData().getChildren( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData(new ExprMetaGetChildrenInfo(getData())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } + }); + + } else if (command instanceof ExprMetaGetChildCount) { + @SuppressWarnings("unchecked") + final DataRequestMonitor drm = (DataRequestMonitor)rm; + final IExpressionDMContext exprCtx = (IExpressionDMContext)(command.getContext()); + + getVariable( + exprCtx, + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + getData().getChildrenCount( + new DataRequestMonitor(fSession.getExecutor(), drm) { + @Override + protected void handleSuccess() { + drm.setData(new ExprMetaGetChildCountInfo(getData())); + drm.done(); + processCommandDone(token, drm.getData()); + } + }); + } + }); + + } else if (command instanceof MIDataEvaluateExpression) { + // This does not use the variable objects but sends the command directly to the back-end + fCommandControl.queueCommand(command, rm); + } else { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + "Unexpected Expression Meta command", null)); //$NON-NLS-1$ + rm.done(); + } + return token; + } + + /* + * This is the command which allows the user to retract a previously issued command. The + * state of the command is that it is in the waiting queue and has not yet been handed + * to the back-end yet. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#removeCommand(org.eclipse.cdt.dsf.mi.service.command.commands.ICommand) + */ + public void removeCommand(ICommandToken token) { + // It is impossible to remove a command from the MIVariableManager. + // This should never be called, if we did things right. + assert false; + } + + /* + * This command allows the user to try and cancel commands which have been handed off to the + * back-end. Some back-ends support this with extended GDB/MI commands. If the support is there + * then we will attempt it. Because of the bidirectional nature of the GDB/MI command stream + * there is no guarantee that this will work. The response to the command could be on its way + * back when the cancel command is being issued. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#cancelCommand(org.eclipse.cdt.dsf.mi.service.command.commands.ICommand) + */ + public void addCommandListener(ICommandListener processor) { fCommandProcessors.add(processor); } + public void removeCommandListener(ICommandListener processor) { fCommandProcessors.remove(processor); } + public void addEventListener(IEventListener processor) {} + public void removeEventListener(IEventListener processor) {} + + + private void processCommandSent(ICommandToken token) { + for (ICommandListener processor : fCommandProcessors) { + processor.commandSent(token); + } + } + + private void processCommandDone(ICommandToken token, ICommandResult result) { + for (ICommandListener processor : fCommandProcessors) { + processor.commandDone(token, result); + } + } + + /** + * @since 1.1 + */ + public void markAllOutOfDate() { + MIRootVariableObject root; + while ((root = updatedRootList.poll()) != null) { + root.markAsOutOfDate(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.IResumedDMEvent e) { + // Program has resumed, all variable objects need to be updated. + // Since only roots can actually be updated in GDB, we only need + // to deal with those. Also, to optimize this operation, we have + // a list of all roots that have been updated, so we only have to + // set those to needing to be updated. + markAllOutOfDate(); + } + + @DsfServiceEventHandler + public void eventDispatched(IRunControl.ISuspendedDMEvent e) { + } + + @DsfServiceEventHandler + public void eventDispatched(IMemoryChangedEvent e) { + // Some memory has changed. We currently do not know the address + // of each of our variable objects, so there is no way to know + // which one is affected. Mark them all as out of date. + // The views will fully refresh on a MemoryChangedEvent + markAllOutOfDate(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/BreakpointActionAdapter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/BreakpointActionAdapter.java new file mode 100644 index 00000000000..f1c469cb755 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/BreakpointActionAdapter.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.breakpoint.actions; + +import org.eclipse.cdt.debug.core.breakpointactions.ILogActionEnabler; +import org.eclipse.cdt.debug.core.breakpointactions.IResumeActionEnabler; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.core.runtime.IAdaptable; + +public class BreakpointActionAdapter implements IAdaptable { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fServiceTracker; + private final IDMContext fContext; + + public BreakpointActionAdapter(DsfExecutor executor, DsfServicesTracker serviceTracker, IDMContext context) { + fExecutor = executor; + fServiceTracker = serviceTracker; + fContext = context; + } + + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if (adapter.equals(ILogActionEnabler.class)) { + return new MILogActionEnabler(fExecutor, fServiceTracker, fContext); + } + if (adapter.equals(IResumeActionEnabler.class)) { + return new MIResumeActionEnabler(fExecutor, fServiceTracker, fContext); + } + return null; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MILogActionEnabler.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MILogActionEnabler.java new file mode 100644 index 00000000000..1caac64543e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MILogActionEnabler.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.breakpoint.actions; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.debug.core.breakpointactions.ILogActionEnabler; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +public class MILogActionEnabler implements ILogActionEnabler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fServiceTracker; + private final IBreakpointsTargetDMContext fContext; + + public MILogActionEnabler(DsfExecutor executor, DsfServicesTracker serviceTracker, IDMContext context) { + fExecutor = executor; + fServiceTracker = serviceTracker; + fContext = (IBreakpointsTargetDMContext) context; + } + + public String evaluateExpression(final String expression) throws Exception { + // Use a Query to synchronize the call + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor drm) { + final IExpressions expressionService = fServiceTracker.getService(IExpressions.class); + if (expressionService != null) { + final IExpressionDMContext expressionDMC = expressionService.createExpression(fContext, expression); + String formatId = IFormattedValues.NATURAL_FORMAT; + FormattedValueDMContext valueDmc = expressionService.getFormattedValueContext(expressionDMC, formatId); + expressionService.getFormattedExpressionValue( + valueDmc, + new DataRequestMonitor(fExecutor, drm) { + @Override + protected void handleCompleted() { + String result = expression + ": evaluation failed."; //$NON-NLS-1$ + if (isSuccess()) { + result = getData().getFormattedValue(); + } + drm.setData(result); + drm.done(); + } + } + ); + } + } + }; + fExecutor.execute(query); + + try { + // The happy case + return query.get(); + } catch (InterruptedException e) { + return "Error evaluating \"" + expression + "\" (InterruptedException)."; //$NON-NLS-1$ //$NON-NLS-2$ + } catch (ExecutionException e) { + return "Error evaluating \"" + expression + "\" (ExecutionException)."; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MIResumeActionEnabler.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MIResumeActionEnabler.java new file mode 100644 index 00000000000..fddd003e5a6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/breakpoint/actions/MIResumeActionEnabler.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.breakpoint.actions; + +import org.eclipse.cdt.debug.core.breakpointactions.IResumeActionEnabler; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +public class MIResumeActionEnabler implements IResumeActionEnabler { + + private final DsfExecutor fExecutor; + private final DsfServicesTracker fServiceTracker; + private final IExecutionDMContext fContext; + + public MIResumeActionEnabler(DsfExecutor executor, DsfServicesTracker serviceTracker, IDMContext context) { + fExecutor = executor; + fServiceTracker = serviceTracker; + fContext = (IExecutionDMContext) context; + } + + public void resume() throws Exception { + final IRunControl runControlService = fServiceTracker.getService(IRunControl.class); + if (runControlService != null) { + runControlService.resume(fContext, new RequestMonitor(fExecutor, null)); + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java new file mode 100644 index 00000000000..b7fad0c5dd4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractCLIProcess.java @@ -0,0 +1,350 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIInterpreterExecConsole; +import org.eclipse.cdt.dsf.mi.service.command.commands.RawCommand; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConsoleStreamOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MILogStreamOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * This Process implementation tracks the process the GDB process. This + * process object is displayed in Debug view and is used to + * accept CLI commands and to write their output to the console. + * + * @see org.eclipse.debug.core.model.IProcess + */ +@ThreadSafe +public abstract class AbstractCLIProcess extends Process + implements IEventListener, ICommandListener +{ + public static final String PRIMARY_PROMPT = "(gdb)"; //$NON-NLS-1$ + public static final String SECONDARY_PROMPT = ">"; //$NON-NLS-1$ + + private final DsfSession fSession; + private final ICommandControlService fCommandControl; + private final OutputStream fOutputStream = new CLIOutputStream(); + + // Client process console stream. + private final PipedInputStream fMIInConsolePipe; + private final PipedOutputStream fMIOutConsolePipe; + private final PipedInputStream fMIInLogPipe; + private final PipedOutputStream fMIOutLogPipe; + + private boolean fDisposed = false; + + /** + * Counter for tracking console commands sent by services. + * + * Services may issue console commands when the available MI commands are + * not sufficient. However, these commands may produce console and log + * output which should not be written to the user CLI terminal. + * + * This counter is incremented any time a console command is seen which was + * not generated by this class. It is decremented whenever a service CLI + * command is finished. When counter value is 0, the CLI process writes + * the console output. + */ + private int fSuppressConsoleOutputCounter = 0; + + private int fPrompt = 1; // 1 --> Primary prompt "(gdb)"; 2 --> Secondary Prompt ">" + + /** + * @since 1.1 + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + public AbstractCLIProcess(ICommandControlService commandControl) throws IOException { + fSession = commandControl.getSession(); + fCommandControl = commandControl; + + commandControl.addEventListener(this); + commandControl.addCommandListener(this); + + PipedInputStream miInConsolePipe = null; + PipedOutputStream miOutConsolePipe = null; + PipedInputStream miInLogPipe = null; + PipedOutputStream miOutLogPipe = null; + + try { + // Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154 + miOutConsolePipe = new PipedOutputStream(); + miInConsolePipe = new LargePipedInputStream(miOutConsolePipe); + miOutLogPipe = new PipedOutputStream(); + miInLogPipe = new LargePipedInputStream(miOutLogPipe); + } catch (IOException e) { + ILog log = GdbPlugin.getDefault().getLog(); + if (log != null) { + log.log(new Status( + IStatus.ERROR, GdbPlugin.PLUGIN_ID, -1, "Error when creating log pipes", e)); //$NON-NLS-1$ + } + } + // Must initialize these outside of the try block because they are final. + fMIOutConsolePipe = miOutConsolePipe; + fMIInConsolePipe = miInConsolePipe; + fMIOutLogPipe = miOutLogPipe; + fMIInLogPipe = miInLogPipe; + } + + @Deprecated + public AbstractCLIProcess(AbstractMIControl commandControl) throws IOException { + this ( (ICommandControlService)commandControl ); + } + + protected DsfSession getSession() { return fSession; } + + @Deprecated + protected AbstractMIControl getCommandControl() { return (AbstractMIControl)fCommandControl; } + + /** + * @since 1.1 + */ + protected ICommandControlService getCommandControlService() { return fCommandControl; } + + protected boolean isDisposed() { return fDisposed; } + + @ConfinedToDsfExecutor("fSession#getExecutor") + public void dispose() { + fCommandControl.removeEventListener(this); + fCommandControl.removeCommandListener(this); + + closeIO(); + fDisposed = true; + } + + private void closeIO() { + try { + fMIOutConsolePipe.close(); + } catch (IOException e) {} + try { + fMIInConsolePipe.close(); + } catch (IOException e) {} + try { + fMIOutLogPipe.close(); + } catch (IOException e) {} + try { + fMIInLogPipe.close(); + } catch (IOException e) {} + + } + + /** + * @see java.lang.Process#getErrorStream() + */ + @Override + public InputStream getErrorStream() { + return fMIInLogPipe; + } + + /** + * @see java.lang.Process#getInputStream() + */ + @Override + public InputStream getInputStream() { + return fMIInConsolePipe; + } + + /** + * @see java.lang.Process#getOutputStream() + */ + @Override + public OutputStream getOutputStream() { + return fOutputStream; + } + + + public void eventReceived(Object output) { + if (fSuppressConsoleOutputCounter > 0) return; + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + if (oobr instanceof MIConsoleStreamOutput) + { + MIConsoleStreamOutput out = (MIConsoleStreamOutput) oobr; + String str = out.getString(); + // Process the console stream too. + setPrompt(str); + try { + fMIOutConsolePipe.write(str.getBytes()); + fMIOutConsolePipe.flush(); + } catch (IOException e) { + } + } else if (oobr instanceof MILogStreamOutput) { + MILogStreamOutput out = (MILogStreamOutput) oobr; + String str = out.getString(); + if (str != null) { + try { + fMIOutLogPipe.write(str.getBytes()); + fMIOutLogPipe.flush(); + } catch (IOException e) { + } + } + } + } + } + + public void commandQueued(ICommandToken token) { + // Ignore + } + + public void commandSent(ICommandToken token) { + ICommand command = token.getCommand(); + // Check if the command is a CLI command and if it did not originate from this class. + if (command instanceof CLICommand && + !(command instanceof ProcessCLICommand || command instanceof ProcessMIInterpreterExecConsole)) + { + fSuppressConsoleOutputCounter++; + } + } + + public void commandRemoved(ICommandToken token) { + // Ignore + } + + public void commandDone(ICommandToken token, ICommandResult result) { + ICommand command = token.getCommand(); + if (token.getCommand() instanceof CLICommand && + !(command instanceof ProcessCLICommand || command instanceof ProcessMIInterpreterExecConsole)) + { + fSuppressConsoleOutputCounter--; + } + } + + void setPrompt(String line) { + fPrompt = 0; + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=109733 + if (line == null) + return; + line = line.trim(); + if (line.equals(PRIMARY_PROMPT)) { + fPrompt = 1; + } else if (line.equals(SECONDARY_PROMPT)) { + fPrompt = 2; + } + } + + public boolean inPrimaryPrompt() { + return fPrompt == 1; + } + + public boolean inSecondaryPrompt() { + return fPrompt == 2; + } + + private boolean isMIOperation(String operation) { + // The definition of an MI command states that it starts with + // [ token ] "-" + // where 'token' is optional and a sequence of digits. + // However, we don't accept a token from the user, because + // we will be adding our own token when actually sending the command. + if (operation.startsWith("-")) { //$NON-NLS-1$ + return true; + } + return false; + } + + private class CLIOutputStream extends OutputStream { + private final StringBuffer buf = new StringBuffer(); + + @Override + public void write(int b) throws IOException { + buf.append((char)b); + if (b == '\n') { + // Throw away the newline. + final String bufString = buf.toString().trim(); + buf.setLength(0); + try { + fSession.getExecutor().execute(new DsfRunnable() { public void run() { + try { + post(bufString); + } catch (IOException e) { + // Pipe closed. + } + }}); + } catch (RejectedExecutionException e) { + // Session disposed. + } + } + } + + // Encapsulate the string sent to gdb in a fake + // command and post it to the TxThread. + public void post(String str) throws IOException { + if (isDisposed()) return; + ICommand cmd = null; + // 1- + // if We have the secondary prompt it means + // that GDB is waiting for more feedback, use a RawCommand + // 2- + // Do not use the interpreter-exec for stepping operation + // the UI will fall out of step. + // Also, do not use "interpreter-exec console" for MI commands. + // 3- + // Normal Command Line Interface. + boolean secondary = inSecondaryPrompt(); + if (secondary) { + cmd = new RawCommand(getCommandControlService().getContext(), str); + } + else if (! isMIOperation(str) && + ! CLIEventProcessor.isSteppingOperation(str)) + { + cmd = new ProcessMIInterpreterExecConsole(getCommandControlService().getContext(), str); + } + else { + cmd = new ProcessCLICommand(getCommandControlService().getContext(), str); + } + final ICommand finalCmd = cmd; + fSession.getExecutor().execute(new DsfRunnable() { public void run() { + if (isDisposed()) return; + // Do not wait around for the answer. + getCommandControlService().queueCommand(finalCmd, null); + }}); + } + } + + private class ProcessCLICommand extends CLICommand { + public ProcessCLICommand(IDMContext ctx, String oper) { + super(ctx, oper); + } + } + + private class ProcessMIInterpreterExecConsole extends MIInterpreterExecConsole { + public ProcessMIInterpreterExecConsole(IDMContext ctx, String cmd) { + super(ctx, cmd); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java new file mode 100644 index 00000000000..bcace281b8b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/AbstractMIControl.java @@ -0,0 +1,772 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Modified for handling of multiple stacks and threads + * Nokia - create and use backend service. + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIStackSelectFrame; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIThreadSelect; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIList; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIParser; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Base implementation of an MI control service. It provides basic handling + * of input/output channels, and processing of the commands. + *

+ * Extending classes need to implement the initialize() and shutdown() methods. + */ +public abstract class AbstractMIControl extends AbstractDsfService + implements ICommandControlService +{ + /* + * Thread control variables for the transmit and receive threads. + */ + + private TxThread fTxThread; + private RxThread fRxThread; + + // MI did not always support the --thread/--frame options + // This boolean is used to know if we should use -thread-select and -stack-select-frame instead + private boolean fUseThreadAndFrameOptions; + // currentStackLevel and currentThreadId are only necessary when + // we must use -thread-select and -stack-select-frame + private int fCurrentStackLevel = -1; + private String fCurrentThreadId = null; + + + private final BlockingQueue fTxCommands = new LinkedBlockingQueue(); + private final Map fRxCommands = Collections.synchronizedMap(new HashMap()); + + /** + * Handle that's inserted into the TX commands queue to signal + * that the TX thread should shut down. + */ + private final CommandHandle fTerminatorHandle = new CommandHandle(null, null); + + /* + * Various listener control variables used to keep track of listeners who want to monitor + * what the control object is doing. + */ + + private final List fCommandProcessors = new ArrayList(); + private final List fEventProcessors = new ArrayList(); + + /** + * Current command which have not been handed off to the backend yet. + */ + + private final List fCommandQueue = new ArrayList(); + + /** + * Flag indicating that the command control has stopped processing commands. + */ + private boolean fStoppedCommandProcessing = false; + + public AbstractMIControl(DsfSession session) { + super(session); + fUseThreadAndFrameOptions = false; + } + + /** + * @since 1.1 + */ + public AbstractMIControl(DsfSession session, boolean useThreadAndFrameOptions) { + super(session); + fUseThreadAndFrameOptions = useThreadAndFrameOptions; + } + + /** + * Starts the threads that process the debugger input/output channels. + * To be invoked by the initialization routine of the extending class. + * + * @param inStream + * @param outStream + */ + + protected void startCommandProcessing(InputStream inStream, OutputStream outStream) { + + fTxThread = new TxThread(outStream); + fRxThread = new RxThread(inStream); + fTxThread.start(); + fRxThread.start(); + } + + /** + * Stops the threads that process the debugger input/output channels, and notifies the + * results of the outstanding commands. To be invoked by the shutdown routine of the + * extending class. + * + * @param inStream + * @param outStream + */ + + private Status genStatus(String str) { + return new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, str, null); + } + + protected void stopCommandProcessing() { + // Guard against calling this multiple times (e.g. as a result of a + // user request and an event from the back end). + if (fStoppedCommandProcessing) return; + fStoppedCommandProcessing = true; + + /* + * First go through the commands which have been queueud and not yet sent to the backend. + */ + for (CommandHandle commandHandle : fCommandQueue) { + if (commandHandle.getRequestMonitor() == null) continue; + commandHandle.getRequestMonitor().setStatus(genStatus("Connection is shut down")); //$NON-NLS-1$ + commandHandle.getRequestMonitor().done(); + } + fCommandQueue.clear(); + + /* + * Now go through the commands which are outstanding in that they have been sent to the backend. + */ + synchronized(fRxCommands) { + for (CommandHandle commandHandle : fRxCommands.values()) { + if (commandHandle.getRequestMonitor() == null) continue; + commandHandle.getRequestMonitor().setStatus(genStatus( "Connection is shut down")); //$NON-NLS-1$ + commandHandle.getRequestMonitor().done(); + } + fRxCommands.clear(); + } + + /* + * Now handle any requests which have not been transmitted, but weconsider them handed off. + */ + List txCommands = new ArrayList(); + fTxCommands.drainTo(txCommands); + for (CommandHandle commandHandle : txCommands) { + if (commandHandle.getRequestMonitor() == null) continue; + commandHandle.getRequestMonitor().setStatus(genStatus("Connection is shut down")); //$NON-NLS-1$ + commandHandle.getRequestMonitor().done(); + } + + // Queue a null value to tell the send thread to shut down. + fTxCommands.add(fTerminatorHandle); + } + + /** + * Queues the given MI command to be sent to the debugger back end. + * + * @param command Command to be executed. This parameter must be an + * instance of DsfMICommand, otherwise a ClassCastException will be + * thrown. + * @param rm Request completion monitor + * + * @see org.eclipse.cdt.dsf.debug.service.command.ICommandControl#addCommand(org.eclipse.cdt.dsf.debug.service.command.ICommand, org.eclipse.cdt.dsf.concurrent.RequestMonitor) + */ + + public ICommandToken queueCommand(final ICommand command, DataRequestMonitor rm) { + + // Cast the command to MI Command type. This will cause a cast exception to be + // thrown if the client did not give an MI command as an argument. + @SuppressWarnings("unchecked") + MICommand miCommand = (MICommand) command; + + // Cast the return token to match the result type of MI Command. This is checking + // against an erased type so it should never throw any exceptions. + @SuppressWarnings("unchecked") + DataRequestMonitor miDone = (DataRequestMonitor) rm; + + final CommandHandle handle = new CommandHandle(miCommand, miDone); + + // If the command control stopped processing commands, just return an error immediately. + if (fStoppedCommandProcessing) { + rm.setStatus(genStatus("Connection is shut down")); //$NON-NLS-1$ + rm.done(); + } else { + /* + * We only allow three outstanding commands to be on the wire to the backend + * at any one time. This allows for coalescing as well as canceling + * existing commands on a state change. So we add it to the waiting list and let + * the user know they can now work with this item if need be. + */ + fCommandQueue.add(handle); + processCommandQueued(handle); + + if (fRxCommands.size() < 3) { + // In a separate dispatch cycle. This allows command listeners + // to respond to the command queued event. + getExecutor().execute(new DsfRunnable() { + public void run() { + processNextQueuedCommand(); + } + }); + } + } + + return handle; + } + + private void processNextQueuedCommand() { + if (fCommandQueue.size() > 0) { + final CommandHandle handle = fCommandQueue.remove(0); + if (handle != null) { + processCommandSent(handle); + + // Older debuggers didn't support the --thread/--frame options + // Also, not all commands support those options (e.g., CLI commands) + if (!fUseThreadAndFrameOptions || !handle.getCommand().supportsThreadAndFrameOptions()) { + // Without the --thread/--frame, we need to send the proper + // -thread-select and -stack-frame-select before sending the command + + final IDMContext targetContext = handle.fCommand.getContext(); + final String targetThread = handle.getThreadId(); + final int targetFrame = handle.getStackFrameId(); + + // The thread-select and frame-select make sense only if the thread is stopped. + IRunControl runControl = getServicesTracker().getService(IRunControl.class); + IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(targetContext, IMIExecutionDMContext.class); + if (runControl != null && execDmc != null && runControl.isSuspended(execDmc)) { + // Before the command is sent, Check the Thread Id and send it to + // the queue only if the id has been changed. Also, don't send a threadId of 0, + // because that id is only used internally for single-threaded programs + if (targetThread != null && !targetThread.equals("0") && !targetThread.equals(fCurrentThreadId)) { //$NON-NLS-1$ + fCurrentThreadId = targetThread; + resetCurrentStackLevel(); + CommandHandle cmdHandle = new CommandHandle(new MIThreadSelect(targetContext, targetThread), null); + cmdHandle.generateTokenId(); + fTxCommands.add(cmdHandle); + } + + // Before the command is sent, Check the Stack level and send it to + // the queue only if the level has been changed. + if (targetFrame >= 0 && targetFrame != fCurrentStackLevel) { + fCurrentStackLevel = targetFrame; + CommandHandle cmdHandle = new CommandHandle(new MIStackSelectFrame(targetContext, targetFrame), null); + cmdHandle.generateTokenId(); + fTxCommands.add(cmdHandle); + } + } + } + + handle.generateTokenId(); + fTxCommands.add(handle); + } + } + } + + /* + * This is the command which allows the user to retract a previously issued command. The + * state of the command is that it is in the waiting queue and has not yet been handed + * to the backend yet. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#removeCommand(org.eclipse.cdt.dsf.mi.service.command.commands.ICommand) + */ + public void removeCommand(ICommandToken token) { + + synchronized(fCommandQueue) { + + for ( CommandHandle handle : fCommandQueue ) { + if ( handle.equals(token)) { + fCommandQueue.remove(handle); + + final CommandHandle finalHandle = handle; + getExecutor().execute(new DsfRunnable() { + public void run() { + processCommandRemoved(finalHandle); + } + }); + break; + } + } + } + } + + /* + * Allows a user ( typically a cache manager ) to sign up a listener to monitor command queue + * activity. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#addCommandListener(org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl.ICommandListener) + */ + public void addCommandListener(ICommandListener processor) { fCommandProcessors.add(processor); } + + /* + * Allows a user ( typically a cache manager ) to remove a monitoring listener. + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#removeCommandListener(org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl.ICommandListener) + */ + public void removeCommandListener(ICommandListener processor) { fCommandProcessors.remove(processor); } + + /* + * Allows a user to sign up to a listener which handles out of band messages ( events ). + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#addEventListener(org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl.IEventListener) + */ + public void addEventListener(IEventListener processor) { fEventProcessors.add(processor); } + + /* + * Allows a user to remove a event monitoring listener. + * + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl#removeEventListener(org.eclipse.cdt.dsf.mi.service.command.IDebuggerControl.IEventListener) + */ + public void removeEventListener(IEventListener processor) { fEventProcessors.remove(processor); } + + abstract public MIControlDMContext getControlDMContext(); + + /** + * @since 1.1 + */ + public boolean isActive() { + return !fStoppedCommandProcessing; + } + + /* + * These are the service routines which perform the various callouts back to the listeners. + */ + + private void processCommandQueued(CommandHandle commandHandle) { + for (ICommandListener processor : fCommandProcessors) { + processor.commandQueued(commandHandle); + } + } + private void processCommandRemoved(CommandHandle commandHandle) { + for (ICommandListener processor : fCommandProcessors) { + processor.commandRemoved(commandHandle); + } + } + + private void processCommandSent(CommandHandle commandHandle) { + for (ICommandListener processor : fCommandProcessors) { + processor.commandSent(commandHandle); + } + } + + private void processCommandDone(CommandHandle commandHandle, ICommandResult result) { + /* + * Tell the listeners we have completed this one. + */ + for (ICommandListener processor : fCommandProcessors) { + processor.commandDone(commandHandle, result); + } + } + + private void processEvent(MIOutput output) { + for (IEventListener processor : fEventProcessors) { + processor.eventReceived(output); + } + } + + /* + * A global counter for all command, the token will be use to identify uniquely a command. + * Unless the value wraps around which is unlikely. + */ + private int fTokenIdCounter = 0 ; + + private int getNewTokenId() { + int count = ++fTokenIdCounter; + // If we ever wrap around. + if (count <= 0) { + count = fTokenIdCounter = 1; + } + return count; + } + + /* + * Support class which creates a convenient wrapper for holding all information about an + * individual request. + */ + + private class CommandHandle implements ICommandToken { + + private MICommand fCommand; + private DataRequestMonitor fRequestMonitor; + private int fTokenId ; + + CommandHandle(MICommand c, DataRequestMonitor d) { + fCommand = c; + fRequestMonitor = d; + fTokenId = -1; // Only initialize to a real value when needed + } + + public MICommand getCommand() { return fCommand; } + public DataRequestMonitor getRequestMonitor() { return fRequestMonitor; } + // This method allows us to generate the token Id when we area actually going to use + // it. It is meant to help order the token ids based on when commands will actually + // be sent + public void generateTokenId() { fTokenId = getNewTokenId(); } + public Integer getTokenId() { return fTokenId; } + + public int getStackFrameId() { + IFrameDMContext frameCtx = DMContexts.getAncestorOfType(fCommand.getContext(), IFrameDMContext.class); + if(frameCtx != null) + return frameCtx.getLevel(); + return -1; + } + + public String getThreadId() { + IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(fCommand.getContext(), IMIExecutionDMContext.class); + if(execCtx != null) + return Integer.toString(execCtx.getThreadId()); + return null; + } + + @Override + public String toString() { + return Integer.toString(fTokenId) + fCommand; + } + } + + /* + * This is the transmitter thread. When a command is given to this thread it has been + * considered to be sent, even if it has not actually been sent yet. This assumption + * makes it easier from state management. Whomever fill this pipeline handles all of + * the required state notification ( callbacks ). This thread simply physically gives + * the message to the backend. + */ + + private class TxThread extends Thread { + + final private OutputStream fOutputStream; + + public TxThread(OutputStream outStream) { + super("MI TX Thread"); //$NON-NLS-1$ + fOutputStream = outStream; + } + + @Override + public void run () { + while (true) { + CommandHandle commandHandle = null; + + /* + * Note: Acquiring locks for both fRxCommands and fTxCommands collections. + */ + synchronized(fTxCommands) { + try { + commandHandle = fTxCommands.take(); + } catch (InterruptedException e) { + break; // Shutting down. + } + + if (commandHandle == fTerminatorHandle) { + + break; // Null command is an indicator that we're shutting down. + } + + /* + * We note that this is an outstanding request at this point. + */ + fRxCommands.put(commandHandle.getTokenId(), commandHandle); + } + + /* + * Construct the new command and push this command out the pipeline. + */ + + final String str; + // Not all commands support the --thread/--frame options (e.g., CLI commands) + if (fUseThreadAndFrameOptions && commandHandle.getCommand().supportsThreadAndFrameOptions()) { + str = commandHandle.getTokenId() + commandHandle.getCommand().constructCommand(commandHandle.getThreadId(), + commandHandle.getStackFrameId()); + } else { + str = commandHandle.getTokenId() + commandHandle.getCommand().constructCommand(); + } + + try { + if (fOutputStream != null) { + fOutputStream.write(str.getBytes()); + fOutputStream.flush(); + + GdbPlugin.debug(GdbPlugin.getDebugTime() + " " + str); //$NON-NLS-1$ + } + } catch (IOException e) { + // Shutdown thread in case of IO error. + break; + } + } + } + } + + private class RxThread extends Thread { + private final InputStream fInputStream; + private final MIParser fMiParser = new MIParser(); + + /** + * List of out of band records since the last result record. Out of band records are + * required for processing the results of CLI commands. + */ + private final List fAccumulatedOOBRecords = new ArrayList(); + + public RxThread(InputStream inputStream) { + super("MI RX Thread"); //$NON-NLS-1$ + fInputStream = inputStream; + } + + @Override + public void run() { + BufferedReader reader = new BufferedReader(new InputStreamReader(fInputStream)); + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.length() != 0) { + GdbPlugin.debug(GdbPlugin.getDebugTime() + " " + line +"\n"); //$NON-NLS-1$ //$NON-NLS-2$ + processMIOutput(line); + } + } + } catch (IOException e) { + // Socket is shut down. + } catch (RejectedExecutionException e) { + // Dispatch thread is down. + } + } + + private MIResult findResultRecord(MIResult[] results, String variable) { + for (int i = 0; i < results.length; i++) { + if (variable.equals(results[i].getVariable())) { + return results[i]; + } + } + return null; + } + + private String getStatusString(MICommand origCommand, MIOutput response ) { + + // Attempt to extract a message from the result record: + String message = null; + String[] parameters = null; + if (response != null && response.getMIResultRecord() != null) { + MIResult[] results = response.getMIResultRecord().getMIResults(); + + // Extract the parameters + MIResult paramsRes = findResultRecord(results, "parameters"); //$NON-NLS-1$ + if (paramsRes != null && paramsRes.getMIValue() instanceof MIList) { + MIValue[] paramValues = ((MIList)paramsRes.getMIValue()).getMIValues(); + parameters = new String[paramValues.length]; + for (int i = 0; i < paramValues.length; i++) { + if (paramValues[i] instanceof MIConst) { + parameters[i] = ((MIConst)paramValues[i]).getString(); + } else { + parameters[i] = ""; //$NON-NLS-1$ + } + } + } + MIResult messageRes = findResultRecord(results, "message"); //$NON-NLS-1$ + if (messageRes != null && messageRes.getMIValue() instanceof MIConst) { + message = ((MIConst)messageRes.getMIValue()).getString(); + } + // FRCH: I believe that the actual string is "msg" ... + // FRCH: (at least for the version of gdb I'm using) + else { + messageRes = findResultRecord(results, "msg"); //$NON-NLS-1$ + if (messageRes != null && messageRes.getMIValue() instanceof MIConst) { + message = ((MIConst)messageRes.getMIValue()).getString(); + } + } + } + StringBuilder clientMsg = new StringBuilder(); + clientMsg.append("Failed to execute MI command:\n"); //$NON-NLS-1$ + clientMsg.append(origCommand.toString()); + if (message != null) { + clientMsg.append("Error message from debugger back end:\n"); //$NON-NLS-1$ + if (parameters != null) { + try { + clientMsg.append(MessageFormat.format(message, (Object[])parameters)); + } catch(IllegalArgumentException e2) { + // Message format string invalid. Fallback to just appending the strings. + clientMsg.append(message); + clientMsg.append(parameters); + } + } else { + clientMsg.append(message); + } + } + return clientMsg.toString(); + } + + void processMIOutput(String line) { + MIParser.RecordType recordType = fMiParser.getRecordType(line); + + if (recordType == MIParser.RecordType.ResultRecord) { + final MIResultRecord rr = fMiParser.parseMIResultRecord(line); + + + /* + * Find the command in the current output list. If we cannot then this is + * some form of asynchronous notification. Or perhaps general IO. + */ + int id = rr.getToken(); + final CommandHandle commandHandle = fRxCommands.remove(id); + + if (commandHandle != null) { + final MIOutput response = new MIOutput( + rr, fAccumulatedOOBRecords.toArray(new MIOOBRecord[fAccumulatedOOBRecords.size()]) ); + fAccumulatedOOBRecords.clear(); + + MIInfo result = commandHandle.getCommand().getResult(response); + DataRequestMonitor rm = commandHandle.getRequestMonitor(); + + /* + * Not all users want to get there results. They indicate so by not having + * a completion object. + */ + if ( rm != null ) { + rm.setData(result); + + /* + * We need to indicate if this request had an error or not. + */ + String errorResult = rr.getResultClass(); + + if ( errorResult.equals(MIResultRecord.ERROR) ) { + String status = getStatusString(commandHandle.getCommand(),response); + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, status, null)); + } + + /* + * We need to complete the command on the DSF thread for data security. + */ + final ICommandResult finalResult = result; + getExecutor().execute(new DsfRunnable() { + public void run() { + /* + * Complete the specific command. + */ + if (commandHandle.getRequestMonitor() != null) { + commandHandle.getRequestMonitor().done(); + } + + /* + * Now tell the generic listeners about it. + */ + processCommandDone(commandHandle, finalResult); + } + @Override + public String toString() { + return "MI command output received for: " + commandHandle.getCommand(); //$NON-NLS-1$ + } + }); + } else { + /* + * While the specific requestor did not care about the completion we + * need to call any listeners. This could have been a CLI command for + * example and the CommandDone listeners there handle the IO as part + * of the work. + */ + final ICommandResult finalResult = result; + getExecutor().execute(new DsfRunnable() { + public void run() { + processCommandDone(commandHandle, finalResult); + } + @Override + public String toString() { + return "MI command output received for: " + commandHandle.getCommand(); //$NON-NLS-1$ + } + }); + } + } else { + /* + * GDB apparently can sometimes send multiple responses to the same command. In those cases, + * the command handle is gone, so post the result as an event. To avoid processing OOB records + * as events multiple times, do not include the accumulated OOB record list in the response + * MIOutput object. + */ + final MIOutput response = new MIOutput(rr, new MIOOBRecord[0]); + + getExecutor().execute(new DsfRunnable() { + public void run() { + processEvent(response); + } + @Override + public String toString() { + return "MI asynchronous output received: " + response; //$NON-NLS-1$ + } + }); + } + } else if (recordType == MIParser.RecordType.OOBRecord) { + // Process OOBs + final MIOOBRecord oob = fMiParser.parseMIOOBRecord(line); + fAccumulatedOOBRecords.add(oob); + final MIOutput response = new MIOutput(oob); + + + + /* + * OOBS are events. So we pass them to any event listeners who want to see them. Again this must + * be done on the DSF thread for integrity. + */ + getExecutor().execute(new DsfRunnable() { + public void run() { + processEvent(response); + } + @Override + public String toString() { + return "MI asynchronous output received: " + response; //$NON-NLS-1$ + } + }); + } + + getExecutor().execute(new DsfRunnable() { + public void run() { + processNextQueuedCommand(); + } + }); + } + } + + // we keep track of currentStackLevel and currentThreadId because in + // some cases we must use -thread-select and -stack-select-frame + public void resetCurrentThreadLevel(){ + fCurrentThreadId = null; + } + + public void resetCurrentStackLevel(){ + fCurrentStackLevel = -1; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor.java new file mode 100644 index 00000000000..974ff4ca90c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor.java @@ -0,0 +1,354 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson AB - Additional handling of events + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.ISignals.ISignalsDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIInterpreterExecConsole; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointChangedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIDetachedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalChangedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConsoleStreamOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +/** + * GDB debugger output listener. + */ +@ConfinedToDsfExecutor("fConnection#getExecutor") +public class CLIEventProcessor + implements ICommandListener, IEventListener +{ + private final ICommandControlService fCommandControl; + private final ICommandControlDMContext fControlDmc; + + // Last Thread ID created + private static int fLastThreadId; + + private final DsfServicesTracker fServicesTracker; + + /** + * @since 1.1 + */ + public CLIEventProcessor(ICommandControlService connection, ICommandControlDMContext controlDmc) { + fCommandControl = connection; + fControlDmc = controlDmc; + fServicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fCommandControl.getSession().getId()); + connection.addCommandListener(this); + connection.addEventListener(this); + // Re-set the counter + fLastThreadId = 0; + } + + @Deprecated + public CLIEventProcessor(AbstractMIControl connection, IContainerDMContext containerDmc, MIInferiorProcess inferior) { + this(connection, DMContexts.getAncestorOfType(containerDmc, ICommandControlDMContext.class)); + } + + public void dispose() { + fCommandControl.removeCommandListener(this); + fCommandControl.removeEventListener(this); + fServicesTracker.dispose(); + } + + @Deprecated + public void resetInferior(MIInferiorProcess inferior) { + } + + public void commandSent(ICommandToken token) { + if (token.getCommand() instanceof CLICommand) { + processStateChanges( (CLICommand)token.getCommand() ); + } + else if (token.getCommand() instanceof MIInterpreterExecConsole) { + processStateChanges( (MIInterpreterExecConsole)token.getCommand() ); + } + } + + public void commandDone(ICommandToken token, ICommandResult result) { + if (token.getCommand() instanceof CLICommand) { + processSettingChanges( (CLICommand)token.getCommand() ); + } + else if (token.getCommand() instanceof MIInterpreterExecConsole) { + processSettingChanges( (MIInterpreterExecConsole)token.getCommand() ); + } + } + + public void commandQueued(ICommandToken token) { + // No action + } + + public void commandRemoved(ICommandToken token) { + // No action + } + + public void eventReceived(Object output) { + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + if (oobr instanceof MIConsoleStreamOutput) { + // Process Events of type DsfMIConsoleStreamOutput here + MIConsoleStreamOutput exec = (MIConsoleStreamOutput) oobr; + + // Look for events with Pattern ^[New Thread 1077300144 (LWP 7973) + Pattern pattern = Pattern.compile("(^\\[New Thread.*LWP\\s*)(\\d*)", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(exec.getCString()); + if (matcher.find()) { + String threadId = Integer.toString(++fLastThreadId); + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService != null) { + String groupId = MIProcesses.UNIQUE_GROUP_ID; + + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); + MIEvent e = new MIThreadCreatedEvent(processContainerDmc, threadId); + fCommandControl.getSession().dispatchEvent(e, fCommandControl.getProperties()); + } + } + + // For GDB thread exit events, we won't use the events generated by GDB. This event is + // raised in GDBRunControl class by polling and comparing the ExecutionContexts returned by + // -thread-list-ids command. This is done as threads reported by exit event are still reported till + // they completely exit the system. + } + } + } + + + private void processStateChanges(CLICommand cmd) { + String operation = cmd.getOperation().trim(); + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processStateChanges(0, operation); + } + + private void processStateChanges(MIInterpreterExecConsole exec) { + String[] operations = exec.getParameters(); + if (operations != null && operations.length > 0) { + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processStateChanges(0, operations[0]); + } + } + + private void processStateChanges(int token, String operation) { + // Get the command name. + int indx = operation.indexOf(' '); + if (indx != -1) { + operation = operation.substring(0, indx).trim(); + } else { + operation = operation.trim(); + } + + // Check the type of command + + int type = getSteppingOperationKind(operation); + if (type != -1) { + // if it was a step instruction set state running + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService != null) { + String groupId = MIProcesses.UNIQUE_GROUP_ID; + + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); + MIEvent event = new MIRunningEvent(processContainerDmc, token, type); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + } + + /** + * An attempt to discover the command type and + * fire an event if necessary. + */ + private void processSettingChanges(CLICommand cmd) { + String operation = cmd.getOperation().trim(); + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processSettingChanges(cmd.getContext(), 0, operation); + } + + private void processSettingChanges(MIInterpreterExecConsole exec) { + String[] operations = exec.getParameters(); + if (operations != null && operations.length > 0) { + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processSettingChanges(exec.getContext(), 0, operations[0]); + } + } + + private void processSettingChanges(IDMContext dmc, int token, String operation) { + // Get the command name. + int indx = operation.indexOf(' '); + if (indx != -1) { + operation = operation.substring(0, indx).trim(); + } else { + operation = operation.trim(); + } + + // Check the type of command + + if (isSettingBreakpoint(operation) || + isSettingWatchpoint(operation) || + isChangeBreakpoint(operation) || + isDeletingBreakpoint(operation)) + { + // We know something change, we just do not know what. + // So the easiest way is to let the top layer handle it. + MIEvent event = new MIBreakpointChangedEvent( + DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class), 0); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } else if (isSettingSignal(operation)) { + // We do no know which signal let the upper layer find it. + MIEvent event = new MISignalChangedEvent( + DMContexts.getAncestorOfType(dmc, ISignalsDMContext.class), ""); //$NON-NLS-1$ + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } else if (isDetach(operation)) { + // if it was a "detach" command change the state. + MIEvent event = new MIDetachedEvent(DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class), token); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + + private static int getSteppingOperationKind(String operation) { + int type = -1; + /* execution commands: n, next, s, step, si, stepi, u, until, finish, + c, continue, fg */ + if (operation.equals("n") || operation.equals("next")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.NEXT; + } else if (operation.equals("ni") || operation.equals("nexti")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.NEXTI; + } else if (operation.equals("s") || operation.equals("step")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.STEP; + } else if (operation.equals("si") || operation.equals("stepi")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.STEPI; + } else if (operation.equals("u") || //$NON-NLS-1$ + (operation.startsWith("unt") && "until".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.UNTIL; + } else if (operation.startsWith("fin") && "finish".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.FINISH; + } else if (operation.equals("c") || operation.equals("fg") || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("cont") && "continue".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.startsWith("sig") && "signal".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.startsWith("j") && "jump".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.equals("r") || operation.equals("run")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } + return type; + } + + /** + * Return true if the operation is a stepping operation. + * + * @param operation + * @return + */ + public static boolean isSteppingOperation(String operation) { + int type = getSteppingOperationKind(operation); + return type != -1; + } + + private boolean isSettingBreakpoint(String operation) { + boolean isbreak = false; + /* breakpoints: b, break, hbreak, tbreak, rbreak, thbreak */ + /* watchpoints: watch, rwatch, awatch, tbreak, rbreak, thbreak */ + if ((operation.startsWith("b") && "break".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("tb") && "tbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("hb") && "hbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("thb") && "thbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("rb") && "rbreak".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isbreak = true; + } + return isbreak; + } + + private boolean isSettingWatchpoint(String operation) { + boolean isWatch = false; + /* watchpoints: watch, rwatch, awatch, tbreak, rbreak, thbreak */ + if ((operation.startsWith("wa") && "watch".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("rw") && "rwatch".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("aw") && "awatch".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isWatch = true; + } + return isWatch; + } + + private boolean isDeletingBreakpoint(String operation) { + boolean isDelete = false; + /* deleting breaks: clear, delete */ + if ((operation.startsWith("cl") && "clear".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.equals("d") || (operation.startsWith("del") && "delete".indexOf(operation) != -1))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + isDelete = true; + } + return isDelete; + } + + private boolean isChangeBreakpoint(String operation) { + boolean isChange = false; + /* changing breaks: enable, disable */ + if ((operation.equals("dis") || operation.equals("disa") || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("disa") && "disable".indexOf(operation) != -1)) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.equals("en") || (operation.startsWith("en") && "enable".indexOf(operation) != -1)) || //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + (operation.startsWith("ig") && "ignore".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("cond") && "condition".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isChange = true; + } + return isChange; + } + + private boolean isSettingSignal(String operation) { + boolean isChange = false; + /* changing signal: handle, signal */ + if (operation.startsWith("ha") && "handle".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + isChange = true; + } + return isChange; + } + + /** + * @param operation + * @return + */ + private boolean isDetach(String operation) { + return (operation.startsWith("det") && "detach".indexOf(operation) != -1); //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor_7_0.java new file mode 100644 index 00000000000..51e2167938f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/CLIEventProcessor_7_0.java @@ -0,0 +1,290 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson AB - Additional handling of events + * Ericsson - Version 7.0 + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.ISignals.ISignalsDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIInterpreterExecConsole; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointChangedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIDetachedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalChangedEvent; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +/** + * GDB debugger output listener. + * @since 1.1 + */ +@ConfinedToDsfExecutor("fConnection#getExecutor") +public class CLIEventProcessor_7_0 + implements ICommandListener, IEventListener +{ + private final ICommandControlService fCommandControl; + private final ICommandControlDMContext fControlDmc; + + private final DsfServicesTracker fServicesTracker; + + public CLIEventProcessor_7_0(ICommandControlService connection, ICommandControlDMContext controlDmc) { + fCommandControl = connection; + fControlDmc = controlDmc; + fServicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fCommandControl.getSession().getId()); + fCommandControl.addCommandListener(this); + fCommandControl.addEventListener(this); + } + + public void dispose() { + fCommandControl.removeCommandListener(this); + fCommandControl.removeEventListener(this); + fServicesTracker.dispose(); + } + + public void commandSent(ICommandToken token) { + if (token.getCommand() instanceof CLICommand) { + processStateChanges( (CLICommand)token.getCommand() ); + } + else if (token.getCommand() instanceof MIInterpreterExecConsole) { + processStateChanges( (MIInterpreterExecConsole)token.getCommand() ); + } + } + + public void commandDone(ICommandToken token, ICommandResult result) { + if (token.getCommand() instanceof CLICommand) { + processSettingChanges( (CLICommand)token.getCommand() ); + } + else if (token.getCommand() instanceof MIInterpreterExecConsole) { + processSettingChanges( (MIInterpreterExecConsole)token.getCommand() ); + } + } + + public void commandQueued(ICommandToken token) { + // No action + } + + public void commandRemoved(ICommandToken token) { + // No action + } + + public void eventReceived(Object output) { + } + + + private void processStateChanges(CLICommand cmd) { + String operation = cmd.getOperation().trim(); + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processStateChanges(0, operation); + } + + private void processStateChanges(MIInterpreterExecConsole exec) { + String[] operations = exec.getParameters(); + if (operations != null && operations.length > 0) { + // In refactoring we are no longer generating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processStateChanges(0, operations[0]); + } + } + + private void processStateChanges(int token, String operation) { + // Get the command name. + int indx = operation.indexOf(' '); + if (indx != -1) { + operation = operation.substring(0, indx).trim(); + } else { + operation = operation.trim(); + } + + // Check the type of command + int type = getSteppingOperationKind(operation); + if (type != -1) { + // Should set MIrunControlEventProcessor_7_0.fLastRunningCmdType + } + } + + /** + * An attempt to discover the command type and + * fire an event if necessary. + */ + private void processSettingChanges(CLICommand cmd) { + String operation = cmd.getOperation().trim(); + // In refactoring we are no longer genwerating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processSettingChanges(cmd.getContext(), 0, operation); + } + + private void processSettingChanges(MIInterpreterExecConsole exec) { + String[] operations = exec.getParameters(); + if (operations != null && operations.length > 0) { + // In refactoring we are no longer genwerating the token id as + // part of the command. It is passed here and stored away and + // then never really used. So it has just been changed to 0. + processSettingChanges(exec.getContext(), 0, operations[0]); + } + } + + private void processSettingChanges(IDMContext dmc, int token, String operation) { + // Get the command name. + int indx = operation.indexOf(' '); + if (indx != -1) { + operation = operation.substring(0, indx).trim(); + } else { + operation = operation.trim(); + } + + // Check the type of command + + if (isSettingBreakpoint(operation) || + isSettingWatchpoint(operation) || + isChangeBreakpoint(operation) || + isDeletingBreakpoint(operation)) + { + // We know something change, we just do not know what. + // So the easiest way is to let the top layer handle it. + MIEvent event = new MIBreakpointChangedEvent( + DMContexts.getAncestorOfType(dmc, IBreakpointsTargetDMContext.class), 0); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } else if (isSettingSignal(operation)) { + // We do no know which signal let the upper layer find it. + MIEvent event = new MISignalChangedEvent( + DMContexts.getAncestorOfType(dmc, ISignalsDMContext.class), ""); //$NON-NLS-1$ + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } else if (isDetach(operation)) { + // if it was a "detach" command change the state. + MIEvent event = new MIDetachedEvent(DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class), token); + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + + private static int getSteppingOperationKind(String operation) { + int type = -1; + /* execution commands: n, next, s, step, si, stepi, u, until, finish, + c, continue, fg */ + if (operation.equals("n") || operation.equals("next")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.NEXT; + } else if (operation.equals("ni") || operation.equals("nexti")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.NEXTI; + } else if (operation.equals("s") || operation.equals("step")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.STEP; + } else if (operation.equals("si") || operation.equals("stepi")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.STEPI; + } else if (operation.equals("u") || //$NON-NLS-1$ + (operation.startsWith("unt") && "until".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.UNTIL; + } else if (operation.startsWith("fin") && "finish".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.FINISH; + } else if (operation.equals("c") || operation.equals("fg") || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("cont") && "continue".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.startsWith("sig") && "signal".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.startsWith("j") && "jump".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } else if (operation.equals("r") || operation.equals("run")) { //$NON-NLS-1$ //$NON-NLS-2$ + type = MIRunningEvent.CONTINUE; + } + return type; + } + + /** + * Return true if the operation is a stepping operation. + * + * @param operation + * @return + */ + public static boolean isSteppingOperation(String operation) { + int type = getSteppingOperationKind(operation); + return type != -1; + } + + private boolean isSettingBreakpoint(String operation) { + boolean isbreak = false; + /* breakpoints: b, break, hbreak, tbreak, rbreak, thbreak */ + /* watchpoints: watch, rwatch, awatch, tbreak, rbreak, thbreak */ + if ((operation.startsWith("b") && "break".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("tb") && "tbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("hb") && "hbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("thb") && "thbreak".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("rb") && "rbreak".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isbreak = true; + } + return isbreak; + } + + private boolean isSettingWatchpoint(String operation) { + boolean isWatch = false; + /* watchpoints: watch, rwatch, awatch, tbreak, rbreak, thbreak */ + if ((operation.startsWith("wa") && "watch".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("rw") && "rwatch".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("aw") && "awatch".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isWatch = true; + } + return isWatch; + } + + private boolean isDeletingBreakpoint(String operation) { + boolean isDelete = false; + /* deleting breaks: clear, delete */ + if ((operation.startsWith("cl") && "clear".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.equals("d") || (operation.startsWith("del") && "delete".indexOf(operation) != -1))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + isDelete = true; + } + return isDelete; + } + + private boolean isChangeBreakpoint(String operation) { + boolean isChange = false; + /* changing breaks: enable, disable */ + if ((operation.equals("dis") || operation.equals("disa") || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("disa") && "disable".indexOf(operation) != -1)) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.equals("en") || (operation.startsWith("en") && "enable".indexOf(operation) != -1)) || //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + (operation.startsWith("ig") && "ignore".indexOf(operation) != -1) || //$NON-NLS-1$ //$NON-NLS-2$ + (operation.startsWith("cond") && "condition".indexOf(operation) != -1)) { //$NON-NLS-1$ //$NON-NLS-2$ + isChange = true; + } + return isChange; + } + + private boolean isSettingSignal(String operation) { + boolean isChange = false; + /* changing signal: handle, signal */ + if (operation.startsWith("ha") && "handle".indexOf(operation) != -1) { //$NON-NLS-1$ //$NON-NLS-2$ + isChange = true; + } + return isChange; + } + + /** + * @param operation + * @return + */ + private boolean isDetach(String operation) { + return (operation.startsWith("det") && "detach".indexOf(operation) != -1); //$NON-NLS-1$ //$NON-NLS-2$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/LargePipedInputStream.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/LargePipedInputStream.java new file mode 100644 index 00000000000..a23f3077b24 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/LargePipedInputStream.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + + +class LargePipedInputStream extends PipedInputStream { + + private final int LARGE_BUF_SIZE = 1024 * 1024; // 1 megs + + public LargePipedInputStream(PipedOutputStream pipedoutputstream) + throws IOException + { + super(pipedoutputstream); + buffer = new byte[LARGE_BUF_SIZE]; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java new file mode 100644 index 00000000000..a61b42e5d4c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIBackendCLIProcess.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIBackend; +import org.eclipse.cdt.dsf.mi.service.IMIBackend.BackendStateChangedEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * CLI Process object implementation which uses the {@link IMIBackend} service + * to monitor and control the underlying process. + * + * @since 1.1 + */ +public class MIBackendCLIProcess extends AbstractCLIProcess { + + private IMIBackend fMIBackend; + private AtomicInteger fExitCode = new AtomicInteger(-1); + private BackedExitedEventListener fExitedEventListener; + + @ConfinedToDsfExecutor("getSession()#getExecutor") + public MIBackendCLIProcess(ICommandControlService commandControl, IMIBackend backend) throws IOException { + super(commandControl); + fMIBackend = backend; + if (fMIBackend.getState() == IMIBackend.State.TERMINATED) { + fExitCode.set(fMIBackend.getExitCode()); + } + fExitedEventListener = new BackedExitedEventListener(); + getSession().addServiceEventListener(fExitedEventListener, null); + } + + public class BackedExitedEventListener { + private final List fWaitForRMs = new ArrayList(); + + @DsfServiceEventHandler + public void eventDispatched(BackendStateChangedEvent event) { + if (event.getState() == IMIBackend.State.TERMINATED && + event.getBackendId().equals(fMIBackend.getId())) + { + fExitCode.set(fMIBackend.getExitCode()); + for (RequestMonitor rm : fWaitForRMs) { + rm.done(); + } + fWaitForRMs.clear(); + } + } + + void dispose() { + for (RequestMonitor rm : fWaitForRMs) { + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Backend terminate event never received", null)); //$NON-NLS-1$ + rm.done(); + } + fWaitForRMs.clear(); + } + } + + /** + * @see java.lang.Process#waitFor() + */ + @Override + public int waitFor() throws InterruptedException { + if (!DsfSession.isSessionActive(getSession().getId())) { + return fExitCode.get(); + } + + try { + Query query = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + if ( !DsfSession.isSessionActive(getSession().getId()) || + isDisposed() || + fMIBackend.getState() == IMIBackend.State.TERMINATED ) + { + rm.setData(new Object()); + rm.done(); + } else { + fExitedEventListener.fWaitForRMs.add( + new RequestMonitor(ImmediateExecutor.getInstance(), rm) { + @Override + protected void handleSuccess() { + rm.setData(new Object()); + rm.done(); + } + }); + } + } + }; + getSession().getExecutor().execute(query); + query.get(); + } catch (RejectedExecutionException e) { + } catch (ExecutionException e) { + } + return fExitCode.get(); + } + + + /** + * @see java.lang.Process#exitValue() + */ + @Override + public int exitValue() { + if (!DsfSession.isSessionActive(getSession().getId())) { + return fExitCode.get(); + } + try { + getSession().getExecutor().submit(new Callable() { + public Object call() throws Exception { + if (fMIBackend.getState() != IMIBackend.State.TERMINATED) { + throw new IllegalThreadStateException("Backend Process has not exited"); //$NON-NLS-1$ + } + return null; + }}).get(); + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (ExecutionException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException)e.getCause(); + } + } + return fExitCode.get(); + } + /** + * @see java.lang.Process#destroy() + */ + @Override + public void destroy() { + try { + getSession().getExecutor().execute(new DsfRunnable() { public void run() { + if (!DsfSession.isSessionActive(getSession().getId())) return; + if (isDisposed()) return; + + fMIBackend.destroy(); + }}); + } catch (RejectedExecutionException e) { + // Session disposed. + } + } + + @Override + public void dispose() { + fExitedEventListener.dispose(); + getSession().removeServiceEventListener(fExitedEventListener); + super.dispose(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIControlDMContext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIControlDMContext.java new file mode 100644 index 00000000000..7292890f7d7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIControlDMContext.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.osgi.framework.Constants; + +/** + * + */ +public class MIControlDMContext extends AbstractDMContext + implements ICommandControlDMContext +{ + final static String PROP_INSTANCE_ID = GdbPlugin.PLUGIN_ID + ".miControlInstanceId"; //$NON-NLS-1$ + + private final String fCommandControlFilter; + private final String fCommandControlId; + + public MIControlDMContext(String sessionId, String commandControlId) { + this(sessionId, DMContexts.EMPTY_CONTEXTS_ARRAY, commandControlId); + } + + public MIControlDMContext(String sessionId, IDMContext[] parents, String commandControlId) { + super(sessionId, parents); + + fCommandControlId = commandControlId; + fCommandControlFilter = + "(&" + //$NON-NLS-1$ + "(" + Constants.OBJECTCLASS + "=" + ICommandControl.class.getName() + ")" + //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + "(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")" + //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + "(" + PROP_INSTANCE_ID + "=" + commandControlId + ")" + //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + ")"; //$NON-NLS-1$ + } + + public String getCommandControlFilter() { + return fCommandControlFilter; + } + + /** + * @since 1.1 + */ + public String getCommandControlId() { + return fCommandControlId; + } + + @Override + public boolean equals(Object obj) { + return baseEquals(obj) && fCommandControlId.equals(((MIControlDMContext)obj).fCommandControlId); + } + + @Override + public int hashCode() { + return baseHashCode() + fCommandControlId.hashCode(); + } + + @Override + public String toString() { + return baseToString() + fCommandControlId; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java new file mode 100644 index 00000000000..6a7e99bebe9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIInferiorProcess.java @@ -0,0 +1,545 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Hewlett-Packard Development Company - fix for bug 109733 + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.MIProcesses.ContainerExitedDMEvent; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLICommand; +import org.eclipse.cdt.dsf.mi.service.command.commands.CLIExecAbort; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIGDBShowExitCode; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIExecAsyncOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowExitCodeInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MITargetStreamOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.utils.pty.PTY; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * This Process implementation tracks the process that is being debugged + * by GDB. The process object is displayed in Debug view and is used to + * channel the STDIO of the interior process to the console view. + * + * @see org.eclipse.debug.core.model.IProcess + */ +public class MIInferiorProcess extends Process + implements IEventListener, ICommandListener +{ + + /** + * Event indicating that the GDB inferior process has started. This event + * implements the {@link IStartedDMEvent} from the IRunControl service. + * @deprecated + */ + @Deprecated + public static class InferiorStartedDMEvent extends AbstractDMEvent + implements IStartedDMEvent + { + public InferiorStartedDMEvent(IExecutionDMContext context) { + super(context); + } + } + + /** + * Event indicating that the GDB inferior process has exited. This event + * implements the {@link IExitedDMEvent} from the IRunControl service. + * @deprecated + */ + @Deprecated + public static class InferiorExitedDMEvent extends AbstractDMEvent + implements IExitedDMEvent + { + public InferiorExitedDMEvent(IExecutionDMContext context) { + super(context); + } + } + + public enum State { RUNNING, STOPPED, TERMINATED } + + private final OutputStream fOutputStream; + private final InputStream fInputStream; + + private final PipedOutputStream fInputStreamPiped; + + private final PipedInputStream fErrorStream; + private final PipedOutputStream fErrorStreamPiped; + + private final DsfSession fSession; + private final PTY fPty; + + private final ICommandControlService fCommandControl; + + private IContainerDMContext fContainerDMContext; + + @ConfinedToDsfExecutor("fSession#getExecutor") + private boolean fDisposed = false; + + /** + * Counter for tracking console commands sent by services. + * + * The CLI 'monitor' command produces target output which should + * not be written to the target console, since it is in response to a CLI + * command. In fact, CLI commands should never have their output sent + * to the target console. + * + * This counter is incremented any time a CLI command is seen. It is + * decremented whenever a CLI command is finished. When counter + * value is 0, the inferior process writes the target output. + */ + private int fSuppressTargetOutputCounter = 0; + + Integer fExitCode = null; + + private State fState = State.RUNNING; + + private String fInferiorPid = null; + + /** + * Creates an inferior process object which uses the given output stream + * to write the user standard input into. + * + * @param commandControl Command control that this inferior process belongs to. + * @param inferiorExecCtx The execution context controlling the execution + * state of the inferior process. + * @param gdbOutputStream The output stream to use to write user IO into. + * @since 1.1 + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + public MIInferiorProcess(ICommandControlService commandControl, OutputStream gdbOutputStream) { + this(commandControl, gdbOutputStream, null); + } + + @Deprecated + public MIInferiorProcess(AbstractMIControl commandControl, IExecutionDMContext inferiorExecCtx, OutputStream gdbOutputStream) { + this(commandControl, gdbOutputStream, null); + } + + /** + * @deprecated {@link #MIInferiorProcess(ICommandControlService, IExecutionDMContext, OutputStream)} + * should be used instead. + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + @Deprecated + public MIInferiorProcess(AbstractMIControl commandControl, OutputStream gdbOutputStream) { + this(commandControl, gdbOutputStream, null); + } + + /** + * Creates an inferior process object which uses the given terminal + * to write the user standard input into. + * + * @param commandControl Command control that this inferior process belongs to. + * @param inferiorExecCtx The execution context controlling the execution + * state of the inferior process. + * @param p The terminal to use to write user IO into. + * @since 1.1 + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + public MIInferiorProcess(ICommandControlService commandControl, PTY p) { + this(commandControl, null, p); + } + + @Deprecated + public MIInferiorProcess(AbstractMIControl commandControl, IExecutionDMContext inferiorExecCtx, PTY p) { + this(commandControl, (OutputStream)null, p); + } + + /** + * @deprecated Should use {@link #MIInferiorProcess(ICommandControlService, IExecutionDMContext, PTY)} + * instead. + */ + @ConfinedToDsfExecutor("fSession#getExecutor") + @Deprecated + public MIInferiorProcess(AbstractMIControl commandControl, PTY p) { + this(commandControl, (OutputStream)null, p); + } + + @ConfinedToDsfExecutor("fSession#getExecutor") + private MIInferiorProcess(ICommandControlService commandControl, final OutputStream gdbOutputStream, PTY p) { + fCommandControl = commandControl; + fSession = commandControl.getSession(); + + commandControl.addEventListener(this); + commandControl.addCommandListener(this); + + fPty = p; + if (fPty != null) { + fOutputStream = fPty.getOutputStream(); + fInputStream = fPty.getInputStream(); + fInputStreamPiped = null; + } else { + fOutputStream = new OutputStream() { + @Override + public void write(int b) throws IOException { + // Have to re-dispatch to dispatch thread to check state + if (getState() != State.RUNNING) { + throw new IOException("Target is not running"); //$NON-NLS-1$ + } + gdbOutputStream.write(b); + } + }; + + fInputStreamPiped = new PipedOutputStream(); + PipedInputStream inputStream = null; + try { + // Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154 + inputStream = new LargePipedInputStream(fInputStreamPiped); + } catch (IOException e) { + } + fInputStream = inputStream; + + } + + // Note: We do not have any err stream from gdb/mi so this gdb + // err channel instead. + fErrorStreamPiped = new PipedOutputStream(); + PipedInputStream errorStream = null; + try { + // Using a LargePipedInputStream see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223154 + errorStream = new LargePipedInputStream(fErrorStreamPiped); + } catch (IOException e) { + } + fErrorStream = errorStream; + } + + @ConfinedToDsfExecutor("fSession#getExecutor") + public void dispose() { + fCommandControl.removeEventListener(this); + fCommandControl.removeCommandListener(this); + + closeIO(); + + setState(State.TERMINATED); + + fDisposed = true; + } + + protected DsfSession getSession() { + return fSession; + } + + @Deprecated + protected AbstractMIControl getCommandControl() { return (AbstractMIControl)fCommandControl; } + + /** + * @since 1.1 + */ + protected ICommandControlService getCommandControlService() { return fCommandControl; } + + protected boolean isDisposed() { return fDisposed; } + + @Override + public OutputStream getOutputStream() { + return fOutputStream; + } + + @Override + public InputStream getInputStream() { + return fInputStream; + } + + @Override + public InputStream getErrorStream() { + return fErrorStream; + } + + public synchronized void waitForSync() throws InterruptedException { + while (getState() != State.TERMINATED) { + wait(100); + } + } + + /** + * @see java.lang.Process#waitFor() + */ + @Override + public int waitFor() throws InterruptedException { + waitForSync(); + return exitValue(); + } + + /** + * @see java.lang.Process#exitValue() + */ + @Override + public int exitValue() { + if (fExitCode != null) { + return fExitCode; + } + + try { + Query exitCodeQuery = new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + // Guard against session disposed. + if (!DsfSession.isSessionActive(fSession.getId())) { + cancel(false); + return; + } + + if (isDisposed()) { + rm.setData(0); + rm.done(); + } else if (getState() != State.TERMINATED) { + // This will cause ExecutionException to be thrown with a CoreException, + // which will in turn contain the IllegalThreadStateException. + rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "GDB is still running.", new IllegalThreadStateException())); //$NON-NLS-1$ + rm.done(); + } else { + getCommandControlService().queueCommand( + new MIGDBShowExitCode(getCommandControlService().getContext()), + new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + rm.setData(getData().getCode()); + rm.done(); + } + }); + + } + } + }; + fSession.getExecutor().execute(exitCodeQuery); + fExitCode = exitCodeQuery.get(); + return fExitCode; + } catch (RejectedExecutionException e) { + } catch (InterruptedException e) { + } catch (CancellationException e) { + } catch (ExecutionException e) { + // Che + if (e.getCause() instanceof CoreException && + ((CoreException)e.getCause()).getStatus().getException() instanceof RuntimeException ) + { + throw (RuntimeException)((CoreException)e.getCause()).getStatus().getException(); + } + } + return 0; + } + + /** + * @see java.lang.Process#destroy() + */ + @Override + public void destroy() { + try { + fSession.getExecutor().execute(new DsfRunnable() { + public void run() { + doDestroy(); + } + }); + } catch (RejectedExecutionException e) { + // Session disposed. + } + closeIO(); + } + + private void closeIO() { + try { + fOutputStream.close(); + } catch (IOException e) {} + try { + fInputStream.close(); + } catch (IOException e) {} + try { + if (fInputStreamPiped != null) fInputStreamPiped.close(); + } catch (IOException e) {} + try { + fErrorStream.close(); + } catch (IOException e) {} + try { + fErrorStreamPiped.close(); + } catch (IOException e) {} + } + + @ConfinedToDsfExecutor("fSession#getExecutor") + private void doDestroy() { + if (isDisposed() || !fSession.isActive() || getState() == State.TERMINATED) return; + + // To avoid a RejectedExecutionException, use an executor that + // immediately executes in the same dispatch cycle. + CLIExecAbort cmd = new CLIExecAbort(getCommandControlService().getContext()); + getCommandControlService().queueCommand( + cmd, + new DataRequestMonitor(ImmediateExecutor.getInstance(), null) { + @Override + protected void handleCompleted() { + setState(MIInferiorProcess.State.TERMINATED); + } + } + ); + } + + public State getState() { + return fState; + } + + public IExecutionDMContext getExecutionContext() { + return fContainerDMContext; + } + + /** + * @since 1.1 + */ + public void setContainerContext(IContainerDMContext containerDmc) { + fContainerDMContext = containerDmc; + } + + synchronized void setState(State state) { + if (fState == State.TERMINATED) return; + fState = state; + if (fState == State.TERMINATED) { + if (fContainerDMContext != null) { + // This may not be necessary in 7.0 because of the =thread-group-exited event + getSession().dispatchEvent( + new ContainerExitedDMEvent(fContainerDMContext), + getCommandControlService().getProperties()); + } + closeIO(); + } + notifyAll(); + } + + public OutputStream getPipedOutputStream() { + return fInputStreamPiped; + } + + public OutputStream getPipedErrorStream() { + return fErrorStreamPiped; + } + + public PTY getPTY() { + return fPty; + } + + /** + * @since 1.1 + */ + public String getPid() { + return fInferiorPid; + } + + /** + * @since 1.1 + */ + public void setPid(String pid) { + fInferiorPid = pid; + } + + public void eventReceived(Object output) { + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + if (oobr instanceof MIExecAsyncOutput) { + MIExecAsyncOutput async = (MIExecAsyncOutput)oobr; + + String state = async.getAsyncClass(); + if ("stopped".equals(state)) { //$NON-NLS-1$ + boolean handled = false; + MIResult[] results = async.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("reason")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String reason = ((MIConst) value).getString(); + if ("exited-signalled".equals(reason) || "exited-normally".equals(reason) || "exited".equals(reason)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + setState(State.TERMINATED); + } else { + setState(State.STOPPED); + } + handled = true; + } + } + } + + if (!handled) { + setState(State.STOPPED); + } + } + } else if (oobr instanceof MITargetStreamOutput) { + if (fSuppressTargetOutputCounter > 0) return; + MITargetStreamOutput tgtOut = (MITargetStreamOutput)oobr; + if (fInputStreamPiped != null && tgtOut.getString() != null) { + try { + fInputStreamPiped.write(tgtOut.getString().getBytes()); + fInputStreamPiped.flush(); + } catch (IOException e) { + } + } + } + } + } + + public void commandQueued(ICommandToken token) { + // No action + } + + public void commandSent(ICommandToken token) { + if (token.getCommand() instanceof CLICommand) { + fSuppressTargetOutputCounter++; + } + } + + public void commandRemoved(ICommandToken token) { + // No action + } + + public void commandDone(ICommandToken token, ICommandResult result) { + if (token.getCommand() instanceof CLICommand) { + fSuppressTargetOutputCounter--; + } + + MIInfo cmdResult = (MIInfo) result ; + MIOutput output = cmdResult.getMIOutput(); + MIResultRecord rr = output.getMIResultRecord(); + + // Check if the state changed. + String state = rr.getResultClass(); + + if ("running".equals(state)) { setState(State.RUNNING); }//$NON-NLS-1$ + else if ("exit".equals(state)) { setState(State.TERMINATED); }//$NON-NLS-1$ + else if ("error".equals(state)) { setState(State.STOPPED); }//$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor.java new file mode 100644 index 00000000000..caa2f6fa7f5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor.java @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Additional handling of events + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNextInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecReturn; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStepInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorSignalExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MILocationReachedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISteppingRangeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIExecAsyncOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +/** + * MI debugger output listener that listens for the parsed MI output, and + * generates corresponding MI events. The generated MI events are then + * received by other services and clients. + */ +public class MIRunControlEventProcessor + implements IEventListener, ICommandListener +{ + private static final String STOPPED_REASON = "stopped"; //$NON-NLS-1$ + + /** + * The connection service that this event processor is registered with. + */ + private final AbstractMIControl fCommandControl; + + /** + * Container context used as the context for the run control events generated + * by this processor. + */ + private final ICommandControlDMContext fControlDmc; + + private final DsfServicesTracker fServicesTracker; + + /** + * Creates the event processor and registers it as listener with the debugger + * control. + * @param connection + * @param inferior + * @since 1.1 + */ + public MIRunControlEventProcessor(AbstractMIControl connection, ICommandControlDMContext controlDmc) { + fCommandControl = connection; + fControlDmc = controlDmc; + fServicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fCommandControl.getSession().getId()); + connection.addEventListener(this); + connection.addCommandListener(this); + } + + @Deprecated + public MIRunControlEventProcessor(AbstractMIControl connection, IContainerDMContext containerDmc) { + this(connection, DMContexts.getAncestorOfType(containerDmc, ICommandControlDMContext.class)); + + } + + /** + * This processor must be disposed before the control service is un-registered. + */ + public void dispose() { + fCommandControl.removeEventListener(this); + fCommandControl.removeCommandListener(this); + fServicesTracker.dispose(); + } + + public void eventReceived(Object output) { + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + List> events = new LinkedList>(); + if (oobr instanceof MIExecAsyncOutput) { + MIExecAsyncOutput exec = (MIExecAsyncOutput) oobr; + // Change of state. + String state = exec.getAsyncClass(); + if ("stopped".equals(state)) { //$NON-NLS-1$ + // Re-set the thread and stack level to -1 when stopped event is recvd. + // This is to synchronize the state between GDB back-end and AbstractMIControl. + fCommandControl.resetCurrentThreadLevel(); + fCommandControl.resetCurrentStackLevel(); + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("reason")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + String reason = ((MIConst) val).getString(); + MIEvent e = createEvent(reason, exec); + if (e != null) { + events.add(e); + continue; + } + } + } + } + // We were stopped for some unknown reason, for example + // GDB for temporary breakpoints will not send the + // "reason" ??? still fire a stopped event. + if (events.isEmpty()) { + MIEvent e = createEvent(STOPPED_REASON, exec); + if (e != null) { + events.add(e); + } + } + + for (MIEvent event : events) { + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + } + } + } + + protected MIEvent createEvent(String reason, MIExecAsyncOutput exec) { + String threadId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + + if (var.equals("thread-id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + threadId = ((MIConst)val).getString(); + } + } + } + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService == null) { + return null; + } + + String groupId = MIProcesses.UNIQUE_GROUP_ID; + + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); + + IExecutionDMContext execDmc = processContainerDmc; + if (threadId != null) { + IThreadDMContext threadDmc = procService.createThreadContext(procDmc, threadId); + execDmc = procService.createExecutionContext(processContainerDmc, threadDmc, threadId); + } + + MIEvent event = null; + if ("breakpoint-hit".equals(reason)) { //$NON-NLS-1$ + event = MIBreakpointHitEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ( + "watchpoint-trigger".equals(reason) //$NON-NLS-1$ + || "read-watchpoint-trigger".equals(reason) //$NON-NLS-1$ + || "access-watchpoint-trigger".equals(reason)) { //$NON-NLS-1$ + event = MIWatchpointTriggerEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("watchpoint-scope".equals(reason)) { //$NON-NLS-1$ + event = MIWatchpointScopeEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("end-stepping-range".equals(reason)) { //$NON-NLS-1$ + event = MISteppingRangeEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("signal-received".equals(reason)) { //$NON-NLS-1$ + event = MISignalEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("location-reached".equals(reason)) { //$NON-NLS-1$ + event = MILocationReachedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("function-finished".equals(reason)) { //$NON-NLS-1$ + event = MIFunctionFinishedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("exited-normally".equals(reason) || "exited".equals(reason)) { //$NON-NLS-1$ //$NON-NLS-2$ + event = MIInferiorExitEvent.parse(fCommandControl.getContext(), exec.getToken(), exec.getMIResults()); + } else if ("exited-signalled".equals(reason)) { //$NON-NLS-1$ + event = MIInferiorSignalExitEvent.parse(fCommandControl.getContext(), exec.getToken(), exec.getMIResults()); + } else if (STOPPED_REASON.equals(reason)) { + event = MIStoppedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } + return event; + } + + public void commandQueued(ICommandToken token) { + // Do nothing. + } + + public void commandSent(ICommandToken token) { + // Do nothing. + } + + public void commandRemoved(ICommandToken token) { + // Do nothing. + } + + public void commandDone(ICommandToken token, ICommandResult result) { + ICommand cmd = token.getCommand(); + MIInfo cmdResult = (MIInfo) result ; + MIOutput output = cmdResult.getMIOutput(); + MIResultRecord rr = output.getMIResultRecord(); + if (rr != null) { + int id = rr.getToken(); + // Check if the state changed. + String state = rr.getResultClass(); + if ("running".equals(state)) { //$NON-NLS-1$ + int type = 0; + // Check the type of command + // if it was a step instruction set state stepping + + if (cmd instanceof MIExecNext) { type = MIRunningEvent.NEXT; } + else if (cmd instanceof MIExecNextInstruction) { type = MIRunningEvent.NEXTI; } + else if (cmd instanceof MIExecStep) { type = MIRunningEvent.STEP; } + else if (cmd instanceof MIExecStepInstruction) { type = MIRunningEvent.STEPI; } + else if (cmd instanceof MIExecUntil) { type = MIRunningEvent.UNTIL; } + else if (cmd instanceof MIExecFinish) { type = MIRunningEvent.FINISH; } + else if (cmd instanceof MIExecReturn) { type = MIRunningEvent.RETURN; } + else if (cmd instanceof MIExecContinue) { type = MIRunningEvent.CONTINUE; } + else { type = MIRunningEvent.CONTINUE; } + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService != null) { + String groupId = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); + + fCommandControl.getSession().dispatchEvent( + new MIRunningEvent(processContainerDmc, id, type), fCommandControl.getProperties()); + } + } else if ("exit".equals(state)) { //$NON-NLS-1$ + // No need to do anything, terminate() will. + // Send exited? + } else if ("connected".equals(state)) { //$NON-NLS-1$ + } else if ("error".equals(state)) { //$NON-NLS-1$ + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java new file mode 100644 index 00000000000..578c8a5ec12 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/MIRunControlEventProcessor_7_0.java @@ -0,0 +1,360 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + * Ericsson - Version 7.0 + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command; + +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.debug.service.command.ICommandToken; +import org.eclipse.cdt.dsf.debug.service.command.IEventListener; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNextInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecReturn; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStepInstruction; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIInferiorSignalExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MILocationReachedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIRunningEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISignalEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MISteppingRangeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadCreatedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadExitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupCreatedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIThreadGroupExitedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIExecAsyncOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MINotifyAsyncOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; + +/** + * MI debugger output listener that listens for the parsed MI output, and + * generates corresponding MI events. The generated MI events are then + * received by other services and clients. + * @since 1.1 + */ +public class MIRunControlEventProcessor_7_0 + implements IEventListener, ICommandListener +{ + private static final String STOPPED_REASON = "stopped"; //$NON-NLS-1$ + private static final String RUNNING_REASON = "running"; //$NON-NLS-1$ + + private Integer fLastRunningCmdType = null; + /** + * The connection service that this event processor is registered with. + */ + private final AbstractMIControl fCommandControl; + + /** + * Container context used as the context for the run control events generated + * by this processor. + */ + private final ICommandControlDMContext fControlDmc; + + private final DsfServicesTracker fServicesTracker; + + /** + * Creates the event processor and registers it as listener with the debugger + * control. + * @param connection + * @param inferior + */ + public MIRunControlEventProcessor_7_0(AbstractMIControl connection, ICommandControlDMContext controlDmc) { + fCommandControl = connection; + fControlDmc = controlDmc; + fServicesTracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), fCommandControl.getSession().getId()); + connection.addEventListener(this); + connection.addCommandListener(this); + } + + /** + * This processor must be disposed before the control service is un-registered. + */ + public void dispose() { + fCommandControl.removeEventListener(this); + fCommandControl.removeCommandListener(this); + fServicesTracker.dispose(); + } + + public void eventReceived(Object output) { + for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) { + List> events = new LinkedList>(); + if (oobr instanceof MIExecAsyncOutput) { + MIExecAsyncOutput exec = (MIExecAsyncOutput) oobr; + // Change of state. + String state = exec.getAsyncClass(); + if ("stopped".equals(state)) { //$NON-NLS-1$ + // Re-set the thread and stack level to -1 when stopped event is recvd. + // This is to synchronize the state between GDB back-end and AbstractMIControl. + fCommandControl.resetCurrentThreadLevel(); + fCommandControl.resetCurrentStackLevel(); + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("reason")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + String reason = ((MIConst) val).getString(); + MIEvent e = createEvent(reason, exec); + if (e != null) { + events.add(e); + continue; + } + } + } + } + // We were stopped for some unknown reason, for example + // GDB for temporary breakpoints will not send the + // "reason" ??? still fire a stopped event. + if (events.isEmpty()) { + MIEvent e = createEvent(STOPPED_REASON, exec); + if (e != null) { + events.add(e); + } + } + + for (MIEvent event : events) { + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + else if ("running".equals(state)) { //$NON-NLS-1$ + MIEvent event = createEvent(RUNNING_REASON, exec); + if (event != null) { + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + } else if (oobr instanceof MINotifyAsyncOutput) { + // Parse the string and dispatch the corresponding event + MINotifyAsyncOutput exec = (MINotifyAsyncOutput) oobr; + String miEvent = exec.getAsyncClass(); + if ("thread-created".equals(miEvent) || "thread-exited".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ + String threadId = null; + String groupId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("group-id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst) val).getString(); + } + } else if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + threadId = ((MIConst) val).getString(); + } + } + } + + // Until GDB is officially supporting multi-process, we may not get + // a groupId. In this case, we are running single process and we'll + // need its groupId + if (groupId == null) { + groupId = MIProcesses.UNIQUE_GROUP_ID; + } + + // Here, threads are created and removed. We cannot use the IMIProcesses service + // to map a threadId to a groupId, because there would be a race condition. + // Since we have the groupId anyway, we have no problems. + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + + if (procService != null) { + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); + + MIEvent event = null; + if ("thread-created".equals(miEvent)) { //$NON-NLS-1$ + event = new MIThreadCreatedEvent(processContainerDmc, exec.getToken(), threadId); + } else if ("thread-exited".equals(miEvent)) { //$NON-NLS-1$ + event = new MIThreadExitEvent(processContainerDmc, exec.getToken(), threadId); + } + + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } else if ("thread-group-created".equals(miEvent) || "thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ //$NON-NLS-2$ + + String groupId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + if (var.equals("id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst) val).getString().trim(); + } + } + } + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (groupId != null && procService != null) { + IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); + + MIEvent event = null; + if ("thread-group-created".equals(miEvent)) { //$NON-NLS-1$ + event = new MIThreadGroupCreatedEvent(procDmc, exec.getToken(), groupId); + } else if ("thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ + event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), groupId); + } + + fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); + } + } + } + } + } + + protected MIEvent createEvent(String reason, MIExecAsyncOutput exec) { + String threadId = null; + String groupId = null; + + MIResult[] results = exec.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + + if (var.equals("thread-id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + threadId = ((MIConst)val).getString(); + } + } else if (var.equals("group-id")) { //$NON-NLS-1$ + if (val instanceof MIConst) { + groupId = ((MIConst)val).getString(); + } + } + } + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + if (procService == null) { + return null; + } + + IProcessDMContext procDmc = null; + IContainerDMContext containerDmc = null; + if (groupId == null) { + // MI does not currently provide the group-id in these events + if (threadId != null) { + containerDmc = procService.createContainerContextFromThreadId(fControlDmc, threadId); + procDmc = DMContexts.getAncestorOfType(containerDmc, IProcessDMContext.class); + } + } else { + // This code would only trigger if the groupId was provided by MI + procDmc = procService.createProcessContext(fControlDmc, groupId); + containerDmc = procService.createContainerContext(procDmc, groupId); + } + + IExecutionDMContext execDmc = containerDmc; + if (threadId != null) { + IThreadDMContext threadDmc = procService.createThreadContext(procDmc, threadId); + execDmc = procService.createExecutionContext(containerDmc, threadDmc, threadId); + } + + MIEvent event = null; + if ("breakpoint-hit".equals(reason)) { //$NON-NLS-1$ + event = MIBreakpointHitEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ( + "watchpoint-trigger".equals(reason) //$NON-NLS-1$ + || "read-watchpoint-trigger".equals(reason) //$NON-NLS-1$ + || "access-watchpoint-trigger".equals(reason)) { //$NON-NLS-1$ + event = MIWatchpointTriggerEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("watchpoint-scope".equals(reason)) { //$NON-NLS-1$ + event = MIWatchpointScopeEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("end-stepping-range".equals(reason)) { //$NON-NLS-1$ + event = MISteppingRangeEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("signal-received".equals(reason)) { //$NON-NLS-1$ + event = MISignalEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("location-reached".equals(reason)) { //$NON-NLS-1$ + event = MILocationReachedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("function-finished".equals(reason)) { //$NON-NLS-1$ + event = MIFunctionFinishedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if ("exited-normally".equals(reason) || "exited".equals(reason)) { //$NON-NLS-1$ //$NON-NLS-2$ + event = MIInferiorExitEvent.parse(fCommandControl.getContext(), exec.getToken(), exec.getMIResults()); + } else if ("exited-signalled".equals(reason)) { //$NON-NLS-1$ + event = MIInferiorSignalExitEvent.parse(fCommandControl.getContext(), exec.getToken(), exec.getMIResults()); + } else if (STOPPED_REASON.equals(reason)) { + event = MIStoppedEvent.parse(execDmc, exec.getToken(), exec.getMIResults()); + } else if (RUNNING_REASON.equals(reason)) { + // Retrieve the type of command from what we last stored + int type = MIRunningEvent.CONTINUE; + if (fLastRunningCmdType != null) { + type = fLastRunningCmdType; + fLastRunningCmdType = null; + } + event = new MIRunningEvent(execDmc, exec.getToken(), type); + } + return event; + } + + public void commandQueued(ICommandToken token) { + // Do nothing. + } + + public void commandSent(ICommandToken token) { + // Do nothing. + } + + public void commandRemoved(ICommandToken token) { + // Do nothing. + } + + public void commandDone(ICommandToken token, ICommandResult result) { + ICommand cmd = token.getCommand(); + MIInfo cmdResult = (MIInfo) result ; + MIOutput output = cmdResult.getMIOutput(); + MIResultRecord rr = output.getMIResultRecord(); + if (rr != null) { + // Check if the state changed. + String state = rr.getResultClass(); + if ("running".equals(state)) { //$NON-NLS-1$ + // Store the type of command that is the trigger for the coming + // *running event + if (cmd instanceof MIExecNext) { fLastRunningCmdType = MIRunningEvent.NEXT; } + else if (cmd instanceof MIExecNextInstruction) { fLastRunningCmdType = MIRunningEvent.NEXTI; } + else if (cmd instanceof MIExecStep) { fLastRunningCmdType = MIRunningEvent.STEP; } + else if (cmd instanceof MIExecStepInstruction) { fLastRunningCmdType = MIRunningEvent.STEPI; } + else if (cmd instanceof MIExecUntil) { fLastRunningCmdType = MIRunningEvent.UNTIL; } + else if (cmd instanceof MIExecFinish) { fLastRunningCmdType = MIRunningEvent.FINISH; } + else if (cmd instanceof MIExecReturn) { fLastRunningCmdType = MIRunningEvent.RETURN; } + else if (cmd instanceof MIExecContinue) { fLastRunningCmdType = MIRunningEvent.CONTINUE; } + else { fLastRunningCmdType = MIRunningEvent.CONTINUE; } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Adjustable.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Adjustable.java new file mode 100644 index 00000000000..4f16f5fb11d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/Adjustable.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation for bug 219920 + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +/** + * The implementor of this interface may adjust its output. + * + * This is used for MICommands where the output of each option and/or parameter + * may be adjusted independently to conform to the current version of the MI + * interface. + * + */ +public interface Adjustable { + + String getValue(); + + String getAdjustedValue(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIAttach.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIAttach.java new file mode 100644 index 00000000000..2f2fcfd31e5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIAttach.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * This command connects to a remote target. + */ +public class CLIAttach extends CLICommand { + + public CLIAttach(IDMContext ctx, int pid) { + super(ctx, "attach " + Integer.toString(pid)); //$NON-NLS-1$ + } + + /** + * @since 1.1 + */ + public CLIAttach(ICommandControlDMContext ctx, String pid) { + super(ctx, "attach " + pid); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLICommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLICommand.java new file mode 100644 index 00000000000..f78c38ad463 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLICommand.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * Represents a CLI command. + */ +public class CLICommand extends MICommand +{ + public CLICommand(IDMContext ctx, String oper) { + super(ctx, oper); + } + + @Override + public boolean supportsThreadAndFrameOptions() { return false; } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIDetach.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIDetach.java new file mode 100644 index 00000000000..d4c3361e29f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIDetach.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * This command disconnects from a remote target. + * @since 1.1 + */ +public class CLIDetach extends CLICommand { + + public CLIDetach(IDMContext ctx) { + super(ctx, "detach"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIExecAbort.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIExecAbort.java new file mode 100644 index 00000000000..e16d8d4ef63 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIExecAbort.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * kill + * + * Terminates the user (MI inferior) process + * + */ +public class CLIExecAbort extends CLICommand +{ + /** + * @since 1.1 + */ + public CLIExecAbort(ICommandControlDMContext ctx) { + super(ctx, "kill"); //$NON-NLS-1$ + } + + @Deprecated + public CLIExecAbort(MIControlDMContext ctx) { + this ((ICommandControlDMContext)ctx); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoSharedLibrary.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoSharedLibrary.java new file mode 100644 index 00000000000..c54e838973f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoSharedLibrary.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + + +import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext; +import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoSharedLibraryInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * info sharedlibrary + * + */ +public class CLIInfoSharedLibrary extends CLICommand { + + public CLIInfoSharedLibrary(ISymbolDMContext ctx) { + super(ctx, "info sharedlibrary"); //$NON-NLS-1$ + } + public CLIInfoSharedLibrary(IModuleDMContext ctx) { + super(ctx, "info sharedlibrary"); //$NON-NLS-1$ + } + @Override + public CLIInfoSharedLibraryInfo getResult(MIOutput output) { + return (CLIInfoSharedLibraryInfo)getMIInfo(output); + } + + public MIInfo getMIInfo(MIOutput out) { + MIInfo info = null; + if (out != null) { + info = new CLIInfoSharedLibraryInfo(out); + } + return info; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoThreads.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoThreads.java new file mode 100644 index 00000000000..a40892d1141 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIInfoThreads.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + + + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIInfoThreadsInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * info threads + * + */ +public class CLIInfoThreads extends CLICommand { + + public CLIInfoThreads(IContainerDMContext ctx) { + super(ctx, "info threads"); //$NON-NLS-1$ + } + + @Override + public CLIInfoThreadsInfo getResult(MIOutput output) { + return (CLIInfoThreadsInfo)getMIInfo(output); + } + + public MIInfo getMIInfo(MIOutput out) { + MIInfo info = null; + if (out != null) { + info = new CLIInfoThreadsInfo(out); + } + return info; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMonitorListProcesses.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMonitorListProcesses.java new file mode 100644 index 00000000000..b6ce092e40e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLIMonitorListProcesses.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.CLIMonitorListProcessesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * monitor list processes + * + * This command is not available in the current version of GDBServer. However it should + * be available in the future. + * + */ +public class CLIMonitorListProcesses extends CLICommand +{ + public CLIMonitorListProcesses(IDMContext ctx) { + super(ctx, "monitor list processes"); //$NON-NLS-1$ + } + + @Override + public CLIMonitorListProcessesInfo getResult(MIOutput output) { + return (CLIMonitorListProcessesInfo)getMIInfo(output); + } + + public MIInfo getMIInfo(MIOutput out) { + MIInfo info = null; + if (out != null) { + info = new CLIMonitorListProcessesInfo(out); + } + return info; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLISource.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLISource.java new file mode 100644 index 00000000000..0eb5f5493f5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/CLISource.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * source FILE + * + * Source a file of commands + * + */ +public class CLISource extends CLICommand { + /** + * @since 1.1 + */ + public CLISource(ICommandControlDMContext ctx, String file) { + super(ctx, "source " + file); //$NON-NLS-1$ + } + + @Deprecated + public CLISource(MIControlDMContext ctx, String file) { + this ((ICommandControlDMContext)ctx, file); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaCommand.java new file mode 100644 index 00000000000..a9fba2d6340 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaCommand.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +public class ExprMetaCommand implements ICommand { + + private final IDMContext fCtx; + + public ExprMetaCommand(IDMContext ctx) { + fCtx = ctx; + } + + /* + * Takes the supplied command and coalesces it with this one. + * The result is a new third command which represent the two + * original command. + */ + public ICommand coalesceWith( ICommand command ) { + return null ; + } + + public IDMContext getContext(){ + return fCtx; + } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (!(other.getClass().equals(getClass()))) return false; + + // Since other is the same class is this, we are sure it is of type DsfExprMetaCommand also + ExprMetaCommand otherCmd = (ExprMetaCommand)other; + return fCtx == null ? otherCmd.fCtx == null : fCtx.equals(otherCmd.fCtx); + } + + @Override + public int hashCode() { + return fCtx == null ? getClass().hashCode() : getClass().hashCode() ^ fCtx.hashCode(); + } + + @Override + public String toString() { + return getClass().getName() + (fCtx == null ? "null" : fCtx.toString()); //$NON-NLS-1$ + } + + public String getCommandControlFilter() { + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetAttributes.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetAttributes.java new file mode 100644 index 00000000000..577d2c46bca --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetAttributes.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo; + +public class ExprMetaGetAttributes extends ExprMetaCommand { + + public ExprMetaGetAttributes(IExpressionDMContext ctx) { + super(ctx); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java new file mode 100644 index 00000000000..ff8dafa57c5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildCount.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; + +public class ExprMetaGetChildCount extends ExprMetaCommand { + + public ExprMetaGetChildCount(IExpressionDMContext ctx) { + super(ctx); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java new file mode 100644 index 00000000000..ea0487ea31c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetChildren.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; + +public class ExprMetaGetChildren extends ExprMetaCommand { + + public ExprMetaGetChildren(IExpressionDMContext ctx) { + super(ctx); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetValue.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetValue.java new file mode 100644 index 00000000000..dd6def4bb66 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetValue.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; + +public class ExprMetaGetValue extends ExprMetaCommand { + + public ExprMetaGetValue(FormattedValueDMContext ctx) { + super(ctx); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetVar.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetVar.java new file mode 100644 index 00000000000..e344b54b6ab --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/ExprMetaGetVar.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; + +public class ExprMetaGetVar extends ExprMetaCommand { + + public ExprMetaGetVar(IExpressionDMContext ctx) { + super(ctx); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakAfter.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakAfter.java new file mode 100644 index 00000000000..92dcd8ad205 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakAfter.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -break-after NUMBER COUNT + * + * The breakpoint number NUMBER is not in effect until it has been hit + * COUNT times. The count becomes part of the `-break-list' output + * (see the description of the DsfMIBreakList). + */ + +public class MIBreakAfter extends MICommand +{ + public MIBreakAfter(IBreakpointsTargetDMContext ctx, int breakpoint, int ignoreCount) { + super(ctx, "-break-after"); //$NON-NLS-1$ + setParameters(new String[] { Integer.toString(breakpoint), Integer.toString(ignoreCount) }); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakCondition.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakCondition.java new file mode 100644 index 00000000000..6030532743a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakCondition.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -break-condition NUMBER EXPR + * + * Breakpoint NUMBER will stop the program only if the condition in + * EXPR is true. The condition becomes part of the `-break-list' output + * (see the description of the DsfMIBreakList). + */ + +public class MIBreakCondition extends MICommand +{ + // In this particular case, because of a GDB peculiarity, setParameters() is + // not used and the whole command is formatted on the parent's constructor. + // See bug 213076 for more information. + + public MIBreakCondition(IBreakpointsTargetDMContext ctx, int breakpoint, String condition) { + super(ctx, "-break-condition " + Integer.toString(breakpoint) + " " + condition); //$NON-NLS-1$ //$NON-NLS-2$ +// super(ctx, "-break-condition"); //$NON-NLS-1$ +// setParameters(new String[] { Integer.toString(breakpoint), condition }); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDelete.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDelete.java new file mode 100644 index 00000000000..d74a8d156e1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDelete.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -break-delete ( BREAKPOINT )+ + * + * Delete the breakpoint(s) whose number(s) are specified in the + * argument list. This is obviously reflected in the breakpoint list. + * + * Result: + * ^done + * + */ + +public class MIBreakDelete extends MICommand +{ + public MIBreakDelete (IBreakpointsTargetDMContext ctx, int[] array) { + super(ctx, "-break-delete"); //$NON-NLS-1$ + if (array != null && array.length > 0) { + String[] brkids = new String[array.length]; + for (int i = 0; i < array.length; i++) { + brkids[i] = Integer.toString(array[i]); + } + setParameters(brkids); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDisable.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDisable.java new file mode 100644 index 00000000000..9c0e92b06f4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakDisable.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -break-disable ( BREAKPOINT )+ + * + * Disable the named BREAKPOINT(s). The field `enabled' in the break + * list is now set to `n' for the named BREAKPOINT(s). + * + * Result: + * ^done + */ + +public class MIBreakDisable extends MICommand +{ + public MIBreakDisable (IBreakpointsTargetDMContext ctx, int[] array) { + super(ctx, "-break-disable"); //$NON-NLS-1$ + if (array != null && array.length > 0) { + String[] brkids = new String[array.length]; + for (int i = 0; i < array.length; i++) { + brkids[i] = Integer.toString(array[i]); + } + setParameters(brkids); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakEnable.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakEnable.java new file mode 100644 index 00000000000..1a6c75a44f0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakEnable.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -break-enable ( BREAKPOINT )+ + * + * Enable (previously disabled) BREAKPOINT(s). + * + * Result: + * ^done + */ + +public class MIBreakEnable extends MICommand +{ + public MIBreakEnable (IBreakpointsTargetDMContext ctx, int[] array) { + super(ctx, "-break-enable"); //$NON-NLS-1$ + if (array != null && array.length > 0) { + String[] brkids = new String[array.length]; + for (int i = 0; i < array.length; i++) { + brkids[i] = Integer.toString(array[i]); + } + setParameters(brkids); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakInsert.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakInsert.java new file mode 100644 index 00000000000..e2dfb494a63 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakInsert.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for bug 219920 + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -break-insert [ -t ] [ -h ] [ -r ] + * [ -c CONDITION ] [ -i IGNORE-COUNT ] + * [ -p THREAD ] [ LINE | ADDR ] + * + * If specified, LINE, can be one of: + * * function + * * filename:linenum + * * filename:function + * * *address + * + * The possible optional parameters of this command are: + * + * '-t' + * Insert a temporary breakpoint. + * + * '-h' + * Insert a hardware breakpoint. + * + * '-r' + * Insert a regular breakpoint in all the functions whose names match + * the given regular expression. Other flags are not applicable to + * regular expression. + * + * '-c CONDITION' + * Make the breakpoint conditional on CONDITION. + * + * '-i IGNORE-COUNT' + * Initialize the IGNORE-COUNT (number of breakpoint hits before breaking). + * + * '-p THREAD' + * THREAD on which to apply the breakpoint + */ +public class MIBreakInsert extends MICommand +{ + public MIBreakInsert(IBreakpointsTargetDMContext ctx, String func) { + this(ctx, false, false, null, 0, func, 0); + } + + public MIBreakInsert(IBreakpointsTargetDMContext ctx, boolean isTemporary, boolean isHardware, + String condition, int ignoreCount, String line, int tid) { + super(ctx, "-break-insert"); //$NON-NLS-1$ + + // Determine the number of optional parameters that are present + // and allocate a corresponding string array + int i = 0; + if (isTemporary) { + i++; + } + if (isHardware) { + i++; + } + if (condition != null && condition.length() > 0) { + i += 2; + } + if (ignoreCount > 0) { + i += 2; + } + if (tid > 0) { + i += 2; + } + String[] opts = new String[i]; + + // Fill in the optional parameters + i = 0; + if (isTemporary) { + opts[i] = "-t"; //$NON-NLS-1$ + i++; + } + if (isHardware) { + opts[i] = "-h"; //$NON-NLS-1$ + i++; + } + if (condition != null && condition.length() > 0) { + opts[i] = "-c"; //$NON-NLS-1$ + i++; + opts[i] = condition; + i++; + } + if (ignoreCount > 0) { + opts[i] = "-i"; //$NON-NLS-1$ + i++; + opts[i] = Integer.toString(ignoreCount); + i++; + } + if (tid > 0) { + opts[i] = "-p"; //$NON-NLS-1$ + i++; + opts[i] = Integer.toString(tid); + } + + if (opts.length > 0) { + setOptions(opts); + } + setParameters(new Adjustable[]{ new PathAdjustable(line)}); + } + + @Override + public MIBreakInsertInfo getResult(MIOutput output) { + return new MIBreakInsertInfo(output); + } + + /** + * This adjustable makes sure that the path parameter will not get the + * backslashes substituted with double backslashes. + */ + private class PathAdjustable + extends + org.eclipse.cdt.dsf.mi.service.command.commands.MICommand.MIStandardParameterAdjustable { + + public PathAdjustable(String path) { + super(path); + } + + @Override + public String getAdjustedValue() { + String adjustedValue = super.getAdjustedValue(); + return adjustedValue.replace("\\\\", "\\"); //$NON-NLS-1$//$NON-NLS-2$ + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakList.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakList.java new file mode 100644 index 00000000000..81f8a674ba1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakList.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * -break-list + * + * Displays the list of inserted breakpoints, showing the following + * fields: + * + * `Number' + * number of the breakpoint + * + * `State' + * type of the breakpoint: `breakpoint' or `watchpoint' + * + * `Disposition' + * should the breakpoint be deleted or disabled when it is hit: `keep' + * or `nokeep' + * + * `Enabled' + * is the breakpoint enabled or no: `y' or `n' + * + * `Address' + * memory location at which the breakpoint is set + * + * `What' + * logical location of the breakpoint, expressed by function name, + * + * `Times' + * number of times the breakpoint has been hit + * + * If there are no breakpoints or watchpoints, the `BreakpointTable' + * `body' field is an empty list. + * + */ +public class MIBreakList extends MICommand +{ + public MIBreakList (IBreakpointsTargetDMContext ctx) { + super(ctx, "-break-list"); //$NON-NLS-1$ + } + + @Override + public MIBreakListInfo getResult(MIOutput output) { + return new MIBreakListInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakWatch.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakWatch.java new file mode 100644 index 00000000000..c0d18f26a31 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIBreakWatch.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -break-watch [ -a | -r ] LOCATION + * + * Create a watchpoint on LOCATION. Without either of the options, the watchpoint + * created is a regular watchpoint, i.e., a watchpoint that triggers when the + * memory LOCATION is accessed for writing. + + * The possible optional parameters of this command are: + * + * '-a' + * Creates an access watchpoint i.e. a watchpoint that triggers either + * on a read from or on a write to the memory location. + * + * '-r' + * Creates a read watchpoint i.e. a watchpoint that triggers only when + * the memory location is accessed for reading. + */ +public class MIBreakWatch extends MICommand +{ + public MIBreakWatch(IBreakpointsTargetDMContext ctx, boolean isRead, boolean isWrite, String expression) + { + super(ctx, "-break-watch"); //$NON-NLS-1$ + + if (isRead) { + if (isWrite) + setOptions(new String[] { "-a" }); //$NON-NLS-1$ + else + setOptions(new String[] { "-r" }); //$NON-NLS-1$ + } + + setParameters(new String[]{ expression }); + } + + @Override + public MIBreakInsertInfo getResult(MIOutput output) { + return new MIBreakInsertInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MICommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MICommand.java new file mode 100644 index 00000000000..a389d372ea7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MICommand.java @@ -0,0 +1,351 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference implementation and bug 219920 + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * Represents any MI command. + */ +public class MICommand implements ICommand { + + /* + * Variables. + */ + final static String[] empty = new String[0]; + + List fOptions = new ArrayList(); + List fParameters = new ArrayList(); + String fOperation = new String(); + IDMContext fCtx; + + /* + * Constructors. + */ + + public MICommand(IDMContext ctx, String operation) { + this(ctx, operation, empty, empty); + } + + public MICommand(IDMContext ctx, String operation, String[] options) { + this(ctx, operation, options, empty); + } + + public MICommand(IDMContext ctx, String operation, String[] options, String[] params) { + assert(ctx != null && DMContexts.getAncestorOfType(ctx, MIControlDMContext.class) != null); + fCtx = ctx; + fOperation = operation; + fOptions = optionsToAdjustables(options); + fParameters = parametersToAdjustables(params); + } + + private final List optionsToAdjustables(String[] options) { + List result = new ArrayList(); + if (options != null) { + for (String option : options) { + result.add(new MIStandardOptionAdjustable(option)); + } + } + return result; + } + + private final List parametersToAdjustables(String[] parameters) { + List result = new ArrayList(); + if (parameters != null) { + for (String parameter : parameters) { + result.add(new MIStandardParameterAdjustable(parameter)); + } + } + return result; + } + + public String getCommandControlFilter() { + MIControlDMContext controlDmc = DMContexts.getAncestorOfType(getContext(), MIControlDMContext.class); + return controlDmc.getCommandControlFilter(); + } + + /* + * Returns the operation of this command. + */ + public String getOperation() { + return fOperation; + } + + /* + * Returns an array of command's options. An empty collection is + * returned if there are no options. + */ + public String[] getOptions() { + List result = new ArrayList(); + for (Adjustable option : fOptions) { + result.add(option.getValue()); + } + return result.toArray(new String[fOptions.size()]); + } + + public void setOptions(String[] options) { + fOptions = optionsToAdjustables(options); + } + + /* + * Returns an array of command's parameters. An empty collection is + * returned if there are no parameters. + */ + public String[] getParameters() { + List result = new ArrayList(); + for (Adjustable parameter : fParameters) { + result.add(parameter.getValue()); + } + return result.toArray(new String[fParameters.size()]); + } + + public void setParameters(String[] params) { + fParameters = parametersToAdjustables(params); + } + + public void setParameters(Adjustable... params) { + fParameters = Arrays.asList(params); + } + + /* + * Returns the constructed command without using the --thread/--frame options. + */ + public String constructCommand() { + return constructCommand(null, -1); + } + /* + * Returns the constructed command potentially using the --thread/--frame options. + */ + /** + * @since 1.1 + */ + public String constructCommand(String threadId, int frameId) { + StringBuffer command = new StringBuffer(getOperation()); + + // Add the --thread option + if (threadId != null) { + command.append(" --thread " + threadId); //$NON-NLS-1$ + + // Add the --frame option, but only if we are using the --thread option + if (frameId >= 0) { + command.append(" --frame " + frameId); //$NON-NLS-1$ + } + } + + String opt = optionsToString(); + if (opt.length() > 0) { + command.append(' ').append(opt); + } + String p = parametersToString(); + if (p.length() > 0) { + command.append(' ').append(p); + } + command.append('\n'); + return command.toString(); + } + +// /* +// * Checks to see if the current command can be coalesced with the +// * supplied command. +// */ +// public boolean canCoalesce( ICommand command ) { +// return false ; +// } + + /* + * Takes the supplied command and coalesces it with this one. + * The result is a new third command which represent the two + * original command. + */ + public ICommand coalesceWith( ICommand command ) { + return null ; + } + + + public IDMContext getContext(){ + return fCtx; + } + /** + * Produces the corresponding ICommandResult result for this + * command. + * + * @return result for this command + */ + public MIInfo getResult(MIOutput MIresult) { + return ( new MIInfo(MIresult) ); + } + + protected String optionsToString() { + StringBuffer sb = new StringBuffer(); + if (fOptions != null && fOptions.size() > 0) { + for (Adjustable option : fOptions) { + sb.append(option.getAdjustedValue()); + } + } + return sb.toString().trim(); + } + + protected String parametersToString() { + String[] options = getOptions(); + StringBuffer buffer = new StringBuffer(); + if (fParameters != null && fParameters.size() > 0) { + // According to GDB/MI spec + // Add a "--" separator if any parameters start with "-" + if (options != null && options.length > 0) { + for (Adjustable parameter : fParameters) { + if (parameter.getValue().startsWith("-")) {//$NON-NLS-1$ + buffer.append('-').append('-'); + break; + } + } + } + + for (Adjustable parameter : fParameters) { + buffer.append(' ').append(parameter.getAdjustedValue()); + } + } + return buffer.toString().trim(); + } + + protected static boolean containsWhitespace(String s) { + for (int i = 0; i < s.length(); i++) { + if (Character.isWhitespace(s.charAt(i))) { + return true; + } + } + return false; + } + + /** + * @since 1.1 + */ + public boolean supportsThreadAndFrameOptions() { return true; } + + /** + * Compare commands based on the MI command string that they generate, + * without the token. + */ + @Override + public boolean equals(Object obj) { + if(obj instanceof MICommand){ + MICommand otherCmd = (MICommand)obj; + return ((fCtx == null && otherCmd.fCtx == null) || (fCtx != null && fCtx.equals(otherCmd.fCtx))) && + constructCommand().equals(otherCmd.constructCommand()); + } + return false; + } + + @Override + public int hashCode() { + return constructCommand().hashCode(); + } + + @Override + public String toString() { + return constructCommand(); + } + + public static class MIStandardOptionAdjustable extends MICommandAdjustable { + + public MIStandardOptionAdjustable(String option) { + super(option); + } + + public String getAdjustedValue() { + StringBuilder builder = new StringBuilder(); + String option = value; + // If the option argument contains " or \ it must be escaped + if (option.indexOf('"') != -1 || option.indexOf('\\') != -1) { + StringBuilder buf = new StringBuilder(); + for (int j = 0; j < option.length(); j++) { + char c = option.charAt(j); + if (c == '"' || c == '\\') { + buf.append('\\'); + } + buf.append(c); + } + option = buf.toString(); + } + + // If the option contains a space according to + // GDB/MI spec we must surround it with double quotes. + if (option.indexOf('\t') != -1 || option.indexOf(' ') != -1) { + builder.append(' ').append('"').append(option).append('"'); + } else { + builder.append(' ').append(option); + } + return builder.toString(); + } + } + + public static class MIStandardParameterAdjustable extends + MICommandAdjustable { + public MIStandardParameterAdjustable(String parameter) { + super(parameter); + } + + public String getAdjustedValue() { + StringBuilder builder = new StringBuilder(); + for (int j = 0; j < value.length(); j++) { + char c = value.charAt(j); + if (c == '"' || c == '\\') { + builder.append('\\'); + } + builder.append(c); + } + + // If the string contains spaces instead of escaping + // surround the parameter with double quotes. + if (containsWhitespace(value)) { + builder.insert(0, '"'); + builder.append('"'); + } + + return builder.toString(); + } + } + + public static abstract class MICommandAdjustable implements Adjustable { + + protected final String value; + + /** + * Creates a new instance. + * + * @param builder + * The string builder is an optimization option, if two + * commands are not processed at the same time a shared + * builder can be used to save memory. + * @param value + * The value that should be adjusted. + */ + public MICommandAdjustable(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataDisassemble.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataDisassemble.java new file mode 100644 index 00000000000..cc76d5e29c9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataDisassemble.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataDisassembleInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -data-disassemble + * [ -s START-ADDR -e END-ADDR ] + * | [ -f FILENAME -l LINENUM [ -n LINES ] ] + * -- MODE + * + * Where: + * + * '-s START-ADDR' + * is the beginning address (or '$pc') + * + * '-e END-ADDR' + * is the end address + * + * '-f FILENAME' + * is the name of the file to disassemble + * + * '-l LINENUM' + * is the line number to disassemble around + * + * -n LINES' + * is the the number of disassembly lines to be produced. If it is + * -1, the whole function will be disassembled, in case no END-ADDR is + * specified. If END-ADDR is specified as a non-zero value, and + * LINES is lower than the number of disassembly lines between + * START-ADDR and END-ADDR, only LINES lines are displayed; if LINES + * is higher than the number of lines between START-ADDR and + * END-ADDR, only the lines up to END-ADDR are displayed. + * + * '-- MODE' + * is either 0 (meaning only disassembly) or 1 (meaning mixed source + * and disassembly). + */ + +public class MIDataDisassemble extends MICommand { + + public MIDataDisassemble(IDisassemblyDMContext ctx, String start, String end, boolean mode) { + super(ctx, "-data-disassemble"); //$NON-NLS-1$ + setOptions(new String[]{"-s", start, "-e", end}); //$NON-NLS-1$ //$NON-NLS-2$ + String mixed = "0"; //$NON-NLS-1$ + if (mode) { + mixed = "1"; //$NON-NLS-1$ + } + setParameters(new String[]{mixed}); + } + + public MIDataDisassemble(IDisassemblyDMContext ctx, String file, int linenum, int lines, boolean mode) { + super(ctx, "-data-disassemble"); //$NON-NLS-1$ + setOptions(new String[]{"-f", file, "-l", //$NON-NLS-1$ //$NON-NLS-2$ + Integer.toString(linenum), "-n", Integer.toString(lines)}); //$NON-NLS-1$ + String mixed = "0"; //$NON-NLS-1$ + if (mode) { + mixed = "1"; //$NON-NLS-1$ + } + setParameters(new String[]{mixed}); + } + + /* + * GDB the -data-disassemble uses "--" as a separator wit only the MODE + * So override the MICommand + */ + @Override + protected String parametersToString() { + String[] parameters = getParameters(); + if (parameters != null && parameters.length > 0) { + return "-- " + parameters[0]; //$NON-NLS-1$ + } + return new String(); + } + + @Override + public MIDataDisassembleInfo getResult(MIOutput output) { + return new MIDataDisassembleInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataEvaluateExpression.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataEvaluateExpression.java new file mode 100644 index 00000000000..a0c27e67341 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataEvaluateExpression.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * -data-evaluate-expression EXPR + * + * Evaluate EXPR as an expression. The expression could contain an + *inferior function call. The function call will execute synchronously. + *If the expression contains spaces, it must be enclosed in double quotes. + * + */ +public class MIDataEvaluateExpression extends MICommand +{ + /** + * @since 1.1 + */ + public MIDataEvaluateExpression(ICommandControlDMContext ctx, String expr) { + super(ctx, "-data-evaluate-expression", new String[]{expr}); //$NON-NLS-1$ + } + + @Deprecated + public MIDataEvaluateExpression(MIControlDMContext ctx, String expr) { + this ((ICommandControlDMContext)ctx, expr); + } + + + public MIDataEvaluateExpression(IMIExecutionDMContext execDmc, String expr) { + super(execDmc, "-data-evaluate-expression", new String[]{expr}); //$NON-NLS-1$ + } + + public MIDataEvaluateExpression(IFrameDMContext frameDmc, String expr) { + super(frameDmc, "-data-evaluate-expression", new String[]{expr}); //$NON-NLS-1$ + } + + public MIDataEvaluateExpression(IExpressionDMContext exprDmc) { + super(exprDmc, "-data-evaluate-expression", new String[]{exprDmc.getExpression()}); //$NON-NLS-1$ + } + + @Override + public MIDataEvaluateExpressionInfo getResult(MIOutput output) { + return new MIDataEvaluateExpressionInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterNames.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterNames.java new file mode 100644 index 00000000000..72017966af1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterNames.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterNamesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * -data-list-register-names [ ( REGNO )+ ] + * + * Show a list of register names for the current target. If no + * arguments are given, it shows a list of the names of all the registers. + * If integer numbers are given as arguments, it will print a list of the + * names of the registers corresponding to the arguments. To ensure + * consistency between a register name and its number, the output list may + * include empty register names. + * + */ +public class MIDataListRegisterNames extends MICommand +{ + public MIDataListRegisterNames(IContainerDMContext ctx) { + super(ctx, "-data-list-register-names"); //$NON-NLS-1$ + } + + public MIDataListRegisterNames(IContainerDMContext ctx, int [] regnos) { + this(ctx); + if (regnos != null && regnos.length > 0) { + String[] array = new String[regnos.length]; + for (int i = 0; i < regnos.length; i++) { + array[i] = Integer.toString(regnos[i]); + } + setParameters(array); + } + } + + @Override + public MIDataListRegisterNamesInfo getResult(MIOutput output) { + return new MIDataListRegisterNamesInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterValues.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterValues.java new file mode 100644 index 00000000000..514cc2e5d61 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataListRegisterValues.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIFormat; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataListRegisterValuesInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * + * -data-list-register-values FMT [ ( REGNO )*] + * + * Display the registers' contents. FMT is the format according to + * which the registers' contents are to be returned, followed by an + * optional list of numbers specifying the registers to display. A + * missing list of numbers indicates that the contents of all the + * registers must be returned. + * + */ +public class MIDataListRegisterValues extends MICommand { + + int[] regnums; + int fFmt; + + public MIDataListRegisterValues(IMIExecutionDMContext ctx, int fmt) { + this(ctx, fmt, null); + } + + public MIDataListRegisterValues(IMIExecutionDMContext ctx, int fmt, int [] regnos) { + super(ctx, "-data-list-register-values"); //$NON-NLS-1$ + regnums = regnos; + + String format = "x"; //$NON-NLS-1$ + switch (fmt) { + case MIFormat.NATURAL: format = "N"; break ; //$NON-NLS-1$ + case MIFormat.RAW: format = "r"; break ; //$NON-NLS-1$ + case MIFormat.DECIMAL: format = "d"; break ; //$NON-NLS-1$ + case MIFormat.BINARY: format = "t"; break ; //$NON-NLS-1$ + case MIFormat.OCTAL: format = "o"; break ; //$NON-NLS-1$ + case MIFormat.HEXADECIMAL: format = "x"; break ; //$NON-NLS-1$ + default: format = "x"; break ; //$NON-NLS-1$ + } + + fFmt = fmt; + + setOptions(new String[]{format}); + + if (regnos != null && regnos.length > 0) { + String[] array = new String[regnos.length]; + for (int i = 0; i < regnos.length; i++) { + array[i] = Integer.toString(regnos[i]); + } + setParameters(array); + } + } + + public int[] getRegList() { + return regnums; + } + + @Override + public MIDataListRegisterValuesInfo getResult(MIOutput output) { + return new MIDataListRegisterValuesInfo(output); + } + + /* + * Takes the supplied command and coalesces it with this one. + * The result is a new third command which represent the two + * original command. + */ + @Override + public MIDataListRegisterValues coalesceWith(ICommand command ) { + /* + * Can coalesce only with other DsfMIDataListRegisterValues commands. + */ + if (! (command instanceof MIDataListRegisterValues) ) return null; + + MIDataListRegisterValues cmd = (MIDataListRegisterValues) command; + + /* + * If the format is different then this cannot be added to the list. + */ + if ( fFmt != cmd.fFmt ) return null; + + int[] newregnos = new int[ regnums.length + cmd.regnums.length]; + + /* + * We need to add the new register #'s to the list. If one is already there + * then do not add it twice. So copy the original list of this command. + */ + + for ( int idx = 0 ; idx < regnums.length ; idx ++) { + newregnos[ idx ] = regnums[ idx ]; + } + + int curloc = regnums.length; + + for ( int ndx = 0 ; ndx < cmd.regnums.length; ndx ++) { + + int curnum = cmd.regnums[ ndx ] ; + int ldx; + + /* + * Search the current list to see if this entry is in it. + */ + + for ( ldx = 0 ; ldx < regnums.length; ldx ++ ) { + if ( newregnos[ ldx ] == curnum ) { + break ; + } + } + + if ( ldx == regnums.length ) { + + /* + * Since we did not find a match add it at the end of the list. + */ + newregnos[ curloc ] = curnum; + curloc ++; + } + } + + /* + * Create a final proper array set of the new combined list. + */ + int[] finalregnums = new int[ curloc ] ; + + for ( int fdx = 0 ; fdx < curloc ; fdx ++ ) { + finalregnums[ fdx ] = newregnos[ fdx ]; + } + + /* + * Now construct a new one. The format we will use is this command. + */ + return( new MIDataListRegisterValues((IMIExecutionDMContext)getContext(), fFmt, finalregnums)); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataReadMemory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataReadMemory.java new file mode 100644 index 00000000000..a1a446a0ae9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataReadMemory.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson AB - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.MIFormat; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataReadMemoryInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -data-read-memory [ -o BYTE-OFFSET ] + * ADDRESS WORD-FORMAT WORD-SIZE + * NR-ROWS NR-COLS [ ASCHAR ] + * + * where: + * + * 'ADDRESS' + * An expression specifying the address of the first memory word to be + * read. Complex expressions containing embedded white space should + * be quoted using the C convention. + * + * 'WORD-FORMAT' + * The format to be used to print the memory words. The notation is + * the same as for GDB's `print' command (*note Output formats: + * Output Formats.). + * + * 'WORD-SIZE' + * The size of each memory word in bytes. + * + * 'NR-ROWS' + * The number of rows in the output table. + * + * 'NR-COLS' + * The number of columns in the output table. + * + * 'ASCHAR' + * If present, indicates that each row should include an ASCII dump. + * The value of ASCHAR is used as a padding character when a byte is + * not a member of the printable ASCII character set (printable ASCII + * characters are those whose code is between 32 and 126, + * inclusively). + * + * 'BYTE-OFFSET' + * An offset to add to ADDRESS before fetching the memory. + * + */ +public class MIDataReadMemory extends MICommand { + + int fword_size; + + public MIDataReadMemory( + IDMContext ctx, + long offset, + String address, + int word_format, + int word_size, + int rows, + int cols, + Character asChar) + { + super(ctx, "-data-read-memory"); //$NON-NLS-1$ + + // Save this for the result parser + fword_size = word_size; + + if (offset != 0) { + setOptions(new String[] { "-o", Long.toString(offset * word_size)}); //$NON-NLS-1$ + } + + String format = "x"; //$NON-NLS-1$ + switch (word_format) { + case MIFormat.UNSIGNED : + format = "u"; //$NON-NLS-1$ + break; + + case MIFormat.FLOAT : + format = "f"; //$NON-NLS-1$ + break; + + case MIFormat.ADDRESS : + format = "a"; //$NON-NLS-1$ + break; + + case MIFormat.INSTRUCTION : + format = "i"; //$NON-NLS-1$ + break; + + case MIFormat.CHAR : + format = "c"; //$NON-NLS-1$ + break; + + case MIFormat.STRING : + format = "s"; //$NON-NLS-1$ + break; + + case MIFormat.DECIMAL : + case MIFormat.NATURAL : + format = "d"; //$NON-NLS-1$ + break; + + case MIFormat.BINARY : + format = "t"; //$NON-NLS-1$ + break; + + case MIFormat.OCTAL : + format = "o"; //$NON-NLS-1$ + break; + + case MIFormat.HEXADECIMAL : + case MIFormat.RAW : + default : + format = "x"; //$NON-NLS-1$ + break; + } + + if (asChar == null) { + setParameters( + new String[] { + address, + format, + Integer.toString(1), // wordSize + Integer.toString(rows), + Integer.toString(cols * word_size)}); + } else { + setParameters( + new String[] { + address, + format, + Integer.toString(1), // wordSize + Integer.toString(rows), + Integer.toString(cols * word_size), + asChar.toString()}); + } + } + + @Override + public MIDataReadMemoryInfo getResult(MIOutput out) { + return new MIDataReadMemoryInfo(out, fword_size); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataWriteMemory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataWriteMemory.java new file mode 100644 index 00000000000..d2b8ab81daf --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIDataWriteMemory.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson Communication - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.MIFormat; +import org.eclipse.cdt.dsf.mi.service.command.output.MIDataWriteMemoryInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -data-write-memory [ -o COLUMN_OFFSET ] + * ADDRESS WORD-FORMAT WORD-SIZE VALUE + * + * where: + * + * 'COLUMN_OFFSET' + * The cell offset from the beginning of the memory grid row + * + * 'ADDRESS' + * Row address of the cell to be written + * + * 'WORD-FORMAT' + * The format to be used to print the memory words + * + * 'WORD-SIZE' + * The size of each memory word in bytes + * + * 'VALUE' + * The value to be written into the cell + * + * Writes VALUE into ADDRESS + (COLUMN_OFFSET * WORD_SIZE). + * + */ +public class MIDataWriteMemory extends MICommand { + + public MIDataWriteMemory( + IDMContext ctx, + long offset, + String address, + int wordFormat, + int wordSize, + String value) + { + super(ctx, "-data-write-memory"); //$NON-NLS-1$ + + if (offset != 0) { + setOptions(new String[] { "-o", Long.toString(offset)}); //$NON-NLS-1$ + } + + String format = "x"; //$NON-NLS-1$ + switch (wordFormat) { + case MIFormat.UNSIGNED : + format = "u"; //$NON-NLS-1$ + break; + + case MIFormat.FLOAT : + format = "f"; //$NON-NLS-1$ + break; + + case MIFormat.ADDRESS : + format = "a"; //$NON-NLS-1$ + break; + + case MIFormat.INSTRUCTION : + format = "i"; //$NON-NLS-1$ + break; + + case MIFormat.CHAR : + format = "c"; //$NON-NLS-1$ + break; + + case MIFormat.STRING : + format = "s"; //$NON-NLS-1$ + break; + + case MIFormat.DECIMAL : + case MIFormat.NATURAL : + format = "d"; //$NON-NLS-1$ + break; + + case MIFormat.BINARY : + format = "t"; //$NON-NLS-1$ + break; + + case MIFormat.OCTAL : + format = "o"; //$NON-NLS-1$ + break; + + case MIFormat.HEXADECIMAL : + case MIFormat.RAW : + default : + format = "x"; //$NON-NLS-1$ + break; + } + + setParameters( + new String[] { + address, + format, + Integer.toString(wordSize), + value}); + } + + @Override + public MIDataWriteMemoryInfo getResult(MIOutput out) { + return new MIDataWriteMemoryInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentCD.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentCD.java new file mode 100644 index 00000000000..a087cf06cd8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentCD.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation' + * Ericsson - Updated for DSF + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -environment-cd PATHDIR + * + * Set GDB's working directory. + * @since 1.1 + * + */ +public class MIEnvironmentCD extends MICommand +{ + public MIEnvironmentCD(ICommandControlDMContext ctx, String path) { + super(ctx, "-environment-cd", new String[]{path}); //$NON-NLS-1$ + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentDirectory.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentDirectory.java new file mode 100644 index 00000000000..2539cd953bc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIEnvironmentDirectory.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * ERicsson - Updated + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -environment-directory [-r] PATHDIR + * + * Add directory PATHDIR to beginning of search path for source files. + * -r will first reset the path to its default + * + */ +public class MIEnvironmentDirectory extends MICommand { + + public MIEnvironmentDirectory(IDMContext ctx, String[] paths, boolean reset) { + super(ctx, "-environment-directory"); //$NON-NLS-1$ + + String[] options; + if (reset) { + if (paths == null) { + options = new String[] {"-r"}; //$NON-NLS-1$ + } else { + options = new String[paths.length + 1]; + options[0] = "-r"; //$NON-NLS-1$ + for (int i = 1; i < options.length; i++) { + options[i] = paths[i-1]; + } + } + } else { + options = paths; + } + + setOptions(options); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecContinue.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecContinue.java new file mode 100644 index 00000000000..057a1553897 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecContinue.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-continue [--all] + * + * Asynchronous command. Resumes the execution of the inferior program + * until a breakpoint is encountered, or until the inferior exits. + * + */ +public class MIExecContinue extends MICommand +{ + public MIExecContinue(IExecutionDMContext dmc) { + this(dmc, false); + } + + /** + * @since 1.1 + */ + public MIExecContinue(IExecutionDMContext dmc, boolean allThreads) { + super(dmc, "-exec-continue"); //$NON-NLS-1$ + if (allThreads) { + setParameters(new String[] { "--all" }); //$NON-NLS-1$ + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecFinish.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecFinish.java new file mode 100644 index 00000000000..4974a26caa5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecFinish.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -exec-finish + *

+ * Asynchronous command. Resumes the execution of the inferior program + * until the current function is exited. Displays the results returned by + * the function. + *

+ *

+ * The -exec-finish command operates on the selected stack + * frame. Therefore the constructor requires a stack frame context. + *

+ * + */ +public class MIExecFinish extends MICommand +{ + public MIExecFinish(IFrameDMContext dmc) { + super(dmc, "-exec-finish"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecInterrupt.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecInterrupt.java new file mode 100644 index 00000000000..4ca8a25224f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecInterrupt.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson AB - Modified for Execution Contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-interrupt [--all] + * + * Asynchronous command. Interrupts the background execution of the + * target. Note how the token associated with the stop message is the one + * for the execution command that has been interrupted. The token for the + * interrupt itself only appears in the `^done' output. If the user is + * trying to interrupt a non-running program, an error message will be + * printed. + * + */ +public class MIExecInterrupt extends MICommand +{ + public MIExecInterrupt(IExecutionDMContext dmc) { + this(dmc, false); + } + + /** + * @since 1.1 + */ + public MIExecInterrupt(IExecutionDMContext dmc, boolean allThreads) { + super(dmc, "-exec-interrupt"); //$NON-NLS-1$ + if (allThreads) { + setParameters(new String[] { "--all" }); //$NON-NLS-1$ + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNext.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNext.java new file mode 100644 index 00000000000..0cff0563682 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNext.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-next [count] + * + * Asynchronous command. Resumes execution of the inferior program, + * stopping when the beginning of the next source line is reached. + * + */ +public class MIExecNext extends MICommand +{ + public MIExecNext(IExecutionDMContext dmc) { + this(dmc, 1); + } + + public MIExecNext(IExecutionDMContext dmc, int count) { + super(dmc, "-exec-next", new String[] { Integer.toString(count) }); //$NON-NLS-1$ + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNextInstruction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNextInstruction.java new file mode 100644 index 00000000000..75609e99b11 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecNextInstruction.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-next-instruction [count] + * + * Asynchronous command. Executes one machine instruction. If the + * instruction is a function call continues until the function returns. If + * the program stops at an instruction in the middle of a source line, the + * address will be printed as well. + * + */ +public class MIExecNextInstruction extends MICommand +{ + public MIExecNextInstruction(IExecutionDMContext dmc) { + this(dmc, 1); + } + + public MIExecNextInstruction(IExecutionDMContext dmc, int count) { + super(dmc, "-exec-next-instruction", new String[] { Integer.toString(count) }); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecReturn.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecReturn.java new file mode 100644 index 00000000000..4b8f7e8ee3a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecReturn.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-return [arg] + * + *

+ * Makes current function return immediately. Doesn't execute the + * inferior. Displays the new current frame. + *

+ *

+ * The -exec-return command operates on the selected stack + * frame. Therefore the constructor requires a stack frame context. + *

+ * + */ +public class MIExecReturn extends MICommand +{ + public MIExecReturn(IFrameDMContext dmc) { + super(dmc, "-exec-return"); //$NON-NLS-1$ + } + + public MIExecReturn(IFrameDMContext dmc, String arg) { + super(dmc, "-exec-return", new String[] { arg }); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecRun.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecRun.java new file mode 100644 index 00000000000..3d4ed3a1118 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecRun.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-run + * + * Asynchronous command. Starts execution of the inferior from the + * beginning. The inferior executes until either a breakpoint is + * encountered or the program exits. + * + */ +public class MIExecRun extends MICommand +{ + public MIExecRun(IExecutionDMContext dmc) { + super(dmc, "-exec-run"); //$NON-NLS-1$ + } + + public MIExecRun(IExecutionDMContext dmc, String[] args) { + super(dmc, "-exec-run", args); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStep.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStep.java new file mode 100644 index 00000000000..25c31eb5972 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStep.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-step [count] + * + * Asynchronous command. Resumes execution of the inferior program, + * stopping when the beginning of the next source line is reached, if the + * next source line is not a function call. If it is, stop at the first + * instruction of the called function. + * + */ +public class MIExecStep extends MICommand +{ + public MIExecStep(IExecutionDMContext dmc) { + this(dmc, 1); + } + + public MIExecStep(IExecutionDMContext dmc, int count) { + super(dmc, "-exec-step", new String[] { Integer.toString(count) }); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStepInstruction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStepInstruction.java new file mode 100644 index 00000000000..c87f003260a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecStepInstruction.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-step-instruction [count] + + * Asynchronous command. Resumes the inferior which executes one + * machine instruction. The output, once GDB has stopped, will vary + * depending on whether we have stopped in the middle of a source line or + * not. In the former case, the address at which the program stopped will + * be printed as well. + * + */ +public class MIExecStepInstruction extends MICommand +{ + public MIExecStepInstruction(IExecutionDMContext dmc) { + this(dmc, 1); + } + + public MIExecStepInstruction(IExecutionDMContext dmc, int count) { + super(dmc, "-exec-step-instruction", new String[] { Integer.toString(count) }); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecUntil.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecUntil.java new file mode 100644 index 00000000000..f8395447a1f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIExecUntil.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -exec-until [ LOCATION ] + * + * Asynchronous command. Executes the inferior until the LOCATION + * specified in the argument is reached. If there is no argument, the + * inferior executes until a source line greater than the current one is + * reached. The reason for stopping in this case will be + * `location-reached'. + * + */ +public class MIExecUntil extends MICommand +{ + public MIExecUntil(IExecutionDMContext dmc) { + super(dmc, "-exec-until"); //$NON-NLS-1$ + } + + public MIExecUntil(IExecutionDMContext dmc, String loc) { + super(dmc, "-exec-until", new String[] { loc }); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecAndSymbols.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecAndSymbols.java new file mode 100644 index 00000000000..d81075a0aa4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecAndSymbols.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + + +/** + * -file-exec-and-symbols [FILE] + * + * Specify the executable file to be debugged. Unlike `-file-exec-and-symbols', + * the symbol table is not read from this file. If used without argument, GDB + * clears the information about the executable file. No output is produced, + * except a completion notification. + */ +public class MIFileExecAndSymbols extends MICommand +{ + /** + * @since 1.1 + */ + public MIFileExecAndSymbols(ICommandControlDMContext dmc, String file) { + super(dmc, "-file-exec-and-symbols", null, new String[] {file}); //$NON-NLS-1$ + } + + @Deprecated + public MIFileExecAndSymbols(MIControlDMContext dmc, String file) { + this ((ICommandControlDMContext)dmc, file); + } + + /** + * @since 1.1 + */ + public MIFileExecAndSymbols(ICommandControlDMContext dmc) { + super(dmc, "-file-exec-and-symbols"); //$NON-NLS-1$ + } + + @Deprecated + public MIFileExecAndSymbols(MIControlDMContext dmc) { + this ((ICommandControlDMContext)dmc); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecFile.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecFile.java new file mode 100644 index 00000000000..fa8afbae943 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileExecFile.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for handling of contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + + +/** + * -file-exec-file [FILE] + * + * Specify the executable file to be debugged. Unlike `-file-exec-and-symbols', + * the symbol table is not read from this file. If used without argument, GDB + * clears the information about the executable file. No output is produced, + * except a completion notification. + */ +public class MIFileExecFile extends MICommand +{ + /** + * @since 1.1 + */ + public MIFileExecFile(ICommandControlDMContext dmc, String file) { + super(dmc, "-file-exec-file", null, new String[] {file}); //$NON-NLS-1$ + } + + @Deprecated + public MIFileExecFile(MIControlDMContext dmc, String file) { + this ((ICommandControlDMContext)dmc, file); + } + + /** + * @since 1.1 + */ + public MIFileExecFile(ICommandControlDMContext dmc) { + super(dmc, "-file-exec-file"); //$NON-NLS-1$ + } + + @Deprecated + public MIFileExecFile(MIControlDMContext dmc) { + this ((ICommandControlDMContext)dmc); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileSymbolFile.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileSymbolFile.java new file mode 100644 index 00000000000..135520233f5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIFileSymbolFile.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for handling of contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + + +/** + * -file-symbol-file [FILE] + * + * Read symbol table info from the specified file argument. When used without + * arguments, clears GDB's symbol table info. No output is produced, except + * for a completion notification. + */ +public class MIFileSymbolFile extends MICommand +{ + /** + * @since 1.1 + */ + public MIFileSymbolFile(ICommandControlDMContext dmc, String file) { + super(dmc, "-file-symbol-file", null, new String[] {file}); //$NON-NLS-1$ + } + + @Deprecated + public MIFileSymbolFile(MIControlDMContext dmc, String file) { + this ((ICommandControlDMContext)dmc, file); + } + + /** + * @since 1.1 + */ + public MIFileSymbolFile(ICommandControlDMContext dmc) { + super(dmc, "-file-symbol-file"); //$NON-NLS-1$ + } + + @Deprecated + public MIFileSymbolFile(MIControlDMContext dmc) { + this ((ICommandControlDMContext)dmc); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBExit.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBExit.java new file mode 100644 index 00000000000..7ef52f291b8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBExit.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -gdb-exit + * + * Exit GDB immediately. + * + */ +public class MIGDBExit extends MICommand +{ + public MIGDBExit(IDMContext ctx) { + super(ctx, "-gdb-exit"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSet.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSet.java new file mode 100644 index 00000000000..1a6e1a537f5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSet.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -gdb-set + * + */ +public class MIGDBSet extends MICommand +{ + public MIGDBSet(IDMContext ctx, String[] params) { + super(ctx, "-gdb-set", null, params); //$NON-NLS-1$ + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java new file mode 100644 index 00000000000..6e3da6d1c8b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIGDBSetArgs.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; + +/** + * + * -gdb-set args ARGS + * + * Set the inferior program arguments, to be used in the next `-exec-run'. + * @since 1.1 + * + */ +public class MIGDBSetArgs extends MIGDBSet +{ + public MIGDBSetArgs(ICommandControlDMContext dmc) { + super(dmc, new String[] {"args"}); //$NON-NLS-1$ + } + + public MIGDBSetArgs(ICommandControlDMContext dmc, String arguments) { + super(dmc, null); + + // We do not want to quote the arguments of this command so we must + // split them into individual strings + String[] argArray = arguments.split(" "); //$NON-NLS-1$ + String[] cmdArray = new String[argArray.length + 1]; + cmdArray[0] = "args"; //$NON-NLS-1$ + for (int i=0; i { + + /** + * @since 1.1 + */ + public MIGDBShowExitCode(ICommandControlDMContext ctx) { + super(ctx, "$_exitcode"); //$NON-NLS-1$ + } + + @Deprecated + public MIGDBShowExitCode(MIControlDMContext ctx) { + this ((ICommandControlDMContext)ctx); + } + + @Override + public MIGDBShowExitCodeInfo getResult(MIOutput output) { + return new MIGDBShowExitCodeInfo(output); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInferiorTTYSet.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInferiorTTYSet.java new file mode 100644 index 00000000000..a4ee49d1c4c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInferiorTTYSet.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + + +/** + * -inferior-tty-set TTY + * + * Set terminal for future runs of the program being debugged. + */ +public class MIInferiorTTYSet extends MICommand +{ + /** + * @since 1.1 + */ + public MIInferiorTTYSet(ICommandControlDMContext dmc, String tty) { + super(dmc, "-inferior-tty-set", null, new String[] {tty}); //$NON-NLS-1$ + } + + @Deprecated + public MIInferiorTTYSet(MIControlDMContext dmc, String tty) { + this ((ICommandControlDMContext)dmc, tty); + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExec.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExec.java new file mode 100644 index 00000000000..9e5690e79a2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExec.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * MIInterpreterExec + * + * -interpreter-exec + * + * -interpreter-exec interpreter command + * + * Execute the specified command in the given interpreter. + * + * -interpreter-exec console "break main" + * &"During symbol reading, couldn't parse type; debugger out of date?.\n" + * &"During symbol reading, bad structure-type format.\n" + * ~"Breakpoint 1 at 0x8074fc6: file ../../src/gdb/main.c, line 743.\n" + * ^done + * + */ +public class MIInterpreterExec extends MICommand { + + /** + * @param oper + */ + public MIInterpreterExec(IDMContext ctx, String interpreter, String cmd) { + super(ctx, "-interpreter-exec", new String[]{interpreter}, new String[] {cmd}); //$NON-NLS-1$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExecConsole.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExecConsole.java new file mode 100644 index 00000000000..ac656fbd0e1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIInterpreterExecConsole.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * MIInterpreterExecConsole + */ +public class MIInterpreterExecConsole extends MIInterpreterExec { + + /** + * @param interpreter + * @param cmd + */ + public MIInterpreterExecConsole(IDMContext ctx, String cmd) { + super(ctx, "console", cmd); //$NON-NLS-1$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIListThreadGroups.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIListThreadGroups.java new file mode 100644 index 00000000000..110af743f56 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIListThreadGroups.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import java.util.ArrayList; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIListThreadGroupsInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + * -list-thread-groups [--available | GROUP] + * + * When used without GROUP parameter, this will list top-level + * thread groups that are being debugged. When used with the GROUP + * parameter, the children of the specified group will be listed. + * The children can be either threads, or other groups. At present, + * GDB will not report both threads and groups as children at the + * same time, but it may change in future. + * + * With the --available option, instead of reporting groups that are + * being debugged, GDB will report all thread groups available on the + * target, not only the presently debugged ones. Using the --available + * option together with explicit GROUP is not likely to work on all targets. + * + * The output of the command is: + * + * ^done,threads=[],groups=[] + * + * where each thread group is like this: + * + * {id="xxx",type="process",pid="yyy",num_children="1"} + * + * The id of a thread group should be considered an opaque string. + * @since 1.1 + * + */ +public class MIListThreadGroups extends MICommand { + + // List all groups being debugged + public MIListThreadGroups(ICommandControlDMContext ctx) { + this(ctx, false); + } + + // List all groups or threads being debugged which are children of the specified group + public MIListThreadGroups(ICommandControlDMContext ctx, String groupId) { + this(ctx, groupId, false); + } + + // List all groups available on the target + public MIListThreadGroups(ICommandControlDMContext ctx, boolean listAll) { + this(ctx, null, listAll); + } + + // There should be no reason to have both listAll and groupId specified, + // so this constructor is private, and exists to avoid duplicating code. + private MIListThreadGroups(ICommandControlDMContext ctx, String groupId, boolean listAll) { + super(ctx, "-list-thread-groups"); //$NON-NLS-1$ + + final ArrayList arguments = new ArrayList(); + if (listAll) { + arguments.add("--available"); //$NON-NLS-1$ + } + + if (groupId != null) { + arguments.add(groupId); + } + + if (!arguments.isEmpty()) { + setParameters(arguments.toArray(new String[0])); + } + } + + @Override + public MIListThreadGroupsInfo getResult(MIOutput out) { + return new MIListThreadGroupsInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackInfoDepth.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackInfoDepth.java new file mode 100644 index 00000000000..6b3aa994066 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackInfoDepth.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackInfoDepthInfo; + +/** + * + * -stack-info-depth [maxDepth] + * + * + */ +public class MIStackInfoDepth extends MICommand +{ + + public MIStackInfoDepth(IMIExecutionDMContext ctx) { + super(ctx, "-stack-info-depth"); //$NON-NLS-1$ + } + + public MIStackInfoDepth(IMIExecutionDMContext ctx, int maxDepth) { + super(ctx, "-stack-info-depth", new String[]{Integer.toString(maxDepth)}); //$NON-NLS-1$ + } + + @Override + public MIStackInfoDepthInfo getResult(MIOutput out) { + return new MIStackInfoDepthInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListArguments.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListArguments.java new file mode 100644 index 00000000000..581c0316af4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListArguments.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of execution contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListArgumentsInfo; + +/** + * + * -stack-list-arguments SHOW-VALUES + * [ LOW-FRAME HIGH-FRAME ] + * + * Display a list of the arguments for the frames between LOW-FRAME and + * HIGH-FRAME (inclusive). If LOW-FRAME and HIGH-FRAME are not provided, + * list the arguments for the whole call stack. + * + * The SHOW-VALUES argument must have a value of 0 or 1. A value of 0 + * means that only the names of the arguments are listed, a value of 1 + * means that both names and values of the arguments are printed. + * + */ +public class MIStackListArguments extends MICommand +{ + public MIStackListArguments(IMIExecutionDMContext execDmc, boolean showValues) { + super(execDmc, "-stack-list-arguments", new String[] { showValues ? "1" : "0" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public MIStackListArguments(IFrameDMContext frameDmc, boolean showValues) { + super(frameDmc, "-stack-list-arguments", new String[] { showValues ? "1" : "0" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public MIStackListArguments(IMIExecutionDMContext execDmc, boolean showValues, int low, int high) { + super(execDmc, "-stack-list-arguments", //$NON-NLS-1$ + new String[] {showValues ? "1" : "0", Integer.toString(low), Integer.toString(high)}); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public MIStackListArgumentsInfo getResult(MIOutput out) { + return new MIStackListArgumentsInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListFrames.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListFrames.java new file mode 100644 index 00000000000..ecfa09d536b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListFrames.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of Frame contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + + +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListFramesInfo; + +/** + * + * -stack-list-frames [ LOW-FRAME HIGH-FRAME ] + * + * List the frames currently on the stack. For each frame it displays + * the following info: + * + * `LEVEL' + * The frame number, 0 being the topmost frame, i.e. the innermost + * function. + * + * `ADDR' + * The `$pc' value for that frame. + * + * `FUNC' + * Function name. + * + * `FILE' + * File name of the source file where the function lives. + * + * `FULLNAME' + * Absolute file name of the source file where the function lives. + * @since gdb 6.4 + * + * `LINE' + * Line number corresponding to the `$pc'. + * + * If invoked without arguments, this command prints a backtrace for the + * whole stack. If given two integer arguments, it shows the frames whose + * levels are between the two arguments (inclusive). If the two arguments + * are equal, it shows the single frame at the corresponding level. + * + */ +public class MIStackListFrames extends MICommand +{ + public MIStackListFrames(IMIExecutionDMContext execDmc) { + super(execDmc, "-stack-list-frames"); //$NON-NLS-1$ + } + + public MIStackListFrames(IMIExecutionDMContext execDmc, int low, int high) { + super(execDmc, "-stack-list-frames", new String[] { Integer.toString(low), Integer.toString(high) }); //$NON-NLS-1$ + } + + @Override + public MIStackListFramesInfo getResult(MIOutput out) { + return new MIStackListFramesInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListLocals.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListLocals.java new file mode 100644 index 00000000000..8b1e320103b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackListLocals.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStackListLocalsInfo; + +/** + * + * -stack-list-locals PRINT-VALUES + * + * Display the local variable names for the current frame. With an + * argument of 0 prints only the names of the variables, with argument of 1 + * prints also their values. + * + */ +public class MIStackListLocals extends MICommand +{ + + public MIStackListLocals(IFrameDMContext frameCtx, boolean printValues) { + super(frameCtx, "-stack-list-locals", new String[] { printValues ? "1" : "0" } ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public MIStackListLocalsInfo getResult(MIOutput out) { + return new MIStackListLocalsInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackSelectFrame.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackSelectFrame.java new file mode 100644 index 00000000000..fd641750490 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIStackSelectFrame.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for additional features in DSF Reference implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * + * -stack-select-frame FRAMENUM + * + * Change the current frame. Select a different frame FRAMENUM on the + * stack. + * + */ +public class MIStackSelectFrame extends MICommand { + + public MIStackSelectFrame(IDMContext ctx, int frameNum) { + super(ctx, "-stack-select-frame", new String[]{Integer.toString(frameNum)}, new String[0]); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetAttach.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetAttach.java new file mode 100644 index 00000000000..36dd2f088bc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetAttach.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -target-attach < --pid PID | THREAD_GROUP_ID > + * + * This command attaches to the process specified by the PID + * or THREAD_GROUP_ID + * @since 1.1 + */ +public class MITargetAttach extends MICommand { + + public MITargetAttach(ICommandControlDMContext ctx, String groupId) { + super(ctx, "-target-attach", new String[] {groupId}); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetDetach.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetDetach.java new file mode 100644 index 00000000000..71f8c3659f8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetDetach.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * -target-detach < --pid PID | THREAD_GROUP_ID > + * + * This command detaches from the process specified by the PID + * or THREAD_GROUP_ID + * @since 1.1 + */ +public class MITargetDetach extends MICommand { + + public MITargetDetach(ICommandControlDMContext ctx, String groupId) { + super(ctx, "-target-detach", new String[] {groupId}); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetSelect.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetSelect.java new file mode 100644 index 00000000000..feac6f217b3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MITargetSelect.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + +/** + * This command connects to a remote target. + */ +public class MITargetSelect extends MICommand { + + @Deprecated + public MITargetSelect(IDMContext ctx, String host, String port) { + this(ctx, host, port, true); + } + + @Deprecated + public MITargetSelect(IDMContext ctx, String serialDevice) { + this(ctx, serialDevice, true); + } + + /** + * @since 1.1 + */ + public MITargetSelect(IDMContext ctx, String host, String port, boolean extended) { + super(ctx, "-target-select", new String[] { extended ? "extended-remote" : "remote", host + ":" + port}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + /** + * @since 1.1 + */ + public MITargetSelect(IDMContext ctx, String serialDevice, boolean extended) { + super(ctx, "-target-select", new String[] { extended ? "extended-remote" : "remote", serialDevice}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadInfo.java new file mode 100644 index 00000000000..9756739ec06 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadInfo.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadInfoInfo; + +/** + * + * -thread-info [ thread-id ] + * + * Reports information about either a specific thread, if [thread-id] is present, + * or about all threads. When printing information about all threads, also reports + * the current thread. + * @since 1.1 + * + */ +public class MIThreadInfo extends MICommand { + + public MIThreadInfo(ICommandControlDMContext dmc) { + super(dmc, "-thread-info"); //$NON-NLS-1$ + } + + public MIThreadInfo(ICommandControlDMContext dmc, String threadId) { + super(dmc, "-thread-info", new String[]{ threadId }); //$NON-NLS-1$ + } + + @Override + public MIThreadInfoInfo getResult(MIOutput out) { + return new MIThreadInfoInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadListIds.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadListIds.java new file mode 100644 index 00000000000..b574f4e9f62 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadListIds.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadListIdsInfo; + +/** + * + * -thread-list-ids + * + * Produces a list of the currently known GDB thread ids. At the end + * of the list it also prints the total number of such threads. + * + */ +public class MIThreadListIds extends MICommand { + + public MIThreadListIds(IContainerDMContext contDmc) { + super(contDmc, "-thread-list-ids"); //$NON-NLS-1$ + } + + @Override + public MIThreadListIdsInfo getResult(MIOutput out) { + return new MIThreadListIdsInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadSelect.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadSelect.java new file mode 100644 index 00000000000..5c4c98f7735 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIThreadSelect.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson AB - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; + + +/** + * + * -thread-select THREADNUM + * + * Make THREADNUM the current thread. It prints the number of the new + * current thread, and the topmost frame for that thread. + * + */ + +public class MIThreadSelect extends MICommand +{ + public MIThreadSelect(IDMContext ctx, int threadNum) { + super(ctx, "-thread-select", new String[]{Integer.toString(threadNum)}); //$NON-NLS-1$ + } + + /** + * @since 1.1 + */ + public MIThreadSelect(IDMContext ctx, String threadNum) { + super(ctx, "-thread-select", new String[]{threadNum}); //$NON-NLS-1$ + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarAssign.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarAssign.java new file mode 100644 index 00000000000..f9168847112 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarAssign.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarAssignInfo; + +/** + * + * -var-assign NAME EXPRESSION + * + * Assigns the value of EXPRESSION to the variable object specified by + * NAME. The object must be `editable'. + * + */ +public class MIVarAssign extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarAssign(ICommandControlDMContext ctx, String name, String expression) { + super(ctx, "-var-assign", new String[]{name, expression}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarAssign(MIControlDMContext ctx, String name, String expression) { + this ((ICommandControlDMContext)ctx, name, expression); + } + @Override + public MIVarAssignInfo getResult(MIOutput out) { + return new MIVarAssignInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarCreate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarCreate.java new file mode 100644 index 00000000000..11b54439d12 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarCreate.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarCreateInfo; + + +/** + * + * -var-create {NAME | "-"} + * {FRAME-ADDR | "*"} EXPRESSION + * + * This operation creates a variable object, which allows the + * monitoring of a variable, the result of an expression, a memory cell or + * a CPU register. + * + * The NAME parameter is the string by which the object can be + * referenced. It must be unique. If `-' is specified, the varobj system + * will generate a string "varNNNNNN" automatically. It will be unique + * provided that one does not specify NAME on that format. The command + * fails if a duplicate name is found. + * + * The frame under which the expression should be evaluated can be + * specified by FRAME-ADDR. A `*' indicates that the current frame should + * be used. + * + * EXPRESSION is any expression valid on the current language set (must + * not begin with a `*'), or one of the following: + * + * * `*ADDR', where ADDR is the address of a memory cell + * + * * `*ADDR-ADDR' -- a memory address range (TBD) + * + * * `$REGNAME' -- a CPU register name + * + */ +public class MIVarCreate extends MICommand +{ + public MIVarCreate(IExpressionDMContext dmc, String expression) { + this(dmc, "-", "*", expression); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public MIVarCreate(IExpressionDMContext dmc, String name, String expression) { + this(dmc, name, "*", expression); //$NON-NLS-1$ + } + + public MIVarCreate(IExpressionDMContext dmc, String name, String frameAddr, String expression) { + super(dmc, "-var-create", new String[]{name, frameAddr, expression}); //$NON-NLS-1$ + } + + @Override + public MIVarCreateInfo getResult(MIOutput out) { + return new MIVarCreateInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarDelete.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarDelete.java new file mode 100644 index 00000000000..6fbcbefab19 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarDelete.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarDeleteInfo; + +/** + * + * -var-delete NAME + * + * Deletes a previously created variable object and all of its children. + * + * Returns an error if the object NAME is not found. + * + */ +public class MIVarDelete extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarDelete(ICommandControlDMContext dmc, String name) { + super(dmc, "-var-delete", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarDelete(MIControlDMContext dmc, String name) { + this ((ICommandControlDMContext)dmc, name); + } + + @Override + public MIVarDeleteInfo getResult(MIOutput out) { + return new MIVarDeleteInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarEvaluateExpression.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarEvaluateExpression.java new file mode 100644 index 00000000000..afb16f60e20 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarEvaluateExpression.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of frame contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarEvaluateExpressionInfo; + +/** + * + * -var-evaluate-expression NAME + * + * Evaluates the expression that is represented by the specified + * variable object and returns its value as a string in the current format + * specified for the object: + * + * value=VALUE + * + */ +public class MIVarEvaluateExpression extends MICommand { + + /** + * @since 1.1 + */ + public MIVarEvaluateExpression(ICommandControlDMContext dmc, String name) { + super(dmc, "-var-evaluate-expression", new String[] { name }); //$NON-NLS-1$ + } + + @Deprecated + public MIVarEvaluateExpression(MIControlDMContext dmc, String name) { + this ((ICommandControlDMContext)dmc, name); + } + + @Override + public MIVarEvaluateExpressionInfo getResult(MIOutput out) { + return new MIVarEvaluateExpressionInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoExpression.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoExpression.java new file mode 100644 index 00000000000..7886da1de71 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoExpression.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoExpressionInfo; + +/** + * + * -var-info-expression NAME + * + * Returns what is represented by the variable object NAME: + * + * lang=LANG-SPEC,exp=EXPRESSION + * + * where LANG-SPEC is `{"C" | "C++" | "Java"}'. + * + */ + +//MIVarInfoExpression.java +public class MIVarInfoExpression extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarInfoExpression(ICommandControlDMContext ctx, String name) { + super(ctx, "-var-info-expression", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarInfoExpression(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarInfoExpressionInfo getResult(MIOutput out) { + return new MIVarInfoExpressionInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java new file mode 100644 index 00000000000..fd9c1f96419 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoNumChildren.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoNumChildrenInfo; + +/** + * + * -var-info-num-children NAME + * + * Returns the number of children of a variable object NAME: + * + * numchild=N + * + */ +public class MIVarInfoNumChildren extends MICommand +{ + public MIVarInfoNumChildren(IExpressionDMContext ctx, String name) { + super(ctx, "-var-info-num-children", new String[]{name}); //$NON-NLS-1$ + } + + @Override + public MIVarInfoNumChildrenInfo getResult(MIOutput out) { + return new MIVarInfoNumChildrenInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java new file mode 100644 index 00000000000..453f1095f19 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoPathExpression.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoPathExpressionInfo; + +/** + * + * -var-info-path-expression NAME + * + * as of GDB 6.7 + * + * Print full expression that this variable object represents: + * + * (gdb) -var-info-path-expression C.Base.public.m_size + * ^done,path_expr=((Base)c).m_size) + * + */ + +public class MIVarInfoPathExpression extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarInfoPathExpression(ICommandControlDMContext dmc, String name) { + super(dmc, "-var-info-path-expression", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarInfoPathExpression(MIControlDMContext dmc, String name) { + this ((ICommandControlDMContext)dmc, name); + } + + @Override + public MIVarInfoPathExpressionInfo getResult(MIOutput out) { + return new MIVarInfoPathExpressionInfo(out); + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoType.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoType.java new file mode 100644 index 00000000000..66b5856ffb5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarInfoType.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarInfoTypeInfo; + + +/** + * + * -var-info-type NAME + * + * Returns the type of the specified variable NAME. The type is + * returned as a string in the same format as it is output by the GDB CLI: + * + * type=TYPENAME + * + */ +public class MIVarInfoType extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarInfoType(ICommandControlDMContext ctx, String name) { + super(ctx, "-var-info-type", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarInfoType(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarInfoTypeInfo getResult(MIOutput out) { + return new MIVarInfoTypeInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java new file mode 100644 index 00000000000..194b42299eb --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarListChildren.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for handling of frame contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarListChildrenInfo; + +/** + * + * -var-list-children NAME + * + * Returns a list of the children of the specified variable object: + * + * numchild=N,children={{name=NAME, + * numchild=N,type=TYPE},(repeats N times)} + * + */ +public class MIVarListChildren extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarListChildren(ICommandControlDMContext ctx, String name) { + super(ctx, "-var-list-children", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarListChildren(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarListChildrenInfo getResult(MIOutput out) { + return new MIVarListChildrenInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetFormat.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetFormat.java new file mode 100644 index 00000000000..3eff20a43b9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarSetFormat.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of frame contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarSetFormatInfo; + +/** + * + * -var-set-format NAME FORMAT-SPEC + * + * Sets the output format for the value of the object NAME to be + * FORMAT-SPEC. + * + * The syntax for the FORMAT-SPEC is as follows: + * + * FORMAT-SPEC ==> + * {binary | decimal | hexadecimal | octal | natural} + * + */ +public class MIVarSetFormat extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarSetFormat(ICommandControlDMContext ctx, String name, String fmt) { + super(ctx, "-var-set-format"); //$NON-NLS-1$ + setParameters(new String[]{name, getFormat(fmt)}); + } + + @Deprecated + public MIVarSetFormat(MIControlDMContext ctx, String name, String fmt) { + this ((ICommandControlDMContext)ctx, name, fmt); + } + + private String getFormat(String fmt){ + String format = "natural"; //$NON-NLS-1$ + + if (IFormattedValues.HEX_FORMAT.equals(fmt)) { + format = "hexadecimal"; //$NON-NLS-1$ + } else if (IFormattedValues.BINARY_FORMAT.equals(fmt)) { + format = "binary"; //$NON-NLS-1$ + } else if (IFormattedValues.OCTAL_FORMAT.equals(fmt)) { + format = "octal"; //$NON-NLS-1$ + } else if (IFormattedValues.NATURAL_FORMAT.equals(fmt)) { + format = "natural"; //$NON-NLS-1$ + } else if (IFormattedValues.DECIMAL_FORMAT.equals(fmt)) { + format = "decimal"; //$NON-NLS-1$ + } + return format; + } + + @Override + public MIVarSetFormatInfo getResult(MIOutput out) { + return new MIVarSetFormatInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowAttributes.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowAttributes.java new file mode 100644 index 00000000000..7bc5c0703de --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowAttributes.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowAttributesInfo; + +/** + * + * -var-show-attributes NAME + * + * List attributes of the specified variable object NAME: + * + * status=ATTR [ ( ,ATTR )* ] + * + * where ATTR is `{ { editable | noneditable } | TBD }'. + * + */ +//DsfMIVarShowAttributesInfo + +public class MIVarShowAttributes extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarShowAttributes(ICommandControlDMContext ctx, String name) { + super(ctx, "-var-show-attributes", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarShowAttributes(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarShowAttributesInfo getResult(MIOutput out) { + return new MIVarShowAttributesInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowFormat.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowFormat.java new file mode 100644 index 00000000000..4afc4672eff --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarShowFormat.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarShowFormatInfo; + +/** + * + * -var-show-format NAME + * + * Returns the format used to display the value of the object NAME. + * + * FORMAT ==> + * FORMAT-SPEC + * + */ +public class MIVarShowFormat extends MICommand +{ + /** + * @since 1.1 + */ + public MIVarShowFormat(ICommandControlDMContext ctx, String name) { + super(ctx, "-var-show-format", new String[]{name}); //$NON-NLS-1$ + } + + @Deprecated + public MIVarShowFormat(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarShowFormatInfo getResult(MIOutput out) { + return new MIVarShowFormatInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarUpdate.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarUpdate.java new file mode 100644 index 00000000000..4176085c434 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/MIVarUpdate.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for handling of frame contexts + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIVarUpdateInfo; + +/** + * + * -var-update [print-values] {NAME | "*"} + * + * Update the value of the variable object NAME by evaluating its + * expression after fetching all the new values from memory or registers. + * A `*' causes all existing variable objects to be updated. + * If print-values has a value for of 0 or --no-values, print only the names of the variables; + * if print-values is 1 or --all-values, also print their values; + * if it is 2 or --simple-values print the name and value for simple data types and just + * the name for arrays, structures and unions. + */ +public class MIVarUpdate extends MICommand { + + /** + * @since 1.1 + */ + public MIVarUpdate(ICommandControlDMContext dmc, String name) { + super(dmc, "-var-update", new String[] { "1", name }); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Deprecated + public MIVarUpdate(MIControlDMContext ctx, String name) { + this ((ICommandControlDMContext)ctx, name); + } + + @Override + public MIVarUpdateInfo getResult(MIOutput out) { + return new MIVarUpdateInfo(out); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/RawCommand.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/RawCommand.java new file mode 100644 index 00000000000..268ec30533a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/RawCommand.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; + +/** + */ +public class RawCommand extends MICommand { + + String fRaw; + + public RawCommand(IDMContext ctx, String operation) { + super(ctx, operation); + fRaw = operation; + } + + @Override + public boolean supportsThreadAndFrameOptions() { return false; } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.mi.core.command.Command#getMIOutput() + */ + public MIOutput getMIOutput() { + return new MIOutput(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/IMIDMEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/IMIDMEvent.java new file mode 100644 index 00000000000..ecef182204e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/IMIDMEvent.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.events; + + +/** + * Common interface for events that are directly caused by some MI event. + * + * @since 1.1 + */ +public interface IMIDMEvent { + + /** + * Returns the underlying MI event that triggered this event. + *

+ * Note: the return type is an object which can be safely cast to + * an MIEvent. However returning a parametrized MIEvent type here + * leads to compiler warnings related to generics (see bug 240997) + *

+ * @see MIEvent + */ + public Object getMIEvent(); +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointChangedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointChangedEvent.java new file mode 100644 index 00000000000..d302f7cd3ca --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointChangedEvent.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; + + +/** + * + */ +@Immutable +public class MIBreakpointChangedEvent extends MIEvent { + + final private int no; + + public MIBreakpointChangedEvent(IBreakpointsTargetDMContext ctx, int number) { + this(ctx, 0, number); + } + + public MIBreakpointChangedEvent(IBreakpointsTargetDMContext ctx, int id, int number) { + super(ctx, id, null); + no = number; + } + + public int getNumber() { + return no; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java new file mode 100644 index 00000000000..a215920b33e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIBreakpointHitEvent.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * ^stopped,reason="breakpoint-hit",bkptno="1",thread-id="0",frame={addr="0x08048468",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="4"} + * + */ +@Immutable +public class MIBreakpointHitEvent extends MIStoppedEvent { + + int bkptno; + + protected MIBreakpointHitEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, int bkptno) { + super(ctx, token, results, frame); + this.bkptno = bkptno; + } + + public int getNumber() { + return bkptno; + } + + @Deprecated + public static MIBreakpointHitEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + int bkptno = -1; + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value != null && value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("bkptno")) { //$NON-NLS-1$ + try { + bkptno = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MIBreakpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptno); + } + + /** + * @since 1.1 + */ + public static MIBreakpointHitEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + int bkptno = -1; + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value != null && value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("bkptno")) { //$NON-NLS-1$ + try { + bkptno = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MIBreakpointHitEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), bkptno); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIDetachedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIDetachedEvent.java new file mode 100644 index 00000000000..a1ee505c16b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIDetachedEvent.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; + + +/** + * + * ^running + */ +@Immutable +public class MIDetachedEvent extends MIEvent { + + /** + * @since 1.1 + */ + public MIDetachedEvent(ICommandControlDMContext ctx, int token) { + super(ctx, token, null); + } + + @Deprecated + public MIDetachedEvent(MIControlDMContext ctx, int token) { + this ((ICommandControlDMContext)ctx, token); + } + + @Override + public String toString() { + return "Detached"; //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIErrorEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIErrorEvent.java new file mode 100644 index 00000000000..4778d7c9855 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIErrorEvent.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MILogStreamOutput; +import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIStreamRecord; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * (gdb) + * &"warning: Cannot insert breakpoint 2:\n" + * &"Cannot access memory at address 0x8020a3\n" + * 30^error,msg=3D"Cannot access memory at address 0x8020a3"=20 + */ +@Immutable +public class MIErrorEvent extends MIStoppedEvent { + + final private String msg; + final private String log; + final private MIOOBRecord[] oobs; + + protected MIErrorEvent( + IExecutionDMContext ctx, int token, MIResult[] results, MIOOBRecord[] oobs, String msg, String log) + { + super(ctx, token, results, null); + this.msg = msg; + this.log = log; + this.oobs = oobs; + } + + public String getMessage() { + return msg; + } + + public String getLogMessage() { + return log; + } + + public static MIErrorEvent parse( + IContainerDMContext containerDmc, int token, MIResult[] results, MIOOBRecord[] oobs) + { + String msg = "", log = ""; //$NON-NLS-1$ //$NON-NLS-2$ + + if (results != null) { + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("msg")) { //$NON-NLS-1$ + msg = str; + } + } + } + if (oobs != null) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < oobs.length; i++) { + if (oobs[i] instanceof MILogStreamOutput) { + MIStreamRecord o = (MIStreamRecord)oobs[i]; + sb.append(o.getString()); + } + } + log = sb.toString(); + } + return new MIErrorEvent(containerDmc, token, results, oobs, msg, log); + } + + @Override + public String toString() { + if (oobs != null) { + StringBuilder builder = new StringBuilder(); + for (MIOOBRecord oob : oobs) { + builder.append(oob.toString()); + } + builder.append(super.toString()); + return builder.toString(); + } else { + return super.toString(); + } + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIEvent.java new file mode 100644 index 00000000000..52c6d16d4fc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIEvent.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; + + +/** + */ +@Immutable +public abstract class MIEvent extends AbstractDMEvent { + private final int fToken; + private final MIResult[] fResults; + + public MIEvent(V dmc, int token, MIResult[] results) { + super(dmc); + fToken = token; + fResults = results; + } + + public int getToken() { + return fToken; + } + + public MIResult[] getResults() { + return fResults; + } + + @Override + public String toString() { + if (fResults == null) { + return super.toString(); + } else if (fResults.length == 1) { + return fResults[0].toString(); + } else { + StringBuilder builder = new StringBuilder(); + for (MIResult result : fResults) { + builder.append(result); + builder.append('\n'); + } + return builder.toString(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIFunctionFinishedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIFunctionFinishedEvent.java new file mode 100644 index 00000000000..7b67b23fc1f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIFunctionFinishedEvent.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped,reason="function-finished",thread-id="0",frame={addr="0x0804855a",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="17"},gdb-result-var="$1",return-value="10" + */ +@Immutable +public class MIFunctionFinishedEvent extends MIStoppedEvent { + + final private String gdbResult; + final private String returnValue; + final private String returnType; + + protected MIFunctionFinishedEvent( + IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, String gdbResult, + String returnValue, String returnType) + { + super(ctx, token, results, frame); + this.gdbResult = gdbResult; + this.returnValue = returnValue; + this.returnType = returnType; + } + + public String getGDBResultVar() { + return gdbResult; + } + + public String getReturnValue() { + return returnValue; + } + + public String getReturnType() { + return returnType; + } + + @Deprecated + public static MIFunctionFinishedEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + String gdbResult = ""; //$NON-NLS-1$ + String returnValue = ""; //$NON-NLS-1$ + String returnType = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("gdb-result-var")) { //$NON-NLS-1$ + gdbResult = str; + } else if (var.equals("return-value")) { //$NON-NLS-1$ + returnValue = str; + } else if (var.equals("return-type")) { //$NON-NLS-1$ + returnType = str; + } + } + + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MIFunctionFinishedEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), gdbResult, returnValue, returnType); + } + + /** + * @since 1.1 + */ + public static MIFunctionFinishedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + String gdbResult = ""; //$NON-NLS-1$ + String returnValue = ""; //$NON-NLS-1$ + String returnType = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("gdb-result-var")) { //$NON-NLS-1$ + gdbResult = str; + } else if (var.equals("return-value")) { //$NON-NLS-1$ + returnValue = str; + } else if (var.equals("return-type")) { //$NON-NLS-1$ + returnType = str; + } + } + + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MIFunctionFinishedEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), gdbResult, returnValue, returnType); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIGDBExitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIGDBExitEvent.java new file mode 100644 index 00000000000..d6d688887c6 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIGDBExitEvent.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; + + +/** + * Gdb Session terminated. + * + * @deprecated This event is not used in DSF-GDB as it has been replaced by + * {@link ICommandControlService.ICommandControlShutdownDMEvent}. + */ +@Deprecated +@Immutable +public class MIGDBExitEvent extends MIEvent { + + /** + * @since 1.1 + */ + public MIGDBExitEvent(ICommandControlDMContext ctx, int token) { + super(ctx, token, null); + } + + @Deprecated + public MIGDBExitEvent(MIControlDMContext ctx, int token) { + this ((ICommandControlDMContext)ctx, token); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorExitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorExitEvent.java new file mode 100644 index 00000000000..8b3c5451637 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorExitEvent.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped,reason="exited-normally" + * *stopped,reason="exited",exit-code="04" + * ^done,reason="exited",exit-code="04" + * + */ +@Immutable +public class MIInferiorExitEvent extends MIEvent { + + final private int code; + + /** + * @since 1.1 + */ + public MIInferiorExitEvent(ICommandControlDMContext ctx, int token, MIResult[] results, int code) { + super(ctx, token, results); + this.code = code; + } + + @Deprecated + public MIInferiorExitEvent(MIControlDMContext ctx, int token, MIResult[] results, int code) { + this ((ICommandControlDMContext)ctx, token, results, code); + } + + public int getExitCode() { + return code; + } + + @Deprecated + public static MIInferiorExitEvent parse(MIControlDMContext ctx, int token, MIResult[] results) { + return parse((ICommandControlDMContext)ctx, token, results); + } + + /** + * @since 1.1 + */ + public static MIInferiorExitEvent parse(ICommandControlDMContext ctx, int token, MIResult[] results) + { + int code = 0; + if (results != null) { + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("exit-code")) { //$NON-NLS-1$ + try { + code = Integer.decode(str.trim()).intValue(); + } catch (NumberFormatException e) { + } + } + } + } + return new MIInferiorExitEvent(ctx, token, results, code); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorSignalExitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorSignalExitEvent.java new file mode 100644 index 00000000000..1ddb7e79dfc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIInferiorSignalExitEvent.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.MIControlDMContext; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * signal 2 + * "signal 2\n" + * ^done,reason="exited-signalled",signal-name="SIGINT",signal-meaning="Interrupt" + * + */ +@Immutable +public class MIInferiorSignalExitEvent extends MIEvent { + + final private String sigName; + final private String sigMeaning; + + /** + * @since 1.1 + */ + public MIInferiorSignalExitEvent(ICommandControlDMContext ctx, int token, MIResult[] results, String sigName, String sigMeaning) { + super(ctx, token, results); + this.sigName = sigName; + this.sigMeaning = sigMeaning; + } + + @Deprecated + public MIInferiorSignalExitEvent(MIControlDMContext ctx, int token, MIResult[] results, String sigName, String sigMeaning) { + this((ICommandControlDMContext)ctx, token, results, sigName, sigMeaning); + } + + public String getName() { + return sigName; + } + + public String getMeaning() { + return sigMeaning; + } + + @Deprecated + public static MIInferiorSignalExitEvent parse(MIControlDMContext ctx, int token, MIResult[] results) { + return parse((ICommandControlDMContext)ctx, token, results); + } + + /** + * @since 1.1 + */ + public static MIInferiorSignalExitEvent parse(ICommandControlDMContext ctx, int token, MIResult[] results) + { + String sigName = ""; //$NON-NLS-1$ + String sigMeaning = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("signal-name")) { //$NON-NLS-1$ + sigName = str; + } else if (var.equals("signal-meaning")) { //$NON-NLS-1$ + sigMeaning = str; + } + } + return new MIInferiorSignalExitEvent(ctx, token, results, sigName, sigMeaning); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MILocationReachedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MILocationReachedEvent.java new file mode 100644 index 00000000000..8084a5e3d7e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MILocationReachedEvent.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; + +/** + * *stopped,reason="location-reached",thread-id="0",frame={addr="0x0804858e",func="main2",args=[],file="hello.c",line="27"} + */ +@Immutable +public class MILocationReachedEvent extends MIStoppedEvent { + + protected MILocationReachedEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame) { + super(ctx, token, results, frame); + } + + @Deprecated + public static MILocationReachedEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MILocationReachedEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } + + /** + * @since 1.1 + */ + public static MILocationReachedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MILocationReachedEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIRunningEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIRunningEvent.java new file mode 100644 index 00000000000..610ef393f2c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIRunningEvent.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; + + +/** + * + * ^running + */ +@Immutable +public class MIRunningEvent extends MIEvent { + public static final int CONTINUE = 0; + public static final int NEXT = 1; + public static final int NEXTI = 2; + public static final int STEP = 3; + public static final int STEPI = 4; + public static final int FINISH = 5; + public static final int UNTIL = 6; + public static final int RETURN = 7; + + final private int type; + final private int threadId; + + public MIRunningEvent(IExecutionDMContext ctx, int token, int t) { + this(ctx, token, t, -1); + } + + public MIRunningEvent(IExecutionDMContext ctx, int token, int t, int threadId) { + super(ctx, token, null); + type = t; + this.threadId = threadId; + } + + public int getType() { + return type; + } + + public int getThreadId() { + return threadId; + } + + @Override + public String toString() { + return "Running"; //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java new file mode 100644 index 00000000000..c2fa6e7b7fd --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISharedLibEvent.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; + +/** + * + */ +@Immutable +public class MISharedLibEvent extends MIStoppedEvent { + + protected MISharedLibEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame) { + super(ctx, token, results, frame); + } + + @Deprecated + public static MIStoppedEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MISharedLibEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } + + public static MIStoppedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MISharedLibEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalChangedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalChangedEvent.java new file mode 100644 index 00000000000..ac40fd34ce7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalChangedEvent.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.ISignals.ISignalsDMContext; + + +/** + * + */ +@Immutable +public class MISignalChangedEvent extends MIEvent { + + final private String name; + + public MISignalChangedEvent(ISignalsDMContext ctx, String n) { + this(ctx, 0, n); + } + + public MISignalChangedEvent(ISignalsDMContext ctx, int id, String n) { + super(ctx, id, null); + name = n; + } + + public String getName() { + return name; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalEvent.java new file mode 100644 index 00000000000..d93549e5727 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISignalEvent.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped,reason="signal-received",signal-name="SIGINT",signal-meaning="Interrupt",thread-id="0",frame={addr="0x400e18e1",func="__libc_nanosleep",args=[],file="__libc_nanosleep",line="-1"} + * + */ +@Immutable +public class MISignalEvent extends MIStoppedEvent { + + final private String sigName; + final private String sigMeaning; + + protected MISignalEvent( + IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, + String sigName, String sigMeaning) + { + super(ctx, token, results, frame); + this.sigName = sigName; + this.sigMeaning = sigMeaning; + } + + public String getName() { + return sigName; + } + + public String getMeaning() { + return sigMeaning; + } + + + @Deprecated + public static MISignalEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + String sigName = ""; //$NON-NLS-1$ + String sigMeaning = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("signal-name")) { //$NON-NLS-1$ + sigName = str; + } else if (var.equals("signal-meaning")) { //$NON-NLS-1$ + sigMeaning = str; + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MISignalEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), sigName, sigMeaning); + + } + + /** + * @since 1.1 + */ + public static MISignalEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + String sigName = ""; //$NON-NLS-1$ + String sigMeaning = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + + if (var.equals("signal-name")) { //$NON-NLS-1$ + sigName = str; + } else if (var.equals("signal-meaning")) { //$NON-NLS-1$ + sigMeaning = str; + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MISignalEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), sigName, sigMeaning); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISteppingRangeEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISteppingRangeEvent.java new file mode 100644 index 00000000000..e4ee34beb26 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MISteppingRangeEvent.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; + +/** + * + * *stopped,reason="end-stepping-range",thread-id="0",frame={addr="0x08048538",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="13"} + */ +@Immutable +public class MISteppingRangeEvent extends MIStoppedEvent { + + protected MISteppingRangeEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame) { + super(ctx, token, results, frame); + } + + @Deprecated + public static MISteppingRangeEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MISteppingRangeEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } + + /** + * @since 1.1 + */ + public static MISteppingRangeEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MISteppingRangeEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame()); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIStoppedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIStoppedEvent.java new file mode 100644 index 00000000000..f897211df89 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIStoppedEvent.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MITuple; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped + * + */ +@Immutable +public class MIStoppedEvent extends MIEvent { + + final private MIFrame frame; + + protected MIStoppedEvent(IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame) { + super(ctx, token, results); + this.frame = frame; + } + + public MIFrame getFrame() { + return frame; + } + + @Deprecated + public static MIStoppedEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + int threadId = -1; + MIFrame frame = null; + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("thread-id")) { //$NON-NLS-1$ + if (value instanceof MIConst) { + String str = ((MIConst)value).getString(); + try { + threadId = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } else if (var.equals("frame")) { //$NON-NLS-1$ + if (value instanceof MITuple) { + frame = new MIFrame((MITuple)value); + } + } + } + IExecutionDMContext execDmc = containerDmc; + if (runControl != null && threadId != -1) { + execDmc = runControl.createMIExecutionContext(containerDmc, threadId); + } + return new MIStoppedEvent(execDmc, token, results, frame); + } + + /** + * @since 1.1 + */ + public static MIStoppedEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + MIFrame frame = null; + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("frame")) { //$NON-NLS-1$ + if (value instanceof MITuple) { + frame = new MIFrame((MITuple)value); + } + } + } + return new MIStoppedEvent(dmc, token, results, frame); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadCreatedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadCreatedEvent.java new file mode 100644 index 00000000000..7c19b5f5a95 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadCreatedEvent.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; + + +/** + * This can only be detected by gdb/mi after GDB 6.8. + * + */ +@Immutable +public class MIThreadCreatedEvent extends MIEvent { + + final private String fThreadId; + + public MIThreadCreatedEvent(IContainerDMContext ctx, int id) { + this(ctx, 0, id); + } + + public MIThreadCreatedEvent(IContainerDMContext ctx, int token, int id) { + super(ctx, token, null); + fThreadId = Integer.toString(id); + } + + /** + * @since 1.1 + */ + public MIThreadCreatedEvent(IContainerDMContext ctx, String threadId) { + this(ctx, 0, threadId); + } + + /** + * @since 1.1 + */ + public MIThreadCreatedEvent(IContainerDMContext ctx, int token, String threadId) { + super(ctx, token, null); + fThreadId = threadId; + } + + public int getId() { + try { + return Integer.parseInt(fThreadId); + } + catch (NumberFormatException e) { + return 0; + } + } + + /** + * @since 1.1 + */ + public String getStrId() { + return fThreadId; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadExitEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadExitEvent.java new file mode 100644 index 00000000000..8f6bff97cf0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadExitEvent.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; + + +/** + * This can not be detected yet by gdb/mi. + * + */ +@Immutable +public class MIThreadExitEvent extends MIEvent { + + final private String fThreadId; + + public MIThreadExitEvent(IContainerDMContext ctx, int id) { + this(ctx, 0, id); + } + + public MIThreadExitEvent(IContainerDMContext ctx, int token, int id) { + super(ctx, token, null); + fThreadId = Integer.toString(id); + } + + /** + * @since 1.1 + */ + public MIThreadExitEvent(IContainerDMContext ctx, String threadId) { + this(ctx, 0, threadId); + } + + /** + * @since 1.1 + */ + public MIThreadExitEvent(IContainerDMContext ctx, int token, String threadId) { + super(ctx, token, null); + fThreadId = threadId; + } + + public int getId() { + try { + return Integer.parseInt(fThreadId); + } + catch (NumberFormatException e) { + return 0; + } + } + + /** + * @since 1.1 + */ + public String getStrId() { + return fThreadId; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupCreatedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupCreatedEvent.java new file mode 100644 index 00000000000..a825f0d1851 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupCreatedEvent.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; + + +/** + * This can only be detected by gdb/mi after GDB 6.8. + * @since 1.1 + * + */ +@Immutable +public class MIThreadGroupCreatedEvent extends MIEvent { + + final private String fGroupId; + + public MIThreadGroupCreatedEvent(IProcessDMContext ctx, int token, String groupId) { + super(ctx, token, null); + fGroupId = groupId; + } + + public String getGroupId() { return fGroupId; } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java new file mode 100644 index 00000000000..dacd3260abf --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIThreadGroupExitedEvent.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; + + +/** + * This can only be detected by gdb/mi after GDB 6.8. + * @since 1.1 + * + */ +@Immutable +public class MIThreadGroupExitedEvent extends MIEvent { + + final private String fGroupId; + + public MIThreadGroupExitedEvent(IProcessDMContext ctx, int token, String groupId) { + super(ctx, token, null); + fGroupId = groupId; + } + + public String getGroupId() { return fGroupId; } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointScopeEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointScopeEvent.java new file mode 100644 index 00000000000..3d12e73f769 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointScopeEvent.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped,reason="watchpoint-scope",wpnum="5", + * + */ +@Immutable +public class MIWatchpointScopeEvent extends MIStoppedEvent { + + final private int number; + + protected MIWatchpointScopeEvent( + IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, int number) + { + super(ctx, token, results, frame); + this.number = number; + } + + public int getNumber() { + return number; + } + + @Deprecated + public static MIWatchpointScopeEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + int number = 0; + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("wpnum")) { //$NON-NLS-1$ + if (value instanceof MIConst) { + String str = ((MIConst) value).getString(); + try { + number = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } + } + + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MIWatchpointScopeEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), number); + } + + /** + * @since 1.1 + */ + public static MIWatchpointScopeEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + int number = 0; + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("wpnum")) { //$NON-NLS-1$ + if (value instanceof MIConst) { + String str = ((MIConst) value).getString(); + try { + number = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } + } + + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MIWatchpointScopeEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), number); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointTriggerEvent.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointTriggerEvent.java new file mode 100644 index 00000000000..783fcd64b26 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/events/MIWatchpointTriggerEvent.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.events; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; +import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; +import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; +import org.eclipse.cdt.dsf.mi.service.command.output.MITuple; +import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; + +/** + * *stopped,reason="watchpoint-trigger",wpt={number="2",exp="i"},value={old="0",new="1"},thread-id="0",frame={addr="0x08048534",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbffff18c"}],file="hello.c",line="10"} + * + */ +@Immutable +public class MIWatchpointTriggerEvent extends MIStoppedEvent { + + final private int number; + final private String exp; + final private String oldValue; + final private String newValue; + + protected MIWatchpointTriggerEvent( + IExecutionDMContext ctx, int token, MIResult[] results, MIFrame frame, + int number, String exp, String oldValue, String newValue) + { + super(ctx, token, results, frame); + this.number = number; + this.exp = exp; + this.oldValue = oldValue; + this.newValue = newValue; + } + + public int getNumber() { + return number; + } + + public String getExpression() { + return exp; + } + + public String getOldValue() { + return oldValue; + } + + public String getNewValue() { + return newValue; + } + + @Deprecated + public static MIWatchpointTriggerEvent parse( + MIRunControl runControl, IContainerDMContext containerDmc, int token, MIResult[] results) + { + int number = 0; + String exp = ""; //$NON-NLS-1$ + String oldValue = ""; //$NON-NLS-1$ + String newValue = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("wpt") || var.equals("hw-awpt") || var.equals("hw-rwpt")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (value instanceof MITuple) { + for (MIResult wptResult : ((MITuple) value).getMIResults()) { + String wptVar = wptResult.getVariable(); + MIValue wptValue = wptResult.getMIValue(); + + if (wptVar.equals("number")) { //$NON-NLS-1$ + if (wptValue instanceof MIConst) { + String str = ((MIConst) wptValue).getString(); + try { + number = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + } else if (wptVar.equals("exp")) { //$NON-NLS-1$ + if (wptValue instanceof MIConst) { + exp = ((MIConst) wptValue).getString(); + } + } + } + } + } else if (var.equals("value")) { //$NON-NLS-1$ + if (value instanceof MITuple) { + for (MIResult valueResult : ((MITuple)value).getMIResults()) { + String valueVar = valueResult.getVariable(); + MIValue valueValue = valueResult.getMIValue(); + String str = ""; //$NON-NLS-1$ + if (valueValue instanceof MIConst) { + str = ((MIConst) valueValue).getString(); + } + + if (valueVar.equals("old")) { //$NON-NLS-1$ + oldValue = str; + } else if (valueVar.equals("new")) { //$NON-NLS-1$ + newValue = str; + } else if (valueVar.equals("value")) { //$NON-NLS-1$ + oldValue = newValue = str; + } + } + + } + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(runControl, containerDmc, token, results); + return new MIWatchpointTriggerEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), number, exp, oldValue, newValue); + } + + /** + * @since 1.1 + */ + public static MIWatchpointTriggerEvent parse(IExecutionDMContext dmc, int token, MIResult[] results) + { + int number = 0; + String exp = ""; //$NON-NLS-1$ + String oldValue = ""; //$NON-NLS-1$ + String newValue = ""; //$NON-NLS-1$ + + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("wpt") || var.equals("hw-awpt") || var.equals("hw-rwpt")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (value instanceof MITuple) { + for (MIResult wptResult : ((MITuple) value).getMIResults()) { + String wptVar = wptResult.getVariable(); + MIValue wptValue = wptResult.getMIValue(); + + if (wptVar.equals("number")) { //$NON-NLS-1$ + if (wptValue instanceof MIConst) { + String str = ((MIConst) wptValue).getString(); + try { + number = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + } else if (wptVar.equals("exp")) { //$NON-NLS-1$ + if (wptValue instanceof MIConst) { + exp = ((MIConst) wptValue).getString(); + } + } + } + } + } else if (var.equals("value")) { //$NON-NLS-1$ + if (value instanceof MITuple) { + for (MIResult valueResult : ((MITuple)value).getMIResults()) { + String valueVar = valueResult.getVariable(); + MIValue valueValue = valueResult.getMIValue(); + String str = ""; //$NON-NLS-1$ + if (valueValue instanceof MIConst) { + str = ((MIConst) valueValue).getString(); + } + + if (valueVar.equals("old")) { //$NON-NLS-1$ + oldValue = str; + } else if (valueVar.equals("new")) { //$NON-NLS-1$ + newValue = str; + } else if (valueVar.equals("value")) { //$NON-NLS-1$ + oldValue = newValue = str; + } + } + + } + } + } + MIStoppedEvent stoppedEvent = MIStoppedEvent.parse(dmc, token, results); + return new MIWatchpointTriggerEvent(stoppedEvent.getDMContext(), token, results, stoppedEvent.getFrame(), number, exp, oldValue, newValue); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoSharedLibraryInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoSharedLibraryInfo.java new file mode 100644 index 00000000000..d7c8bca063d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoSharedLibraryInfo.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson AB - Modified for DSF GDB reference implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + */ +public class CLIInfoSharedLibraryInfo extends MIInfo { + + DsfMISharedInfo[] shared; + + public class DsfMISharedInfo { + + String from; + String to; + boolean isread; + String name; + + public DsfMISharedInfo (String start, String end, boolean read, String location) { + from = start; + to = end; + isread = read; + name = location; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public boolean isRead() { + return isread; + } + + public String getName() { + return name; + } + + public void setSymbolsRead(boolean read) { + isread = read; + } + } + + public CLIInfoSharedLibraryInfo(MIOutput out) { + super(out); + parse(); + } + + public DsfMISharedInfo[] getMIShared() { + return shared; + } + + void parse() { + List aList = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIOOBRecord[] oobs = out.getMIOOBRecords(); + for (int i = 0; i < oobs.length; i++) { + if (oobs[i] instanceof MIConsoleStreamOutput) { + MIStreamRecord cons = (MIStreamRecord) oobs[i]; + String str = cons.getString(); + // We are interested in the shared info + parseShared(str.trim(), aList); + } + } + } + shared = new DsfMISharedInfo[aList.size()]; + for (int i = 0; i < aList.size(); i++) { + shared[i] = aList.get(i); + } + } + + void parseShared(String str, List aList) { + if (str.length() > 0) { + // Parsing pattern of type ~"0x40000970 0x4001331f Yes /lib/ld-linux.so.2\n" + Pattern pattern = Pattern.compile("(0x.*)(0x.*)(Yes|No)(\\s*)(.*)", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(str); + if (matcher.find()) { + DsfMISharedInfo s = new DsfMISharedInfo(matcher.group(1), matcher.group(2), + (matcher.group(3).equals("Yes"))?true:false, //$NON-NLS-1$ + matcher.group(5)); + aList.add(s); + + } + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoThreadsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoThreadsInfo.java new file mode 100644 index 00000000000..16c138201de --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIInfoThreadsInfo.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * GDB/MI thread list parsing. +~"\n" +~" 2 Thread 2049 (LWP 29354) " +~"* 1 Thread 1024 (LWP 29353) " + + */ +public class CLIInfoThreadsInfo extends MIInfo { + + protected List info; + + public CLIInfoThreadsInfo(MIOutput out) { + super(out); + parse(); + } + + public class ThreadInfo { + String fName; + String fGdbId; + String fPid; + boolean fIsCurrentThread = false; + + public ThreadInfo(String tid, String pid, String name, boolean isCurrentThread) + { + this.fName = name; + this.fGdbId = tid; + this.fPid = pid; + this.fIsCurrentThread = isCurrentThread; + } + + public String getName(){ return fName ;} + // GDB id given to a thread. Needed to compare with ID stored in DMC fetched via DsfMIThreadListIds command + public String getId(){ return fGdbId; } + public String getOsId(){return fPid; } + public boolean isCurrentThread(){return fIsCurrentThread; } + } + + public List getThreadInfo(){ + return info; + } + + protected void parse() { + info = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIOOBRecord[] oobs = out.getMIOOBRecords(); + for (int i = 0; i < oobs.length; i++) { + if (oobs[i] instanceof MIConsoleStreamOutput) { + MIStreamRecord cons = (MIStreamRecord) oobs[i]; + String str = cons.getString(); + // We are interested in finding the current thread + parseThreadInfo(str.trim(), info); + } + } + } + } + + protected void parseThreadInfo(String str, List info) { + // Fetch the OS ThreadId & Find the current thread + if(str.length() > 0 ){ + Pattern pattern = Pattern.compile("(^\\*?\\s*\\d+)(\\s*Thread\\s*)(0x[0-9a-fA-F]+|-?\\d+)(\\s*\\(LWP\\s*)(\\d*)", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(str); + boolean isCurrentThread = false; + if (matcher.find()) { + String id = matcher.group(1).trim(); + if (id.charAt(0) == '*') { + isCurrentThread = true; + id = id.substring(1).trim(); + } + info.add(new ThreadInfo(id, matcher.group(5), "", isCurrentThread)); //$NON-NLS-1$ + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIMonitorListProcessesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIMonitorListProcessesInfo.java new file mode 100644 index 00000000000..df8de5eea87 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/CLIMonitorListProcessesInfo.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.core.IProcessInfo; + + +/** + * + * @" 26 N 0 N 32794 Idle 2 Http daemon\n" + */ +public class CLIMonitorListProcessesInfo extends MIInfo { + + @Deprecated + public class ProcessInfo implements IProcessInfo, Comparable { + int pid; + String name; + + public ProcessInfo(String pidString, String name) { + try { + pid = Integer.parseInt(pidString); + } catch (NumberFormatException e) { + } + this.name = name; + } + + public ProcessInfo(int pid, String name) { + this.pid = pid; + this.name = name; + } + + /** + * @see org.eclipse.cdt.core.IProcessInfo#getName() + */ + public String getName() { + return name; + } + + /** + * @see org.eclipse.cdt.core.IProcessInfo#getPid() + */ + public int getPid() { + return pid; + } + + public int compareTo(ProcessInfo other) { + int nameCompare = getName().compareTo(other.getName()); + if (nameCompare != 0) return nameCompare; + else return (getPid() < other.getPid()) ? -1 : 1; + } + } + + IProcessInfo[] fProcessList; + + public CLIMonitorListProcessesInfo(MIOutput out) { + super(out); + parse(); + } + + public IProcessInfo[] getProcessList() { + return fProcessList; + } + + private void parse() { + List aList = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIOOBRecord[] oobs = out.getMIOOBRecords(); + for (int i = 0; i < oobs.length; i++) { + if (oobs[i] instanceof MITargetStreamOutput) { + MIStreamRecord cons = (MIStreamRecord) oobs[i]; + String str = cons.getString().trim(); + if (str.length() > 0) { + // Parsing pattern of type @" 26 N 0 N 32794 Idle 2 Http daemon\n" + Pattern pattern = Pattern.compile("(\\d*)\\s(Y|N)\\s*\\d*\\s(Y|N)\\s*\\d*\\s*\\D*\\s*\\d\\s(.*)", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(str); + if (matcher.find()) { + ProcessInfo proc = new ProcessInfo(matcher.group(1), matcher.group(4)); + aList.add(proc); + } + } + } + } + } + fProcessList = aList.toArray(new IProcessInfo[aList.size()]); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetAttributesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetAttributesInfo.java new file mode 100644 index 00000000000..6df0811cd6f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetAttributesInfo.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +public class ExprMetaGetAttributesInfo implements ICommandResult { + + private final boolean editable; + + public ExprMetaGetAttributesInfo(boolean e) { + editable = e; + } + + public boolean getEditable() { return editable; } + + public V getSubsetResult(ICommand command) { + return null; + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildCountInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildCountInfo.java new file mode 100644 index 00000000000..9d6be79dfb9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildCountInfo.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +public class ExprMetaGetChildCountInfo implements ICommandResult { + + private final int childNum; + + public ExprMetaGetChildCountInfo(int n) { + childNum = n; + } + + public int getChildNum() { return childNum; } + + public V getSubsetResult(ICommand command) { + return null; + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildrenInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildrenInfo.java new file mode 100644 index 00000000000..9fe5fcd3ee3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetChildrenInfo.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.mi.service.ExpressionService.ExpressionInfo; + +public class ExprMetaGetChildrenInfo implements ICommandResult { + + private final ExpressionInfo[] childrenExpressions; + + public ExprMetaGetChildrenInfo(ExpressionInfo[] c) { + childrenExpressions = c; + } + + public ExpressionInfo[] getChildrenExpressions() { return childrenExpressions; } + + public V getSubsetResult(ICommand command) { + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetValueInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetValueInfo.java new file mode 100644 index 00000000000..0c08c82cbfd --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetValueInfo.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +public class ExprMetaGetValueInfo implements ICommandResult { + + private final String value; + + public ExprMetaGetValueInfo(String v) { + value = v; + } + + public String getValue() { return value; } + + public V getSubsetResult(ICommand command) { + return null; + } + } \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java new file mode 100644 index 00000000000..d1690100a85 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/ExprMetaGetVarInfo.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +public class ExprMetaGetVarInfo implements ICommandResult { + + private final String expression; + private final int numChild; + private final String type; + private final boolean editable; + + public ExprMetaGetVarInfo(String e, int n, String t, boolean edit) { + expression = e; + numChild = n; + type = t; + editable = edit; + } + + public String getExpr() { return expression; } + public int getNumChildren() { return numChild; } + public String getType() { return type; } + public boolean getEditable() { return editable; } + + public V getSubsetResult(ICommand command) { + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIArg.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIArg.java new file mode 100644 index 00000000000..bcce631fd84 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIArg.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Represents a set name=value. + */ +public class MIArg { + String name; + String value; + + public MIArg(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + /** + * Parsing a DsfMIList of the form: + * [{name="xxx",value="yyy"},{name="xxx",value="yyy"},..] + * [name="xxx",name="xxx",..] + * [{name="xxx"},{name="xxx"}] + */ + public static MIArg[] getMIArgs(MIList miList) { + List aList = new ArrayList(); + MIValue[] values = miList.getMIValues(); + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof MITuple) { + MIArg arg = getMIArg((MITuple)values[i]); + if (arg != null) { + aList.add(arg); + } + } + } + MIResult[] results = miList.getMIResults(); + for (int i = 0; i < results.length; i++) { + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getCString(); + aList.add(new MIArg(str, "")); //$NON-NLS-1$ + } + } + return (aList.toArray(new MIArg[aList.size()])); + } + + /** + * Parsing a DsfMITuple of the form: + * {{name="xxx",value="yyy"},{name="xxx",value="yyy"},..} + * {name="xxx",name="xxx",..} + * {{name="xxx"},{name="xxx"}} + */ + public static MIArg[] getMIArgs(MITuple miTuple) { + List aList = new ArrayList(); + MIValue[] values = miTuple.getMIValues(); + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof MITuple) { + MIArg arg = getMIArg((MITuple)values[i]); + if (arg != null) { + aList.add(arg); + } + } + } + MIResult[] results = miTuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getCString(); + aList.add(new MIArg(str, "")); //$NON-NLS-1$ + } + } + return (aList.toArray(new MIArg[aList.size()])); + } + /** + * Parsing a DsfMITuple of the form: + * {name="xxx",value="yyy"} + * {name="xxx"} + */ + public static MIArg getMIArg(MITuple tuple) { + MIResult[] args = tuple.getMIResults(); + MIArg arg = null; + if (args.length > 0) { + // Name + String aName = ""; //$NON-NLS-1$ + MIValue value = args[0].getMIValue(); + if (value != null && value instanceof MIConst) { + aName = ((MIConst)value).getCString(); + } else { + aName = ""; //$NON-NLS-1$ + } + + // Value + String aValue = ""; //$NON-NLS-1$ + if (args.length == 2) { + value = args[1].getMIValue(); + if (value != null && value instanceof MIConst) { + aValue = ((MIConst)value).getCString(); + } else { + aValue = ""; //$NON-NLS-1$ + } + } + + arg = new MIArg(aName, aValue); + } + return arg; + } + + @Override + public String toString() { + return name + "=" + value; //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIAsyncRecord.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIAsyncRecord.java new file mode 100644 index 00000000000..9dffcebe43e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIAsyncRecord.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * base Abstract class for the OOB stream MI responses. + */ +public abstract class MIAsyncRecord extends MIOOBRecord { + + final static MIResult[] nullResults = new MIResult[0]; + + MIResult[] results = null; + String asynClass = ""; //$NON-NLS-1$ + int token = -1; + + public int getToken() { + return token; + } + + public void setToken(int t) { + token = t; + } + + public String getAsyncClass() { + return asynClass; + } + + public void setAsyncClass(String a) { + asynClass = a; + } + + public MIResult[] getMIResults() { + if (results == null) { + return nullResults; + } + return results; + } + + public void setMIResults(MIResult[] res) { + results = res; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + if (token > 0) { + buffer.append(token); + } + if (this instanceof MIExecAsyncOutput) { + buffer.append('*'); + } else if (this instanceof MIStatusAsyncOutput) { + buffer.append('+'); + } else if (this instanceof MINotifyAsyncOutput) { + buffer.append('='); + } + buffer.append(asynClass); + if (results != null) { + for (int i = 0; i < results.length; i++) { + buffer.append(','); + buffer.append(results[i].toString()); + } + } + buffer.append('\n'); + return buffer.toString(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakInsertInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakInsertInfo.java new file mode 100644 index 00000000000..3259cb368d8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakInsertInfo.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * -break-insert main + * ^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x08048468",func="main",file="hello.c",line="4",times="0"} + * -break-insert -a p + * ^done,hw-awpt={number="2",exp="p"} + * -break-watch -r p + * ^done,hw-rwpt={number="4",exp="p"} + * -break-watch p + * ^done,wpt={number="6",exp="p"} + */ +public class MIBreakInsertInfo extends MIInfo { + + MIBreakpoint[] breakpoints; + + public MIBreakInsertInfo(MIOutput record) { + super(record); + breakpoints = null; + List aList = new ArrayList(1); + if (isDone()) { + MIResultRecord rr = record.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue val = results[i].getMIValue(); + MIBreakpoint bpt = null; + if (var.equals("wpt")) { //$NON-NLS-1$ + if (val instanceof MITuple) { + bpt = new MIBreakpoint((MITuple)val); + bpt.setEnabled(true); + bpt.setWriteWatchpoint(true); + } + } else if (var.equals("bkpt")) { //$NON-NLS-1$ + if (val instanceof MITuple) { + bpt = new MIBreakpoint((MITuple)val); + bpt.setEnabled(true); + } + } else if (var.equals("hw-awpt")) { //$NON-NLS-1$ + if (val instanceof MITuple) { + bpt = new MIBreakpoint((MITuple)val); + bpt.setAccessWatchpoint(true); + bpt.setEnabled(true); + } + } else if (var.equals("hw-rwpt")) { //$NON-NLS-1$ + if (val instanceof MITuple) { + bpt = new MIBreakpoint((MITuple)val); + bpt.setReadWatchpoint(true); + bpt.setEnabled(true); + } + } + if (bpt != null) { + aList.add(bpt); + } + } + } + } + breakpoints = aList.toArray(new MIBreakpoint[aList.size()]); + } + + public MIBreakpoint[] getMIBreakpoints() { + return breakpoints; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakListInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakListInfo.java new file mode 100644 index 00000000000..003af994573 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakListInfo.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + + + +/** + * A -break-list result-record is the form: + *
+ * ^done,BreakpointTable={nr_rows="1",nr_cols="6",hdr=[..],body=[brkpt={},brkpt={}]}
+ *-break-list
+^done,BreakpointTable={nr_rows="6",nr_cols="6",hdr=[{width="3",alignment="-1",col_name="number",colhdr="Num"},{width="14",alignment="-1",col_name="type",colhdr="State"},{width="4",alignment="-1",col_name="disp",colhdr="Disp"},{width="3",alignment="-1",col_name="enabled",colhdr="Enb"},{width="10",alignment="-1",col_name="addr",colhdr="Address"},{width="40",alignment="2",col_name="what",colhdr="What"}],body=[bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="2",type="breakpoint",disp="del",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="3",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",cond="1",times="0"},bkpt={number="4",type="hw breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"},bkpt={number="5",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="0",thread="0",times="0"},bkpt={number="6",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="1",thread="1",times="0"}]}
+ * 
+ */ +public class MIBreakListInfo extends MIInfo { + + MIBreakpoint[] breakpoints; + + public MIBreakListInfo(MIOutput rr) { + super(rr); + } + + public MIBreakpoint[] getMIBreakpoints() { + if (breakpoints == null) { + parse(); + } + return breakpoints; + } + + void parse() { + List aList = new ArrayList(1); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("BreakpointTable")) { //$NON-NLS-1$ + parseTable(results[i].getMIValue(), aList); + } + } + } + } + breakpoints = aList.toArray(new MIBreakpoint[aList.size()]); + } + + void parseTable(MIValue val, List aList) { + if (val instanceof MITuple) { + MIResult[] table = ((MITuple)val).getMIResults(); + for (int j = 0; j < table.length; j++) { + String variable = table[j].getVariable(); + if (variable.equals("body")) { //$NON-NLS-1$ + parseBody(table[j].getMIValue(), aList); + } + } + } + } + + void parseBody(MIValue body, List aList) { + if (body instanceof MIList) { + MIResult[] bkpts = ((MIList)body).getMIResults(); + for (int i = 0; i < bkpts.length; i++) { + String b = bkpts[i].getVariable(); + if (b.equals("bkpt")) { //$NON-NLS-1$ + MIValue value = bkpts[i].getMIValue(); + if (value instanceof MITuple) { + aList.add(new MIBreakpoint((MITuple)value)); + } + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakpoint.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakpoint.java new file mode 100644 index 00000000000..aaddbb5ecdd --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIBreakpoint.java @@ -0,0 +1,294 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for the breakpoint service + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * Contain info about the GDB/MI breakpoint. + * + * (gdb) + * -break-insert main + * ^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"} + * (gdb) + * -break-insert -t main + * ^done,bkpt={number="2",type="breakpoint",disp="del",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"} + * (gdb) + * -break-insert -c 1 main + * ^done,bkpt={number="3",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",cond="1",times="0"} + * (gdb) + * -break-insert -h main + * ^done,bkpt={number="4",type="hw breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",times="0"} + * (gdb) + * -break-insert -p 0 main + * ^done,bkpt={number="5",type="breakpoint",disp="keep",enabled="y",addr="0x0804846b",func="main",file="hello.c",line="4",thread="0",thread="0",times="0"} + * (gdb) + * -break-watch -a p + * ^done,hw-awpt={number="2",exp="p"} + * (gdb) + * -break-watch -r p + * ^done,hw-rwpt={number="4",exp="p"} + * (gdb) + * -break-watch p + * ^done,wpt={number="6",exp="p"} + * (gdb) + */ +public class MIBreakpoint { + + int number = -1; + String type = ""; //$NON-NLS-1$ + String disp = ""; //$NON-NLS-1$ + boolean enabled = false; + String address = ""; //$NON-NLS-1$ + String func = ""; //$NON-NLS-1$ + String fullName = ""; //$NON-NLS-1$ + String file = ""; //$NON-NLS-1$ + int line = -1; + String cond = ""; //$NON-NLS-1$ + int times = 0; + String exp = ""; //$NON-NLS-1$ + String threadId = "0"; //$NON-NLS-1$ + int ignore = 0; + + boolean isWpt = false; + boolean isAWpt = false; + boolean isRWpt = false; + boolean isWWpt = false; + boolean isHdw = false; + + public MIBreakpoint() { + } + + public MIBreakpoint(MIBreakpoint other) { + number = other.number; + type = new String(other.type); + disp = new String(other.disp); + enabled = other.enabled; + type = new String(other.type); + address = new String(other.address); + func = new String(other.func); + fullName = new String(other.fullName); + file = new String(other.file); + line = other.line; + cond = new String(other.cond); + times = other.times; + exp = new String(other.exp); + threadId = new String(other.threadId); + ignore = other.ignore; + isWpt = other.isWpt; + isAWpt = other.isAWpt; + isRWpt = other.isRWpt; + isWWpt = other.isWWpt; + isHdw = other.isHdw; + } + + public MIBreakpoint(MITuple tuple) { + parse(tuple); + } + + /////////////////////////////////////////////////////////////////////////// + // Properties getters + /////////////////////////////////////////////////////////////////////////// + + public int getNumber() { + return number; + } + + public String getType() { + return type; + } + + public String getDisposition() { + return disp; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean e) { + enabled = e; + } + + public String getAddress() { + return address; + } + + public String getFunction() { + return func; + } + + public String getFile() { + return file; + } + + public String getFullName() { + return fullName; + } + + public int getLine() { + return line; + } + + public String getCondition() { + return cond; + } + + public void setCondition(String condition) { + cond = condition; + } + + public int getIgnoreCount() { + return ignore; + } + + public void setIgnoreCount(int ignoreCount) { + ignore = ignoreCount; + } + + public String getThreadId() { + return threadId; + } + + public int getTimes() { + return times; + } + + public String getExpression() { + return exp; + } + + public boolean isTemporary() { + return getDisposition().equals("del"); //$NON-NLS-1$ + } + + public boolean isHardware() { + return isHdw; + } + + public void setHardware(boolean b) { + isWpt = b; + isHdw = b; + } + + public boolean isWatchpoint() { + return isWpt; + } + + public void isWatchpoint(boolean b) { + isWpt = b; + } + + public boolean isAccessWatchpoint() { + return isAWpt; + } + + public void setAccessWatchpoint(boolean b) { + isWpt = b; + isAWpt = b; + } + + public boolean isReadWatchpoint() { + return isRWpt; + } + + public void setReadWatchpoint(boolean b) { + isWpt = b; + isRWpt = b; + } + + public boolean isWriteWatchpoint() { + return isWWpt; + } + + public void setWriteWatchpoint(boolean b) { + isWpt = b; + isWWpt = b; + } + + // Parse the result string + void parse(MITuple tuple) { + MIResult[] results = tuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value != null && value instanceof MIConst) { + str = ((MIConst)value).getCString(); + } + + if (var.equals("number")) { //$NON-NLS-1$ + try { + number = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("type")) { //$NON-NLS-1$ + type = str; + //type="hw watchpoint" + if (type.startsWith("hw")) { //$NON-NLS-1$ + isHdw = true; + isWWpt = true; + isWpt = true; + } + //type="acc watchpoint" + if (type.startsWith("acc")) { //$NON-NLS-1$ + isAWpt = true; + isWpt = true; + } + //type="read watchpoint" + if (type.startsWith("read")) { //$NON-NLS-1$ + isRWpt = true; + isWpt = true; + } + // type="breakpoint" + // default ok. + } else if (var.equals("disp")) { //$NON-NLS-1$ + disp = str; + } else if (var.equals("enabled")) { //$NON-NLS-1$ + enabled = str.equals("y"); //$NON-NLS-1$ + } else if (var.equals("addr")) { //$NON-NLS-1$ + try { + address = str.trim(); + } catch (NumberFormatException e) { + } + } else if (var.equals("func")) { //$NON-NLS-1$ + func = str; + } else if (var.equals("file")) { //$NON-NLS-1$ + file = str; + } else if (var.equals("fullname")) { //$NON-NLS-1$ + fullName = str; + } else if (var.equals("thread")) { //$NON-NLS-1$ + threadId = str; + } else if (var.equals("line")) { //$NON-NLS-1$ + try { + line = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("times")) { //$NON-NLS-1$ + try { + times = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("what") || var.equals("exp")) { //$NON-NLS-1$ //$NON-NLS-2$ + exp = str; + } else if (var.equals("ignore")) { //$NON-NLS-1$ + try { + ignore = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("cond")) { //$NON-NLS-1$ + cond = str; + } + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConsoleStreamOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConsoleStreamOutput.java new file mode 100644 index 00000000000..99b1b04193f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConsoleStreamOutput.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * @see MIStreamRecord + */ +public class MIConsoleStreamOutput extends MIStreamRecord { +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConst.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConst.java new file mode 100644 index 00000000000..957566cba47 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIConst.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI const value represents a ios-c string. + */ +public class MIConst extends MIValue { + String cstring = ""; //$NON-NLS-1$ + + public String getCString() { + return cstring; + } + + public void setCString(String str) { + cstring = str; + } + + /** + * Translate gdb c-string. + */ + public String getString() { + return getString(cstring); + } + + public static String getString(String str) { + StringBuffer buffer = new StringBuffer(); + boolean escape = false; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '\\') { + if (escape) { + buffer.append(c); + escape = false; + } else { + escape = true; + } + } else { + if (escape) { + if (isIsoCSpecialChar(c)) { + buffer.append(isoC(c)); + } else { + buffer.append('\\'); + buffer.append(c); + } + } else { + buffer.append(c); + } + escape = false; + } + } + + // If escape is still true it means that the + // last char was an '\'. + if (escape) { + buffer.append('\\'); + } + + return buffer.toString(); + } + + @Override + public String toString() { + return getCString(); + } + + /** + * Assuming that the precedent character was the + * escape sequence '\' + */ + private static String isoC(char c) { + String s = new Character(c).toString(); + if (c == '"') { + s = "\""; //$NON-NLS-1$ + } else if (c == '\'') { + s = "\'"; //$NON-NLS-1$ + } else if (c == '?') { + s = "?"; //$NON-NLS-1$ + } else if (c == 'a') { + s = "\007"; //$NON-NLS-1$ + } else if (c == 'b') { + s = "\b"; //$NON-NLS-1$ + } else if (c == 'f') { + s = "\f"; //$NON-NLS-1$ + } else if (c == 'n') { + s = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$ + } else if (c == 'r') { + s = "\r"; //$NON-NLS-1$ + } else if (c == 't') { + s = "\t"; //$NON-NLS-1$ + } else if (c == 'v') { + s = "\013"; //$NON-NLS-1$ + } + return s; + } + + private static boolean isIsoCSpecialChar(char c) { + switch (c) { + case '"': + case '\'': + case '?': + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + return true; + } + return false; + + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataDisassembleInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataDisassembleInfo.java new file mode 100644 index 00000000000..a04f3d9b05d --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataDisassembleInfo.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Modified for DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * The parsed output of the data-disassemble command. The output format is + * determined by the mode field on the request. + * + * -data-disassemble -s $pc -e "$pc + 20" -- 0 + * ^done,asm_insns=[ + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * {address="0x000107c8",func-name="main",offset="12",inst="or %o2, 0x140, %o1\t! 0x11940 <_lib_version+8>"}, + * {address="0x000107cc",func-name="main",offset="16",inst="sethi %hi(0x11800), %o2"}, + * {address="0x000107d0",func-name="main",offset="20",inst="or %o2, 0x168, %o4\t! 0x11968 <_lib_version+48>"}] + * + * -data-disassemble -f basics.c -l 32 -- 0 + * ^done,asm_insns=[ + * {address="0x000107bc",func-name="main",offset="0",inst="save %sp, -112, %sp"}, + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * [...] + * {address="0x0001081c",func-name="main",offset="96",inst="ret "}, + * {address="0x00010820",func-name="main",offset="100",inst="restore "}] + * + * -data-disassemble -f basics.c -l 32 -n 3 -- 1 + * ^done,asm_insns=[ + * src_and_asm_line={line="31",file="/dir1/dir2/basics.c",line_asm_insn=[ + * {address="0x000107bc",func-name="main",offset="0",inst="save %sp, -112, %sp"}]}, + * src_and_asm_line={line="32",file="/dir1/dir2/basics.c",line_asm_insn=[ + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}]}] + * + */ + +public class MIDataDisassembleInfo extends MIInfo { + + // The parsed information + private boolean mixed; + private MIMixedInstruction[] mixedCode; + private MIInstruction[] assemblyCode; + + public MIDataDisassembleInfo(MIOutput record) { + super(record); + mixed = false; + parse(); + } + + public boolean isMixed() { + return mixed; + } + + public MIInstruction[] getMIAssemblyCode() { + return assemblyCode; + } + + public MIMixedInstruction[] getMIMixedCode() { + return mixedCode; + } + + /** + * Find the relevant tag in the output record ("asm_insns") and then + * parse its value. + */ + private void parse() { + List asmList = new ArrayList(); + List srcList = new ArrayList(); + + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + // Technically, there should be only one field (asm_insns), but just in case... + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("asm_insns")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIList) { + parseResult((MIList) value, srcList, asmList); + } + } + } + } + } + + assemblyCode = asmList.toArray(new MIInstruction[asmList.size()]); + mixedCode = srcList.toArray(new MIMixedInstruction[srcList.size()]); + } + + /** + * Parse the back-end result. Depending on the requested mode + * ("-- 0" or "-- 1" on the request), the result has one of the + * following forms: + * + * [1] Mode == 0 (assembly instructions only) + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * ..., + * {address="0x00010820",func-name="main",offset="100",inst="restore "} + * + * [2] Mode == 1 (Mixed source and assembly code) + * src_and_asm_line={ + * line="31",file="/dir1/dir2/basics.c", + * line_asm_insn=[ + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * ..., + * {address="0x00010820",func-name="main",offset="100",inst="restore "} + * ] + * }, + * ..., + * src_and_asm_line={ + * line="31",file="/dir1/dir2/basics.c", + * line_asm_insn=[ + * ..., + * ] + * } + */ + private void parseResult(MIList list, + List srcList, List asmList) { + + // Mixed mode (with source) + MIResult[] results = list.getMIResults(); + if (results != null && results.length > 0) { + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("src_and_asm_line")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + srcList.add(new MIMixedInstruction((MITuple) value)); + } + } + } + mixed = true; + } + + // Non-mixed mode + MIValue[] values = list.getMIValues(); + if (values != null && values.length > 0) { + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof MITuple) { + asmList.add(new MIInstruction((MITuple) values[i])); + } + } + mixed = false; + } + + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataEvaluateExpressionInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataEvaluateExpressionInfo.java new file mode 100644 index 00000000000..ca28a203a2f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataEvaluateExpressionInfo.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI Data evaluate expression parsing response. + */ +public class MIDataEvaluateExpressionInfo extends MIInfo { + + String fValue; + + public MIDataEvaluateExpressionInfo(MIOutput rr) { + super(rr); + fValue = ""; //$NON-NLS-1$ + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord outr = out.getMIResultRecord(); + if (outr != null) { + MIResult[] results = outr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("value")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + fValue = ((MIConst)value).getCString(); + } + } + } + } + } + } + + public String getValue() { + return fValue; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterNamesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterNamesInfo.java new file mode 100644 index 00000000000..d6310ff068e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterNamesInfo.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * GDB/MI data list regiter names response extraction. + */ +public class MIDataListRegisterNamesInfo extends MIInfo { + + String[] names; + protected int realNameCount = 0; + + public MIDataListRegisterNamesInfo(MIOutput rr) { + super(rr); + names = null; + List aList = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord outr = out.getMIResultRecord(); + if (outr != null) { + MIResult[] results = outr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("register-names")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIList) { + parseRegisters((MIList) value, aList); + } + } + } + } + } + names = aList.toArray(new String[aList.size()]); + } + + /* + * Returns the register names. + */ + public String[] getRegisterNames() { + + /* + * The expectation is that we return an empty list. The + * constructor quarantees this so we are good here. + */ + return names; + } + + private void parseRegisters(MIList list, List aList) { + MIValue[] values = list.getMIValues(); + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof MIConst) { + String str = ((MIConst) values[i]).getCString(); + + /* this cannot filter nulls because index is critical in retreival + * and index is assigned in the layers above. The MI spec allows + * empty returns, for some register names. */ + if (str != null && str.length() > 0) { + realNameCount++; + aList.add(str); + } else { + aList.add(""); //$NON-NLS-1$ + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterValuesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterValuesInfo.java new file mode 100644 index 00000000000..bb97de9abb8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataListRegisterValuesInfo.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIDataListRegisterValues; + +/** + * GDB/MI data list register values extraction. + */ +public class MIDataListRegisterValuesInfo extends MIInfo { + + MIRegisterValue[] registers; + + public MIDataListRegisterValuesInfo(MIOutput rr) { + super(rr); + registers = null; + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord outr = out.getMIResultRecord(); + if (outr != null) { + MIResult[] results = outr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("register-values")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIList) { + registers = MIRegisterValue.getMIRegisterValues((MIList)value); + } + } + } + } + } + if (registers == null) { + registers = new MIRegisterValue[0]; + } + } + + /* + * Returns the array of registers values. + */ + + public MIRegisterValue[] getMIRegisterValues() { + + /* + * The expectation is that we return an empty list. The + * constructor quarantees this so we are good here. + */ + return registers; + } + + /** + * Returns the desired subset of results. When this function is being called + * the data here represents a coalesced request which is a superset of at + * least two original requests. We are extracting the data associated with + * the specified original request which we know is contained in this result. + */ + @Override + public V getSubsetResult(ICommand cmd) { + if (cmd instanceof MIDataListRegisterValues) { + MIDataListRegisterValues command = (MIDataListRegisterValues) cmd; + List aList = new ArrayList(); + int[] wantedRegNos = command.getRegList(); + + /* + * Search through the larger answer set finding the ones we want. + */ + for (MIRegisterValue regVal : registers) { + for ( int curRegNo : wantedRegNos ) { + if ( regVal.getNumber() == curRegNo ) { + aList.add( regVal ); + } + } + } + + /* + * Now construct a new complete answer. + */ + MIRegisterValue[] finalRegSet = aList.toArray(new MIRegisterValue[aList.size()]); + MIDataListRegisterValuesInfo finalSubset = new MIDataListRegisterValuesInfo( getMIOutput()); + finalSubset.registers = finalRegSet; + + @SuppressWarnings("unchecked") + V vFinalSubset = (V)finalSubset; + return vFinalSubset ; + } else { + return super.getSubsetResult(cmd); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataReadMemoryInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataReadMemoryInfo.java new file mode 100644 index 00000000000..e258bfe6763 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataReadMemoryInfo.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson AB - Modified for new DSF Reference Implementation + * Ericsson AB - Reverted to byte[] and processed multi-line results + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.math.BigInteger; + +import org.eclipse.debug.core.model.MemoryByte; + +/** + * + * -data-read-memory result + * + * (gdb) + * nn-data-read-memory [command parameters] + * nn^done,addr="ADDRESS",nr-bytes="NR_BYTES",total-bytes="TOTAL_BYTES", + * next-row="NEXT_ROW",prev-row="PREV_ROW",next-page="NEXT_PAGE", + * prev-page="PREV_PAGE",memory=[ + * {addr="addr1",data=["0x00","0x01", ...]}, + * {addr="addr2",data=["0x02","0x03", ...]}, + * {addr="addr3",data=["0x04","0x05", ...]}, + * ...] + * (gdb) + * + * where: + * + * 'ADDRESS' + * Address (in hex) of the first byte fetched. + * + * 'NR_BYTES' + * Number of bytes read. + * + * 'TOTAL_BYTES' + * Number of bytes requested (nr-rows * nr-columns * word-size). + * + * 'NEXT_ROW' + * Address (in hex) of the next row. + * + * 'PREV_ROW' + * Address (in hex) of the previous row. + * + * 'NEXT_PAGE' + * Address (in hex) of the next page. + * + * 'PREV_PAGE' + * Address (in hex) of the previous page. + * + * 'MEMORY' + * Memory bytes retrieved, nr-rows of nr-columns words. + * + */ +public class MIDataReadMemoryInfo extends MIInfo { + + // The parsed values of interest + BigInteger fAddress = new BigInteger("0"); //$NON-NLS-1$ + int fBytesRead; + int fBytesRequested; + MemoryByte[] fMemoryBlock; + + /** + * Constructor + * + * @param output + */ + public MIDataReadMemoryInfo(MIOutput output) { + + super(output); + + fMemoryBlock = new MemoryByte[0]; + if (isDone()) { + parseResult(1); + } + } + + /** + * Constructor + * + * @param output + */ + public MIDataReadMemoryInfo(MIOutput output, int word_size) { + + super(output); + + fMemoryBlock = new MemoryByte[0]; + if (isDone()) { + parseResult(word_size); + } + } + + /** + * Parse the back-end-result. The result is an array of the following form: + * + * [0] addr="address" + * [1] nr-bytes="x" + * [2] total-bytes="y" + * [3] next-row="address2" + * [4] prev-row="address3" + * [5] next-page="address4" + * [6] prev-page="address5" + * [7] memory=[{addr="addr1",data=["0x00","0x01",...]}] + * + * At this point, we only have interest in "memory". + */ + private void parseResult(int word_size) { + + // Get the GDB/MI result record + MIOutput output = getMIOutput(); + MIResultRecord record = output.getMIResultRecord(); + + // Parse the result record + if (record != null) { + + // Parse the output results + // Note: we assume that the result respects the output format + // i.e. nothing missing, nothing out of order. + MIResult[] results = record.getMIResults(); + for (int i = 0; i < results.length; i++) { + + // Get the variable name + String var = results[i].getVariable(); + + // Parse 'addr="address"', the address of the first byte to read + if (var.equals("addr")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String address = ((MIConst) value).getCString(); + fAddress = new BigInteger(address.substring(2), 16); // Strip the "0x" + } + } + + // Parse 'nr-bytes="x"', the number of bytes read + if (var.equals("total-bytes")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String size = ((MIConst) value).getCString(); + fBytesRead = Integer.parseInt(size); + } + } + + // Parse '"total-bytes="y"', the number of bytes requested + // Instantiate the corresponding output buffer with invalid bytes + if (var.equals("total-bytes")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String size = ((MIConst) value).getCString(); + fBytesRequested = Integer.parseInt(size); + fMemoryBlock = new MemoryByte[fBytesRequested]; + for (int j = 0; j < fMemoryBlock.length; j++) + fMemoryBlock[j] = new MemoryByte((byte) 0, (byte) 0); + } + } + + // Parse 'memory=[{addr="addr1",data=["0x00","0x01",...]}]' + if (var.equals("memory")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIList) { + parseMemoryLines((MIList) value, word_size); + } + } + } + } + } + + /** + * Parse the actual memory lines of the general form: + * + * [{addr="addr1",data=["0x00","0x01",...]}] + * [{addr="addr2",data=["0x00","0x01",...]}] + * + * Since we haven't implemented coalescing yet, we conveniently simplify + * the processing by assuming that the memory block address matches the + * one of the request. Therefore, we only have to fill the memoryBlock[] + * with the incoming bytes. + * + * This will have to be revisited as soon as we start considering + * multiple (and possibly canceled) requests. + */ + private void parseMemoryLines(MIList lines, int word_size) { + + // Parse each line and append the results to the result block + MIValue[] lineValues = lines.getMIValues(); + for (int i = 0; i < lineValues.length; i++) { + + // Each line has 2 tuples: "addr" and "data" + if (lineValues[i] instanceof MITuple) { + MITuple tuple = (MITuple) lineValues[i]; + MIResult[] results = tuple.getMIResults(); + + // The offset of this particular output line in the result buffer + int offset = 0; + + // The 1st tuple ('addr="addr1"') gives us the address of the first byte read + MIValue addrValue = results[0].getMIValue(); + if (addrValue instanceof MIConst) { + String address = ((MIConst) addrValue).getCString(); + BigInteger startAddress = new BigInteger(address.substring(2), 16); // Strip the "0x" + offset = startAddress.subtract(fAddress).intValue(); + } + + // The 2nd tuple ("data=[...]") gives us the actual bytes + MIValue value = results[1].getMIValue(); + if (value instanceof MIList) { + MIList list = (MIList) value; + MIValue[] values = list.getMIValues(); + + MemoryByte[] byteValues = new MemoryByte[values.length * word_size]; + + // Parse the result array + for (int j = 0; j < values.length; j++) { + if (values[j] instanceof MIConst) { + String str = ((MIConst) values[j]).getCString(); + try { + long word = Long.decode(str.trim()).longValue(); + for (int k = 0; k < word_size; k++) { + int bit_shift = (word_size - k - 1) * 8; + byteValues[j * word_size + k] = new MemoryByte((byte) ((word >> bit_shift) % 256)); + } + } catch (NumberFormatException e) { + for (int k = 0; k < word_size; k++) + byteValues[j * word_size + k] = new MemoryByte((byte) -1, (byte) 0); + } + } + } + // Copy the parsed line to the memory block + System.arraycopy(byteValues, 0, fMemoryBlock, offset, byteValues.length); + } + } + } + } + + /** + * Return the memory block + */ + public MemoryByte[] getMIMemoryBlock() { + return fMemoryBlock; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataWriteMemoryInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataWriteMemoryInfo.java new file mode 100644 index 00000000000..a8f2e692a28 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIDataWriteMemoryInfo.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson Communication - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * -data-write-memory result + * + * (gdb) + * nn-data-write-memory [command parameters] + * nn^done + * + */ +public class MIDataWriteMemoryInfo extends MIInfo { + + /** + * Constructor + * + * @param output + */ + public MIDataWriteMemoryInfo(MIOutput output) { + + super(output); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIExecAsyncOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIExecAsyncOutput.java new file mode 100644 index 00000000000..4d85143bdc2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIExecAsyncOutput.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * @see MIAsyncRecord + */ +public class MIExecAsyncOutput extends MIAsyncRecord { +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIFrame.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIFrame.java new file mode 100644 index 00000000000..c27bafd290f --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIFrame.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI Frame tuple parsing. + */ +public class MIFrame { + + int level; + String addr; + String func = ""; //$NON-NLS-1$ + String file = ""; //$NON-NLS-1$ + // since gdb 6.4 + String fullname = ""; //$NON-NLS-1$ + int line; + MIArg[] args = new MIArg[0]; + + public MIFrame(MITuple tuple) { + parse(tuple); + } + + public MIArg[] getArgs() { + return args; + } + + public String getFile() { + String fname = getFullname(); + return ( fname.length() != 0 ) ? fname : file; + } + + public String getFullname() { + return fullname; + } + + public String getFunction() { + return func; + } + + public int getLine() { + return line; + } + + public String getAddress() { + return addr; + } + + public int getLevel() { + return level; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("level=\"" + level + "\""); //$NON-NLS-1$//$NON-NLS-2$ + buffer.append(",addr=\"" + addr + "\""); //$NON-NLS-1$//$NON-NLS-2$ + buffer.append(",func=\"" + func + "\""); //$NON-NLS-1$//$NON-NLS-2$ + buffer.append(",file=\"" + file + "\""); //$NON-NLS-1$//$NON-NLS-2$ + buffer.append(",line=\"").append(line).append('"'); //$NON-NLS-1$ + buffer.append(",args=["); //$NON-NLS-1$ + for (int i = 0; i < args.length; i++) { + if (i != 0) { + buffer.append(','); + } + buffer.append("{name=\"" + args[i].getName() + "\"");//$NON-NLS-1$//$NON-NLS-2$ + buffer.append(",value=\"" + args[i].getValue() + "\"}");//$NON-NLS-1$//$NON-NLS-2$ + } + buffer.append(']'); + return buffer.toString(); + } + + void parse(MITuple tuple) { + MIResult[] results = tuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + if (value != null && value instanceof MIConst) { + str = ((MIConst)value).getCString(); + } + + if (var.equals("level")) { //$NON-NLS-1$ + try { + level = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("addr")) { //$NON-NLS-1$ + try { + addr = str.trim(); + } catch (NumberFormatException e) { + } + } else if (var.equals("func")) { //$NON-NLS-1$ + func = null; + if ( str != null ) { + str = str.trim(); + if ( str.equals( "??" ) ) //$NON-NLS-1$ + func = ""; //$NON-NLS-1$ + else + { + // In some situations gdb returns the function names that include parameter types. + // To make the presentation consistent truncate the parameters. PR 46592 + int end = str.indexOf( '(' ); + if ( end != -1 ) + func = str.substring( 0, end ); + else + func = str; + } + } + } else if (var.equals("file")) { //$NON-NLS-1$ + file = str; + } else if (var.equals("fullname")) { //$NON-NLS-1$ + fullname = str; + } else if (var.equals("line")) { //$NON-NLS-1$ + try { + line = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } else if (var.equals("args")) { //$NON-NLS-1$ + if (value instanceof MIList) { + args = MIArg.getMIArgs((MIList)value); + } else if (value instanceof MITuple) { + args = MIArg.getMIArgs((MITuple)value); + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIGDBShowExitCodeInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIGDBShowExitCodeInfo.java new file mode 100644 index 00000000000..6e625fc00c5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIGDBShowExitCodeInfo.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI show parsing. + * (gdb) + * -data-evaluate-expression $_exitcode + * ^done,value="10" + * (gdb) + */ +public class MIGDBShowExitCodeInfo extends MIDataEvaluateExpressionInfo { + + public MIGDBShowExitCodeInfo(MIOutput o) { + super(o); + } + + public int getCode() { + int code = 0; + String exp = getValue(); + try { + code = Integer.parseInt(exp); + } catch (NumberFormatException e) { + } + return code; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInfo.java new file mode 100644 index 00000000000..15d803a2449 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInfo.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.debug.service.command.ICommand; +import org.eclipse.cdt.dsf.debug.service.command.ICommandResult; + +/** + * Base class for teh parsing/info GDB/MI classes. + */ +public class MIInfo implements ICommandResult { + + private final MIOutput miOutput; + + public MIInfo(MIOutput record) { + miOutput = record; + } + + public MIOutput getMIOutput () { + return miOutput; + } + + public boolean isDone() { + return isResultClass(MIResultRecord.DONE); + } + + public boolean isRunning() { + return isResultClass(MIResultRecord.RUNNING); + } + + public boolean isConnected() { + return isResultClass(MIResultRecord.CONNECTED); + } + + public boolean isError() { + return isResultClass(MIResultRecord.ERROR); + } + + public boolean isExit() { + return isResultClass(MIResultRecord.EXIT); + } + + @Override + public String toString() { + if (miOutput != null) { + return miOutput.toString(); + } + return ""; //$NON-NLS-1$ + } + + boolean isResultClass(String rc) { + if (miOutput != null) { + MIResultRecord rr = miOutput.getMIResultRecord(); + if (rr != null) { + String clazz = rr.getResultClass(); + return clazz.equals(rc); + } + } + return false; + } + + public String getErrorMsg() { + if (miOutput != null) { + MIResultRecord rr = miOutput.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("msg")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String s = ((MIConst)value).getCString(); + return s; + } + } + } + } + } + return ""; //$NON-NLS-1$ + } + + /** + * Default implementation of this method returns null, which means that a subset + * result canot be calculated for the given command. Deriving classes should + * override this method as needed. + */ + public V getSubsetResult(ICommand command) { + return null; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInstruction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInstruction.java new file mode 100644 index 00000000000..1980b5fd2b7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIInstruction.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Adapted for DSF + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.service.IInstruction; + +public class MIInstruction implements IInstruction { + + // The parsed information + BigInteger address; + String function = ""; //$NON-NLS-1$ + long offset; + String opcode = ""; //$NON-NLS-1$ + String args = ""; //$NON-NLS-1$ + + public MIInstruction(MITuple tuple) { + parse(tuple); + } + + public BigInteger getAdress() { + return address; + } + + public String getFuntionName() { + return function; + } + + public long getOffset() { + return offset; + } + + public String getInstruction() { + return opcode + " " + args; //$NON-NLS-1$; + } + + public String getOpcode() { + return opcode; + } + + public String getArgs() { + return args; + } + + /** + * Parse the assembly instruction result. Each instruction has the following + * fields: + * - Address + * - Function name + * - Offset + * - Instruction + * + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * ..., + * {address="0x00010820",func-name="main",offset="100",inst="restore "} + * + * In addition, the opcode and arguments are extracted form the assembly instruction. + */ + private void parse(MITuple tuple) { + MIResult[] results = tuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + + if (value != null && value instanceof MIConst) { + str = ((MIConst)value).getCString(); + } + + if (var.equals("address")) { //$NON-NLS-1$ + try { + address = decodeAddress(str.trim()); + } catch (NumberFormatException e) { + } + continue; + } + + if (var.equals("func-name")) { //$NON-NLS-1$ + function = str; + continue; + } + + if (var.equals("offset")) { //$NON-NLS-1$ + try { + offset = Long.decode(str.trim()).longValue(); + } catch (NumberFormatException e) { + } + continue; + } + + if (var.equals("inst")) { //$NON-NLS-1$ + /* for the instruction, we do not want the C string but the + translated string since the only thing we are doing is + displaying it. */ + str = ((MIConst) value).getString(); + + char chars[] = str.toCharArray(); + int index = 0; + + // count the non-whitespace characters. + while( (index < chars.length) && (chars[index] > '\u0020')) + index++; + + opcode = str.substring( 0, index ); + + // skip any whitespace characters + while( index < chars.length && chars[index] >= '\u0000' && chars[index] <= '\u0020') + index++; + + // guard no argument + if( index < chars.length ) + args = str.substring( index ); + } + } + + } + + /** + * Decode given string representation of a non-negative integer. A + * hexadecimal encoded integer is expected to start with 0x. + * + * @param string + * decimal or hexadecimal representation of an non-negative integer + * @return address value as BigInteger + */ + private static BigInteger decodeAddress(String string) { + if (string.startsWith("0x")) { //$NON-NLS-1$ + return new BigInteger(string.substring(2), 16); + } + return new BigInteger(string); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIList.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIList.java new file mode 100644 index 00000000000..e7fe733baf8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIList.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI list semantic. + */ +public class MIList extends MIValue { + + final static MIResult[] nullResults = new MIResult[0]; + final static MIValue[] nullValues = new MIValue[0]; + + MIResult[] results = nullResults; + MIValue[] values = nullValues; + + public MIResult[] getMIResults() { + return results; + } + + public void setMIResults(MIResult[] res) { + results = res; + } + + public MIValue[] getMIValues() { + return values; + } + + public void setMIValues(MIValue[] vals) { + values = vals; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append('['); + for (int i = 0; i < results.length; i++) { + if (i != 0) { + buffer.append(','); + } + buffer.append(results[i].toString()); + } + for (int i = 0; i < values.length; i++) { + if (i != 0) { + buffer.append(','); + } + buffer.append(values[i].toString()); + } + buffer.append(']'); + return buffer.toString(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java new file mode 100644 index 00000000000..9770c0db865 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIListThreadGroupsInfo.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * GDB/MI thread group parsing. + * + * The description field can be different depending on the target we are connected to. + * + * -list-thread-groups --available: + * ^done,groups=[{id="160",description="name: JIM_InstallerProcess, type 555481, locked: N, system: N, state: Idle"}, + * {id="161",description="name: JIM_TcpSetupHandlerProcess, type 555505, locked: N, system: N, state: Idle"}, + * {id="162",description="name: JUnitProcess_PT, type 1094605, locked: N, system: N, state: Idle"}] + * + * -list-thread-groups: + * ^done,groups=[{id="162",type="process",pid="162"}] + * + * list-thread-groups GROUPID, in the case of a running thread or a stopped thread: + * ^done,threads=[{id="1",target-id="Thread 162.32942",details="JUnitProcess_PT (Ready) 1030373359 44441",frame={level="0",addr="0x00000000",func="??",args=[]},state="stopped"}] + * ^done,threads=[{id="1",target-id="Thread 162.32942",details="JUnitProcess_PT Idle 981333916 42692",state="running"}] + * @since 1.1 + */ +public class MIListThreadGroupsInfo extends MIInfo { + + public interface IThreadGroupInfo { + String getGroupId(); + String getPid(); + String getName(); + String getDesciption(); + } + + @Immutable + private static class ThreadGroupInfo implements IThreadGroupInfo { + final String fGroupId; + final String fDescription; + final String fName; + + public ThreadGroupInfo(String id, String description) { + fGroupId = id; + fDescription = description; + + fName = parseName(fDescription); + } + + private static String parseName(String desc) { + String name = ""; //$NON-NLS-1$ + + // Find the string "name: " followed by the smallest set of characters that + // is followed by a comma, or by the end of the line. + Pattern pattern = Pattern.compile("name: (.*?)(, |$)", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(desc); + if (matcher.find()) { + name = matcher.group(1); + } + + return name; + } + + public String getGroupId() { return fGroupId; } + public String getPid() { return fGroupId; } + + public String getName() { return fName; } + + public String getDesciption() { return fDescription; } + } + + + private IThreadGroupInfo[] fGroupList; + private MIThreadInfoInfo fThreadInfo; + + public MIListThreadGroupsInfo(MIOutput out) { + super(out); + parse(); + } + + public IThreadGroupInfo[] getGroupList() { return fGroupList; } + public MIThreadInfoInfo getThreadInfo() { return fThreadInfo; } + + private void parse() { + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("groups")) { //$NON-NLS-1$ + MIValue val = results[i].getMIValue(); + if (val instanceof MIList) { + parseGroups((MIList)val); + } + } else if (var.equals("threads")) { //$NON-NLS-1$ + // Re-use the MIThreadInfoInfo parsing + fThreadInfo = new MIThreadInfoInfo(out); + } + } + } + } + if (fGroupList == null) { + fGroupList = new IThreadGroupInfo[0]; + } + if (fThreadInfo == null) { + fThreadInfo = new MIThreadInfoInfo(null); + } + } + + private void parseGroups(MIList list) { + MIValue[] values = list.getMIValues(); + fGroupList = new IThreadGroupInfo[values.length]; + for (int i = 0; i < values.length; i++) { + MIResult[] results = ((MITuple)values[i]).getMIResults(); + String id = "", desc = "";//$NON-NLS-1$//$NON-NLS-2$ + + for (MIResult result : results) { + String var = result.getVariable(); + if (var.equals("id")) { //$NON-NLS-1$ + MIValue value = result.getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getCString(); + id = str.trim(); + } + } else if (var.equals("description")) { //$NON-NLS-1$ + MIValue value = result.getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getCString(); + desc = str.trim(); + + } + } + } + fGroupList[i] = new ThreadGroupInfo(id, desc); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MILogStreamOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MILogStreamOutput.java new file mode 100644 index 00000000000..6fee1e36684 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MILogStreamOutput.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * @see MIStreamRecord + */ +public class MILogStreamOutput extends MIStreamRecord { + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIMixedInstruction.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIMixedInstruction.java new file mode 100644 index 00000000000..a96be9ad6b3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIMixedInstruction.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson - Adapted for DSF + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.service.IInstruction; +import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; + +public class MIMixedInstruction implements IMixedInstruction { + + // The parsed information + private String fileName = ""; //$NON-NLS-1$ + private int lineNumber = 0; + private MIInstruction[] assemblyCode; + + public MIMixedInstruction(MITuple tuple) { + parse(tuple); + } + + public String getFileName() { + return fileName; + } + + public int getLineNumber() { + return lineNumber; + } + + public IInstruction[] getInstructions() { + return assemblyCode; + } + + /** + * Parse the mixed instruction result. It has the following 3 fields: + * + * line="31", + * file="/dir1/dir2/basics.c", + * line_asm_insn=[ + * {address="0x000107c0",func-name="main",offset="4",inst="mov 2, %o0"}, + * {address="0x000107c4",func-name="main",offset="8",inst="sethi %hi(0x11800), %o2"}, + * ..., + * {address="0x00010820",func-name="main",offset="100",inst="restore "} + * ] + */ + private void parse(MITuple tuple) { + List instructions = new ArrayList(); + MIResult[] results = tuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + String str = ""; //$NON-NLS-1$ + + if (value != null && value instanceof MIConst) { + str = ((MIConst) value).getCString(); + } + + if (var.equals("line")) { //$NON-NLS-1$ + try { + lineNumber = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + continue; + } + + if (var.equals("file")) { //$NON-NLS-1$ + fileName = str; + continue; + } + + if (var.equals("line_asm_insn")) { //$NON-NLS-1$ + if (value instanceof MIList) { + MIList list = (MIList) value; + MIValue[] values = list.getMIValues(); + for (int j = 0; j < values.length; j++) { + if (values[j] instanceof MITuple) { + instructions.add(new MIInstruction((MITuple) values[j])); + } + } + } + } + } + assemblyCode = instructions.toArray(new MIInstruction[instructions.size()]); + + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MINotifyAsyncOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MINotifyAsyncOutput.java new file mode 100644 index 00000000000..895da8b4811 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MINotifyAsyncOutput.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + + +/** + * @see MIAsyncRecord + */ +public class MINotifyAsyncOutput extends MIAsyncRecord { +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOOBRecord.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOOBRecord.java new file mode 100644 index 00000000000..57c63d4b6ed --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOOBRecord.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * @see MIOOBRecord + */ +public abstract class MIOOBRecord { +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOutput.java new file mode 100644 index 00000000000..ae728c3a76c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIOutput.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + * Ericsson - Modified for additional features in DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI response. + */ +public class MIOutput { + + private final MIResultRecord rr; + private final MIOOBRecord[] oobs; + + public MIOutput() { + this(null, null); + } + + public MIOutput(MIOOBRecord oob) { + this(null, new MIOOBRecord[] { oob }); + } + + + + public MIOutput(MIResultRecord rr, MIOOBRecord[] oobs) { + this.rr = rr; + this.oobs = oobs; + } + + public MIResultRecord getMIResultRecord() { + return rr; + } + + public MIOOBRecord[] getMIOOBRecords() { + return oobs; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < oobs.length; i++) { + buffer.append(oobs[i].toString()); + } + if (rr != null) { + buffer.append(rr.toString()); + } + return buffer.toString(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java new file mode 100644 index 00000000000..2b9a8929f00 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIParser.java @@ -0,0 +1,560 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** +
+`OUTPUT :'
+     `( OUT-OF-BAND-RECORD )* [ RESULT-RECORD ] "(gdb)" NL'
+
+`RESULT-RECORD :'
+     ` [ TOKEN ] "^" RESULT-CLASS ( "," RESULT )* NL'
+
+`OUT-OF-BAND-RECORD :'
+     `ASYNC-RECORD | STREAM-RECORD'
+
+`ASYNC-RECORD :'
+     `EXEC-ASYNC-OUTPUT | STATUS-ASYNC-OUTPUT | NOTIFY-ASYNC-OUTPUT'
+
+`EXEC-ASYNC-OUTPUT :'
+     `[ TOKEN ] "*" ASYNC-OUTPUT'
+
+`STATUS-ASYNC-OUTPUT :'
+     `[ TOKEN ] "+" ASYNC-OUTPUT'
+
+`NOTIFY-ASYNC-OUTPUT :'
+     `[ TOKEN ] "=" ASYNC-OUTPUT'
+
+`ASYNC-OUTPUT :'
+     `ASYNC-CLASS ( "," RESULT )* NL'
+
+`RESULT-CLASS :'
+     `"done" | "running" | "connected" | "error" | "exit"'
+
+`ASYNC-CLASS :'
+     `"stopped" | OTHERS' (where OTHERS will be added depending on the
+     needs--this is still in development).
+
+`RESULT :'
+     ` VARIABLE "=" VALUE'
+
+`VARIABLE :'
+     ` STRING '
+
+`VALUE :'
+     ` CONST | TUPLE | LIST '
+
+`CONST :'
+     `C-STRING'
+
+`TUPLE :'
+     ` "{}" | "{" RESULT ( "," RESULT )* "}" '
+
+`LIST :'
+     ` "[]" | "[" VALUE ( "," VALUE )* "]" | "[" RESULT ( "," RESULT )*
+     "]" '
+
+`STREAM-RECORD :'
+     `CONSOLE-STREAM-OUTPUT | TARGET-STREAM-OUTPUT | LOG-STREAM-OUTPUT'
+
+`CONSOLE-STREAM-OUTPUT :'
+     `"~" C-STRING'
+
+`TARGET-STREAM-OUTPUT :'
+     `"@" C-STRING'
+
+`LOG-STREAM-OUTPUT :'
+     `"&" C-STRING'
+
+`NL :'
+     `CR | CR-LF'
+
+`TOKEN :'
+     _any sequence of digits_.
+
+`C-STRING :'
+     `""" SEVEN-BIT-ISO-C-STRING-CONTENT """'
+
+ */ +public class MIParser { + public enum RecordType { ResultRecord, OOBRecord, PrimaryPrompt } + + public String primaryPrompt = "(gdb)"; //$NON-NLS-1$ + public String cliPrompt = primaryPrompt; + public String secondaryPrompt = ">"; //$NON-NLS-1$ + + public RecordType getRecordType(String line) { + int i = 0; + if (Character.isDigit(line.charAt(0))) { + i = 1; + while (i < line.length() && Character.isDigit(line.charAt(i))) { + i++; + } + } + + if (i < line.length() && line.charAt(i) == '^') { + return RecordType.ResultRecord; + } else if (line.startsWith(primaryPrompt, i)) { + return RecordType.PrimaryPrompt; + //break; // Do nothing. + } else { + return RecordType.OOBRecord; + } + } + + /** + * + */ + public MIResultRecord parseMIResultRecord(String line) { + StringBuffer buffer = new StringBuffer(line); + // Fetch the Token/Id + int id = parseToken(buffer); + // Consume the '^' + buffer.deleteCharAt(0); + + MIResultRecord rr = new MIResultRecord(); + rr.setToken(id); + if (buffer.toString().startsWith(MIResultRecord.DONE)) { + rr.setResultClass(MIResultRecord.DONE); + buffer.delete(0, MIResultRecord.DONE.length()); + } else if (buffer.toString().startsWith(MIResultRecord.ERROR)) { + rr.setResultClass(MIResultRecord.ERROR); + buffer.delete(0, MIResultRecord.ERROR.length()); + } else if (buffer.toString().startsWith(MIResultRecord.EXIT)) { + rr.setResultClass(MIResultRecord.EXIT); + buffer.delete(0, MIResultRecord.EXIT.length()); + } else if (buffer.toString().startsWith(MIResultRecord.RUNNING)) { + rr.setResultClass(MIResultRecord.RUNNING); + buffer.delete(0, MIResultRecord.RUNNING.length()); + } else if (buffer.toString().startsWith(MIResultRecord.CONNECTED)) { + rr.setResultClass(MIResultRecord.CONNECTED); + buffer.delete(0, MIResultRecord.CONNECTED.length()); + } else { + // Error throw an exception? + } + + // Results are separated by commas. + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + MIResult[] res = processMIResults(new FSB(buffer)); + rr.setMIResults(res); + } + return rr; + } + + /** + * Find OutOfBand Records depending on the starting token. + */ + public MIOOBRecord parseMIOOBRecord(String line) { + StringBuffer buffer = new StringBuffer(line); + int id = parseToken(buffer); + MIOOBRecord oob = null; + char c = buffer.length() != 0 ? buffer.charAt(0) : 0; + if (c == '*' || c == '+' || c == '=') { + // Consume the first char + buffer.deleteCharAt(0); + MIAsyncRecord async = null; + switch (c) { + case '*' : + async = new MIExecAsyncOutput(); + break; + + case '+' : + async = new MIStatusAsyncOutput(); + break; + + case '=' : + async = new MINotifyAsyncOutput(); + break; + } + async.setToken(id); + // Extract the Async-Class + int i = buffer.toString().indexOf(','); + if (i != -1) { + String asyncClass = buffer.substring(0, i); + async.setAsyncClass(asyncClass); + // Consume the async-class and the comma + buffer.delete(0, i + 1); + } else { + async.setAsyncClass(buffer.toString().trim()); + buffer.setLength(0); + } + MIResult[] res = processMIResults(new FSB(buffer)); + async.setMIResults(res); + oob = async; + } else if (c == '~' || c == '@' || c == '&') { + // Consume the first char + buffer.deleteCharAt(0); + MIStreamRecord stream = null; + switch (c) { + case '~' : + stream = new MIConsoleStreamOutput(); + break; + + case '@' : + stream = new MITargetStreamOutput(); + break; + + case '&' : + stream = new MILogStreamOutput(); + break; + } + // translateCString() assumes that the leading " is deleted + if (buffer.length() > 0 && buffer.charAt(0) == '"') { + buffer.deleteCharAt(0); + } + stream.setCString(translateCString(new FSB(buffer))); + oob = stream; + } else { + // Badly format MI line, just pass it to the user as target stream + MIStreamRecord stream = new MITargetStreamOutput(); + stream.setCString(line + "\n"); //$NON-NLS-1$ + oob = stream; + } + return oob; + } + + private int parseToken(StringBuffer buffer) { + int id = -1; + // Fetch the Token/Id + if (Character.isDigit(buffer.charAt(0))) { + int i = 1; + while (i < buffer.length() && Character.isDigit(buffer.charAt(i))) { + i++; + } + String numbers = buffer.substring(0, i); + try { + id = Integer.parseInt(numbers); + } catch (NumberFormatException e) { + } + // Consume the token. + buffer.delete(0, i); + } + return id; + } + + /** + * Assuming that the usual leading comma was consumed. + * Extract the MI Result comma seperated responses. + */ + private MIResult[] processMIResults(FSB buffer) { + List aList = new ArrayList(); + MIResult result = processMIResult(buffer); + if (result != null) { + aList.add(result); + } + while (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + result = processMIResult(buffer); + if (result != null) { + aList.add(result); + } + } + return aList.toArray(new MIResult[aList.size()]); + } + + /** + * Construct the DsfMIResult. Characters will be consume/delete + * moving forward constructing the AST. + */ + private MIResult processMIResult(FSB buffer) { + MIResult result = new MIResult(); + int equal; + if (buffer.length() > 0 && Character.isLetter(buffer.charAt(0)) && (equal = buffer.indexOf('=')) != -1) { + String variable = buffer.substring(0, equal); + result.setVariable(variable); + buffer.delete(0, equal + 1); + MIValue value = processMIValue(buffer); + result.setMIValue(value); + } else if(buffer.length()>0 && buffer.charAt(0)=='"') { + // This an error but we just swallow it and move on. + MIValue value = processMIValue(buffer); + result.setMIValue(value); + } else { + result.setVariable(buffer.toString()); + result.setMIValue(new MIConst()); // Empty string:??? + buffer.setLength(0); + } + return result; + } + + /** + * Find a DsfMIValue implementation or return null. + */ + private MIValue processMIValue(FSB buffer) { + MIValue value = null; + if (buffer.length() > 0) { + if (buffer.charAt(0) == '{') { + buffer.deleteCharAt(0); + value = processMITuple(buffer); + } else if (buffer.charAt(0) == '[') { + buffer.deleteCharAt(0); + value = processMIList(buffer); + } else if (buffer.charAt(0) == '"') { + buffer.deleteCharAt(0); + MIConst cnst = new MIConst(); + cnst.setCString(translateCString(buffer)); + value = cnst; + } + } + return value; + } + + /** + * Assuming the starting '{' was deleted form the StringBuffer, + * go to the closing '}' consuming/deleting all the characters. + * This is usually call by processMIvalue(); + */ + private MIValue processMITuple(FSB buffer) { + MITuple tuple = new MITuple(); + List valueList = new ArrayList(); + List resultList = new ArrayList(); + // Catch closing '}' + while (buffer.length() > 0 && buffer.charAt(0) != '}') { + // Try for the DsfMIValue first + MIValue value = processMIValue(buffer); + if (value != null) { + valueList.add(value); + } else { + MIResult result = processMIResult(buffer); + if (result != null) { + resultList.add(result); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == '}') { + buffer.deleteCharAt(0); + } + MIValue[] values = valueList.toArray(new MIValue[valueList.size()]); + MIResult[] res = resultList.toArray(new MIResult[resultList.size()]); + tuple.setMIValues(values); + tuple.setMIResults(res); + return tuple; + } + + /** + * Assuming the leading '[' was deleted, find the closing + * ']' consuming/delete chars from the StringBuffer. + */ + private MIValue processMIList(FSB buffer) { + MIList list = new MIList(); + List valueList = new ArrayList(); + List resultList = new ArrayList(); + // catch closing ']' + while (buffer.length() > 0 && buffer.charAt(0) != ']') { + // Try for the DsfMIValue first + MIValue value = processMIValue(buffer); + if (value != null) { + valueList.add(value); + } else { + MIResult result = processMIResult(buffer); + if (result != null) { + resultList.add(result); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ',') { + buffer.deleteCharAt(0); + } + } + if (buffer.length() > 0 && buffer.charAt(0) == ']') { + buffer.deleteCharAt(0); + } + MIValue[] values = valueList.toArray(new MIValue[valueList.size()]); + MIResult[] res = resultList.toArray(new MIResult[resultList.size()]); + list.setMIValues(values); + list.setMIResults(res); + return list; + } + + /* + * MI C-String rather MICOnst values are enclose in double quotes + * and any double quotes or backslash in the string are escaped. + * Assuming the starting double quote was removed. + * This method will stop at the closing double quote remove the extra + * backslach escaping and return the string __without__ the enclosing double quotes + * The orignal StringBuffer will move forward. + */ + private String translateCString(FSB buffer) { + boolean escape = false; + boolean closingQuotes = false; + + StringBuffer sb = new StringBuffer(); + + int index = 0; + for (; index < buffer.length() && !closingQuotes; index++) { + char c = buffer.charAt(index); + if (c == '\\') { + if (escape) { + sb.append(c); + sb.append(c); + escape = false; + } else { + escape = true; + } + } else if (c == '"') { + if (escape) { + sb.append(c); + escape = false; + } else { + // Bail out. + closingQuotes = true; + } + } else { + if (escape) { + sb.append('\\'); + } + sb.append(c); + escape = false; + } + } + buffer.delete(0, index); + return sb.toString(); + } + + /** + * Tests if this string starts with the specified prefix beginning + * a specified index. + * + * @param value the string. + * @param prefix the prefix. + * @return true if prefix starts value. + */ + public boolean startsWith(StringBuffer value, String prefix) { + int vlen = value.length(); + int plen = prefix.length(); + + if (vlen < plen) { + return false; + } + for (int i = 0; i < plen; i++) { + if (value.charAt(i) != prefix.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Fast String Buffer class. MIParser does a lot + * of deleting off the front of a string, that's clearly + * an order N operation for StringBuffer which makes + * the MIParser an order N^2 operation. There are "issues" + * with this for large arrays. Use of FSB rather than String + * Buffer makes MIParser N rather than N^2 because FSB can + * delete from the front in constant time. + */ + public class FSB { + StringBuffer buf; + int pos; + boolean shared; + + public FSB(StringBuffer buf) { + this.buf = buf; + pos = 0; + shared = false; + } + + public FSB(FSB fbuf) { + pos = fbuf.pos; + buf = fbuf.buf; + shared = true; + } + + public int length() { + int res = buf.length() - pos; + if (res < 0) + return 0; + + return res; + } + + public char charAt(int index) { + return buf.charAt(index + pos); + } + + private void resolveCopy() { + if (shared) { + buf = new StringBuffer(buf.toString()); + shared = false; + } + } + + public FSB deleteCharAt(int index) { + if (index == 0) { + pos++; + } else { + resolveCopy(); + buf = buf.deleteCharAt(pos + index); + } + + return this; + } + + public FSB delete(int start, int end) { + if (start == 0) { + pos = pos + end - start; + } else { + resolveCopy(); + buf.delete(start + pos, end + pos); + } + + return this; + } + + public void setLength(int a) { + if (a == 0) + pos = buf.length(); + else { + // panic! fortunately we don't do this. + } + } + + public String substring(int start, int end) { + return buf.substring(start + pos, end + pos); + } + + @Override + public String toString() { + return buf.substring(pos, buf.length()); + } + + int indexOf(char c) { + int len = buf.length(); + for (int i = pos; i < len; i++) { + if (buf.charAt(i) == c) + return i - pos; + } + + return -1; + } + + boolean startsWith(String s) { + int len = Math.min(s.length(), length()); + if (len < s.length()) + return false; + + for (int i = 0; i < len; i++) { + if (s.charAt(i) != buf.charAt(pos + i)) + return false; + } + + return true; + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIRegisterValue.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIRegisterValue.java new file mode 100644 index 00000000000..6aab5736ba5 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIRegisterValue.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * GDB/MI register response parsing. + */ +public class MIRegisterValue { + int number; + String value; + + public MIRegisterValue(int n, String v) { + number = n; + value = v; + } + + public int getNumber() { + return number; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("number=\"").append(number).append('"'); //$NON-NLS-1$ + buffer.append(',').append("value=\"" + value + "\""); //$NON-NLS-1$ //$NON-NLS-2$ + return buffer.toString(); + } + + /** + * Parsing a MIList of the form: + * [{number="1",value="0xffff"},{number="xxx",value="yyy"},..] + */ + public static MIRegisterValue[] getMIRegisterValues(MIList miList) { + List aList = new ArrayList(); + MIValue[] values = miList.getMIValues(); + for (int i = 0; i < values.length; i++) { + if (values[i] instanceof MITuple) { + MIRegisterValue reg = getMIRegisterValue((MITuple)values[i]); + if (reg != null) { + aList.add(reg); + } + } + } + return (aList.toArray(new MIRegisterValue[aList.size()])); + } + + /** + * Parsing a MITuple of the form: + * {number="xxx",value="yyy"} + */ + public static MIRegisterValue getMIRegisterValue(MITuple tuple) { + MIResult[] args = tuple.getMIResults(); + MIRegisterValue arg = null; + if (args.length == 2) { + // Name + String aName = ""; //$NON-NLS-1$ + MIValue value = args[0].getMIValue(); + if (value != null && value instanceof MIConst) { + aName = ((MIConst)value).getCString(); + } else { + aName = ""; //$NON-NLS-1$ + } + + // Value + String aValue = ""; //$NON-NLS-1$ + value = args[1].getMIValue(); + if (value != null && value instanceof MIConst) { + aValue = ((MIConst)value).getCString(); + } else { + aValue = ""; //$NON-NLS-1$ + } + + try { + int reg = Integer.parseInt(aName.trim()); + arg = new MIRegisterValue(reg, aValue.trim()); + } catch (NumberFormatException e) { + } + } + return arg; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResult.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResult.java new file mode 100644 index 00000000000..a3df3aa3745 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResult.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI result sematic (Variable=Value) + */ +public class MIResult { + String variable = ""; //$NON-NLS-1$ + MIValue value = null; + + public String getVariable() { + return variable; + } + + public void setVariable(String var) { + variable = var; + } + + public MIValue getMIValue() { + return value; + } + + public void setMIValue(MIValue val) { + value = val; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(variable); + if (value != null) { + String v = value.toString(); + buffer.append('='); + if (v.length() > 0 && (v.charAt(0) == '[' || v.charAt(0) =='{')) { + buffer.append(v); + } else { + buffer.append("\"" + value.toString() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + return buffer.toString(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResultRecord.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResultRecord.java new file mode 100644 index 00000000000..0fe6e28b0d2 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIResultRecord.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI ResultRecord. + */ +public class MIResultRecord { + + public final static String DONE ="done"; //$NON-NLS-1$ + public final static String RUNNING ="running"; //$NON-NLS-1$ + public final static String CONNECTED ="connected"; //$NON-NLS-1$ + public final static String ERROR ="error"; //$NON-NLS-1$ + public final static String EXIT ="exit"; //$NON-NLS-1$ + + static final MIResult[] nullResults = new MIResult[0]; + MIResult[] results = nullResults; + String resultClass = ""; //$NON-NLS-1$ + int token = -1; + + public int getToken() { + return token; + } + + public void setToken(int t) { + token = t; + } + + /** + */ + public String getResultClass() { + return resultClass; + } + + public void setResultClass(String type) { + resultClass = type; + } + + public MIResult[] getMIResults() { + return results; + } + + public void setMIResults(MIResult[] res) { + results = res; + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + if (token > 0) { + buffer.append(token); + } + buffer.append('^').append(resultClass); + for (int i = 0; i < results.length; i++) { + buffer.append(',').append(results[i].toString()); + } + return buffer.toString(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackInfoDepthInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackInfoDepthInfo.java new file mode 100644 index 00000000000..3c8aef3aa0e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackInfoDepthInfo.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + + +/** + * -stack-info-depth [max-depth] + * ^done,depth="12" + * + */ +public class MIStackInfoDepthInfo extends MIInfo { + + int depth = 0; + + public MIStackInfoDepthInfo(MIOutput record) { + super(record); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + + if (var.equals("depth")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getString(); + try { + depth = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } + } + } + } + } + + public int getDepth() { + return depth; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListArgumentsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListArgumentsInfo.java new file mode 100644 index 00000000000..f20205785cc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListArgumentsInfo.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + + +/** + * GDB/MI stack list arguments parsing. + */ +public class MIStackListArgumentsInfo extends MIInfo { + + MIFrame[] frames; + + public MIStackListArgumentsInfo(MIOutput out) { + super(out); + frames = null; + List aList = new ArrayList(1); + if (isDone()) { + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("stack-args")) { //$NON-NLS-1$ + MIValue val = results[i].getMIValue(); + if (val instanceof MIList) { + parseStack((MIList)val, aList); + } else if (val instanceof MITuple) { + parseStack((MITuple)val, aList); + } + } + } + } + } + frames = aList.toArray(new MIFrame[aList.size()]); + } + + public MIFrame[] getMIFrames() { + return frames; + } + + private void parseStack(MIList miList, List aList) { + MIResult[] results = miList.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("frame")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + aList.add (new MIFrame((MITuple)value)); + } + } + } + } + + private void parseStack(MITuple miTuple, List aList) { + MIResult[] results = miTuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("frame")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + aList.add (new MIFrame((MITuple)value)); + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListFramesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListFramesInfo.java new file mode 100644 index 00000000000..9482894545e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListFramesInfo.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * GDB/MI stack list frames info. + */ +public class MIStackListFramesInfo extends MIInfo { + + MIFrame[] frames; + + public MIStackListFramesInfo(MIOutput out) { + super(out); + frames = null; + List aList = new ArrayList(1); + if (isDone()) { + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("stack")) { //$NON-NLS-1$ + MIValue val = results[i].getMIValue(); + if (val instanceof MIList) { + parseStack((MIList)val, aList); + } else if (val instanceof MITuple) { + parseStack((MITuple)val, aList); + } + } + } + } + } + frames = aList.toArray(new MIFrame[aList.size()]); + } + + public MIFrame[] getMIFrames() { + return frames; + } + + void parseStack(MIList miList, List aList) { + MIResult[] results = miList.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("frame")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + aList.add (new MIFrame((MITuple)value)); + } + } + } + } + + // Old gdb use tuple instead of a list. + void parseStack(MITuple tuple, List aList) { + MIResult[] results = tuple.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("frame")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + aList.add (new MIFrame((MITuple)value)); + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListLocalsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListLocalsInfo.java new file mode 100644 index 00000000000..04bf3633103 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStackListLocalsInfo.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI stack list locals parsing. + * -stack-list-locals 1 + * ^done,locals=[{name="p",value="0x8048600 \"ghislaine\""},{name="buf",value="\"'\", 'x' , \"i,xxxxxxxxx\", 'a' "},{name="buf2",value="\"\\\"?'\\\\()~\""},{name="buf3",value="\"alain\""},{name="buf4",value="\"\\t\\t\\n\\f\\r\""},{name="i",value="0"}] + * + * On MacOS X 10.4 this returns a tuple: + * ^done,locals={{name="p",value="0x8048600 \"ghislaine\""},{name="buf",value="\"'\", 'x' , \"i,xxxxxxxxx\", 'a' "},{name="buf2",value="\"\\\"?'\\\\()~\""},{name="buf3",value="\"alain\""},{name="buf4",value="\"\\t\\t\\n\\f\\r\""},{name="i",value="0"}} + */ +public class MIStackListLocalsInfo extends MIInfo { + + MIArg[] locals; + + public MIStackListLocalsInfo(MIOutput out) { + super(out); + locals = null ; + if (isDone()) { + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("locals")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIList) { + locals = MIArg.getMIArgs((MIList)value); + } else if (value instanceof MITuple) { + locals = MIArg.getMIArgs((MITuple)value); + } + } + } + } + } + if (locals == null) { + locals = new MIArg[0]; + } + } + + public MIArg[] getLocals() { + return locals; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStatusAsyncOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStatusAsyncOutput.java new file mode 100644 index 00000000000..83dfa5d1218 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStatusAsyncOutput.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + + +/** + * @see MIAsyncRecord + */ +public class MIStatusAsyncOutput extends MIAsyncRecord { + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStreamRecord.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStreamRecord.java new file mode 100644 index 00000000000..96b7f7a3069 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIStreamRecord.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI stream record response. + */ +public abstract class MIStreamRecord extends MIOOBRecord { + + String cstring = ""; //$NON-NLS-1$ + + public String getCString() { + return cstring; + } + + public void setCString(String str) { + cstring = str; + } + + public String getString () { + return MIConst.getString(getCString()); + } + + @Override + public String toString() { + if (this instanceof MIConsoleStreamOutput) { return "~\"" + cstring + "\"\n"; } //$NON-NLS-1$ //$NON-NLS-2$ + else if (this instanceof MITargetStreamOutput) { return "@\"" + cstring + "\"\n"; } //$NON-NLS-1$ //$NON-NLS-2$ + else if (this instanceof MILogStreamOutput) { return "&\"" + cstring + "\"\n"; } //$NON-NLS-1$ //$NON-NLS-2$ + else { return "\"" + cstring + "\"\n"; } //$NON-NLS-1$ //$NON-NLS-2$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MITargetStreamOutput.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MITargetStreamOutput.java new file mode 100644 index 00000000000..7134f9f5091 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MITargetStreamOutput.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Wind River Systems - Modified for new DSF Reference Implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * @see MIStreamRecord + */ +public class MITargetStreamOutput extends MIStreamRecord { + + public static final String startTag = "@"; //$NON-NLS-1$ +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThread.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThread.java new file mode 100644 index 00000000000..2053196b653 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThread.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + * Wind River Systems - refactored to match pattern in package + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.dsf.concurrent.Immutable; + +/** + * GDB/MI Thread tuple parsing. + * + * @since 1.1 + */ +@Immutable +public class MIThread { + + final private String fThreadId; + final private String fTargetId; + final private String fOsId; + final private String fParentId; + final private MIFrame fTopFrame; + final private String fDetails; + final private String fState; + + private MIThread(String threadId, String targetId, String osId, String parentId, + MIFrame topFrame, String details, String state) { + fThreadId = threadId; + fTargetId = targetId; + fOsId = osId; + fParentId = parentId; + fTopFrame = topFrame; + fDetails = details; + fState = state; + } + + public String getThreadId() { return fThreadId; } + public String getTargetId() { return fTargetId; } + public String getOsId() { return fOsId; } + public String getParentId() { return fParentId; } + public MIFrame getTopFrame() { return fTopFrame; } + public String getDetails() { return fDetails; } + public String getState() { return fState; } + + public static MIThread parse(MITuple tuple) { + MIResult[] results = tuple.getMIResults(); + + String threadId = null; + String targetId = null; + String osId = null; + String parentId = null; + MIFrame topFrame = null; + String state = null; + String details = null; + + for (int j = 0; j < results.length; j++) { + MIResult result = results[j]; + String var = result.getVariable(); + if (var.equals("id")) { //$NON-NLS-1$ + MIValue val = results[j].getMIValue(); + if (val instanceof MIConst) { + threadId = ((MIConst) val).getCString().trim(); + } + } + else if (var.equals("target-id")) { //$NON-NLS-1$ + MIValue val = results[j].getMIValue(); + if (val instanceof MIConst) { + targetId = ((MIConst) val).getCString().trim(); + osId = parseOsId(targetId); + parentId = parseParentId(targetId); + } + } + else if (var.equals("frame")) { //$NON-NLS-1$ + MITuple val = (MITuple)results[j].getMIValue(); + topFrame = new MIFrame(val); + } + else if (var.equals("state")) { //$NON-NLS-1$ + MIValue val = results[j].getMIValue(); + if (val instanceof MIConst) { + state = ((MIConst) val).getCString().trim(); + } + } + else if (var.equals("details")) { //$NON-NLS-1$ + MIValue val = results[j].getMIValue(); + if (val instanceof MIConst) { + details = ((MIConst) val).getCString().trim(); + } + } + } + + return new MIThread(threadId, targetId, osId, parentId, topFrame, details, state); + } + + private static Pattern fgOsIdPattern1 = Pattern.compile("(Thread\\s*)(0x[0-9a-fA-F]+|-?\\d+)(\\s*\\(LWP\\s*)(\\d*)", 0); //$NON-NLS-1$ + private static Pattern fgOsIdPattern2 = Pattern.compile("Thread\\s*\\d+\\.(\\d+)", 0); //$NON-NLS-1$ + + private static String parseOsId(String str) { + // General format: + // "Thread 0xb7c8ab90 (LWP 7010)" + // "Thread 162.32942" + + Matcher matcher = fgOsIdPattern1.matcher(str); + if (matcher.find()) { + return matcher.group(4); + } + + matcher = fgOsIdPattern2.matcher(str); + if (matcher.find()) { + return matcher.group(1); + } + + return null; + } + + private static Pattern fgIdPattern = Pattern.compile("Thread\\s*(\\d+)\\.\\d+", 0); //$NON-NLS-1$ + + private static String parseParentId(String str) { + // General format: + // "Thread 0xb7c8ab90 (LWP 7010)" + // "Thread 162.32942" + + Matcher matcher = fgIdPattern.matcher(str); + if (matcher.find()) { + return matcher.group(1); + } + + return null; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadInfoInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadInfoInfo.java new file mode 100644 index 00000000000..333246007a9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadInfoInfo.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + * Wind River Systems - refactored to match pattern in package + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + + + +/** + * GDB/MI thread list parsing. + * + * Example 1: + * + * -thread-info + * ^done,threads=[ + * {id="2",target-id="Thread 0xb7c8ab90 (LWP 7010)", + * frame={level="0",addr="0x08048bba",func="my_func",args=[{name="arg",value="0xbff056f5"}], + * file="my_test.cc",fullname="/home/francois/GDB/my_test.cc",line="26"}, + * state="stopped"}, + * {id="1",target-id="Thread 0xb7c8b8d0 (LWP 7007)", + * frame={level="0",addr="0x08048a77",func="timer",args=[{name="duration",value="0xbff056f5 \"10\""}], + * file="my_test.cc",fullname="/home/francois/GDB/my_test.cc",line="39"}, + * state="stopped"} + * ],current-thread-id="2" + * + * + * Example 2: + * + * -thread-info 2 + * ^done,threads=[ + * {id="2",target-id="Thread 0xb7c8ab90 (LWP 7010)", + * frame={level="0",addr="0x08048bba",func="my_func",args=[{name="arg",value="0xbff056f5"}], + * file="my_test.cc",fullname="/home/francois/GDB/my_test.cc",line="26"}, + * state="stopped"} + * ] + * + * + * Example 3 (non-stop): + * + * -thread-info + * ^done,threads=[ + * {id="2",target-id="Thread 0xb7d6d6b0 (LWP 14494)",state="running"}, + * {id="1",target-id="Thread 0xb7c8b8d0 (LWP 7007)", + * frame={level="0",addr="0x08048a77",func="timer",args=[{name="duration",value="0xbff056f5 \"10\""}], + * file="my_test.cc",fullname="/home/francois/GDB/my_test.cc",line="39"}, + * state="stopped"} + * ],current-thread-id="1" + * + * + * Example 4 (non-stop): + * + * -thread-info 1 + * ^done,threads=[{id="1",target-id="Thread 0xb7d6d6b0 (LWP 14494)",state="running"}] + * + * + * Example 5 (Dicos): + * + * -thread-info 1 + * ^done,threads=[ + * {id="1",target-id="Thread 162.32942",details="JUnitProcess_PT (Ready) 175417582794 8572423", + * frame={level="0",addr="0x1559a318",func="mainExpressionTestApp",args=[], + * file="/local/home/lmckhou/TSP/TADE/example/JUnitProcess_OU/src/ExpressionTestApp.cc", + * fullname="/local/home/lmckhou/TSP/TADE/example/JUnitProcess_OU/src/ExpressionTestApp.cc",line="279"}, + * state="stopped"}] + * @since 1.1 + */ +public class MIThreadInfoInfo extends MIInfo { + + private String fCurrentThread = null; + private MIThread[] fThreadList = null; + + public MIThreadInfoInfo(MIOutput out) { + super(out); + parse(); + } + + public String getCurrentThread() { + return fCurrentThread; + } + + public MIThread[] getThreadList() { + return fThreadList; + } + + // General format: + // threads=[{...}],current-thread-id="n" + private void parse() { + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("threads")) { //$NON-NLS-1$ + MIValue val = results[i].getMIValue(); + if (val instanceof MIList) { + parseThreads((MIList) val); + } + } + else if (var.equals("current-thread-id")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + fCurrentThread = ((MIConst) value).getCString().trim(); + } + } + } + } + } + if (fThreadList == null) { + fThreadList = new MIThread[0]; + } + } + + // General formats: + // id="n",target-id="Thread 0xb7c8ab90 (LWP 7010)",frame={...},state="stopped" + // id="n",target-id="Thread 0xb7c8eb90 (LWP 7807)",state="running" + // id="n",target-id="Thread 162.32942",details="...",frame={...},state="stopped" + private void parseThreads(MIList list) { + MIValue[] values = list.getMIValues(); + fThreadList = new MIThread[values.length]; + + for (int i = 0; i < values.length; i++) { + fThreadList[i] = MIThread.parse((MITuple) values[i]); + } + } +} + diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadListIdsInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadListIdsInfo.java new file mode 100644 index 00000000000..eab4bc288ed --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIThreadListIdsInfo.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + * Ericsson AB - Modified for DSF Reference Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI thread list parsing. + */ +public class MIThreadListIdsInfo extends MIInfo { + + int[] threadIds; + private String[] strThreadIds; + + public MIThreadListIdsInfo(MIOutput out) { + super(out); + } + + public int[] getThreadIds() { + if (threadIds == null) { + // To make sure that the threads have be parsed + String[] tIds = getStrThreadIds(); + + threadIds = new int[tIds.length]; + for (int i=0; i aList = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + + if (var.equals("numchild")) { //$NON-NLS-1$ + if (value instanceof MIConst) { + String str = ((MIConst)value).getString(); + try { + numchild = Integer.parseInt(str.trim()); + } catch (NumberFormatException e) { + } + } + } else if (var.equals("children")) { //$NON-NLS-1$ + parseChildren(value, aList); + } + } + } + } + children = aList.toArray(new MIVar[aList.size()]); + } + + public MIVar[] getMIVars() { + return children; + } + + /* + * Some gdb MacOSX do not return a MITuple so we have + * to check for different format. + * See PR 81019 + */ + private void parseChildren(MIValue val, List aList) { + MIResult[] results = null; + if (val instanceof MITuple) { + results = ((MITuple)val).getMIResults(); + } else if (val instanceof MIList) { + results = ((MIList)val).getMIResults(); + } + if (results != null) { + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("child")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + aList.add(new MIVar((MITuple)value)); + } + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarSetFormatInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarSetFormatInfo.java new file mode 100644 index 00000000000..bcd4dd3392b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarSetFormatInfo.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI var-set-format + */ +public class MIVarSetFormatInfo extends MIInfo { + + String value = null; + + public MIVarSetFormatInfo(MIOutput record) { + super(record); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("value")) { //$NON-NLS-1$ + MIValue val = results[i].getMIValue(); + if (val instanceof MIConst) { + value = ((MIConst)val).getCString(); + } + } + } + } + } + } + + public String getValue () { + return value; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowAttributesInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowAttributesInfo.java new file mode 100644 index 00000000000..848449fe691 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowAttributesInfo.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +/** + * GDB/MI var-show-attributes + */ +public class MIVarShowAttributesInfo extends MIInfo { + + String attr = ""; //$NON-NLS-1$ + + public MIVarShowAttributesInfo(MIOutput record) { + super(record); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("attr")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + attr = ((MIConst)value).getString(); + } + } + } + } + } + } + + public String getAttributes () { + return attr; + } + + public boolean isEditable() { + return attr.equals("editable"); //$NON-NLS-1$ + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowFormatInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowFormatInfo.java new file mode 100644 index 00000000000..898bb66875a --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarShowFormatInfo.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import org.eclipse.cdt.dsf.mi.service.MIFormat; + + +/** + * GDB/MI var-show-format + */ +public class MIVarShowFormatInfo extends MIInfo { + + int format = MIFormat.NATURAL; + + public MIVarShowFormatInfo(MIOutput record) { + super(record); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("name")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MIConst) { + String str = ((MIConst)value).getString(); + if ("binary".equals(str)) { //$NON-NLS-1$ + format = MIFormat.BINARY; + } else if ("decimal".equals(str)) { //$NON-NLS-1$ + format = MIFormat.DECIMAL; + } else if ("hexadecimal".equals(str)) { //$NON-NLS-1$ + format = MIFormat.HEXADECIMAL; + } else if ("octal".equals(str)) { //$NON-NLS-1$ + format = MIFormat.OCTAL; + } else if ("natural".equals(str)) { //$NON-NLS-1$ + format = MIFormat.NATURAL; + } + } + } + } + } + } + } + + public int getFormat() { + return format; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java new file mode 100644 index 00000000000..e0953c62108 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/output/MIVarUpdateInfo.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.output; + +import java.util.ArrayList; +import java.util.List; + +/** + * GDB/MI var-update. + * -var-update * + * ^done,changelist={name="var3",value="3",in_scope="true",type_changed="false",name="var2",value="4",in_scope="true",type_changed="false"} + */ +public class MIVarUpdateInfo extends MIInfo { + + MIVarChange[] changeList; + + public MIVarUpdateInfo(MIOutput record) { + super(record); + List aList = new ArrayList(); + if (isDone()) { + MIOutput out = getMIOutput(); + MIResultRecord rr = out.getMIResultRecord(); + if (rr != null) { + MIResult[] results = rr.getMIResults(); + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + if (var.equals("changelist")) { //$NON-NLS-1$ + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + parseChangeList((MITuple)value, aList); + } else if (value instanceof MIList) { + parseChangeList((MIList)value, aList); + } + } + } + } + } + changeList = aList.toArray(new MIVarChange[aList.size()]); + } + + public MIVarChange[] getMIVarChanges() { + return changeList; + } + + /** + * For MI2 the format is now a MIList. + * @param tuple + * @param aList + */ + void parseChangeList(MIList miList, List aList) { + MIValue[] values = miList.getMIValues(); + for (int i = 0; i < values.length; ++i) { + if (values[i] instanceof MITuple) { + parseChangeList((MITuple)values[i], aList); + } else if (values[i] instanceof MIList) { + parseChangeList((MIList)values[i], aList); + } + } + } + + void parseChangeList(MITuple tuple, List aList) { + MIResult[] results = tuple.getMIResults(); + MIVarChange change = null; + for (int i = 0; i < results.length; i++) { + String var = results[i].getVariable(); + MIValue value = results[i].getMIValue(); + if (value instanceof MITuple) { + parseChangeList((MITuple)value, aList); + } + else + { + String str = ""; //$NON-NLS-1$ + if (value instanceof MIConst) { + str = ((MIConst)value).getString(); + } + if (var.equals("name")) { //$NON-NLS-1$ + change = new MIVarChange(str); + aList.add(change); + } else if (var.equals("value")) { //$NON-NLS-1$ + if (change != null) { + change.setValue(str); + } + } else if (var.equals("in_scope")) { //$NON-NLS-1$ + if (change != null) { + change.setInScope("true".equals(str)); //$NON-NLS-1$ + } + } else if (var.equals("type_changed")) { //$NON-NLS-1$ + if (change != null) { + change.setChanged("true".equals(str)); //$NON-NLS-1$ + } + } + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.classpath b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.externalToolBuilders/TestAppBuilder.launch b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.externalToolBuilders/TestAppBuilder.launch new file mode 100644 index 00000000000..e55b58e5eba --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.externalToolBuilders/TestAppBuilder.launch @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.project b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.project new file mode 100644 index 00000000000..abafad66df8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.project @@ -0,0 +1,38 @@ + + + org.eclipse.cdt.tests.dsf.gdb + + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + clean,full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/TestAppBuilder.launch + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.settings/org.eclipse.jdt.core.prefs b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c2002c43d4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Tue Jun 24 11:05:41 PDT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..b01633e7a10 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: GDB/MI reference application tests +Bundle-SymbolicName: org.eclipse.cdt.tests.dsf.gdb;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin +Bundle-Vendor: Ericsson +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.cdt.dsf, + org.eclipse.cdt.core, + org.eclipse.cdt.debug.core, + org.junit4, + org.eclipse.debug.core, + org.eclipse.swt, + org.eclipse.cdt.dsf.gdb +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ClassPath: . diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/TestAppBuilder.xml b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/TestAppBuilder.xml new file mode 100644 index 00000000000..3f8e9f07fed --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/TestAppBuilder.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/about.html b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/about.html new file mode 100644 index 00000000000..04492dd7e1b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/about.html @@ -0,0 +1,24 @@ + + + + +About +

About This Content

+ +

May 14, 2008

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/build.properties b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/build.properties new file mode 100644 index 00000000000..aba8ab478f4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/build.properties @@ -0,0 +1,5 @@ +output.tests.jar = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + . +source.. = src/ diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/BreakpointTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/BreakpointTestApp.cc new file mode 100644 index 00000000000..5474e540718 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/BreakpointTestApp.cc @@ -0,0 +1,47 @@ +//============================================================================ +// Name : BreakpointTestApp.cpp +// Author : Francois Chouinard +// Version : 1.0 +// Copyright : Ericsson AB +// Description : Breakpoint test application +//============================================================================ + +#include +using namespace std; + +const int ARRAY_SIZE = 256; + +char charBlock[ARRAY_SIZE]; +int integerBlock[ARRAY_SIZE]; + +void zeroBlocks(int abc) +{ + for (int i = 0; i < ARRAY_SIZE; i++) { + charBlock[i] = '\0'; + integerBlock[i] = 0; + } +} + +void setBlocks() +{ + for (int i = 0; i < ARRAY_SIZE; i++) { + charBlock[i] = (char) i; + integerBlock[i] = i; + } +} + +void loop() +{ + int j; + + for (int i = 0; i < ARRAY_SIZE; i++) + j = i; +} + +int main() +{ + zeroBlocks(1); + loop(); + setBlocks(); + return 0; +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc new file mode 100644 index 00000000000..269b5bcf2d7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/ExpressionTestApp.cc @@ -0,0 +1,298 @@ +#include + +int gIntVar = 543; +double gDoubleVar = 543.543; +char gCharVar = 'g'; +bool gBoolVar = false; + +int gIntArray[2] = {987, 654}; +double gDoubleArray[2] = {987.654, 654.321}; +char gCharArray[2] = {'g', 'd'}; +bool gBoolArray[2] = {true, false}; + +int *gIntPtr = &gIntVar; +double *gDoublePtr = &gDoubleVar; +char *gCharPtr = &gCharVar; +bool *gBoolPtr = &gBoolVar; + +int *gIntPtr2 = (int*)0x8; +double *gDoublePtr2 = (double*)0x5432; +char *gCharPtr2 = (char*)0x4321; +bool *gBoolPtr2 = (bool*)0x12ABCDEF; + +class bar { +public: + int d; +private: + int e[2]; +}; + +class bar2 { +public: + int f; +private: + int g[2]; +}; + +class foo: public bar, bar2 { +public: + int a[2]; + bar b; +private: + int c; +}; + +struct Z { +public: + int x; + int y; +}; +struct childStruct { +public: + Z z; +}; +void locals2() { + // Check that we get the content of local variables with + // the same name as the calling method + int lIntVar = 6789; + double lDoubleArray[2] = {123.456, 6789.6789}; + char lCharVar = 'i'; + char *lCharPtr = &lCharVar; + bool *lBoolPtr2 = (bool*)0xABCDE123; + + return; +} + +void testLocals() { + + int lIntVar = 12345; + double lDoubleVar = 12345.12345; + char lCharVar = 'm'; + bool lBoolVar = false; + + int lIntArray[2] = {6789, 12345}; + double lDoubleArray[2] = {456.789, 12345.12345}; + char lCharArray[2] = {'i', 'm'}; + bool lBoolArray[2] = {true, false}; + + int *lIntPtr = &lIntVar; + double *lDoublePtr = &lDoubleVar; + char *lCharPtr = &lCharVar; + bool *lBoolPtr = &gBoolVar; + + int *lIntPtr2 = (int*)0x1; + double *lDoublePtr2 = (double*)0x2345; + char *lCharPtr2 = (char*)0x1234; + bool *lBoolPtr2 = (bool*)0x123ABCDE; + + locals2(); + + return; +} + +int testChildren() { + foo f; + + f.d = 1; + + return 0; +} + +int testWrite() { + int a[2] = {3, 456}; + + return 0; +} + +int testName1(int newVal) { + int a = newVal; + return a; +} + +int testName2(int newVal) { + int a = newVal; + return a; +} + +int testSameName1(int newVal) { + int a = newVal; + Z z; + z.x = newVal; + return a; +} +int testSameName1(int newVal, int ignore) { + int a = newVal; + Z z; + z.x = newVal; + return a; +} + + +int testSameName() { + testSameName1(1); + testSameName1(2, 0); + testSameName1(3); + + return 0; +} + +int testConcurrent() { + int a[2] = {28, 32}; + return a[0]; +} + +int testSubblock() { + int a = 8; + int b = 1; + if (a) { + int a = 12; + b = a; + } + return b; +} + +int testAddress() { + int a = 8; + int* a_ptr = &a; + + return a; +} + + + +int testUpdateChildren(int val) { + childStruct a; + a.z.x = val + 10; + a.z.y = val + 11; + + a.z.x = val + 20; + a.z.y = val + 21; + + return a.z.x; +} +int testUpdateChildren2(int val) { + childStruct a; + a.z.x = val + 10; + a.z.y = val + 11; + + a.z.x = val + 20; + a.z.y = val + 21; + + return a.z.x; +} + +int testDeleteChildren() { + foo f; + int a[1111]; + + return 1; +} + +int testUpdateGDBBug() { + // GDB 6.7 has a bug which will cause var-update not to show + // the new value of 'a' if we switch the format to binary, + // since binary of 3 is 11 which is the same as the old value + // in natural format + int a = 11; + a = 3; + return 0; +} + +int testUpdateIssue() { + double a = 1.99; + a = 1.22; +} + +int testUpdateIssue2() { + struct { + double d; + } z; + + z.d = 1.0; + z.d = 1.22; +} + +int testConcurrentReadAndUpdateChild() { + struct { + int d; + }z; + + z.d = 1; + z.d = 2; +} + +int testConcurrentUpdateOutOfScopeChildThenParent1() { + struct { + int d; + }z; + + z.d = 1; +} + +int testConcurrentUpdateOutOfScopeChildThenParent2() { + struct { + int d; + }z; + + z.d = 2; +} + +int testConcurrentUpdateOutOfScopeChildThenParent() { + testConcurrentUpdateOutOfScopeChildThenParent1(); + testConcurrentUpdateOutOfScopeChildThenParent2(); +} + +int testUpdateOfPointer() { + struct { + int a; + int* b; + }z; + + int c = 3; + + z.b = &(z.a); + z.a = 1; + + z.b = &c; + z.a = 2; +} + +int testCanWrite() { + int a; + int* b; + struct { + int in; + } c; + int d[2]; + + return 1; +} + +int main() { + printf("Running ExpressionTest App\n"); + + testLocals(); + testChildren(); + testWrite(); + testName1(1); + testName2(2); + testName1(3); + testSameName(); + testConcurrent(); + testSubblock(); + testAddress(); + testUpdateChildren(0); + testUpdateChildren(100); + testUpdateChildren2(200); + testDeleteChildren(); + testUpdateGDBBug(); + testUpdateIssue(); + testUpdateIssue2(); + testConcurrentReadAndUpdateChild(); + testConcurrentUpdateOutOfScopeChildThenParent(); + testUpdateOfPointer(); + testCanWrite(); + + return 0; +} + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/GDBMIGenericTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/GDBMIGenericTestApp.cc new file mode 100644 index 00000000000..e4007b95427 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/GDBMIGenericTestApp.cc @@ -0,0 +1,15 @@ +#include + +int main() { + printf("Running Generic App\n"); + + const char *path = "/tmp/dsftest.txt"; + const char *mode = "a"; + FILE* fd = fopen(path, mode); + fprintf(fd, "Running Generic App\n"); + fclose(fd); + + + return 0; +} + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Makefile b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Makefile new file mode 100644 index 00000000000..777b8b828b3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/Makefile @@ -0,0 +1,14 @@ +src = $(wildcard *.cc *.c) +destDir = ../bin +GCCFLAGS = -g -pthread + +all: + @mkdir -p $(destDir) +# Name the target with an .exe extension so that CVS does not +# include it when making a patch + @for file in $(src) ; \ + do \ + target=`basename $$file .c` ; \ + target=`basename $$target .cc` ; \ + g++ $(GCCFLAGS) $$file -o $(destDir)/$$target.exe ; \ + done diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MemoryTestApp.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MemoryTestApp.cc new file mode 100644 index 00000000000..bef0b930e0e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MemoryTestApp.cc @@ -0,0 +1,38 @@ +//============================================================================ +// Name : MemoryTestApp.cpp +// Author : Francois Chouinard +// Version : 1.0 +// Copyright : Ericsson AB +// Description : Memory test application +//============================================================================ + +#include +using namespace std; + +const int ARRAY_SIZE = 256; + +char charBlock[ARRAY_SIZE]; +int integerBlock[ARRAY_SIZE]; + +void zeroBlocks() +{ + for (int i = 0; i < ARRAY_SIZE; i++) { + charBlock[i] = '\0'; + integerBlock[i] = 0; + } +} + +void setBlocks() +{ + for (int i = 0; i < ARRAY_SIZE; i++) { + charBlock[i] = (char) i; + integerBlock[i] = i; + } +} + +int main() +{ + zeroBlocks(); + setBlocks(); + return 0; +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc new file mode 100644 index 00000000000..80f99fea6aa --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/data/launch/src/MultiThread.cc @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#define NUM_THREADS 5 + +void *PrintHello(void *threadid) +{ + int tid; + tid = (int)threadid; + printf("Hello World! It's me, thread #%d!\n", tid); + pthread_exit(NULL); +} + +int main(int argc, char *argv[]) +{ + pthread_t threads[NUM_THREADS]; + int rc, t; + for(t=0;t + +int main() { + printf("Running SpecialTestApp\n"); + + const char *path = "/tmp/dsftest.txt"; + const char *mode = "a"; + FILE* fd = fopen(path, mode); + fprintf(fd, "Running SpecialTestApp\n"); + fclose(fd); + + + return 0; +} + diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/plugin.xml b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/plugin.xml new file mode 100644 index 00000000000..36f07bfeb38 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/plugin.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ClassAccessor.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ClassAccessor.java new file mode 100644 index 00000000000..e70c2436967 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/ClassAccessor.java @@ -0,0 +1,38 @@ +package org.eclipse.cdt.dsf.mi.service; + +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.mi.service.ExpressionService.MIExpressionDMC; + +public class ClassAccessor { + + public static class MIExpressionDMCAccessor { + private MIExpressionDMC miExprDmc; + + public MIExpressionDMCAccessor(IExpressionDMContext dmc) { + miExprDmc = (MIExpressionDMC) dmc; + } + + @Override + public boolean equals(Object other) { + return miExprDmc.equals(other); + } + + @Override + public int hashCode() { + return miExprDmc.hashCode(); + } + + @Override + public String toString() { + return miExprDmc.toString(); + } + + public String getExpression() { + return miExprDmc.getExpression(); + } + + public String getRelativeExpression() { + return miExprDmc.getRelativeExpression(); + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/AllTests.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/AllTests.java new file mode 100644 index 00000000000..3bd0d7378a9 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/AllTests.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/* + * This class is meant to be empty. It enables us to define + * the annotations which list all the different JUnit class we + * want to run. When creating a new test class, it should be + * added to the list below. + */ + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + TestMIBreakInsertCommand.class, + TestMICommandConstructCommand.class + /* Add your test class here */ + }) + +public class AllTests {} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMIBreakInsertCommand.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMIBreakInsertCommand.java new file mode 100644 index 00000000000..d83d2c8f84b --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMIBreakInsertCommand.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import static org.junit.Assert.assertEquals; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; +import org.junit.Test; + +/** + * Verifies that the break insert MI command have the correct path substitution. + * + * @author qtobsod + * + */ +public class TestMIBreakInsertCommand { + + @Test + public void pathShouldNotContainDoubleBackSlashes() { + MIBreakInsert target = new MIBreakInsert(new TestContext(), false, + false, null, 1, "c:\\test\\this\\path:14", 4); + + assertEquals("Wrong syntax for command", + "-break-insert -i 1 -p 4 c:\\test\\this\\path:14\n", target + .constructCommand()); + } + + @Test + public void pathWithSlashesShouldNotBeSubstituted() { + MIBreakInsert target = new MIBreakInsert(new TestContext(), false, + false, null, 1, "/test/this/path:14", 4); + + assertEquals("Wrong syntax for command", + "-break-insert -i 1 -p 4 /test/this/path:14\n", target + .constructCommand()); + } + + private class TestContext implements IBreakpointsTargetDMContext { + + public IDMContext[] getParents() { + return null; + } + + public String getSessionId() { + return null; + } + + public Object getAdapter(Class adapter) { + return null; + } + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMICommandConstructCommand.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMICommandConstructCommand.java new file mode 100644 index 00000000000..bed9559b320 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/dsf/mi/service/command/commands/TestMICommandConstructCommand.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.mi.service.command.commands; + +import static org.junit.Assert.assertEquals; + +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MICommand; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.junit.Test; + +/** + * Test verifying that the construct command method handles separators and + * escaping correctly + * + * @author qtobsod + * + */ +public class TestMICommandConstructCommand { + + @Test + public void multipleParametersShouldHaveCorrectSeparators() { + // Setup + MICommand target = new MICommand(new TestContext(), + "-test-operation"); + target.setOptions(new String[] { "-a a_test\\with slashes", + "-b \"hello\"", "-c c_test" }); + target.setParameters(new String[] { "-param1 param", "param2", + "-param3" }); + + // Act + String result = target.constructCommand(); + + // Assert + assertEquals( + "Wrong syntax for command", + "-test-operation \"-a a_test\\\\with slashes\" \"-b \\\"hello\\\"\" \"-c c_test\" -- \"-param1 param\" param2 -param3\n", + result); + } + + private class TestContext implements IDMContext { + + public IDMContext[] getParents() { + return null; + } + + public String getSessionId() { + return null; + } + + public Object getAdapter(Class adapter) { + return null; + } + + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/AllTests.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/AllTests.java new file mode 100644 index 00000000000..de9d228168e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/AllTests.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/* + * This class is meant to be empty. It enables us to define + * the annotations which list all the different JUnit class we + * want to run. When creating a new test class, it should be + * added to the list below. + */ + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + MIRegistersTest.class, + MIRunControlTest.class, + ExpressionServiceTest.class, + MIMemoryTest.class, + MIBreakpointsTest.class, + MIDisassemblyTest.class, + GDBProcessesTest.class + /* Add your test class here */ + }) + +public class AllTests {} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExampleTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExampleTest.java new file mode 100644 index 00000000000..6b3c4875a78 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExampleTest.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +/* + * This is an example of how to write new JUnit test cases + * for services of DSF. + * + * Each test class should extend BaseTestCase + * so as to automatically launch the application before + * each testcase and tear it down after. + * + * Also, each new test class must be added to the list within AllTest. + * + * Finally, each testcase should be @RunWith(BackgroundRunner.class) + * so as to release the UI thread and allow things such as + * timeouts to work in JUnit + */ + +// Each test must run with the BackgroundRunner so as +// to release the UI thread +@RunWith(BackgroundRunner.class) + +public class ExampleTest extends BaseTestCase { + + @BeforeClass + public static void beforeClassMethod() { + // Things to run once specifically for this class, + // before starting this set of tests. + // Any method name can be used + + // To choose your own test application, use the following form + // You must make sure the compiled binary is available in the + // specified location. + // If this method call is not made, the default GDBMIGenericTestApp + // will be used + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + "data/launch/bin/SpecialTestApp.exe"); + + // Other attributes can be changed here + } + + @AfterClass + public static void afterClassMethod() { + // Things to run once specifically for this class, + // after the launch has been performed + // Any method name can be used + } + + @Before + public void beforeMethod() { + // Things to run specifically for this class, + // before each test but after the launch has been performed + // The Launched used is for the default test application + // Any method name can be used + } + + @After + public void afterMethod() { + // Things to run specifically for this class + // after each test but before the launch has been torn down + // Any method name can be used + } + +// @Override +// public void baseBeforeMethod() { +// // Can be used to override and prevent the baseSetup from being run +// // The name baseBeforeMethod must be used +// } + +// @Override +// public void baseAfterMethod() { +// // Can be used to override and prevent the baseTeardown from being run +// // The name baseAfterMethod must be used +// } + + @Test + public void basicTest() { + // First test to run + assertTrue("", true); + } + + @Test(timeout=5000) + public void timeoutTest() { + // Second test to run, which will timeout if not finished on time + assertTrue("", true); + } + + @Test(expected=FileNotFoundException.class) + public void exceptionTest() throws FileNotFoundException { + // Third test to run which expects an exception + throw new FileNotFoundException("Just testing"); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExpressionServiceTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExpressionServiceTest.java new file mode 100644 index 00000000000..471c82a261c --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/ExpressionServiceTest.java @@ -0,0 +1,2987 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMData; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.mi.service.ClassAccessor.MIExpressionDMCAccessor; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.cdt.utils.Addr32; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) +public class ExpressionServiceTest extends BaseTestCase { + + private DsfSession fSession; + + private DsfServicesTracker fServicesTracker; + + private IExpressions fExpService; + + private int fExprChangedEventCount = 0; + + private IExpressionDMContext fExprChangedCtx = null; + + private IExpressionDMContext globalExpressionCtx1 = null; + private IExpressionDMContext globalExpressionCtx2 = null; + + + @BeforeClass + public static void beforeClassMethod() { + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/ExpressionTestApp.exe"); + } + + @Before + public void init() { + fSession = getGDBLaunch().getSession(); + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + + fExpService = fServicesTracker.getService(IExpressions.class); + fSession.addServiceEventListener(this, null); + clearExprChangedData(); + } + + @After + public void shutdown() { + fSession.removeServiceEventListener(this); + fExpService = null; + fServicesTracker.dispose(); + } + + // Handles ExpressionChangedEvent + @DsfServiceEventHandler + public void eventDispatched(IExpressionChangedDMEvent e) { + fExprChangedEventCount++; + fExprChangedCtx = e.getDMContext(); + } + + // Clears the counters + private void clearExprChangedData() { + fExprChangedEventCount = 0; + fExprChangedCtx = null; + } + + // Returns the total number of events received + private int getExprChangedCount() { + return fExprChangedEventCount; + } + + private IExpressionDMContext getExprChangedContext() { + return fExprChangedCtx; + } + + // ********************************************************************* + // Below are the tests for the expression service. + // ********************************************************************* + + /** + * Test that we can correctly evaluate integer expressions. + */ + @Test + public void testLiteralIntegerExpressions() throws Throwable { + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("testLocals"); + + // Create a map of expressions and their expected values. + Map tests = new HashMap(); + + tests.put("0 + 0 - 0", new String[] { "0x0", "0", "0", "0", "0" }); + tests.put("3 + 4", new String[] { "0x7", "07", "111", "7", "7" }); + tests.put("3 + 4 * 5", new String[] { "0x17", "027", "10111", "23", "23" }); + tests.put("5 * 3 + 4", new String[] { "0x13", "023", "10011", "19", "19" }); + tests.put("5 * (3 + 4)", new String[] { "0x23", "043", "100011", "35", "35" }); + tests.put("10 - 15", new String[] { "0xFFFFFFFB", "037777777773", "11111111111111111111111111111011", "-5", + "-5" }); + tests.put("10 + -15", new String[] { "0xFFFFFFFB", "037777777773", "11111111111111111111111111111011", "-5", + "-5" }); + + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + } + + /** + * Test that we can correctly evaluate floating-point expressions. + */ + @Test + public void testLiteralFloatingPointExpressions() throws Throwable { + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("testLocals"); + + // Create a map of expressions and their expected values. + Map tests = new HashMap(); + + tests.put("3.1415 + 1.1111", new String[] { "0x4", "04", "100", "4", "4.2526000000000002" }); + tests.put("100.0 / 3.0", new String[] { "0x21", "041", "100001", "33", "33.333333333333336" }); + tests.put("-100.0 / 3.0", new String[] { "0xffffffffffffffdf", "01777777777777777777737", + "1111111111111111111111111111111111111111111111111111111111011111", "-33", "-33.333333333333336" }); + tests.put("-100.0 / -3.0", new String[] { "0x21", "041", "100001", "33", "33.333333333333336" }); + tests.put("100.0 / 0.5", new String[] { "0xc8", "0310", "11001000", "200", "200" }); + + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + } + + /** + * Test that we can correctly evaluate C expressions involving local + * variables. + */ + @Test + public void testLocalVariables() throws Throwable { + // Run to the point where all local variables are initialized + SyncUtil.SyncRunToLocation("testLocals"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 16); + + // Create a map of expressions to expected values. + Map tests1 = new HashMap(); + + tests1.put("lIntVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345" }); + tests1.put("lDoubleVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999" }); + tests1.put("lCharVar", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'" }); + tests1.put("lBoolVar", new String[] { "0x0", "0", "0", "0", "false" }); + + tests1.put("lIntArray[1]", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345" }); + tests1.put("lDoubleArray[1]", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999" }); + tests1.put("lCharArray[1]", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'" }); + tests1.put("lBoolArray[1]", new String[] { "0x0", "0", "0", "0", "false" }); + + tests1.put("*lIntPtr", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345" }); + tests1.put("*lDoublePtr", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345.123449999999" }); + tests1.put("*lCharPtr", new String[] { "0x6d", "0155", "1101101", "109", "109 'm'" }); + tests1.put("*lBoolPtr", new String[] { "0x0", "0", "0", "0", "false" }); + + tests1.put("lIntPtr2", new String[] { "0x1", "01", "1", "1", "0x1" }); + tests1.put("lDoublePtr2", new String[] { "0x2345", "021505", "10001101000101", "9029", "0x2345" }); + // GDB says a char* is out of bounds, but not the other pointers??? + // tests1.put("CharPtr2", new String[] { "0x1234", "011064", + // "1001000110100", "4660", "0x1234" }); + tests1.put("lBoolPtr2", new String[] { "0x123ABCDE", "02216536336", "10010001110101011110011011110", "305839326", "0x123ABCDE" }); + + executeExpressionSubTests(tests1, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + + // Step into the method and stop until all new local variables are + // initialized + SyncUtil.SyncStep(StepType.STEP_INTO); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 5); + + // Create a map of expressions to expected values. + Map tests2 = new HashMap(); + + tests2.put("lIntVar", new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789" }); + tests2.put("lDoubleArray[1]", + new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789.6788999999999" }); + tests2.put("lCharVar", new String[] { "0x69", "0151", "1101001", "105", "105 'i'" }); + tests2.put("*lCharPtr", new String[] { "0x69", "0151", "1101001", "105", "105 'i'" }); + tests2.put("lBoolPtr2", new String[] { "0xABCDE123", "025363360443", "10101011110011011110000100100011", + "2882396451", "0xABCDE123" }); + + // check variables at current stack frame + executeExpressionSubTests(tests2, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + // check previous stack frame + executeExpressionSubTests(tests1, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 1)); + + // Now return from the method and check that we see the + // original variables + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + + executeExpressionSubTests(tests1, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + } + + /** + * This tests verifies that we can deal with variables in a subblock hiding + * variables with the same name in the outer block. + */ + @Ignore("Sublocks do not work with GDB") + @Test + public void testSubBlock() throws Throwable { + SyncUtil.SyncRunToLocation("testSubblock"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 2); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + Map tests = new HashMap(); + + tests.put("a", new String[] { "0x8", "010", "1000", "8", "8" }); + tests.put("b", new String[] { "0x1", "01", "1", "1", "1" }); + + executeExpressionSubTests(tests, frameDmc); + + // Now enter a subblock with the same variable names + SyncUtil.SyncStep(StepType.STEP_OVER, 2); + + tests = new HashMap(); + + tests.put("a", new String[] { "0xc", "014", "1100", "12", "12" }); + tests.put("b", new String[] { "0x1", "01", "1", "1", "1" }); + + executeExpressionSubTests(tests, frameDmc); + + // Now step to change the b variable + SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + tests = new HashMap(); + + tests.put("a", new String[] { "0xc", "014", "1100", "12", "12" }); + tests.put("b", new String[] { "0xc", "014", "1100", "12", "12" }); + + executeExpressionSubTests(tests, frameDmc); + + // Now exit the sub-block and check that we see the original a but the + // same b + SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + tests = new HashMap(); + + tests.put("a", new String[] { "0x8", "010", "1000", "8", "8" }); + tests.put("b", new String[] { "0xc", "014", "1100", "12", "12" }); + + executeExpressionSubTests(tests, frameDmc); + } + + /** + * This tests verifies that we can obtain children properly. + */ + @Test + public void testChildren() throws Throwable { + + // Get the children of some variables + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("testChildren"); + doTestChildren(stoppedEvent); + + // Now do a step and get the children again, to test the internal cache + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + doTestChildren(stoppedEvent); + } + + /** + * This test verifies that the ExpressionService can write to a variable. + */ + @Test + public void testWriteVariable() throws Throwable { + SyncUtil.SyncRunToLocation("testWrite"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + final IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a[1]"); + + writeAndCheck(exprDmc, "987", IFormattedValues.DECIMAL_FORMAT, "987"); + writeAndCheck(exprDmc, "16", IFormattedValues.HEX_FORMAT, "22"); + writeAndCheck(exprDmc, "0x2e", IFormattedValues.HEX_FORMAT, "46"); + writeAndCheck(exprDmc, "16", IFormattedValues.OCTAL_FORMAT, "14"); + writeAndCheck(exprDmc, "022", IFormattedValues.OCTAL_FORMAT, "18"); + writeAndCheck(exprDmc, "1011", IFormattedValues.BINARY_FORMAT, "11"); + writeAndCheck(exprDmc, "0b1001", IFormattedValues.BINARY_FORMAT, "9"); + writeAndCheck(exprDmc, "456", IFormattedValues.NATURAL_FORMAT, "456"); + + } + + /* + * This method does a write and then a read to make sure the new value was + * properly written. + */ + private void writeAndCheck(final IExpressionDMContext exprDmc, final String newValueFormatted, final String format, + final String newValueInDecimal) throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // Write the new value using its formatted value + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.writeExpression( + exprDmc, + newValueFormatted, + format, + new RequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), + getExprChangedCount() == 1); + + clearExprChangedData(); + + wait.waitReset(); + + // Read the new value in decimal and check that it is what we expected + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.DECIMAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + String actualDecimalValue = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); + + assertTrue("Failed to correctly evaluate '" + exprDmc.getExpression() + "': expected '" + newValueInDecimal + + "', got '" + actualDecimalValue + "'", actualDecimalValue.equalsIgnoreCase(newValueInDecimal)); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + /** + * This tests verifies that we handle invalid formats properly for a write. + */ + @Test + public void testWriteErrorFormat() throws Throwable { + SyncUtil.SyncRunToLocation("testWrite"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a[1]"); + + writeAndCheckError(exprDmc, "goodbye", IFormattedValues.DECIMAL_FORMAT); + writeAndCheckError(exprDmc, "abggg", IFormattedValues.HEX_FORMAT); + writeAndCheckError(exprDmc, "99", IFormattedValues.OCTAL_FORMAT); + writeAndCheckError(exprDmc, "234", IFormattedValues.BINARY_FORMAT); + writeAndCheckError(exprDmc, "hello", IFormattedValues.NATURAL_FORMAT); + writeAndCheckError(exprDmc, "1", "ThisFormatDoesNotExist"); + + IExpressionDMContext notWritableExprDmc = SyncUtil.SyncCreateExpression(frameDmc, "10+5"); + writeAndCheckError(notWritableExprDmc, "1", IFormattedValues.NATURAL_FORMAT); + } + + /* + * This method does a write that should use an invalid value or format, and + * verifies that the operation fails + */ + private void writeAndCheckError(final IExpressionDMContext exprDmc, final String invalidValueFormatted, + final String format) throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // Write the new value using its formatted value + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.writeExpression(exprDmc, invalidValueFormatted, format, new RequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue("Got an OK status for an error test case. Should not be able to write value " + + invalidValueFormatted + " in " + format, !wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + /** + * This test tries multiple format reads during the same executor cycle, to + * make sure the internal MI commands are sequenced properly. + */ + @Test + public void testConcurrentReads() throws Throwable { + // Next we test that we can read the value more than once + // of the same variable object at the exact same time + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a[0]"); + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("28")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format", null)); + } + } + } + }); + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.HEX_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equalsIgnoreCase("0x1c")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + + } + + /** + * This test tries reads and listChildren during the same executor cycle, to + * make sure the internal MI commands are sequenced properly. + */ + @Test + public void testConcurrentReadChildren() throws Throwable { + // Next we test that we can retrieve children while reading the value + // and vice-versa + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // First we get the expected value of the array pointer. + final IExpressionDMContext addrDmc = SyncUtil.SyncCreateExpression(frameDmc, "&a"); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(addrDmc, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + final String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); + + wait.waitReset(); + + // Now perform the test + fExpService.getExecutor().submit(new Runnable() { + public void run() { + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals(actualAddrStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format", null)); + } + } + } + }); + + wait.increment(); + fExpService.getSubExpressions(exprDmc, new DataRequestMonitor( + fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + IExpressionDMContext[] children = getData(); + int failedIndex = -1; + for (int i = 0; i < 2; i++) { + if (!children[i].getExpression().equals("a[" + i + "]")) { + failedIndex = i; + } + } + + if (failedIndex != -1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting child number: " + failedIndex, null)); + } else { + wait.waitFinished(); + } + } + } + }); + + // Use different format to avoid triggering the cache + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.HEX_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals(actualAddrStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + /** + * This test tries reads and getChildrenCount during the same executor + * cycle, to make sure the internal MI commands are sequenced properly. + */ + @Test + public void testConcurrentReadChildrenCount() throws Throwable { + // Next we test that we can retrieve children count while reading the + // value and vice-versa + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // First we get the expected value of the array pointer. + final IExpressionDMContext addrDmc = SyncUtil.SyncCreateExpression(frameDmc, "&a"); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(addrDmc, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + final String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); + + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + wait.increment(); + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); + + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals(actualAddrStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format", null)); + } + } + } + }); + + wait.increment(); + fExpService.getSubExpressionCount(exprDmc, new DataRequestMonitor(fExpService.getExecutor(), + null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + int count = getData(); + if (count != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting count for children. Got" + count + "instead of 2", null)); + } else { + wait.waitFinished(); + } + } + } + }); + + // Use different format to avoid triggering the cache + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.HEX_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals(actualAddrStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + /** + * This test tries reads and writes during the same executor cycle, to make + * sure the internal MI commands are sequenced properly. + */ + @Test + public void testConcurrentReadWrite() throws Throwable { + // Next we test that we can deal with a write request and read request + // at + // the same time and vice-versa + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a[1]"); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("32")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, got " + getData().getFormattedValue() + + " instead of 32", null)); + } + } + } + }); + + wait.increment(); + fExpService.writeExpression(exprDmc, "56", IFormattedValues.NATURAL_FORMAT, new RequestMonitor( + fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + wait.waitFinished(); + } + } + }); + + // Use different format to avoid triggering the cache + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.HEX_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("0x38")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format, got " + getData().getFormattedValue() + + " instead of 0x38", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), + getExprChangedCount() == 1); + exprDmc.equals(getExprChangedContext()); + clearExprChangedData(); + } + + /** + * This test tries many different operations during the same executor cycle, + * to make sure the internal MI commands are sequenced properly. + */ + @Test + public void testConcurrentReadWriteChildren() throws Throwable { + // Finally, we go nuts and request two reads, while requesting + // a get children and get children count. + // Note that we don't request a write, because a write is allowed to + // go through at any time and we don't exactly know when it will + // change the value we are reading. + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a[1]"); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("32")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, got " + getData().getFormattedValue() + + " instead of 32", null)); + } + } + } + }); + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.HEX_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("0x20")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format, got " + getData().getFormattedValue() + + " instead of 0x20", null)); + } + } + } + }); + + wait.increment(); + fExpService.getSubExpressionCount(exprDmc, new DataRequestMonitor(fExpService.getExecutor(), + null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData() != 0) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting child count; expecting 0 got " + getData(), null)); + } else { + wait.waitFinished(); + } + } + } + }); + + wait.increment(); + fExpService.getSubExpressions(exprDmc, new DataRequestMonitor( + fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().length != 0) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 0 got " + getData().length, null)); + } else { + wait.waitFinished(); + } + } + } + }); + + // Must use a different format or else the cache will be triggered + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.OCTAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("040")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format, got " + getData().getFormattedValue() + + " instead of 040", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + exprDmc.equals(getExprChangedContext()); + clearExprChangedData(); + } + + /** + * This test verifies that the ExpressionService caches the evaluation of an + * expression in a specific format. It verifies this by: 1- reading a + * variable 2- writing to that variable 3- reading the variable in a new + * format and seeing the new value 4- reading the variable in the same + * format as step 1 and seeing the old value cached Note that all above + * steps must be done within the same Runnable submitted to the executor. + * This allows the cache to be triggered before it is invalidated by a write + * command, since the write command will need an new executor cycle to send + * an MI command to the back-end + */ + @Test + public void testWriteCache() throws Throwable { + // Test the cache by changing a value but triggering a read before the + // write clears the cache + + SyncUtil.SyncRunToLocation("testConcurrent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a[1]"); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("32")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, got " + getData().getFormattedValue() + + " instead of 32", null)); + } + } + } + }); + + wait.increment(); + fExpService.writeExpression(exprDmc, "56", IFormattedValues.NATURAL_FORMAT, new RequestMonitor( + fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + wait.waitFinished(); + } + } + }); + + // Must use a different format or else the cache will be + // triggered + // This will prove that the write has changed the backend + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.OCTAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("070")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating hex format, got " + getData().getFormattedValue() + + " instead of 070", null)); + } + } + } + }); + + // Test that the cache is triggered, giving us the old value + // This happens because we are calling this operation on the + // same executor run call. + // NOTE that this is not a problem, because the writeExpression + // will eventually + // reset the cache (we'll test this below). + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("32")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, got " + getData().getFormattedValue() + + " instead of 32", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + // Now that we know the writeExpressions completed and the cache was + // reset, do a similar + // request as above to see that the cache has indeed been reset + wait.waitReset(); + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + wait.increment(); + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("56")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, got " + getData().getFormattedValue() + + " instead of 56", null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + assertTrue("ExprChangedEvent problem: expected 1, received " + getExprChangedCount(), + getExprChangedCount() == 1); + exprDmc.equals(getExprChangedContext()); + clearExprChangedData(); + } + + /** + * Test that we can correctly retrieve the address and type size of an + * expression + */ + @Test + public void testExprAddress() throws Throwable { + + SyncUtil.SyncRunToLocation("testAddress"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 2); + + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDmc = SyncUtil.SyncCreateExpression(frameDmc, "a"); + + final IExpressionDMContext exprDmc2 = SyncUtil.SyncCreateExpression(frameDmc, "a_ptr"); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // First get the address of 'a' through 'a_ptr' + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getFormattedExpressionValue(fExpService.getFormattedValueContext(exprDmc2, + IFormattedValues.NATURAL_FORMAT), new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + String actualAddrStr = ((FormattedValueDMData) wait.getReturnInfo()).getFormattedValue(); + wait.waitReset(); + + // Now check the address through our getAddressData + checkAddressData(exprDmc, actualAddrStr, 4); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + + /** + * Test that we can correctly evaluate C expressions involving global + * variables. + * + * @return void + */ + @Test + public void testGlobalVariables() throws Throwable { + + // Step to a stack level of 2 to be able to test differen stack frames + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("locals2"); + + // Create a map of expressions to expected values. + Map tests = new HashMap(); + + // Global variables + tests.put("gIntVar", new String[] { "0x21F", "01037", "1000011111", "543", "543" }); + tests.put("gDoubleVar", new String[] { "0x21F", "01037", "1000011111", "543", "543.54300000000001" }); + tests.put("gCharVar", new String[] { "0x67", "0147", "1100111", "103", "103 'g'" }); + tests.put("gBoolVar", new String[] { "0x0", "0", "0", "0", "false" }); + + tests.put("gIntArray[1]", new String[] { "0x28E", "01216", "1010001110", "654", "654" }); + tests.put("gDoubleArray[1]", new String[] { "0x28E", "01216", "1010001110", "654", "654.32100000000003" }); + tests.put("gCharArray[1]", new String[] { "0x64", "0144", "1100100", "100", "100 'd'" }); + tests.put("gBoolArray[1]", new String[] { "0x0", "0", "0", "0", "false" }); + + tests.put("*gIntPtr", new String[] { "0x21F", "01037", "1000011111", "543", "543" }); + tests.put("*gDoublePtr", new String[] { "0x21F", "01037", "1000011111", "543", "543.54300000000001" }); + tests.put("*gCharPtr", new String[] { "0x67", "0147", "1100111", "103", "103 'g'" }); + tests.put("*gBoolPtr", new String[] { "0x0", "0", "0", "0", "false" }); + + tests.put("gIntPtr2", new String[] { "0x8", "010", "1000", "8", "0x8" }); + tests.put("gDoublePtr2", new String[] { "0x5432", "052062", "101010000110010", "21554", "0x5432" }); + // GDB says a char* is out of bounds, but not the other pointers??? + // tests.put("gCharPtr2", new String[] { "0x4321", "041441", + // "100001100100001", "17185", "0x4321" }); + tests.put("gBoolPtr2", new String[] { "0x12ABCDEF", "02252746757", "10010101010111100110111101111", + "313249263", "0x12ABCDEF" }); + + // Try different stack frames + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0)); + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 1)); + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 2)); + } + + /** + * This test verifies that the ExpressionService can handle having a + * variable with the same name in two different methods but at the same + * stack depth. + */ + @Test + public void testNamingSameDepth() throws Throwable { + SyncUtil.SyncRunToLocation("testName1"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + Map tests = new HashMap(); + tests.put("a", new String[] { "0x1", "01", "1", "1", "1" }); + executeExpressionSubTests(tests, frameDmc); + + SyncUtil.SyncRunToLocation("testName2"); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 1); + frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + tests = new HashMap(); + tests.put("a", new String[] { "0x2", "02", "10", "2", "2" }); + executeExpressionSubTests(tests, frameDmc); + + SyncUtil.SyncRunToLocation("testName1"); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 1); + frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + tests = new HashMap(); + tests.put("a", new String[] { "0x3", "03", "11", "3", "3" }); + executeExpressionSubTests(tests, frameDmc); + } + + /** + * This test verifies that the ExpressionService can handle having a + * variable with the same name in two methods that also have the same name + */ + @Test + public void testNamingSameMethod() throws Throwable { + SyncUtil.SyncRunToLocation("testSameName"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 2); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + Map tests = new HashMap(); + tests.put("a", new String[] { "0x1", "01", "1", "1", "1" }); + executeExpressionSubTests(tests, frameDmc); + + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 2); + frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + tests = new HashMap(); + tests.put("a", new String[] { "0x2", "02", "10", "2", "2" }); + executeExpressionSubTests(tests, frameDmc); + + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 2); + frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + tests = new HashMap(); + tests.put("a", new String[] { "0x3", "03", "11", "3", "3" }); + executeExpressionSubTests(tests, frameDmc); + } + + /** + * This test makes sure that if a request for expression values are made with + * a thread selected, the top-most stack frame is used for evaluation + */ + @Test + public void testThreadContext() throws Throwable { + + // Step to a stack level of 2 to be able to test differen stack frames + SyncUtil.SyncRunToLocation("locals2"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER); + + // Create a map of expressions to expected values. + Map tests = new HashMap(); + + // First make sure we have a different value on the other stack frame and that we select + // a frame that is not the top frame + tests.put("lIntVar", new String[] { "0x3039", "030071", "11000000111001", "12345", "12345" }); + executeExpressionSubTests(tests, SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 1)); + + // Now check that we get the same values as the top stack when selecting the thread only + tests = new HashMap(); + tests.put("lIntVar", new String[] { "0x1a85", "015205", "1101010000101", "6789", "6789" }); + executeExpressionSubTests(tests, stoppedEvent.getDMContext()); + } + + /** + * This test verifies that the ExpressionService can handle having a + * child variable with the same name in two methods that also have the same name + */ + @Test + public void testChildNamingSameMethod() throws Throwable { + SyncUtil.SyncRunToLocation("testSameName"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 4); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + // now get the value of the child + final String valueStr = "1"; + final IExpressionDMContext child = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + + + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 4); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + // now get the value of the child + final String valueStr = "2"; + final IExpressionDMContext child = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + + + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 4); + final IFrameDMContext frameDmc3 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc3, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + // now get the value of the child + final String valueStr = "3"; + final IExpressionDMContext child = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(child, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + child.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + + + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + } + + /** + * This test verifies that the ExpressionService properly updates + * children variables, when we do not update the parent explicitly + */ + @Test + public void testUpdatingChildren() throws Throwable { + SyncUtil.SyncRunToLocation("testUpdateChildren"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 2); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + doUpdateTest(frameDmc, 0); + + // Re-run the test to test out-of-scope update again + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 3); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + doUpdateTest(frameDmc2, 100); + + // Re-run the test within a different method test out-of-scope updates + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 3); + final IFrameDMContext frameDmc3 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + doUpdateTest(frameDmc3, 200); + + } + + + public void doUpdateTest(final IFrameDMContext frameDmc, final int baseValue) throws Throwable { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "a"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // Now list the children of this child + fExpService.getSubExpressions( + getData()[0], + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final IExpressionDMContext[] childDmcs = getData(); + + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (childDmcs.length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + childDmcs.length, null)); + } else { + // now get the value of the two children + for (int i =0; i<2; i++) { + final String valueStr = Integer.toString(baseValue + i + 10); + final int finali = i; + + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(childDmcs[i], IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + childDmcs[finali].getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + } + } + }); + } + + + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now step to change the value of a.z.x and a.z.y and verify the changed values. + // This will confirm that the parent "a" will have been properly updated + // It is a better test to do it for two children because it tests concurrent update requests + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 2); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "a"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // Now list the children of this child + fExpService.getSubExpressions( + getData()[0], + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final IExpressionDMContext[] childDmcs = getData(); + + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (childDmcs.length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + childDmcs.length, null)); + } else { + // now get the value of the two children + for (int i =0; i<2; i++) { + final String valueStr = Integer.toString(baseValue + i + 20); + final int finali = i; + + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(childDmcs[i], IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + childDmcs[finali].getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + } + } + }); + } + + + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * This test creates a variable object with children (not an array) and then gets these children + * to be deleted because of a large number of other variable objects being created. + * We then check that the expression service can handle a request for one of those deleted children, + * which has a complex path. + */ + @Test + public void testDeleteChildren() throws Throwable { + SyncUtil.SyncRunToLocation("testDeleteChildren"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "f"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().length != 5) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 5 got " + getData().length, null)); + } else { + String childStr = "((bar) f)"; + if (!getData()[0].getExpression().equals(childStr)) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Got child " + getData()[0].getExpression() + " instead of " + childStr, null)); + } else { + // Now list the children of the first element + fExpService.getSubExpressions( + getData()[0], + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + String childStr = "((((bar) f)).d)"; + if (!getData()[0].getExpression().equals(childStr)) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Got child " + getData()[0].getExpression() + " instead of " + childStr, null)); + } else { + wait.setReturnInfo(getData()[0]); + wait.waitFinished(); + } + } + } + } + }); + } + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + final IExpressionDMContext deletedChildDmc = (IExpressionDMContext)wait.getReturnInfo(); + + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // Now create more than 1000 expressions to trigger the deletion of the children + // that were created above + for (int i=0; i<1100; i++) { + IExpressionDMContext dmc = fExpService.createExpression(frameDmc, "a[" + i + "]"); + + wait.increment(); + fExpService.getExpressionData( + dmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + wait.waitFinished(); + } + } + }); + } + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // Evaluate the expression of a child that we know is deleted to make sure + // the expression service can handle that + fExpService.getExpressionData( + deletedChildDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + wait.waitFinished(); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + } + + /** + * GDB 6.7 has a bug which will cause var-update not to show + * the new value of 'a' if we switch the format to binary, + * since binary of 3 is 11 which is the same as the old value + * in natural format. Our expression service should work around this. + * + * int main() { + * int a = 11; + * a = 3; + * return 0; + * } + */ + @Test + public void testUpdateGDBBug() throws Throwable { + SyncUtil.SyncRunToLocation("testUpdateGDBBug"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and all its children + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); + + // This call will create the variable object in natural format and then change + // it to binary to fetch the value + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(exprDmc, IFormattedValues.BINARY_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("1011")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating binary format, expected 1011 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now step to change the value of "a" and ask for it again + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and all its children + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc2, "a"); + + // This call will create the variable object in natural format and then change + // it to binary to fetch the value + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(exprDmc, IFormattedValues.BINARY_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("11")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating binary format, expected 11 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * var-update will not show a change if eval-expression is the same + * in the current format. This is a problem for us because we don't + * know if another format changed: + * + * int main() { + * double a = 1.99; + * a = 1.11; + * } + * + * If a is displayed in anything but natural, both values of a are the same + * and we won't know it changed in the natural format. + * + * The test below is in case GDB fixes var-update to keep track of the last + * printed value through eval-expression. Until they do that, we do not have + * a problem because of our caching: where, if we change formats since the last + * var-update, it is impossible for us to set the format back + * to the one of the last -var-update, since we already have that value in our cache. + * So, the -var-update will show a change because of the new current format. + * But if GDB has eval-expression reset their stored printed_value, this test + * will fail and we'll know we have to fix something. + */ + @Test + public void testUpdateIssue() throws Throwable { + SyncUtil.SyncRunToLocation("testUpdateIssue"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and all its children + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc, "a"); + + // check that we have the proper value + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("1.99")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating a, expected 1.99 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + + // ask for hex to set the format to hex + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("0x1")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating a, expected 0x1 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now step to change the value of "a" and ask for it again but in the natural format + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and all its children + IExpressionDMContext exprDmc = fExpService.createExpression(frameDmc2, "a"); + + // trigger the var-update in the last format (hex) + // then request the actual value in natural which should not be taken from the cache + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("1.22")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, expected 1.22 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * var-update will not show a change if eval-expression is the same + * in the current format. This is a problem for us because we don't + * know if another format changed: + * + * int main() { + * struct { + * double d; + * } z; + * + * z.d = 1.0; + * z.d = 1.22; + * } + * + * If a is displayed in anything but natural, both values of a are the same + * and we won't know it changed in the natural format. + * This test uses a child to increase the value of the test. + * Also, it avoids the cache saving us since we start with the 1.0 value + * which is the same in natural and decimal + */ + @Test + public void testUpdateIssue2() throws Throwable { + SyncUtil.SyncRunToLocation("testUpdateIssue2"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // check that we have the proper value + // This will cache the value 1 in the natural format cache + final String valueStr = "1"; + globalExpressionCtx1 = getData()[0]; + + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + + // ask for decimal to set the format to decimal + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.DECIMAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + } + }); + + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now step to change the value of "a" in natural but it remains the same in decimal + SyncUtil.SyncStep(StepType.STEP_OVER, 1); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // trigger the var-update in the last format (decimal) + // then request the actual value in natural which should not be taken from the cache + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + if (getData().getFormattedValue().equals("1.22")) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating natural format, expected 1.22 but got " + + getData().getFormattedValue(), null)); + } + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * This test verifies the state handling of a child variable object + * to make sure that our locking scheme works even though we must deal + * with an update call, internally + */ + @Test + public void testConcurrentReadAndUpdateChild() throws Throwable { + SyncUtil.SyncRunToLocation("testConcurrentReadAndUpdateChild"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 1); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // Ask for one value to create the var object + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and all its children + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); + + wait.increment(); + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // now get the value of the child + final String valueStr = "01"; + globalExpressionCtx1 = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.OCTAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now do two reads in two different formats + // We need to make sure that the locking properly works although we are calling + // the internal update method, which does affect the state of the object + fExpService.getExecutor().submit(new Runnable() { + public void run() { + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.BINARY_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final String valueStr = "1"; + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.HEX_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final String valueStr = "0x1"; + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * This test verifies some of the logic of dealing with out-of-scope variables. + * This particular scenario is that we create a parent with a child and then + * have them go out of scope. Then we request the child which will update the parent + * and mark it as out-of-scope and recreate the child. The parent is not re-created. + * We then ask twice for the parent which is already known to be out-of-scope and we need + * to make sure that the parent is re-created once and only once. + * We had a bug where we would enter an infinite loop in this case. + */ + @Test(timeout=5000) + public void testConcurrentUpdateOutOfScopeChildThenParent() throws Throwable { + SyncUtil.SyncRunToLocation("testConcurrentUpdateOutOfScopeChildThenParent"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 2); + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + // First create the var object and its child + globalExpressionCtx1 = fExpService.createExpression(frameDmc, "z"); + + wait.increment(); + fExpService.getSubExpressions( + globalExpressionCtx1, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // now get the value of the child + final String valueStr = "1"; + globalExpressionCtx2 = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + SyncUtil.SyncStep(StepType.STEP_RETURN); + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_INTO, 2); + + // Now step to another method to make the previous variable objects out-of-scope + // then first request the child and then the parent. We want to test this order + fExpService.getExecutor().submit(new Runnable() { + public void run() { + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final String valueStr = "2"; + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final String valueStr = "{...}"; + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + + // Ask a second time but in a different format, to avoid the cache + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.DECIMAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + final String valueStr = "{...}"; + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(valueStr)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + valueStr, null)); + } + } + }); + + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + //TODO although this test passes, the variable z is created twice, without being + // deleted in GDB. We should fix this + } + + /** + * This test verifies that we properly update a pointer and its child since they can both + * change and be reported by var-update + */ + @Test + public void testUpdateOfPointer() throws Throwable { + SyncUtil.SyncRunToLocation("testUpdateOfPointer"); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 3); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final String firstValue = "1"; + final String secondValue = "2"; + final String thirdValue = "3"; + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + // check that we have the proper value for both children + globalExpressionCtx1 = getData()[0]; + globalExpressionCtx2 = getData()[1]; + + // Get the value of the first child + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(firstValue)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + firstValue, null)); + } + } + }); + + // Get the value of the second child + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + wait.setReturnInfo(getData().getFormattedValue()); + wait.waitFinished(); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + final String pointerValue = (String)wait.getReturnInfo(); + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // also get the child of the pointer + fExpService.getSubExpressions( + globalExpressionCtx2, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // Get the value of the child of the pointer + globalExpressionCtx2 = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(firstValue)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + firstValue, null)); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + // Now step to change the values of all the children + stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER, 2); + final IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + IExpressionDMContext parentDmc = fExpService.createExpression(frameDmc2, "z"); + + fExpService.getSubExpressions( + parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 2) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 2 got " + getData().length, null)); + } else { + // check that we have the proper value for both children + globalExpressionCtx1 = getData()[0]; + globalExpressionCtx2 = getData()[1]; + + // Get the value of the first child + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx1, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(secondValue)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx1.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + secondValue, null)); + } + } + }); + + // Get the value of the second child + wait.increment(); + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (!getData().getFormattedValue().equals(pointerValue)) { + // The value should have changed + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + + " instead of some other value", null)); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + // also get the child of the pointer + fExpService.getSubExpressions( + globalExpressionCtx2, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().length != 1) { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed getting children; expecting 1 got " + getData().length, null)); + } else { + // Get the value of the child of the pointer + globalExpressionCtx2 = getData()[0]; + fExpService.getFormattedExpressionValue( + fExpService.getFormattedValueContext(globalExpressionCtx2, IFormattedValues.NATURAL_FORMAT), + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData().getFormattedValue().equals(thirdValue)) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed evaluating " + globalExpressionCtx2.getExpression() + ", got " + getData().getFormattedValue() + + " instead of " + thirdValue, null)); + } + } + }); + } + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * This test verifies that we properly return if we can write to different expressions + */ + @Test + public void testCanWrite() throws Throwable { + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("testCanWrite"); + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + final int exprCount = 5; + final IExpressionDMContext dmcs[] = new IExpressionDMContext[exprCount]; + final boolean expectedValues[] = new boolean[exprCount]; + + int exprIndex = 0; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "a"); + expectedValues[exprIndex] = true; + exprIndex++; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "b"); + expectedValues[exprIndex] = true; + exprIndex++; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "c"); + expectedValues[exprIndex] = false; + exprIndex++; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "d"); + expectedValues[exprIndex] = false; + exprIndex++; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "d[1]"); + expectedValues[exprIndex] = true; + exprIndex++; + + for (int index = 0; index < exprCount; index++) { + final int finalIndex = index; + wait.increment(); + fExpService.canWriteExpression( + dmcs[finalIndex], + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData() == expectedValues[finalIndex]) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed establishing proper canWrite for " + dmcs[finalIndex].getExpression() + + ", got " + getData() + " instead of " + expectedValues[finalIndex], null)); + } + + + } + }); + } + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * This test verifies that we properly return if we can write to an expression + * that is an L-Value or a Constant + */ + @Ignore("Only works in versions later than GDB6.7") + @Test + public void testCanWriteLValue() throws Throwable { + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLocation("testCanWrite"); // Re-use test + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + final int exprCount = 2; + final IExpressionDMContext dmcs[] = new IExpressionDMContext[exprCount]; + final boolean expectedValues[] = new boolean[exprCount]; + + int exprIndex = 0; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "&a"); + expectedValues[exprIndex] = false; + exprIndex++; + dmcs[exprIndex] = fExpService.createExpression(frameDmc, "1"); + expectedValues[exprIndex] = false; + exprIndex++; + + for (int index = 0; index < exprCount; index++) { + final int finalIndex = index; + wait.increment(); + fExpService.canWriteExpression( + dmcs[finalIndex], + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else if (getData() == expectedValues[finalIndex]) { + wait.waitFinished(); + } else { + wait.waitFinished(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "Failed establishing proper canWrite for " + dmcs[finalIndex].getExpression() + + ", got " + getData() + " instead of " + expectedValues[finalIndex], null)); + } + + + } + }); + } + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + } + + /** + * Executes a group of sub-tests. + * + * @param tests: + * A Map in which the key is an expression to evaluate and the + * value is an array of expected values, one for each of the + * formats supported by the Expressions service (hex, octal, + * binary, decimal, natural). + */ + private void executeExpressionSubTests(final Map tests, IDMContext dmc) + throws Throwable + { + + // Now evaluate each of the above expressions and compare the actual + // value against + // the expected value. + for (final String expressionToEvaluate : tests.keySet()) { + + // Get an IExpressionDMContext object representing the expression to + // be evaluated. + final IExpressionDMContext exprDMC = SyncUtil.SyncCreateExpression(dmc, expressionToEvaluate); + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + // Get the list of available format IDs for this expression and for + // each one, + // get the value of the expression + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getAvailableFormats(exprDMC, new DataRequestMonitor( + fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + final String[] formatIds = getData(); + + // Now run the current sub-test using each of + // the formats available for the type of + // the expression in the sub-test. + + for (final String formatId : formatIds) { + // Get a FormattedValueCMContext object for + // the expression-formatID pair. + final FormattedValueDMContext valueDmc = fExpService.getFormattedValueContext( + exprDMC, formatId); + + // Increment the number of completed + // requests to wait for, since we will send + // multiple concurrent requests + wait.increment(); + + // Evaluate the expression represented by + // the FormattedValueDMContext object + // This actually evaluates the expression. + fExpService.getFormattedExpressionValue(valueDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (!isSuccess()) { + wait.waitFinished(getStatus()); + } else { + + // Get the + // FormattedValueDMData + // object from the waiter. + FormattedValueDMData exprValueDMData = getData(); + + final String[] expectedValues = tests.get(expressionToEvaluate); + + // Check the value of the + // expression for + // correctness. + String actualValue = exprValueDMData.getFormattedValue(); + String expectedValue; + + if (formatId.equals(IFormattedValues.HEX_FORMAT)) + expectedValue = expectedValues[0]; + else if (formatId.equals(IFormattedValues.OCTAL_FORMAT)) + expectedValue = expectedValues[1]; + else if (formatId.equals(IFormattedValues.BINARY_FORMAT)) + expectedValue = expectedValues[2]; + else if (formatId.equals(IFormattedValues.DECIMAL_FORMAT)) + expectedValue = expectedValues[3]; + else if (formatId.equals(IFormattedValues.NATURAL_FORMAT)) + expectedValue = expectedValues[4]; + else + expectedValue = "[Unrecognized format ID: " + formatId + "]"; + + if (actualValue.equalsIgnoreCase(expectedValue)) { + wait.waitFinished(); + } else { + String errorMsg = "Failed to correctly evalutate '" + + expressionToEvaluate + "': expected '" + expectedValue + + "', got '" + actualValue + "'"; + wait.waitFinished(new Status(IStatus.ERROR, + TestsPlugin.PLUGIN_ID, errorMsg, null)); + } + } + } + }); + } + } + } + }); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + } + + private boolean addressesEqual(IExpressionDMAddress addrToTest, String addrStr, int size) { + IAddress addr; + if (addrStr.length() <= 10) { + addr = new Addr32(addrStr); + } else { + addr = new Addr64(addrStr); + } + return addrToTest.getAddress().equals(addr) && addrToTest.getSize() == size; + } + + private void checkAddressData(final IExpressionDMContext dmc, String actualAddrStr, int actualAddrSize) throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + fExpService.getExpressionAddressData(dmc, new DataRequestMonitor(fExpService + .getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + IExpressionDMAddress addr = (IExpressionDMAddress)wait.getReturnInfo(); + + assertTrue("Unable to get address", addr != null); + if (addr != null) { + assertTrue("Received wrong address of " + addr.toString() + " instead of (" + + actualAddrStr + ", " + actualAddrSize + ")", + addressesEqual(addr, actualAddrStr, actualAddrSize)); + } + } + + private void doTestChildren(MIStoppedEvent stoppedEvent) throws Throwable { + + final IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + final IExpressionDMContext exprDMC = SyncUtil.SyncCreateExpression(frameDmc, "f"); + + IExpressionDMContext[] children = + getChildren(exprDMC, new String[] {"bar", "bar2", "a", "b", "c"}); + + // f.bar + IExpressionDMContext[] children1 = + getChildren(children[0], new String[] {"d", "e"}); + // f.bar.d + getChildren(children1[0], new String[0]); + // f.bar.e + IExpressionDMContext[] children2 = + getChildren(children1[1], new String[] {"e[0]", "e[1]"}); + // f.bar.e[0] + getChildren(children2[0], new String[0]); + // f.bar.e[1] + getChildren(children2[1], new String[0]); + + // f.bar2 + children1 = getChildren(children[1], new String[] {"f", "g"}); + // f.bar2.f + getChildren(children1[0], new String[0]); + // f.bar2.g + children2 = getChildren(children1[1], new String[] {"g[0]", "g[1]"}); + // f.bar2.g[0] + getChildren(children2[0], new String[0]); + // f.bar2.g[1] + getChildren(children2[1], new String[0]); + + // f.a + children1 = getChildren(children[2], new String[] {"a[0]", "a[1]"}); + // f.a[0] + getChildren(children1[0], new String[0]); + // f.a[1] + getChildren(children1[1], new String[0]); + + // f.b + children1 = getChildren(children[3], new String[] {"d", "e"}); + // f.b.d + getChildren(children1[0], new String[0]); + // f.b.e + children2 = getChildren(children1[1], new String[] {"e[0]", "e[1]"}); + // f.b.e[0] + getChildren(children2[0], new String[0]); + // f.b.e[1] + getChildren(children2[1], new String[0]); + + // f.c + getChildren(children[4], new String[0]); + + assertTrue("ExprChangedEvent problem: expected 0, received " + getExprChangedCount(), + getExprChangedCount() == 0); + } + + private IExpressionDMContext[] getChildren( + final IExpressionDMContext parentDmc, + String[] expectedValues) throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + fExpService.getExecutor().submit(new Runnable() { + public void run() { + + fExpService.getSubExpressions(parentDmc, + new DataRequestMonitor(fExpService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + IExpressionDMContext[] childDmcs = + (IExpressionDMContext[]) wait.getReturnInfo(); + + String[] childExpressions = new String[childDmcs.length]; + MIExpressionDMCAccessor[] childDmcsAccessor = new MIExpressionDMCAccessor[childDmcs.length]; + + // Convert to a MIExpressionDMCAccessor to be able to call getRelativeExpression + // Also convert to String[] to be able to use Arrays.toString() + for (int i = 0; i < childExpressions.length; i++) { + childDmcsAccessor[i] = new MIExpressionDMCAccessor(childDmcs[i]); + childExpressions[i] = childDmcsAccessor[i].getRelativeExpression(); + } + assertTrue("Expected " + Arrays.toString(expectedValues) + " but got " + Arrays.toString(childExpressions), + expectedValues.length == childExpressions.length); + + for (int i = 0; i < childDmcsAccessor.length; i++) { + assertTrue("Expected: " + expectedValues[i] + " got: " + childDmcsAccessor[i].getRelativeExpression(), + childDmcsAccessor[i].getRelativeExpression().equals(expectedValues[i])); + } + + return childDmcs; + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/GDBProcessesTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/GDBProcessesTest.java new file mode 100644 index 00000000000..12c84252acc --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/GDBProcessesTest.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson AB - Initial implementation of Test cases + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class GDBProcessesTest extends BaseTestCase { + /* + * Path to executable + */ + private static final String EXEC_PATH = "data/launch/bin/"; + /* + * Name of the executable + */ + private static final String EXEC_NAME = "MultiThread.exe"; + + + private DsfSession fSession; + private DsfServicesTracker fServicesTracker; + + private IGDBControl fGdbCtrl; + private MIProcesses fProcService; + + /* + * Create a waiter and a generic completion object. They will be used to + * wait for asynchronous call completion. + */ + private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + + @Before + public void init() throws Exception { + fSession = getGDBLaunch().getSession(); + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + /* + * Get the GDBProcesses & MIRunControl service. + */ + fProcService = fServicesTracker.getService(MIProcesses.class); + fGdbCtrl = fServicesTracker.getService(IGDBControl.class); + } + + @After + public void tearDown() { + fProcService = null; + fServicesTracker.dispose(); + } + + @BeforeClass + public static void beforeClassMethod() { + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + EXEC_PATH + EXEC_NAME); + } + + @Test + /* + * Get the process data for the current program. Process is executable name in case of GDB back end + */ + public void getProcessData() throws InterruptedException{ + /* + * Create a request monitor + */ + final DataRequestMonitor rm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + /* + * Ask the service to get model data for the process. + * There is only one process in case of GDB back end. + */ + fSession.getExecutor().submit(new Runnable() { + public void run() { + String groupId = MIProcesses.UNIQUE_GROUP_ID; + + IProcessDMContext procDmc = fProcService.createProcessContext(fGdbCtrl.getContext(), groupId); + fProcService.getExecutionData(procDmc, rm); + } + }); + /* + * Wait for the operation to get over + */ + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + /* + * Assert false if status is not OK + */ + Assert.assertTrue(fWait.getMessage(), fWait.isOK()); + + /* + * Get process data + */ + IThreadDMData processData = (IThreadDMData)fWait.getReturnInfo(); + + if(processData == null) + Assert.fail("No process data is returned for Process DMC"); + else{ + /* + * Name of the process is the executable name in case of GDB back-end. + */ + assertEquals("Process data should be executable name " + EXEC_NAME, EXEC_NAME, processData.getName()); + } + } + + /* + * getThreadData() for multiple threads + */ + @Test + public void getThreadData() throws InterruptedException{ + final String THREAD_ID = "1"; + final DataRequestMonitor rm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + + fProcService.getExecutor().submit(new Runnable() { + public void run() { + String groupId = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGdbCtrl.getContext(), groupId); + IThreadDMContext threadDmc = fProcService.createThreadContext(procDmc, THREAD_ID); + fProcService.getExecutionData(threadDmc, rm); + } + }); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + IThreadDMData threadData = (IThreadDMData)fWait.getReturnInfo(); + if(threadData == null) + fail("Thread data not returned for thread id = " + THREAD_ID); + else{ + // Thread id is only a series of numbers + Pattern pattern = Pattern.compile("\\d*", Pattern.MULTILINE); //$NON-NLS-1$ + Matcher matcher = pattern.matcher(threadData.getId()); + assertTrue("Thread ID is a series of number", matcher.find()); + // Name is blank in case of GDB back end + assertEquals("Thread name is should have been blank for GDB Back end", "", threadData.getName()); + } + fWait.waitReset(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIBreakpointsTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIBreakpointsTest.java new file mode 100644 index 00000000000..3ce31794507 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIBreakpointsTest.java @@ -0,0 +1,2822 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMData; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsAddedEvent; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsRemovedEvent; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsUpdatedEvent; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.MIBreakpoints.MIBreakpointDMContext; +import org.eclipse.cdt.dsf.mi.service.command.events.MIBreakpointHitEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointScopeEvent; +import org.eclipse.cdt.dsf.mi.service.command.events.MIWatchpointTriggerEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +/* + * This is the Breakpoint Service test suite. + * + * It is meant to be a regression suite to be executed automatically against + * the DSF nightly builds. + * + * It is also meant to be augmented with a proper test case(s) every time a + * feature is added or in the event (unlikely :-) that a bug is found in the + * Breakpoint Service. + * + * Refer to the JUnit4 documentation for an explanation of the annotations. + */ + +@RunWith(BackgroundRunner.class) +public class MIBreakpointsTest extends BaseTestCase { + + // Global constants + public static final String PLUGIN_ID = "org.eclipse.cdt.debug.core" ; //$NON-NLS-1$ + public static final String TEST_APPL = "data/launch/bin/BreakpointTestApp.exe"; //$NON-NLS-1$ + public static final String SOURCE_PATH = "data/launch/src"; //$NON-NLS-1$ + + public static final String SOURCE_PROJECT = "MIBreakpointsTest"; + public static final String SOURCE_FOLDER = "src"; + public static final String SOURCE_FILE = "BreakpointTestApp.cc"; //$NON-NLS-1$ + + // Asynchronous Completion + private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + + // Services references + private DsfSession fSession; + private IBreakpointsTargetDMContext fBreakpointsDmc; + private DsfServicesTracker fServicesTracker; + private MIRunControl fRunControl; + private IBreakpoints fBreakpointService; + + // Event Management + private static Boolean lock = true; + enum Events { BP_ADDED, BP_UPDATED, BP_REMOVED, BP_HIT, WP_HIT, WP_OOS } + final int BP_ADDED = Events.BP_ADDED.ordinal(); + final int BP_UPDATED = Events.BP_UPDATED.ordinal(); + final int BP_REMOVED = Events.BP_REMOVED.ordinal(); + final int BP_HIT = Events.BP_HIT.ordinal(); + final int WP_HIT = Events.WP_HIT.ordinal(); + final int WP_OOS = Events.WP_OOS.ordinal(); + private int[] fBreakpointEvents = new int[Events.values().length]; + private boolean fBreakpointEvent; + private int fBreakpointEventCount; + private int fBreakpointRef; + + // Some useful constants + final String BREAKPOINT_TYPE_TAG = MIBreakpoints.BREAKPOINT_TYPE; + final String BREAKPOINT_TAG = MIBreakpoints.BREAKPOINT; + final String WATCHPOINT_TAG = MIBreakpoints.WATCHPOINT; + + final String FILE_NAME_TAG = MIBreakpoints.FILE_NAME; + final String LINE_NUMBER_TAG = MIBreakpoints.LINE_NUMBER; + final String FUNCTION_TAG = MIBreakpoints.FUNCTION; + final String ADDRESS_TAG = MIBreakpoints.ADDRESS; + final String CONDITION_TAG = MIBreakpoints.CONDITION; + final String IGNORE_COUNT_TAG = MIBreakpoints.IGNORE_COUNT; + final String IS_ENABLED_TAG = MIBreakpoints.IS_ENABLED; + final String THREAD_ID_TAG = MIBreakpointDMData.THREAD_ID; + final String NUMBER_TAG = MIBreakpointDMData.NUMBER; + + final String EXPRESSION_TAG = MIBreakpoints.EXPRESSION; + final String READ_TAG = MIBreakpoints.READ; + final String WRITE_TAG = MIBreakpoints.WRITE; + + // Target application 'special' locations + private final int LINE_NUMBER_1 = 20; + private final int LINE_NUMBER_2 = 21; + private final int LINE_NUMBER_3 = 27; + private final int LINE_NUMBER_4 = 35; + private final String FUNCTION = "zeroBlocks"; + private final String SIGNED_FUNCTION = "zeroBlocks(int)"; + private final String NO_CONDITION = ""; + + // NOTE: The back-end can reformat the condition. In order for the + // comparison to work, better specify the condition as the back-end + // would have it. + private final String CONDITION_1 = "i == 128"; + private final String CONDITION_2 = "i == 64"; + private final String CONDITION_3 = "j == 20"; + private final int IGNORE_COUNT_1 = 128; + private final int IGNORE_COUNT_2 = 20; + + private final String EXPRESSION_1 = "charBlock[20]"; + private final String EXPRESSION_2 = "j"; + + // Error messages + final String UNKNOWN_EXECUTION_CONTEXT = "Unknown execution context"; + final String INVALID_BREAKPOINT_LOCATION = "Invalid breakpoint location"; + final String BREAKPOINT_INSERTION_FAILURE = "Breakpoint insertion failure"; + final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; + + // ======================================================================== + // Housekeeping stuff + // ======================================================================== + + @BeforeClass + public static void testSuiteInitialization() { + // Select the binary to run the tests against + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, TEST_APPL); + } + + @AfterClass + public static void testSuiteCleanup() { + } + + @Before + public void testCaseInitialization() { + + // Get a reference to the breakpoint service + fSession = getGDBLaunch().getSession(); + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + assert(fServicesTracker != null); + + ICommandControlService commandControl = fServicesTracker.getService(ICommandControlService.class); + fBreakpointsDmc = (IBreakpointsTargetDMContext)commandControl.getContext(); + assert(fBreakpointsDmc != null); + + fRunControl = fServicesTracker.getService(MIRunControl.class); + assert(fRunControl != null); + + fBreakpointService = fServicesTracker.getService(IBreakpoints.class); + assert(fBreakpointService != null); + + // Register to breakpoint events + fRunControl.getSession().addServiceEventListener(MIBreakpointsTest.this, null); + + clearEventCounters(); + } + + @After + public void testCaseCleanup() { + + // Clear the references (not strictly necessary) + fRunControl.getSession().removeServiceEventListener(MIBreakpointsTest.this); + fBreakpointService = null; + fRunControl = null; + fServicesTracker.dispose(); + fServicesTracker = null; + + clearEventCounters(); + } + + // ======================================================================== + // Event Management Functions + // ======================================================================== + + /* ----------------------------------------------------------------------- + * eventDispatched + * ------------------------------------------------------------------------ + * Processes BreakpointHitEvent. + * ------------------------------------------------------------------------ + * @param e The BreakpointEvent + * ------------------------------------------------------------------------ + */ + @DsfServiceEventHandler + public void eventDispatched(IBreakpointsAddedEvent e) { + synchronized (lock) { + fBreakpointEvents[BP_ADDED]++; + fBreakpointEventCount++; + fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IBreakpointsUpdatedEvent e) { + synchronized (lock) { + fBreakpointEvents[BP_UPDATED]++; + fBreakpointEventCount++; + fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(IBreakpointsRemovedEvent e) { + synchronized (lock) { + fBreakpointEvents[BP_REMOVED]++; + fBreakpointEventCount++; + fBreakpointRef = ((MIBreakpointDMContext) e.getBreakpoints()[0]).getReference(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(MIBreakpointHitEvent e) { + synchronized (lock) { + fBreakpointEvents[BP_HIT]++; + fBreakpointEventCount++; + fBreakpointRef = e.getNumber(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(MIWatchpointTriggerEvent e) { + synchronized (lock) { + fBreakpointEvents[WP_HIT]++; + fBreakpointEventCount++; + fBreakpointRef = e.getNumber(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + @DsfServiceEventHandler + public void eventDispatched(MIWatchpointScopeEvent e) { + synchronized (lock) { + fBreakpointEvents[WP_OOS]++; + fBreakpointEventCount++; + fBreakpointRef = e.getNumber(); + fBreakpointEvent = true; + lock.notifyAll(); + } + } + + // Clears the counters + private void clearEventCounters() { + synchronized (lock) { + for (int i = 0; i < fBreakpointEvents.length; i++) { + fBreakpointEvents[i] = 0; + } + fBreakpointEvent = false; + fBreakpointEventCount = 0; + } + } + + // Get the breakpoint hit count + private int getBreakpointEventCount(int event) { + int count = 0; + synchronized (lock) { + count = fBreakpointEvents[event]; + } + return count; + } + + // Suspends the thread until an event is flagged + // NOTE: too simple for real life but good enough for this test suite + private void waitForBreakpointEvent() { + synchronized (lock) { + while (!fBreakpointEvent) { + try { + lock.wait(); + } catch (InterruptedException ex) { + } + } + fBreakpointEvent = false; + } + } + + // ======================================================================== + // Helper Functions + // ======================================================================== + + /* ------------------------------------------------------------------------ + * evaluateExpression + * ------------------------------------------------------------------------ + * Invokes the ExpressionService to evaluate an expression. In theory, + * we shouldn't rely on another service to test this one but we need a + * way to access a variable from the test application in order verify + * that the memory operations (read/write) are working properly. + * ------------------------------------------------------------------------ + * @param expression Expression to resolve @return Resolved expression + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private BigInteger evaluateExpression(IDMContext ctx, String expression) throws Throwable { + + final IExpressions fExpressionService = fServicesTracker.getService(IExpressions.class); + assert (fExpressionService != null); + + // Get a stack context (temporary - should be an MIcontainerDMC) + final IExpressionDMContext expressionDMC = SyncUtil.SyncCreateExpression(ctx, expression); + final FormattedValueDMContext formattedValueDMC = SyncUtil.SyncGetFormattedValue(fExpressionService, + expressionDMC, IFormattedValues.DECIMAL_FORMAT); + + // Create the DataRequestMonitor which will store the operation result in the wait object + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Evaluate the expression (asynchronously) + fWait.waitReset(); + fSession.getExecutor().submit(new Runnable() { + public void run() { + fExpressionService.getFormattedExpressionValue(formattedValueDMC, drm); + } + }); + + // Wait for completion + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Return the string formatted by the back-end + String result = ""; + Object returnInfo = fWait.getReturnInfo(); + if (returnInfo instanceof FormattedValueDMData) + result = ((FormattedValueDMData) returnInfo).getFormattedValue(); + return new BigInteger(result); + } + + /* ------------------------------------------------------------------------ + * getBreakpoints + * ------------------------------------------------------------------------ + * Retrieves the installed breakpoints list + * ------------------------------------------------------------------------ + * Typical usage: + * IBreakpointDMContext[] breakpoints = getBreakpoints(context); + * ------------------------------------------------------------------------ + * @param context the execution context + * ------------------------------------------------------------------------ + */ + private IBreakpointDMContext[] getBreakpoints(final IBreakpointsTargetDMContext context) throws InterruptedException + { + // Clear the completion waiter + fWait.waitReset(); + + // Set the Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fBreakpointService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the breakpoint request + fWait.waitReset(); + fBreakpointService.getExecutor().submit(new Runnable() { + public void run() { + fBreakpointService.getBreakpoints(context, drm); + } + }); + + // Wait for completion + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Return the string formatted by the back-end + return drm.getData(); + } + + /* ------------------------------------------------------------------------ + * getBreakpoint + * ------------------------------------------------------------------------ + * Retrieves the installed breakpoint + * ------------------------------------------------------------------------ + * Typical usage: + * IBreakpointDMContext breakpoint = ...; + * IBreakpointDMData bp = getBreakpoint(breakpoint); + * ------------------------------------------------------------------------ + * @param breakpoint the breakpoint to retrieve + * ------------------------------------------------------------------------ + */ + private IBreakpointDMData getBreakpoint(final IBreakpointDMContext breakpoint) throws InterruptedException + { + // Clear the completion waiter + fWait.waitReset(); + + // Set the Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fBreakpointService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the breakpoint request + fWait.waitReset(); + fBreakpointService.getExecutor().submit(new Runnable() { + public void run() { + fBreakpointService.getBreakpointDMData(breakpoint, drm); + } + }); + + // Wait for completion + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Return the string formatted by the back-end + return drm.getData(); + } + + /* ------------------------------------------------------------------------ + * insertBreakpoint + * ------------------------------------------------------------------------ + * Issues an add breakpoint request. + * ------------------------------------------------------------------------ + * Typical usage: + * bp = insertBreakpoint(context, attributes); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param context the execution context + * @param attributes the breakpoint attributes + * ------------------------------------------------------------------------ + */ + private IBreakpointDMContext insertBreakpoint(final IBreakpointsTargetDMContext context, + final Map attributes) throws InterruptedException + { + // Clear the completion waiter + fWait.waitReset(); + + // Set the Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fBreakpointService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the remove breakpoint request + fBreakpointService.getExecutor().submit(new Runnable() { + public void run() { + fBreakpointService.insertBreakpoint(context, attributes, drm); + } + }); + + // Wait for the result and return the breakpoint id + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + return drm.getData(); + } + + /* ------------------------------------------------------------------------ + * removeBreakpoint + * ------------------------------------------------------------------------ + * Issues a remove breakpoint request. + * ------------------------------------------------------------------------ + * Typical usage: + * IBreakpointDMContext breakpoint = ...; + * removeBreakpoint(context, breakpoint); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param breakpoint the breakpoint to remove + * ------------------------------------------------------------------------ + */ + private void removeBreakpoint(final IBreakpointDMContext breakpoint) throws InterruptedException + { + // Clear the completion waiter + fWait.waitReset(); + + // Set the Request Monitor + final RequestMonitor rm = + new RequestMonitor(fBreakpointService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the add breakpoint request + fBreakpointService.getExecutor().submit(new Runnable() { + public void run() { + fBreakpointService.removeBreakpoint(breakpoint, rm); + } + }); + + // Wait for the result + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + } + + /* ------------------------------------------------------------------------ + * updateBreakpoint + * ------------------------------------------------------------------------ + * Issues an update breakpoint request. + * ------------------------------------------------------------------------ + * Typical usage: + * updateBreakpoint(context, breakpoint, properties); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param breakpoint the breakpoint to update + * @param delta the delta properties + * ------------------------------------------------------------------------ + */ + private void updateBreakpoint(final IBreakpointDMContext breakpoint, + final Map delta) throws InterruptedException + { + // Clear the completion waiter + fWait.waitReset(); + + // Set the Request Monitor + final RequestMonitor rm = + new RequestMonitor(fBreakpointService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the update breakpoint request + fBreakpointService.getExecutor().submit(new Runnable() { + public void run() { + fBreakpointService.updateBreakpoint(breakpoint, delta, rm); + } + }); + + // Wait for the result + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + } + + // ======================================================================== + // Test Cases + // ------------------------------------------------------------------------ + // Templates: + // ------------------------------------------------------------------------ + // @Test + // public void basicTest() { + // // First test to run + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @Test(timeout=5000) + // public void timeoutTest() { + // // Second test to run, which will timeout if not finished on time + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @Test(expected=FileNotFoundException.class) + // public void exceptionTest() throws FileNotFoundException { + // // Third test to run which expects an exception + // throw new FileNotFoundException("Just testing"); + // } + // ======================================================================== + + /////////////////////////////////////////////////////////////////////////// + // Add Breakpoint tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // insertBreakpoint_InvalidContext + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_InvalidContext() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Perform the test + String expected = UNKNOWN_EXECUTION_CONTEXT; + insertBreakpoint(null, breakpoint); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_InvalidFileName + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_InvalidFileName() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE + "_bad"); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Perform the test + String expected = BREAKPOINT_INSERTION_FAILURE; + insertBreakpoint(fBreakpointsDmc, breakpoint); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_InvalidLineNumber + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_InvalidLineNumber() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, 0); + + // Perform the test + String expected = BREAKPOINT_INSERTION_FAILURE; + insertBreakpoint(fBreakpointsDmc, breakpoint); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_InvalidFunctionName + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_InvalidFunctionName() throws Throwable { + + // Create a function breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(FUNCTION_TAG, "invalid-function-name"); + + // Perform the test + String expected = BREAKPOINT_INSERTION_FAILURE; + insertBreakpoint(fBreakpointsDmc, breakpoint); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_InvalidAddress + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_InvalidAddress() throws Throwable { + + // Create an address breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(ADDRESS_TAG, "0x0z"); + + // Perform the test + String expected = BREAKPOINT_INSERTION_FAILURE; + insertBreakpoint(fBreakpointsDmc, breakpoint); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + + // ------------------------------------------------------------------------ + // insertBreakpoint_Address + // Set a breakpoint on an address + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_Address() throws Throwable { + + // Create an address breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + final BigInteger ADDRESS= new BigInteger("00affe00", 16); + breakpoint.put(ADDRESS_TAG, "0x"+ADDRESS.toString(16)); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertEquals("BreakpointService problem: breakpoint mismatch (wrong address)", + breakpoint1.getAddresses()[0].getValue(), ADDRESS); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong state)", + breakpoint1.isEnabled()); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_LineNumber + // Set a breakpoint on a line number. + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_LineNumber() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong state)", + breakpoint1.isEnabled()); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_Disabled + // Set a disabled breakpoint on a line number. + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_Disabled() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(IS_ENABLED_TAG, false); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong state)", + !breakpoint1.isEnabled()); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_FunctionName + // Set a breakpoint on a function name. + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_FunctionName() throws Throwable { + + // Create a function breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(FUNCTION_TAG, FUNCTION); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong function)", + breakpoint1.getFunctionName().equals(SIGNED_FUNCTION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_Condition + // Set a conditional breakpoint. + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_Condition() throws Throwable { + + // Create a conditional line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(CONDITION_TAG, CONDITION_1); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(CONDITION_1)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_IgnoreCnt + // Set a breakpoint with an ignore count. + // Ensure that it is set correctly at the back-end. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_IgnoreCnt() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(IGNORE_COUNT_TAG, IGNORE_COUNT_1); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == IGNORE_COUNT_1); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_MultipleBreakpoints + // Set multiple distinct breakpoints. + // Ensure that the state is kosher. + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_MultipleBreakpoints() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + + // Create a function breakpoint + breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(FUNCTION_TAG, FUNCTION); + + // Perform the test + ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint2.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong function)", + breakpoint2.getFunctionName().equals(SIGNED_FUNCTION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint2.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint2.getIgnoreCount() == 0); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 2 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 2); + MIBreakpointDMData svc_bp1 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + MIBreakpointDMData svc_bp2 = (MIBreakpointDMData) getBreakpoint(breakpoints[1]); + + // The breakpoint references are not necessarily retrieved in the order the + // breakpoints were initially set... + int ref1 = breakpoint1.getNumber(); + int ref2 = svc_bp1.getNumber(); + if (ref1 == ref2) { + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp1.equals(breakpoint1)); + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp2.equals(breakpoint2)); + } else { + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp1.equals(breakpoint2)); + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp2.equals(breakpoint1)); + } + } + + // ------------------------------------------------------------------------ + // insertBreakpoint_Duplicate + // Set 2 identical breakpoints. + // For GDB, no problem... + // ------------------------------------------------------------------------ + @Test + public void insertBreakpoint_Duplicate() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint1.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint1.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint1.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint1.getIgnoreCount() == 0); + + // Create a second line breakpoint, same attributes... + ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the breakpoint was correctly installed + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong file name)", + breakpoint2.getFileName().equals(SOURCE_FILE)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong line number)", + breakpoint2.getLineNumber() == LINE_NUMBER_1); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong condition)", + breakpoint2.getCondition().equals(NO_CONDITION)); + assertTrue("BreakpointService problem: breakpoint mismatch (wrong ignore count)", + breakpoint2.getIgnoreCount() == 0); + + // Ensure the BreakpointService holds only the right breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 2 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 2); + MIBreakpointDMData svc_bp1 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + MIBreakpointDMData svc_bp2 = (MIBreakpointDMData) getBreakpoint(breakpoints[1]); + + // The breakpoint references are not necessarily retrieved in the order the + // breakpoints were initially set... + int ref1 = breakpoint1.getNumber(); + int ref2 = svc_bp1.getNumber(); + if (ref1 == ref2) { + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp1.equals(breakpoint1)); + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp2.equals(breakpoint2)); + } else { + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp1.equals(breakpoint2)); + assertTrue("BreakpointService problem: breakpoint mismatch", svc_bp2.equals(breakpoint1)); + } + } + + /////////////////////////////////////////////////////////////////////////// + // Add Watchpoint tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // insertWatchpoint_Write + // Set a write watchpoint. + // Ensure that the state is kosher. + // ------------------------------------------------------------------------ + @Test + public void insertWatchpoint_Write() throws Throwable { + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(WRITE_TAG, true); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the watchpoint was correctly installed + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong expression)", + watchpoint1.getExpression().equals(EXPRESSION_1)); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong read state)", + !watchpoint1.isReadWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong write state)", + watchpoint1.isWriteWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong access state)", + !watchpoint1.isAccessWatchpoint()); + + // Ensure the BreakpointService holds only the right watchpoints + IBreakpointDMContext[] watchpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " watchpoints(s), received " + + watchpoints.length, watchpoints.length == 1); + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(watchpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + watchpoint1.equals(watchpoint2)); + } + + // ------------------------------------------------------------------------ + // insertWatchpoint_Read + // Set a read watchpoint. + // Ensure that the state is kosher. + // ------------------------------------------------------------------------ + @Test + public void insertWatchpoint_Read() throws Throwable { + + // Create a read watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(READ_TAG, true); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the watchpoint was correctly installed + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong expression)", + watchpoint1.getExpression().equals(EXPRESSION_1)); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong read state)", + watchpoint1.isReadWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong write state)", + !watchpoint1.isWriteWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong access state)", + !watchpoint1.isAccessWatchpoint()); + + // Ensure the BreakpointService holds only the right watchpoints + IBreakpointDMContext[] watchpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " watchpoints(s), received " + + watchpoints.length, watchpoints.length == 1); + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(watchpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + watchpoint1.equals(watchpoint2)); + } + + // ------------------------------------------------------------------------ + // insertWatchpoint_Access + // Set an access watchpoint. + // Ensure that the state is kosher. + // ------------------------------------------------------------------------ + @Test + public void insertWatchpoint_Access() throws Throwable { + + // Create an access watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(READ_TAG, true); + watchpoint.put(WRITE_TAG, true); + + // Perform the test + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure that the watchpoint was correctly installed + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong expression)", + watchpoint1.getExpression().equals(EXPRESSION_1)); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong read state)", + !watchpoint1.isReadWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong write state)", + !watchpoint1.isWriteWatchpoint()); + assertTrue("BreakpointService problem: watchpoint mismatch (wrong access state)", + watchpoint1.isAccessWatchpoint()); + + // Ensure the BreakpointService holds only the right watchpoints + IBreakpointDMContext[] watchpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " watchpoints(s), received " + + watchpoints.length, watchpoints.length == 1); + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(watchpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + watchpoint1.equals(watchpoint2)); + } + + /////////////////////////////////////////////////////////////////////////// + // Remove Breakpoint tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // removeBreakpoint_SimpleCase + // Set a breakpoint and then remove it. + // Ensure that the state is kosher. + // ------------------------------------------------------------------------ + @Test + public void removeBreakpoint_SimpleCase() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Remove the installed breakpoint + removeBreakpoint(ref); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_REMOVED event(s), received " + + getBreakpointEventCount(BP_REMOVED), getBreakpointEventCount(BP_REMOVED) == 1); + clearEventCounters(); + + // Ensure the breakpoint was effectively removed + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 0 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 0); + } + + // ------------------------------------------------------------------------ + // removeBreakpoint_InvalidBreakpoint + // Try removing a non-existing breakpoint. + // ------------------------------------------------------------------------ + @Test + public void removeBreakpoint_InvalidBreakpoint() throws Throwable { + + // Create an invalid breakpoint reference + IBreakpointDMContext invalid_ref = + new MIBreakpointDMContext((MIBreakpoints) fBreakpointService, new IDMContext[] { fBreakpointsDmc }, 0); + + // Remove the invalid breakpoint + String expected = UNKNOWN_BREAKPOINT; + removeBreakpoint(invalid_ref); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that right BreakpointEvents were received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + IBreakpointDMContext saved_ref = ref; + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Ensure the breakpoint list is OK + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + + // Remove the installed breakpoint + removeBreakpoint(ref); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_REMOVED event(s), received " + + getBreakpointEventCount(BP_REMOVED), getBreakpointEventCount(BP_REMOVED) == 1); + clearEventCounters(); + + // Ensure the breakpoint list is OK + breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 0 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 0); + + // Remove the un-installed breakpoint + removeBreakpoint(saved_ref); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that right BreakpointEvents were received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + + // Ensure the breakpoint list is OK + breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 0 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 0); + + // Re-install the breakpoint + ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Remove an un-installed breakpoint (again) + removeBreakpoint(saved_ref); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that right BreakpointEvents were received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + + // Ensure that the existing breakpoint is unaffected + breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 1 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(breakpoints[0]); + assertTrue("BreakpointService problem: breakpoint mismatch", + breakpoint1.equals(breakpoint2)); + } + + // ------------------------------------------------------------------------ + // removeBreakpoint_MixedCase + // Set a number of breakpoints and then remove them in disorder. + // Ensure that the right breakpoints are left after each iteration. + // ------------------------------------------------------------------------ + @Test + public void removeBreakpoint_MixedCase() throws Throwable { + + // Create a line breakpoint + for (int i = 0; i < 4; i++) { + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1 + i); + insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + int expected = i + 1; + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + expected + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == expected); + assertTrue("BreakpointEvent problem: expected " + expected + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == expected); + } + clearEventCounters(); + + // Get the list of breakpoints + IBreakpointDMContext[] breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + 4 + " breakpoint(s), received " + + breakpoints.length, breakpoints.length == 4); + + // Remove the breakpoint one at a time in the following order: 1, 3, 2, 4 + int[] indices = { 0, 2, 1, 3 }; + int breakpoints_left = 4; + for (int i = 0; i < breakpoints_left; i++) { + + // Remove the selected breakpoint + IBreakpointDMContext index = breakpoints[indices[i]]; + removeBreakpoint(index); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + breakpoints_left--; + + // Ensure that right BreakpointEvents were received + int expected = i + 1; + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + expected + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == expected); + assertTrue("BreakpointEvent problem: expected " + expected + " BREAKPOINT_REMOVED event(s), received " + + getBreakpointEventCount(BP_REMOVED), getBreakpointEventCount(BP_REMOVED) == expected); + + // Ensure the breakpoint was effectively removed + IBreakpointDMContext[] remaining_breakpoints = getBreakpoints(fBreakpointsDmc); + assertTrue("BreakpointService problem: expected " + breakpoints_left + " breakpoint(s), received " + + remaining_breakpoints.length, remaining_breakpoints.length == breakpoints_left); + for (int j = 0; i < breakpoints_left; i++) { + assertTrue("BreakpointService problem: removed breakpoint still present (" + index + ")", + remaining_breakpoints[j] != index); + } + } + clearEventCounters(); + } + + /////////////////////////////////////////////////////////////////////////// + // Breakpoint Update tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // updateBreakpoint_InvalidBreakpoint + // Updates a non-existing breakpoint. + // For GDB, no problem... + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_InvalidBreakpoint() throws Throwable { + + // Create an invalid breakpoint reference + IBreakpointDMContext invalid_ref = + new MIBreakpointDMContext((MIBreakpoints) fBreakpointService, new IDMContext[] { fBreakpointsDmc }, 0); + + // Update the invalid breakpoint + String expected = UNKNOWN_BREAKPOINT; + Map properties = new HashMap(); + properties.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + properties.put(FILE_NAME_TAG, SOURCE_FILE); + properties.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + updateBreakpoint(invalid_ref, properties); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure that no BreakpointEvent was received + assertTrue("BreakpointEvent problem: expected " + 0 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 0); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_AddCondition + // Set a breakpoint and then add a condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_AddCondition() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Modify the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_1); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + breakpoint2.getCondition().equals(CONDITION_1)); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_RemoveCondition + // Set a conditional breakpoint and then remove the condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_RemoveCondition() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(CONDITION_TAG, CONDITION_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Remove the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, null); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + breakpoint2.getCondition().equals("")); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_ModifyCondition + // Set a conditional breakpoint and then modify the condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_ModifyCondition() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(CONDITION_TAG, CONDITION_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Update the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + breakpoint2.getCondition().equals(CONDITION_2)); + } + + // ------------------------------------------------------------------------ + // updateWatchpoint_AddCondition + // Set a watchpoint and then add a condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateWatchpoint_AddCondition() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_1, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_1); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the watchpoint + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + watchpoint2.getCondition().equals(CONDITION_1)); + } + + // ------------------------------------------------------------------------ + // updateWatchpoint_RemoveCondition + // Set a conditional watchpoint and then remove the condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateWatchpoint_RemoveCondition() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_1, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(WRITE_TAG, true); + watchpoint.put(CONDITION_TAG, CONDITION_1); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Remove the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, null); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the watchpoint + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + watchpoint2.getCondition().equals("")); + } + + // ------------------------------------------------------------------------ + // updateWatchpoint_ModifyCondition + // Set a conditional watchpoint and then modify the condition. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateWatchpoint_ModifyCondition() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_1, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(WRITE_TAG, true); + watchpoint.put(CONDITION_TAG, CONDITION_1); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Update the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData watchpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", + watchpoint2.getCondition().equals(CONDITION_2)); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_AddCount + // Set a breakpoint and then add an ignore count. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_AddCount() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add a count + Map delta = new HashMap(); + delta.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong count)", + breakpoint2.getIgnoreCount() == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_RemoveCount + // Set a conditional breakpoint and then remove the count.. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_RemoveCount() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Remove the count + Map delta = new HashMap(); + delta.put(IGNORE_COUNT_TAG, null); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong count)", + breakpoint2.getIgnoreCount() == 0); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_ModifyCount + // Set a conditional breakpoint and then modify the count. + // Ensure that the new breakpoint reflects the changes + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_ModifyCount() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(IGNORE_COUNT_TAG, IGNORE_COUNT_1); + + // Install the breakpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Update the count + Map delta = new HashMap(); + delta.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoint + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong count)", + breakpoint2.getIgnoreCount() == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_Disable + // Set 2 breakpoints and disable the first one. + // Ensure that we stop on the second one. + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_Disable() throws Throwable { + + // Create a first line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref1 = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Create a second line breakpoint + breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_2); + + // Install the breakpoint + IBreakpointDMContext ref2 = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 2 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 2); + assertTrue("BreakpointEvent problem: expected " + 2 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 2); + clearEventCounters(); + + // Verify the state of the breakpoints + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref2); + assertTrue("BreakpointService problem: breakpoint state error", + breakpoint1.isEnabled() && breakpoint2.isEnabled()); + + // Disable the first breakpoint + Map delta = new HashMap(); + delta.put(IS_ENABLED_TAG, false); + updateBreakpoint(ref1, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoints + breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref1); + breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref2); + assertTrue("BreakpointService problem: breakpoint state error", + !breakpoint1.isEnabled() && breakpoint2.isEnabled()); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the BreakpointEvent was received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint2.getNumber()); + clearEventCounters(); + } + + // ------------------------------------------------------------------------ + // updateBreakpoint_Enable + // In a loop, set 2 breakpoints and disable the first one. After hitting + // the second one, enable the first one again. + // Ensure that we stop on the first one on the next iteration. + // ------------------------------------------------------------------------ + @Test + public void updateBreakpoint_Enable() throws Throwable { + + // Create a first line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + IBreakpointDMContext ref1 = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Create a second line breakpoint + breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_2); + + // Install the breakpoint + IBreakpointDMContext ref2 = insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 2 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 2); + assertTrue("BreakpointEvent problem: expected " + 2 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 2); + clearEventCounters(); + + // Verify the state of the breakpoints + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref1); + MIBreakpointDMData breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref2); + assertTrue("BreakpointService problem: breakpoint state error", + breakpoint1.isEnabled() && breakpoint2.isEnabled()); + + // Disable the first breakpoint + Map delta = new HashMap(); + delta.put(IS_ENABLED_TAG, false); + updateBreakpoint(ref1, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoints + breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref1); + breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref2); + assertTrue("BreakpointService problem: breakpoint state error", + !breakpoint1.isEnabled() && breakpoint2.isEnabled()); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the BreakpointEvent was received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint2.getNumber()); + clearEventCounters(); + + // Enable the first breakpoint + delta = new HashMap(); + delta.put(IS_ENABLED_TAG, true); + updateBreakpoint(ref1, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Verify the state of the breakpoints + breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref1); + breakpoint2 = (MIBreakpointDMData) getBreakpoint(ref2); + assertTrue("BreakpointService problem: breakpoint state error", + breakpoint1.isEnabled() && breakpoint2.isEnabled()); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the BreakpointEvent was received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + } + + /////////////////////////////////////////////////////////////////////////// + // Breakpoint Hit tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // breakpointHit_LineNumber + // Set a breakpoint on a line number. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_LineNumber() throws Throwable { + + // Create a line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + } + + // ------------------------------------------------------------------------ + // breakpointHit_Function + // Set a breakpoint on a function name. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_Function() throws Throwable { + + // Create a function breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(FUNCTION_TAG, FUNCTION); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + } + + // ------------------------------------------------------------------------ + // breakpointHit_Condition + // Set a breakpoint on a line where a variable being increased (loop). + // Set a condition so that the break occurs only after variable == count. + // Ensure that the variable was increased 'count' times. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_Condition() throws Throwable { + + // Create a conditional line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(CONDITION_TAG, CONDITION_1); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == 128); + } + + // ------------------------------------------------------------------------ + // breakpointHit_UpdatedCondition + // Set a breakpoint on a line where a variable being increased (loop). + // Set a condition so that the break occurs only after variable == count. + // Ensure that the variable was increased 'count' times. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_UpdatedCondition() throws Throwable { + + // Create a conditional line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add the condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_1); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == 128); + } + + // ------------------------------------------------------------------------ + // breakpointHit_Count + // Set a breakpoint on a line where a variable being increased (loop). + // Set an ignore count != 0. + // Ensure that the variable was increased 'ignoreCount' times. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_Count() throws Throwable { + + // Create a conditional line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + breakpoint.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_UpdatedCount + // Set a breakpoint on a line where a variable being increased (loop). + // Set an ignore count != 0. + // Ensure that the variable was increased 'ignoreCount' times. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_UpdatedCount() throws Throwable { + + // Create a conditional line breakpoint + Map breakpoint = new HashMap(); + breakpoint.put(BREAKPOINT_TYPE_TAG, BREAKPOINT_TAG); + breakpoint.put(FILE_NAME_TAG, SOURCE_FILE); + breakpoint.put(LINE_NUMBER_TAG, LINE_NUMBER_1); + + // Install the breakpoint + MIBreakpointDMContext ref = (MIBreakpointDMContext) insertBreakpoint(fBreakpointsDmc, breakpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add a count + Map delta = new HashMap(); + delta.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData breakpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_HIT event(s), received " + + getBreakpointEventCount(BP_HIT), getBreakpointEventCount(BP_HIT) == 1); + assertTrue("BreakpointService problem: breakpoint mismatch", + fBreakpointRef == breakpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_WriteWatchpoint + // Set a write watchpoint and go. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_WriteWatchpoint() throws Throwable { + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_HIT), getBreakpointEventCount(WP_HIT) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_ReadWatchpoint + // Set a read watchpoint and go. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_ReadWatchpoint() throws Throwable { + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(READ_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_HIT), getBreakpointEventCount(WP_HIT) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_AccessWatchpoint + // Set an access watchpoint and go. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_AccessWatchpoint() throws Throwable { + + // Create an access watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_1); + watchpoint.put(READ_TAG, true); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_HIT), getBreakpointEventCount(WP_HIT) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int i = evaluateExpression(frameDmc, "i").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", i == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_watchpointUpdateCount + // Set a write watchpoint, add an ignoreCount and go. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Test + public void breakpointHit_watchpointUpdateCount() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_3, true); + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_4, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_2); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add a count + Map delta = new HashMap(); + delta.put(IGNORE_COUNT_TAG, IGNORE_COUNT_2); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_HIT), getBreakpointEventCount(WP_HIT) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int j = evaluateExpression(frameDmc, "j").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", j == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_watchpointUpdateCondition + // Set a write watchpoint, add a condition and go. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Ignore + @Test + public void breakpointHit_watchpointUpdateCondition() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_3, true); + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_4, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_2); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Add a condition + Map delta = new HashMap(); + delta.put(CONDITION_TAG, CONDITION_3); + updateBreakpoint(ref, delta); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_UPDATED event(s), received " + + getBreakpointEventCount(BP_UPDATED), getBreakpointEventCount(BP_UPDATED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_HIT), getBreakpointEventCount(WP_HIT) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Verify that the condition is met + int j = evaluateExpression(frameDmc, "j").intValue(); + assertTrue("BreakpointEvent problem: breakpoint mismatch (wrong condition)", j == IGNORE_COUNT_2); + } + + // ------------------------------------------------------------------------ + // breakpointHit_WatchpointOutOfScope + // Set an access watchpoint and watch it go out of scope. + // Ensure that the correct event is received. + // ------------------------------------------------------------------------ + @Ignore + @Test + public void breakpointHit_WatchpointOutOfScope() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_1, true); + SyncUtil.SyncAddBreakpoint(SOURCE_FILE + ":" + LINE_NUMBER_3, true); + SyncUtil.SyncResumeUntilStopped(); + clearEventCounters(); + + // Create a write watchpoint + Map watchpoint = new HashMap(); + watchpoint.put(BREAKPOINT_TYPE_TAG, WATCHPOINT_TAG); + watchpoint.put(EXPRESSION_TAG, EXPRESSION_2); + watchpoint.put(READ_TAG, true); + watchpoint.put(WRITE_TAG, true); + + // Install the watchpoint + IBreakpointDMContext ref = insertBreakpoint(fBreakpointsDmc, watchpoint); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure that right BreakpointEvents were received + waitForBreakpointEvent(); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT_ADDED event(s), received " + + getBreakpointEventCount(BP_ADDED), getBreakpointEventCount(BP_ADDED) == 1); + clearEventCounters(); + + // Run until the breakpoint is hit and the event generated + SyncUtil.SyncResumeUntilStopped(); + + // Ensure the correct BreakpointEvent was received + waitForBreakpointEvent(); + MIBreakpointDMData watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected " + 1 + " BREAKPOINT event(s), received " + + fBreakpointEventCount, fBreakpointEventCount == 1); + assertTrue("BreakpointEvent problem: expected " + 1 + " WATCHPOINT_HIT event(s), received " + + getBreakpointEventCount(WP_OOS), getBreakpointEventCount(WP_OOS) == 1); + assertTrue("BreakpointService problem: watchpoint mismatch", + fBreakpointRef == watchpoint1.getNumber()); + clearEventCounters(); + + // Ensure the watchpoint is gone + getBreakpoints(fBreakpointsDmc); + watchpoint1 = (MIBreakpointDMData) getBreakpoint(ref); + assertTrue("BreakpointEvent problem: expected watchpoint to be deleted after going out of scope", + watchpoint1 == null); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIDisassemblyTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIDisassemblyTest.java new file mode 100644 index 00000000000..aad8719a4b4 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIDisassemblyTest.java @@ -0,0 +1,596 @@ +/******************************************************************************* + * Copyright (c) 2008 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: + * Ericsson - Initial Implementation + *******************************************************************************/ + +package org.eclipse.cdt.tests.dsf.gdb; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IInstruction; +import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; +import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.MIDisassembly; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.cdt.utils.Addr64; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +/* + * This is the Disassembly Service test suite. + * + * It is meant to be a regression suite to be executed automatically against + * the DSF nightly builds. + * + * It is also meant to be augmented with a proper test case(s) every time a + * feature is added or in the event (unlikely :-) that a bug is found in the + * Disassembly Service. + * + * Refer to the JUnit4 documentation for an explanation of the annotations. + */ + +@RunWith(BackgroundRunner.class) +public class MIDisassemblyTest extends BaseTestCase { + + private static final String FILE_NAME = "MemoryTestApp.cc"; + private static final int LINE_NUMBER = 35; + private static final String INVALID_FILE_NAME = "invalid_filename"; + + private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + private DsfSession fSession; + private DsfServicesTracker fServicesTracker; + private IDisassemblyDMContext fDisassemblyDmc; + private MIDisassembly fDisassembly; + private IExpressions fExpressionService; + + // ======================================================================== + // Housekeeping stuff + // ======================================================================== + + @BeforeClass + public static void testSuiteInitialization() { + // Select the binary to run the tests against + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/MemoryTestApp.exe"); + } + + @AfterClass + public static void testSuiteCleanup() { + } + + @Before + public void testCaseInitialization() { + fSession = getGDBLaunch().getSession(); + + // Get a reference to the memory service + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + assert(fServicesTracker != null); + + ICommandControlService commandControl = fServicesTracker.getService(ICommandControlService.class); + fDisassemblyDmc = (IDisassemblyDMContext)commandControl.getContext(); + assert(fDisassemblyDmc != null); + + fDisassembly = fServicesTracker.getService(MIDisassembly.class); + assert(fDisassembly != null); + + fExpressionService = fServicesTracker.getService(IExpressions.class); + assert(fExpressionService != null); + +// fSession.addServiceEventListener(MIDisassemblyTest.this, null); + } + + @After + public void testCaseCleanup() { + // Clear the references (not strictly necessary) +// fSession.removeServiceEventListener(MIDisassemblyTest.this); + fExpressionService = null; + fDisassembly = null; + fServicesTracker.dispose(); + fServicesTracker = null; + } + + // ======================================================================== + // Helper Functions + // ======================================================================== + + /* ------------------------------------------------------------------------ + * evaluateExpression + * ------------------------------------------------------------------------ + * Invokes the ExpressionService to evaluate an expression. In theory, we + * shouldn't rely on another service to test this one but we need a way to + * access a variable from the test application in order verify that the + * memory operations (read/write) are working properly. + * ------------------------------------------------------------------------ + * @param expression Expression to resolve + * @return Resolved expression + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private IAddress evaluateExpression(String expression) throws Throwable + { + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Create the expression and format contexts + final IExpressionDMContext expressionDMC = SyncUtil.SyncCreateExpression(frameDmc, expression); + final FormattedValueDMContext formattedValueDMC = SyncUtil.SyncGetFormattedValue(fExpressionService, expressionDMC, IFormattedValues.HEX_FORMAT); + + // Create the DataRequestMonitor which will store the operation result in the wait object + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Evaluate the expression (asynchronously) + fSession.getExecutor().submit(new Runnable() { + public void run() { + fExpressionService.getFormattedExpressionValue(formattedValueDMC, drm); + } + }); + + // Wait for completion + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Return the string formatted by the back-end + String result = ""; + Object returnInfo = fWait.getReturnInfo(); + if (returnInfo instanceof FormattedValueDMData) + result = ((FormattedValueDMData) returnInfo).getFormattedValue(); + return new Addr64(result); + } + + /* ------------------------------------------------------------------------ + * getInstruction + * ------------------------------------------------------------------------ + * Issues a disassembly request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getInstruction(dmc, start, end); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param start the start address (null == $pc) + * @param end the end address + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void getInstruction(final IDisassemblyDMContext dmc, + final BigInteger startAddress, final BigInteger endAddress) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fDisassembly.getInstructions(dmc, startAddress, endAddress, drm); + } + }); + } + + /* ------------------------------------------------------------------------ + * getInstruction + * ------------------------------------------------------------------------ + * Issues a disassembly request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getInstruction(dmc, start, end); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param fucntion the function + * @param linenum the line + * @param count the instruction count + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void getInstruction(final IDisassemblyDMContext dmc, + final String function, final int linenum, final int count) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fDisassembly.getInstructions(dmc, function, linenum, count, drm); + } + }); + } + + /* ------------------------------------------------------------------------ + * getMixedInstruction + * ------------------------------------------------------------------------ + * Issues a disassembly request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getInstruction(dmc, start, end); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param start the start address (null == $pc) + * @param end the end address + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void getMixedInstruction(final IDisassemblyDMContext dmc, + final BigInteger startAddress, final BigInteger endAddress) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fDisassembly.getMixedInstructions(dmc, startAddress, endAddress, drm); + } + }); + } + + + /* ------------------------------------------------------------------------ + * getMixedInstruction + * ------------------------------------------------------------------------ + * Issues a disassembly request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getInstruction(dmc, start, end); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param start the start address (null == $pc) + * @param end the end address + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void getMixedInstruction(final IDisassemblyDMContext dmc, + final String function, final int linenum, final int count) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fDisassembly.getMixedInstructions(dmc, function, linenum, count, drm); + } + }); + } + + // ======================================================================== + // Test Cases + // ------------------------------------------------------------------------ + // Templates: + // ------------------------------------------------------------------------ + // @ Test + // public void basicTest() { + // // First test to run + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @ Test(timeout=5000) + // public void timeoutTest() { + // // Second test to run, which will timeout if not finished on time + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @ Test(expected=FileNotFoundException.class) + // public void exceptionTest() throws FileNotFoundException { + // // Third test to run which expects an exception + // throw new FileNotFoundException("Just testing"); + // } + // ======================================================================== + + /////////////////////////////////////////////////////////////////////////// + // getMemory tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // readWithNullContext + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithNullContext() throws Throwable { + + // Setup call parameters + BigInteger startAddress = null; + BigInteger endAddress = null; + + // Perform the test + fWait.waitReset(); + getInstruction(null, startAddress, endAddress); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + String expected = "Unknown context type"; + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + } + + // ------------------------------------------------------------------------ + // readWithInvalidAddress + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithInvalidAddress() throws Throwable { + + // Setup call parameters + BigInteger startAddress = BigInteger.ZERO; + BigInteger endAddress = null; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, startAddress, endAddress); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + String expected = "Cannot access memory at address"; + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + } + + // ------------------------------------------------------------------------ + // readWithNullAddress + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithNullAddress() throws Throwable { + + // Setup call parameters + BigInteger startAddress = null; + BigInteger endAddress = null; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, startAddress, endAddress); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); + assertTrue("No instruction retrieved", result.length != 0); + } + + // ------------------------------------------------------------------------ + // readWithValidAddress + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithValidAddress() throws Throwable { + + // Setup call parameters + Addr64 main = (Addr64) evaluateExpression("&main"); + BigInteger startAddress = main.getValue(); + BigInteger endAddress = null; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, startAddress, endAddress); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); + assertTrue("No instruction retrieved", result.length != 0); + } + + // ------------------------------------------------------------------------ + // readWithInvalidFilename + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithValidFunction() throws Throwable { + + // Setup call parameters + String filename = INVALID_FILE_NAME; + int linenum = 1; + int count = -1; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, filename, linenum, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + String expected = "Invalid filename"; + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + } + + // ------------------------------------------------------------------------ + // readWithInvalidLineNumber + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithInvalidLineNumber() throws Throwable { + + // Setup call parameters + String filename = FILE_NAME; + int linenum = -1; + int count = -1; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, filename, linenum, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + String expected = "Invalid line number"; + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + } + + // ------------------------------------------------------------------------ + // readWithValidFilename + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithValidFilename() throws Throwable { + + // Setup call parameters + String filename = FILE_NAME; + int linenum = LINE_NUMBER; + int count = -1; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, filename, linenum, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); + assertTrue("No instruction retrieved", result.length != 0); + } + + // ------------------------------------------------------------------------ + // readWithLineCount + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readWithLineCount() throws Throwable { + + // Setup call parameters + String filename = FILE_NAME; + int linenum = LINE_NUMBER; + int count = 5; + + // Perform the test + fWait.waitReset(); + getInstruction(fDisassemblyDmc, filename, linenum, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IInstruction[] result = (IInstruction[]) fWait.getReturnInfo(); + assertTrue("Wrong number of instructions retrieved, expected " + count + ", got " + result.length, + result.length == count); + } + + // ------------------------------------------------------------------------ + // readMixedWithValidAddress + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readMixedWithValidAddress() throws Throwable { + + // Setup call parameters + Addr64 main = (Addr64) evaluateExpression("&main"); + BigInteger startAddress = main.getValue(); + BigInteger endAddress = null; + + // Perform the test + fWait.waitReset(); + getMixedInstruction(fDisassemblyDmc, startAddress, endAddress); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IMixedInstruction[] result = (IMixedInstruction[]) fWait.getReturnInfo(); + assertTrue("No instruction retrieved", result.length != 0); + } + + // ------------------------------------------------------------------------ + // readMixedWithLineCount + // ------------------------------------------------------------------------ + @Test(timeout=20000) + public void readMixedWithLineCount() throws Throwable { + + // Setup call parameters + String filename = FILE_NAME; + int linenum = LINE_NUMBER; + int count = 5; + + // Perform the test + fWait.waitReset(); + getMixedInstruction(fDisassemblyDmc, filename, linenum, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Verify the result + assertTrue(fWait.getMessage(), fWait.isOK()); + IMixedInstruction[] result = (IMixedInstruction[]) fWait.getReturnInfo(); + int total = 0; + for (IMixedInstruction mixed : result) { + IInstruction[] inst = mixed.getInstructions(); + total += inst.length; + } + assertTrue("Wrong number of instructions retrieved, expected " + count + ", got " + result.length, + total == count); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIMemoryTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIMemoryTest.java new file mode 100644 index 00000000000..1e0f5b40575 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIMemoryTest.java @@ -0,0 +1,1660 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson AB - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IMemory; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; +import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.debug.core.model.MemoryByte; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +/* + * This is the Memory Service test suite. + * + * It is meant to be a regression suite to be executed automatically against + * the DSF nightly builds. + * + * It is also meant to be augmented with a proper test case(s) every time a + * feature is added or in the event (unlikely :-) that a bug is found in the + * Memory Service. + * + * Refer to the JUnit4 documentation for an explanation of the annotations. + */ + +@RunWith(BackgroundRunner.class) +public class MIMemoryTest extends BaseTestCase { + + private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + private DsfSession fSession; + private DsfServicesTracker fServicesTracker; + private IMemoryDMContext fMemoryDmc; + private MIRunControl fRunControl; + private IMemory fMemoryService; + private IExpressions fExpressionService; + + // Keeps track of the MemoryChangedEvents + private final int BLOCK_SIZE = 256; + private IAddress fBaseAddress; + private Integer fMemoryChangedEventCount = new Integer(0); + private boolean[] fMemoryAddressesChanged = new boolean[BLOCK_SIZE]; + + // ======================================================================== + // Housekeeping stuff + // ======================================================================== + + @BeforeClass + public static void testSuiteInitialization() { + // Select the binary to run the tests against + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "data/launch/bin/MemoryTestApp.exe"); + } + + @AfterClass + public static void testSuiteCleanup() { + } + + @Before + public void testCaseInitialization() { + fSession = getGDBLaunch().getSession(); + + // Get a reference to the memory service + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + assert(fServicesTracker != null); + + ICommandControlService commandControl = fServicesTracker.getService(ICommandControlService.class); + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + fMemoryDmc = (IMemoryDMContext)procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + assert(fMemoryDmc != null); + + fRunControl = fServicesTracker.getService(MIRunControl.class); + assert(fRunControl != null); + + fMemoryService = fServicesTracker.getService(IMemory.class); + assert(fMemoryService != null); + + fExpressionService = fServicesTracker.getService(IExpressions.class); + assert(fExpressionService != null); + + fSession.addServiceEventListener(MIMemoryTest.this, null); + fBaseAddress = null; + clearEventCounters(); + } + + @After + public void testCaseCleanup() { + // Clear the references (not strictly necessary) + fBaseAddress = null; + fSession.removeServiceEventListener(MIMemoryTest.this); + fExpressionService = null; + fMemoryService = null; + fRunControl = null; + fServicesTracker.dispose(); + fServicesTracker = null; + clearEventCounters(); + } + + // ======================================================================== + // Helper Functions + // ======================================================================== + + /* ------------------------------------------------------------------------ + * eventDispatched + * ------------------------------------------------------------------------ + * Processes MemoryChangedEvents. + * First checks if the memory block base address was set so the individual + * test can control if it wants to verify the event(s). + * ------------------------------------------------------------------------ + * @param e The MemoryChangedEvent + * ------------------------------------------------------------------------ + */ + @DsfServiceEventHandler + public void eventDispatched(IMemoryChangedEvent e) { + synchronized(fMemoryChangedEventCount) { + fMemoryChangedEventCount++; + } + IAddress[] addresses = e.getAddresses(); + for (int i = 0; i < addresses.length; i++) { + int offset = Math.abs(addresses[i].distanceTo(fBaseAddress).intValue()); + if (offset < BLOCK_SIZE) + synchronized(fMemoryAddressesChanged) { + fMemoryAddressesChanged[offset] = true; + } + } + } + + // Clears the counters + private void clearEventCounters() { + synchronized(fMemoryChangedEventCount) { + fMemoryChangedEventCount = 0; + } + synchronized(fMemoryAddressesChanged) { + for (int i = 0; i < BLOCK_SIZE; i++) + fMemoryAddressesChanged[i] = false; + } + } + + // Returns the total number of events received + private int getEventCount() { + int count; + synchronized(fMemoryChangedEventCount) { + count = fMemoryChangedEventCount; + } + return count; + } + + // Returns the number of distinct addresses reported + private int getAddressCount() { + int count = 0; + synchronized(fMemoryAddressesChanged) { + for (int i = 0; i < BLOCK_SIZE; i++) + if (fMemoryAddressesChanged[i]) + count++; + } + return count; + } + + /* ------------------------------------------------------------------------ + * evaluateExpression + * ------------------------------------------------------------------------ + * Invokes the ExpressionService to evaluate an expression. In theory, we + * shouldn't rely on another service to test this one but we need a way to + * access a variable from the test application in order verify that the + * memory operations (read/write) are working properly. + * ------------------------------------------------------------------------ + * @param expression Expression to resolve + * @return Resolved expression + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private IAddress evaluateExpression(IDMContext ctx, String expression) throws Throwable + { + // Create the expression and format contexts + final IExpressionDMContext expressionDMC = SyncUtil.SyncCreateExpression(ctx, expression); + final FormattedValueDMContext formattedValueDMC = SyncUtil.SyncGetFormattedValue(fExpressionService, expressionDMC, IFormattedValues.HEX_FORMAT); + + // Create the DataRequestMonitor which will store the operation result in the wait object + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Evaluate the expression (asynchronously) + fSession.getExecutor().submit(new Runnable() { + public void run() { + fExpressionService.getFormattedExpressionValue(formattedValueDMC, drm); + } + }); + + // Wait for completion + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Return the string formatted by the back-end + String result = ""; + Object returnInfo = fWait.getReturnInfo(); + if (returnInfo instanceof FormattedValueDMData) + result = ((FormattedValueDMData) returnInfo).getFormattedValue(); + return new Addr64(result); + } + + /* ------------------------------------------------------------------------ + * readMemory + * ------------------------------------------------------------------------ + * Issues a memory read request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getMemory(dmc, address, offset, count); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param address the memory block address + * @param offset the offset in the buffer + * @param count the number of bytes to read + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void readMemory(final IMemoryDMContext dmc, final IAddress address, + final long offset, final int word_size, final int count) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fMemoryService.getMemory(dmc, address, offset, word_size, count, drm); + } + }); + } + + /* ------------------------------------------------------------------------ + * readMemoryByteAtOffset + * ------------------------------------------------------------------------ + * Issues a memory read request. The result is stored in fWait. + * ------------------------------------------------------------------------ + * Typical usage: + * getMemory(dmc, address, offset, count); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param address the memory block address + * @param offset the offset in the buffer + * @param count the number of bytes to read + * @param result the expected byte + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void readMemoryByteAtOffset(final IMemoryDMContext dmc, final IAddress address, + final long offset, final int word_size, final int count, final MemoryByte[] result) + throws InterruptedException + { + // Set the Data Request Monitor + final DataRequestMonitor drm = + new DataRequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + result[(int) offset] = getData()[0]; + } + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fMemoryService.getMemory(dmc, address, offset, word_size, count, drm); + } + }); + } + + /* ------------------------------------------------------------------------ + * writeMemory + * ------------------------------------------------------------------------ + * Issues a memory write request. + * ------------------------------------------------------------------------ + * Typical usage: + * writeMemory(dmc, address, offset, count, buffer); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param address the memory block address (could be an expression) + * @param offset the offset from address + * @param count the number of bytes to write + * @param buffer the byte buffer to write from + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void writeMemory(final IMemoryDMContext dmc, final IAddress address, + final long offset, final int word_size, final int count, final byte[] buffer) + throws InterruptedException + { + // Set the Data Request Monitor + final RequestMonitor rm = + new RequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the get memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fMemoryService.setMemory(dmc, address, offset, word_size, count, buffer, rm); + } + }); + } + + /* ------------------------------------------------------------------------ + * fillMemory + * ------------------------------------------------------------------------ + * Issues a memory write request. + * ------------------------------------------------------------------------ + * Typical usage: + * writeMemory(dmc, address, offset, count, buffer); + * fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + * assertTrue(fWait.getMessage(), fWait.isOK()); + * ------------------------------------------------------------------------ + * @param dmc the data model context + * @param address the memory block address (could be an expression) + * @param offset the offset from address + * @param count the number of bytes to write + * @param pattern the byte pattern to write + * @throws InterruptedException + * ------------------------------------------------------------------------ + */ + private void fillMemory(final IMemoryDMContext dmc, final IAddress address, + final long offset, final int word_size, final int count, final byte[] pattern) + throws InterruptedException + { + // Set the Data Request Monitor + final RequestMonitor rm = + new RequestMonitor(fSession.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }; + + // Issue the fill memory request + fSession.getExecutor().submit(new Runnable() { + public void run() { + fMemoryService.fillMemory(dmc, address, offset, word_size, count, pattern, rm); + } + }); + } + + // ======================================================================== + // Test Cases + // ------------------------------------------------------------------------ + // Templates: + // ------------------------------------------------------------------------ + // @ Test + // public void basicTest() { + // // First test to run + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @ Test(timeout=5000) + // public void timeoutTest() { + // // Second test to run, which will timeout if not finished on time + // assertTrue("", true); + // } + // ------------------------------------------------------------------------ + // @ Test(expected=FileNotFoundException.class) + // public void exceptionTest() throws FileNotFoundException { + // // Third test to run which expects an exception + // throw new FileNotFoundException("Just testing"); + // } + // ======================================================================== + + /////////////////////////////////////////////////////////////////////////// + // getMemory tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // readWithNullContext + // Test that a null context is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void readWithNullContext() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + IMemoryDMContext dmc = null; + long offset = 0; + int word_size = 1; + int count = 1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Unknown context type"; + fWait.waitReset(); + readMemory(dmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readWithInvalidAddress + // Test that an invalid address is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void readWithInvalidAddress() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + fBaseAddress = new Addr64("0"); + + // Perform the test + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + // Ensure that we receive a block of invalid memory bytes + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value: expected '-1, 0', received '" + buffer[0].getValue() + ", " + buffer[0].getFlags() + "'", + (buffer[0].getValue() == (byte) 0) && (buffer[0].getFlags() == (byte) 0)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readWithInvalidWordSize + // Test that an invalid word size is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void readWithInvalidWordSize() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int count = -1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Word size not supported (!= 1)"; + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, 0, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, 2, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readWithInvalidCount + // Test that an invalid count is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void readWithInvalidCount() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = -1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Invalid word count (< 0)"; + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readCharVaryingBaseAddress + // Test the reading of individual bytes by varying the base address + // ------------------------------------------------------------------------ + @Test + public void readCharVaryingBaseAddress() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Verify that all bytes are '0' + for (int i = 0; i < BLOCK_SIZE; i++) { + IAddress address = fBaseAddress.add(i); + fWait.waitReset(); + readMemory(fMemoryDmc, address, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + buffer[0].getValue() + "'", + (buffer[0].getValue() == (byte) 0)); + } + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:setBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Verify that all bytes are set + for (int i = 0; i < BLOCK_SIZE; i++) { + IAddress address = fBaseAddress.add(i); + fWait.waitReset(); + readMemory(fMemoryDmc, address, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong value read at offset " + i + ": expected '" + i + "', received '" + buffer[0].getValue() + "'", + (buffer[0].getValue() == (byte) i)); + } + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readCharVaryingOffset + // Test the reading of individual bytes by varying the offset + // ------------------------------------------------------------------------ + @Test + public void readCharVaryingOffset() throws Throwable { + + // Run to the point where the array is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + int word_size = 1; + int count = 1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Verify that all bytes are '0' + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + offset + ": expected '" + 0 + "', received '" + buffer[0].getValue() + "'", + (buffer[0].getValue() == (byte) 0)); + } + + // Run to the point where the array is set + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:setBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Verify that all bytes are set + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + offset + ": expected '" + offset + "', received '" + buffer[0].getValue() + "'", + (buffer[0].getValue() == (byte) offset)); + } + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // readCharArray + // Test the reading of a byte array + // ------------------------------------------------------------------------ + @Test + public void readCharArray() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = BLOCK_SIZE; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Get the memory block + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are '0' + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) 0)); + } + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:setBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Get the memory block + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are '0' + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) i)); + } + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + /////////////////////////////////////////////////////////////////////////// + // setMemory tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // writeWithNullContext + // Test that a null context is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void writeWithNullContext() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + byte[] buffer = new byte[count]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Unknown context type"; + fWait.waitReset(); + writeMemory(null, fBaseAddress, offset, word_size, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writeWithInvalidAddress + // Test that an invalid address is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void writeWithInvalidAddress() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + byte[] buffer = new byte[count]; + fBaseAddress = new Addr64("0"); + + // Perform the test + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + String expected = "Cannot access memory at address"; // Error msg returned by gdb + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writeWithInvalidWordSize + // Test that an invalid word size is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void writeWithInvalidWordSize() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int count = -1; + byte[] buffer = new byte[1]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Word size not supported (!= 1)"; + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, 0, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, 2, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writeWithInvalidCount + // Test that an invalid count is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void writeWithInvalidCount() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = -1; + byte[] buffer = new byte[1]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Invalid word count (< 0)"; + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writeWithInvalidBuffer + // Test that the buffer contains at least count bytes + // ------------------------------------------------------------------------ + @Test + public void writeWithInvalidBuffer() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 10; + byte[] buffer = new byte[count - 1]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Buffer too short"; + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writeCharVaryingAddress + // Test the writing of individual bytes by varying the base address + // ------------------------------------------------------------------------ + @Test + public void writeCharVaryingAddress() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = BLOCK_SIZE; + byte[] buffer = new byte[count]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + for (int i = 0; i < count; i++) { + + // [1] Ensure that the memory byte = 0 + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, i, word_size, 1); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[0].getValue() + "'", + (block[0].getValue() == (byte) 0)); + + // [2] Write a byte value (count - i - 1) + IAddress address = fBaseAddress.add(i); + fWait.waitReset(); + byte expected = (byte) (count - i - 1); + buffer[0] = expected; + writeMemory(fMemoryDmc, address, offset, word_size, 1, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // [3] Verify that the correct MemoryChangedEvent was sent + // (I hardly believe there are no synchronization problems here...) + assertTrue("MemoryChangedEvent problem at offset " + i + ": expected " + (i + 1) + " events, received " + getEventCount(), + getEventCount() == (i + 1)); + assertTrue("MemoryChangedEvent problem at offset " + i, fMemoryAddressesChanged[i]); + + // [4] Verify that the memory byte was written correctly + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, i, word_size, 1); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + i + ": expected '" + expected + "', received '" + block[0].getValue() + "'", + (block[0].getValue() == expected)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " events, received " + getEventCount(), + getEventCount() == BLOCK_SIZE); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getEventCount() == BLOCK_SIZE); + } + + // ------------------------------------------------------------------------ + // writeCharVaryingOffset + // Test the writing of individual bytes by varying the base address + // ------------------------------------------------------------------------ + @Test + public void writeCharVaryingOffset() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + int word_size = 1; + int count = BLOCK_SIZE; + byte[] buffer = new byte[count]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + for (int offset = 0; offset < count; offset++) { + + // [1] Ensure that the memory byte = 0 + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, 1); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + offset + ": expected '" + 0 + "', received '" + block[0].getValue() + "'", + (block[0].getValue() == (byte) 0)); + + // [2] Write a byte value (count - offset - 1) + fWait.waitReset(); + byte expected = (byte) (count - offset - 1); + buffer[0] = expected; + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, 1, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // [3] Verify that the correct MemoryChangedEvent was sent + assertTrue("MemoryChangedEvent problem at offset " + offset + ": expected " + (offset + 1) + " events, received " + getEventCount(), + getEventCount() == (offset + 1)); + assertTrue("MemoryChangedEvent problem at offset " + offset, fMemoryAddressesChanged[offset]); + + // [4] Verify that the memory byte was written correctly + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, 1); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + assertTrue("Wrong value read at offset " + offset + ": expected '" + expected + "', received '" + block[0].getValue() + "'", + (block[0].getValue() == expected)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " events, received " + getEventCount(), + getEventCount() == BLOCK_SIZE); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + } + + // ------------------------------------------------------------------------ + // writeCharArray + // Test the writing of a byte array + // ------------------------------------------------------------------------ + @Test + public void writeCharArray() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = BLOCK_SIZE; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Make sure that the memory block is zeroed + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[i].getValue() + "'", + (block[i].getValue() == (byte) 0)); + } + + // Write an initialized memory block + byte[] buffer = new byte[count]; + for (int i = 0; i < count; i++) { + buffer[i] = (byte) i; + } + fWait.waitReset(); + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Make sure that the memory block is initialized + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[i].getValue() + "'", + (block[i].getValue() == (byte) i)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + 1 + " event, received " + getEventCount(), + getEventCount() == 1); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + } + + /////////////////////////////////////////////////////////////////////////// + // fillMemory tests + /////////////////////////////////////////////////////////////////////////// + + // ------------------------------------------------------------------------ + // fillWithNullContext + // Test that a null context is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void fillWithNullContext() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + byte[] pattern = new byte[count]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Unknown context type"; + fWait.waitReset(); + fillMemory(null, fBaseAddress, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // fillWithInvalidAddress + // Test that an invalid address is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void fillWithInvalidAddress() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + SyncUtil.SyncStep(StepType.STEP_RETURN); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + byte[] pattern = new byte[count]; + fBaseAddress = new Addr64("0"); + + // Perform the test + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + String expected = "Cannot access memory at address"; // Error msg returned by gdb + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // fillWithInvalidWordSize + // Test that an invalid word size is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void fillWithInvalidWordSize() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int count = 1; + byte[] pattern = new byte[1]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Word size not supported (!= 1)"; + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, 0, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, 2, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // fillWithInvalidCount + // Test that an invalid count is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void fillWithInvalidCount() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = -1; + byte[] pattern = new byte[1]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Invalid repeat count (< 0)"; + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // fillWithInvalidPattern + // Test that an empty pattern is caught and generates an error + // ------------------------------------------------------------------------ + @Test + public void fillWithInvalidPattern() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + byte[] pattern = new byte[0]; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Perform the test + String expected = "Empty pattern"; + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertFalse(fWait.getMessage(), fWait.isOK()); + assertTrue("Wrong error message: expected '" + expected + "', received '" + fWait.getMessage() + "'", + fWait.getMessage().contains(expected)); + + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + + // ------------------------------------------------------------------------ + // writePatternVaryingAddress + // Test the writing of the pattern by varying the base address + // ------------------------------------------------------------------------ + @Test + public void writePatternVaryingAddress() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 1; + int length = 4; + byte[] pattern = new byte[length]; + for (int i = 0; i < length; i++) pattern[i] = (byte) i; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Ensure that the memory is zeroed + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < (count * length); i++) + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[i].getValue() + "'", + (block[i].getValue() == (byte) 0)); + + for (int i = 0; i < BLOCK_SIZE; i += length) { + IAddress address = fBaseAddress.add(i); + fWait.waitReset(); + fillMemory(fMemoryDmc, address, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + } + + // Verify that the memory is correctly set + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, 0, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < count; i++) + for (int j = 0; j < length; j++) { + int index = i * length + j; + assertTrue("Wrong value read at offset " + index + ": expected '" + j + "', received '" + block[index].getValue() + "'", + (block[index].getValue() == (byte) j)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + (BLOCK_SIZE / length) + " events, received " + getEventCount(), + getEventCount() == (BLOCK_SIZE / length)); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + } + + // ------------------------------------------------------------------------ + // writePatternVaryingOffset + // Test the writing of the pattern by varying the base address + // ------------------------------------------------------------------------ + @Test + public void writePatternVaryingOffset() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 64; + int length = 4; + byte[] pattern = new byte[length]; + for (int i = 0; i < length; i++) pattern[i] = (byte) i; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Ensure that the memory is zeroed + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < (count * length); i++) + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[i].getValue() + "'", + (block[i].getValue() == (byte) 0)); + + for (int i = 0; i < (BLOCK_SIZE / length); i++) { + offset = i * length; + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, word_size, 1, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + } + + // Verify that the memory is correctly set + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, 0, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < count; i++) + for (int j = 0; j < length; j++) { + int index = i * length + j; + assertTrue("Wrong value read at offset " + index + ": expected '" + j + "', received '" + block[index].getValue() + "'", + (block[index].getValue() == (byte) j)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + (BLOCK_SIZE / length) + " events, received " + getEventCount(), + getEventCount() == (BLOCK_SIZE / length)); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + } + + // ------------------------------------------------------------------------ + // writePatternCountTimes + // Test the writing of the pattern [count] times + // ------------------------------------------------------------------------ + @Test + public void writePatternCountTimes() throws Throwable { + + // Run to the point where the variable is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = 64; + int length = 4; + byte[] pattern = new byte[length]; + for (int i = 0; i < length; i++) pattern[i] = (byte) i; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Ensure that the memory is zeroed + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < (count * length); i++) + assertTrue("Wrong value read at offset " + i + ": expected '" + 0 + "', received '" + block[i].getValue() + "'", + (block[i].getValue() == (byte) 0)); + + // Write the pattern [count] times + fWait.waitReset(); + fillMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, pattern); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Verify that the memory is correctly set + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count * length); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + block = (MemoryByte[]) fWait.getReturnInfo(); + for (int i = 0; i < count; i++) + for (int j = 0; j < length; j++) { + int index = i * length + j; + assertTrue("Wrong value read at offset " + index + ": expected '" + j + "', received '" + block[index].getValue() + "'", + (block[index].getValue() == (byte) j)); + } + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + 1 + " events, received " + getEventCount(), + getEventCount() == 1); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + } + + // ------------------------------------------------------------------------ + // asynchronousReadWrite + // Test the asynchronous reading/writing of individual bytes (varying offset) + // ------------------------------------------------------------------------ + @Test + public void asynchronousReadWrite() throws Throwable { + + // Run to the point where the array is zeroed + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:zeroBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + int word_size = 1; + int count = 1; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Verify asynchronously that all bytes are '0' + fWait.waitReset(); + MemoryByte[] buffer = new MemoryByte[BLOCK_SIZE]; + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + fWait.increment(); + readMemoryByteAtOffset(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + } + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + assertTrue("Wrong value read at offset " + offset + ": expected '" + 0 + "', received '" + buffer[offset].getValue() + "'", + (buffer[offset].getValue() == (byte) 0)); + } + + // Write asynchronously + fWait.waitReset(); + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + fWait.increment(); + byte[] block = new byte[count]; + block[0] = (byte) offset; + writeMemory(fMemoryDmc, fBaseAddress, offset, word_size, count, block); + } + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + // Ensure the MemoryChangedEvent events were received + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " events, received " + getEventCount(), + getEventCount() == BLOCK_SIZE); + assertTrue("MemoryChangedEvent problem: expected " + BLOCK_SIZE + " distinct addresses, received " + getAddressCount(), + getAddressCount() == BLOCK_SIZE); + + // Verify asynchronously that all bytes are set + fWait.waitReset(); + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + fWait.increment(); + readMemoryByteAtOffset(fMemoryDmc, fBaseAddress, offset, word_size, count, buffer); + } + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + for (int offset = 0; offset < BLOCK_SIZE; offset++) { + assertTrue("Wrong value read at offset " + offset + ": expected '" + offset + "', received '" + buffer[offset].getValue() + "'", + (buffer[offset].getValue() == (byte) offset)); + } + } + + // ------------------------------------------------------------------------ + // memoryCacheRead + // Get a bunch of blocks to exercise the memory cache + // ------------------------------------------------------------------------ + @Test + public void memoryCacheRead() throws Throwable { + + // Run to the point where the variable is initialized + SyncUtil.SyncAddBreakpoint("MemoryTestApp.cc:setBlocks", true); + SyncUtil.SyncResumeUntilStopped(); + MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_RETURN); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + + // Setup call parameters + long offset = 0; + int word_size = 1; + int count = BLOCK_SIZE; + fBaseAddress = evaluateExpression(frameDmc, "&charBlock"); + + // Get the 'reference' memory block + fWait.waitReset(); + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + MemoryByte[] buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are set to 'i' + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) i)); + } + + // Clear the cache + SyncUtil.SyncStep(StepType.STEP_OVER); + + // Get a first block + fWait.waitReset(); + offset = 0; + count = 64; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a second block + fWait.waitReset(); + offset = 128; + count = 64; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a third block between the first 2 + fWait.waitReset(); + offset = 80; + count = 32; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a block that is contiguous to the end of an existing block + fWait.waitReset(); + offset = 192; + count = 32; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a block that ends beyond an existing block + fWait.waitReset(); + offset = 192; + count = 64; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a block that will require 2 reads (for the gaps between blocks 1-2 and 2-3) + fWait.waitReset(); + offset = 32; + count = 128; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get a block that involves multiple cached blocks + fWait.waitReset(); + offset = 48; + count = 192; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are set to 'i' + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + + // Get the whole block + fWait.waitReset(); + offset = 0; + count = BLOCK_SIZE; + readMemory(fMemoryDmc, fBaseAddress, offset, word_size, count); + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + buffer = (MemoryByte[]) fWait.getReturnInfo(); + + // Verify that all bytes are correctly set + for (int i = 0; i < count; i++) { + assertTrue("Wrong value read at offset " + i + ": expected '" + offset + i + "', received '" + buffer[i].getValue() + "'", + (buffer[i].getValue() == (byte) (offset + i))); + } + // Ensure no MemoryChangedEvent event was received + assertTrue("MemoryChangedEvent problem: expected " + 0 + ", received " + getEventCount(), getEventCount() == 0); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRegistersTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRegistersTest.java new file mode 100644 index 00000000000..b133715d1b7 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRegistersTest.java @@ -0,0 +1,418 @@ +package org.eclipse.cdt.tests.dsf.gdb; + + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.CompositeDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IRegisters; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext; +import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BackgroundRunner; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(BackgroundRunner.class) + +public class MIRegistersTest extends BaseTestCase { + + final int NUMBER_OF_REGISTERS = 50; + /* + * Path to executable + */ + private static final String EXEC_PATH = "data/launch/bin/"; + /* + * Name of the executable + */ + private static final String EXEC_NAME = "MultiThread.exe"; + private static final String SRC_NAME = "MultiThread.cc"; + + // Will be used to wait for asynchronous call to complete + //private final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + private DsfSession fSession; + private DsfServicesTracker fServicesTracker; + private IContainerDMContext fContainerDmc; + private IRegisters fRegService; + + @Before + public void init() throws Exception { + fSession = getGDBLaunch().getSession(); + // We obtain the services we need after the new + // launch has been performed + fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId()); + + ICommandControlService commandControl = fServicesTracker.getService(ICommandControlService.class); + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(commandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + + fRegService = fServicesTracker.getService(IRegisters.class); + } + + @BeforeClass + public static void beforeClassMethod() { + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + EXEC_PATH + EXEC_NAME); + } + + + @After + public void tearDown() { + fServicesTracker.dispose(); + fRegService = null; + } + + /* + * This is a common support method which gets the Register Group Information + * and verifies it. + */ + private IRegisterGroupDMContext getRegisterGroup() throws Throwable { + final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + + final DataRequestMonitor regGroupDone = + new DataRequestMonitor(fRegService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + + fWait.waitFinished(getStatus()); + } + }; + + fRegService.getExecutor().submit(new Runnable() { + public void run() { + fRegService.getRegisterGroups(fContainerDmc, regGroupDone); + } + }); + + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + IRegisterGroupDMContext[] regGroupsDMCs = (IRegisterGroupDMContext[])fWait.getReturnInfo(); + assertTrue("There was more than one register group (" + regGroupsDMCs.length + ")", //$NON-NLS-1$ + regGroupsDMCs.length == 1 ); + fWait.waitReset(); + + return(regGroupsDMCs[0]); + } + + /* + * This is a common support method which gets the Registers names. + */ + + private IRegisterDMContext[] getRegisters(final IFrameDMContext frameDmc) throws Throwable { + final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + final IRegisterGroupDMContext regGroupsDMC = getRegisterGroup(); + + fRegService.getExecutor().submit(new Runnable() { + public void run() { + fRegService.getRegisters( + new CompositeDMContext(new IDMContext[] { regGroupsDMC, frameDmc} ), + new DataRequestMonitor(fRegService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + fWait.setReturnInfo(getData()); + } + + fWait.waitFinished(getStatus()); + } + }); + } + }); + + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(fWait.getMessage(), fWait.isOK()); + + IRegisterDMContext[] regContexts = (IRegisterDMContext[]) fWait.getReturnInfo(); + + fWait.waitReset(); + + assertTrue("The number of registers should have been " + NUMBER_OF_REGISTERS + + " instead of " + regContexts.length, + regContexts.length == NUMBER_OF_REGISTERS); + + return(regContexts); + } + + /************************************************************************* + * + * The tests for the register service. + * + *************************************************************************/ + + @Test + public void getRegisterGroups() throws Throwable { + final IRegisterGroupDMContext regGroupsDMC = getRegisterGroup(); + + Query query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + fRegService.getRegisterGroupData(regGroupsDMC, rm); + } + }; + fSession.getExecutor().execute(query); + + IRegisterGroupDMData data = query.get(); + + assertTrue("The name of the main group should be: General Registers instead of: " + + data.getName(), + data.getName().equals("General Registers")); + } + + @Test + public void getRegistersLength() throws Throwable { + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); + assertTrue("The number of registers should have been " + NUMBER_OF_REGISTERS + + " instead of " + regDMCs.length, + regDMCs.length == NUMBER_OF_REGISTERS); + + } + + @Test + public void getRegisters() throws Throwable { + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); + List regNames = Arrays.asList("eax","ecx","edx","ebx","esp","ebp","esi","edi","eip","eflags","cs","ss","ds","es","fs","gs","st0","st1","st2","st3","st4","st5","st6","st7","fctrl","fstat","ftag","fiseg","fioff","foseg","fooff","fop","xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7","mxcsr","orig_eax","mm0","mm1","mm2","mm3","mm4","mm5","mm6","mm7"); + + Query query = new Query() { + @Override + protected void execute(DataRequestMonitor rm) { + final IRegisterDMData[] datas = new IRegisterDMData[regDMCs.length]; + rm.setData(datas); + final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm); + countingRm.setDoneCount(regDMCs.length); + for (int i = 0; i < regDMCs.length; i++) { + final int index = i; + fRegService.getRegisterData( + regDMCs[index], + new DataRequestMonitor(ImmediateExecutor.getInstance(), countingRm) { + @Override + protected void handleSuccess() { + datas[index] = getData(); + countingRm.done(); + } + }); + } + + } + }; + + fSession.getExecutor().execute(query); + + IRegisterDMData[] datas = query.get(); + + for(IRegisterDMData data: datas){ + String regName = data.getName(); + Assert.assertFalse("GDB does not support register name: " + regName, !regNames.contains(regName)); + } + } + + private String getModelDataForRegisterDataValue(IFrameDMContext frameDmc, String format, int regNo) throws Throwable { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); + final FormattedValueDMContext valueDmc = fRegService.getFormattedValueContext(regDMCs[regNo], format); + + final DataRequestMonitor regRm = + new DataRequestMonitor(fRegService.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }; + + fRegService.getExecutor().submit(new Runnable() { + public void run() { + fRegService.getFormattedExpressionValue(valueDmc, regRm); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + + FormattedValueDMData data = (FormattedValueDMData)wait.getReturnInfo(); + return data.getFormattedValue(); + } + + + private static String REGISTER_VALUE = ""; + @Test + public void getModelDataForRegisterDataValueInDifferentNumberFormats() throws Throwable { + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, 0); + REGISTER_VALUE = val; + assertTrue("Register Value is not in NATURAL format " , Integer.parseInt(val)== Integer.parseInt(REGISTER_VALUE)); + + val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, 0); + assertTrue("Register Value is not in HEX_FORMAT " ,val.startsWith("0x")); + + val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, 0); + Assert.assertEquals("Register Value is not in BINARY_FORMAT ", val, Integer.toBinaryString(Integer.parseInt(REGISTER_VALUE))); + + val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.DECIMAL_FORMAT , 0); + Assert.assertEquals("Register Value is not in DECIMAL_FORMAT", Integer.parseInt(val), Integer.parseInt(REGISTER_VALUE)); + + val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, 0); + assertTrue("Register Value is not in OCTAL_FORMAT " ,val.startsWith("0")); + } + + + @Test + public void compareRegisterForMultipleExecutionContexts() throws Throwable { + MIStoppedEvent stoppedEvent = SyncUtil.SyncRunToLine(SRC_NAME, "22"); + IContainerDMContext containerDmc = DMContexts.getAncestorOfType(stoppedEvent.getDMContext(), IContainerDMContext.class); + + // Get execution context to thread 2 + IExecutionDMContext execDmc = SyncUtil.SyncCreateExecutionContext(containerDmc, 2); + IFrameDMContext frameDmc2 = SyncUtil.SyncGetStackFrame(execDmc, 0); + + String thread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0); + String thread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1); + String thread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2); + String thread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3); + String thread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4); + String thread2RegVal5 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5); + + // Get execution context to thread 1 + execDmc = SyncUtil.SyncCreateExecutionContext(containerDmc, 2); + IFrameDMContext frameDmc1 = SyncUtil.SyncGetStackFrame(execDmc, 0); + getModelDataForRegisterDataValue(frameDmc1, IFormattedValues.NATURAL_FORMAT, 0); + + // Re-set the execution context to 2 and Fetch from the Cache + String dupliThread2RegVal0 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 0); + String dupliThread2RegVal1 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 1); + String dupliThread2RegVal2 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 2); + String dupliThread2RegVal3 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 3); + String dupliThread2RegVal4 = getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 4); + String dupliThread2RegVal5= getModelDataForRegisterDataValue(frameDmc2, IFormattedValues.NATURAL_FORMAT, 5); + + // If Values not equal , then context haven't been re-set properly + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal0.equals(dupliThread2RegVal0)); + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal1.equals(dupliThread2RegVal1)); + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal2.equals(dupliThread2RegVal2)); + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal3.equals(dupliThread2RegVal3)); + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal4.equals(dupliThread2RegVal4)); + assertTrue("Multiple context not working. Execution Context is not reset to 2", thread2RegVal5.equals(dupliThread2RegVal5)); + + } + + private void writeRegister(IFrameDMContext frameDmc, final int regIndex, final String regValue, final String formatId) throws Throwable { + final AsyncCompletionWaitor fWait = new AsyncCompletionWaitor(); + + final IRegisterDMContext[] regDMCs = getRegisters(frameDmc); + + fRegService.getExecutor().submit(new Runnable() { + public void run() { + fRegService.writeRegister( + regDMCs[regIndex], + regValue, formatId, + new RequestMonitor(fRegService.getExecutor(), null) { + @Override + protected void handleCompleted() { + fWait.waitFinished(getStatus()); + } + }); + + } + }); + + fWait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + fWait.waitReset(); + } + + + @Test + public void writeRegisterNaturalFormat() throws Throwable{ + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + String regValue = "10"; + int regIndex = 3; + writeRegister(frameDmc, 3, regValue, IFormattedValues.NATURAL_FORMAT); + String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.NATURAL_FORMAT, regIndex); + assertTrue("Failed writing register. New value should have been " + regValue, regValue.equals(val)); + } + + @Test + public void writeRegisterHEXFormat() throws Throwable{ + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + String regValue = "0x10"; + int regIndex = 3; + writeRegister(frameDmc, 3, regValue, IFormattedValues.HEX_FORMAT); + String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.HEX_FORMAT, regIndex); + assertTrue("Failed writing register. New value should have been " + regValue, regValue.equals(val)); + } + + @Test + @Ignore + public void writeRegisterBinaryFormat() throws Throwable{ + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + //String regValue = "0100101001"; + String regValue = "10"; + int regIndex = 3; + writeRegister(frameDmc, 3, regValue, IFormattedValues.BINARY_FORMAT); + String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.BINARY_FORMAT, regIndex); + assertTrue("Failed writing register. New value should have been " + regValue + " instead of " + val, regValue.equals(val)); + } + + @Test + public void writeRegisterOctalFormat() throws Throwable{ + MIStoppedEvent stoppedEvent = getInitialStoppedEvent(); + IFrameDMContext frameDmc = SyncUtil.SyncGetStackFrame(stoppedEvent.getDMContext(), 0); + //String regValue = "10"; + String regValue = "012"; + int regIndex = 3; + writeRegister(frameDmc, 3, regValue, IFormattedValues.OCTAL_FORMAT); + String val = getModelDataForRegisterDataValue(frameDmc, IFormattedValues.OCTAL_FORMAT, regIndex); + assertTrue("Failed writing register. New value should have been " + regValue + "instead of " + val, regValue.equals(val)); + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRunControlTest.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRunControlTest.java new file mode 100644 index 00000000000..82548f06da8 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/MIRunControlTest.java @@ -0,0 +1,568 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson AB - Initial implementation of Test cases + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb; + + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.tests.dsf.gdb.framework.AsyncCompletionWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.BaseTestCase; +import org.eclipse.cdt.tests.dsf.gdb.framework.ServiceEventWaitor; +import org.eclipse.cdt.tests.dsf.gdb.framework.SyncUtil; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.core.runtime.IStatus; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + + +/* + * Tests MIRunControl class for Multi-threaded application. + */ +public class MIRunControlTest extends BaseTestCase { + + private DsfServicesTracker fServicesTracker; + + private IGDBControl fGDBCtrl; + private MIRunControl fRunCtrl; + private IMIProcesses fProcService; + + private IContainerDMContext fContainerDmc; + + /* + * Path to executable + */ + private static final String EXEC_PATH = "data/launch/bin/"; + /* + * Name of the executable + */ + private static final String EXEC_NAME = "MultiThread.exe"; + private static final String SOURCE_NAME = "MultiThread.cc"; + + @Before + public void init() throws Exception { + fServicesTracker = + new DsfServicesTracker(TestsPlugin.getBundleContext(), + getGDBLaunch().getSession().getId()); + fGDBCtrl = fServicesTracker.getService(IGDBControl.class); + + IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(fGDBCtrl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + fContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + fRunCtrl = fServicesTracker.getService(MIRunControl.class); + fProcService = fServicesTracker.getService(IMIProcesses.class); + } + + + @After + public void tearDown() { + fServicesTracker.dispose(); + } + + @BeforeClass + public static void beforeClassMethod() { + setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, + EXEC_PATH + EXEC_NAME); + } + + /* + * For Multi-threaded application - In case of one thread, Thread id should start with 1. + */ + @Test + public void getExecutionContext() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + /* + * Create a request monitor + */ + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + + /* + * Test getExecutionContexts() when only one thread exist. + */ + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + fRunCtrl.getExecutionContexts(containerDmc, rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + /* + * Get data from the Request Monitor + */ + IExecutionDMContext[] ctxts = (IExecutionDMContext[])wait.getReturnInfo(); + + // Context can not be null + if(ctxts == null) + Assert.fail("Context returned is null. At least one context should have been returned"); + else { + // Only one Context in this case + if(ctxts.length > 1) + Assert.fail("Context returned can not be more than 1. This test case is for single context application."); + + IMIExecutionDMContext dmc = (IMIExecutionDMContext)ctxts[0]; + // Thread id for the main thread should be one + Assert.assertEquals(1, dmc.getThreadId()); + } + wait.waitReset(); + } + + + /* + * Get Execution DMCs for a valid container DMC + * Testing for two execution DMC with id 1 & 2 + */ + @Test + public void getExecutionContexts() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + /* + * Create a request monitor + */ + final DataRequestMonitor rmExecutionCtxts = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + + // Prepare a waiter to make sure we have received the thread started event + final ServiceEventWaitor startedEventWaitor = + new ServiceEventWaitor( + getGDBLaunch().getSession(), + IStartedDMEvent.class); + + try{ + /* + * Run till line for 2 threads to be created + */ + SyncUtil.SyncRunToLine(fContainerDmc, SOURCE_NAME, "22", true); + } + catch(Throwable t){ + Assert.fail("Exception in SyncUtil.SyncRunToLine: " + t.getMessage()); + } + + // Make sure thread started event was received because it could arrive + // after the stopped event is received + IStartedDMEvent startedEvent = null; + try { + startedEvent = startedEventWaitor.waitForEvent(1000); + } catch (Exception e) { + Assert.fail("Timeout waiting for Thread create event"); + return; + } + + if (((IMIExecutionDMContext)startedEvent.getDMContext()).getThreadId() != 2) + Assert.fail("Thread create event has failed expected thread id 2 but got " + + ((IMIExecutionDMContext)startedEvent.getDMContext()).getThreadId()); + + /* + * Test getExecutionContexts for a valid container DMC + */ + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + fRunCtrl.getExecutionContexts(containerDmc, rmExecutionCtxts); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + wait.waitReset(); + /* + * Get data + */ + IExecutionDMContext[] data = rmExecutionCtxts.getData(); + /* + * Contexts returned can not be null + */ + if(data == null) + Assert.fail("No context returned. 2 Contexts with id 1 & 2 should have been returned"); + else{ + // 2 Contexts shd be returned + Assert.assertTrue(data.length==2); + IMIExecutionDMContext dmc1 = (IMIExecutionDMContext)data[0]; + IMIExecutionDMContext dmc2 = (IMIExecutionDMContext)data[1]; + // Context ids should be 1 & 2 + Assert.assertTrue(dmc1.getThreadId()==2 && dmc2.getThreadId() == 1); + } + } + + /* + * Testing getModelData() for ExecutionDMC + */ + @Test + public void getModelDataForThread() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + /* + * Create a request monitor + */ + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + /* + * Call getModelData for Execution DMC + */ + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + fRunCtrl.getExecutionData(fRunCtrl.createMIExecutionContext(containerDmc, 1), rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + IRunControl.IExecutionDMData data = rm.getData(); + if(data == null) + Assert.fail("No data returned."); + else{ + /* + * getModelData should return StateChangeReason. + */ + Assert.assertTrue(" State change reason for a normal execution should be USER_REQUEST." , + StateChangeReason.USER_REQUEST == data.getStateChangeReason()); + } + } + + @Test + public void getModelDataForThreadWhenStep() throws Throwable { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + /* + * Run till step returns + */ + final MIStoppedEvent stoppedEvent = SyncUtil.SyncStep(StepType.STEP_OVER); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + /* + * getModelData for Execution DMC + */ + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + fRunCtrl.getExecutionData(stoppedEvent.getDMContext(), rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + IRunControl.IExecutionDMData data = rm.getData(); + if(data == null) + Assert.fail("No data Returned."); + else{ + /* + * getModelData for Execution DMC in case Step has been performed. + */ + Assert.assertTrue("getModelData for ExecutionDMC in case of step should be STEP." , + StateChangeReason.STEP == data.getStateChangeReason()); + } + } + + /* + * getModelData() for ExecutionDMC when a breakpoint is hit + */ + @Test + public void getModelDataForThreadWhenBreakpoint() throws Throwable { + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + /* + * Add a breakpoint + */ + SyncUtil.SyncAddBreakpoint(SOURCE_NAME + ":21", false); + + /* + * Resume till the breakpoint is hit + */ + final MIStoppedEvent stoppedEvent = SyncUtil.SyncResumeUntilStopped(); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + fRunCtrl.getExecutionData(stoppedEvent.getDMContext(), rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + IRunControl.IExecutionDMData data = rm.getData(); + if(data == null) + Assert.fail("No data Returned."); + else{ + /* + * getModelData for ExecutionDMC in case a breakpoint is hit + */ + Assert.assertTrue("getModelData for an Execution DMC when a breakpoint is hit is not BREAKPOINT and is " + data.getStateChangeReason(), + StateChangeReason.BREAKPOINT == data.getStateChangeReason()); + } + } + + /* + * getModelData() for Container DMC + */ + @Test + public void getModelDataForContainer() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + fRunCtrl.getExecutionData(fContainerDmc, rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + IRunControl.IExecutionDMData data = rm.getData(); + if(data == null) + Assert.fail("No data returned."); + else{ + /* + * StateChangeReason in getModelData for Container DMC is null. + */ + Assert.assertTrue(" State change reason for a normal execution should be USER_REQUEST." , + StateChangeReason.USER_REQUEST == data.getStateChangeReason()); + } + } + + /* + * getExecutionContexts for an invalid container DMC + */ + @Ignore + @Test + public void getExecutionContextsForInvalidContainerDMC() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; +// final IContainerDMContext ctxt = new GDBControlDMContext("-1", getClass().getName() + ":" + 1); + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + // Pass an invalid dmc + fRunCtrl.getExecutionContexts(fContainerDmc, rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertTrue(wait.getMessage(), !wait.isOK()); + + IStatus status = rm.getStatus(); + Assert.assertEquals("Error message for invalid container", IStatus.ERROR, status.getSeverity()); + } + + /* + * Cache after ContainerSuspendEvent should be re-set + */ + @Test + public void cacheAfterContainerSuspendEvent() throws InterruptedException{ + /* + * Step to fire ContainerSuspendEvent + */ + try { + SyncUtil.SyncStep(StepType.STEP_OVER); + } catch (Throwable e) { + Assert.fail("Exception in SyncUtil.SyncStep: " + e.getMessage()); + } + /* + * Cache should be re-set + */ + //TODO TRy going to back end and fetching values instead + //Assert.assertEquals(fRunCtrl.getCache().getCachedContext().size(), 0); + } + + + //Also test Cache after ContainerResumeEvent + @Test + public void resume() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + //TestsPlugin.debug("handleCompleted over"); + } + }; + final ServiceEventWaitor eventWaitor = + new ServiceEventWaitor( + getGDBLaunch().getSession(), + IResumedDMEvent.class); + + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + fRunCtrl.resume(containerDmc, rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + + try { + eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER); + } catch (Exception e) { + Assert.fail("Exception raised:: " + e.getMessage()); + e.printStackTrace(); + return; + } + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + wait.waitReset(); + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + + wait.setReturnInfo(fRunCtrl.isSuspended(containerDmc)); + wait.waitFinished(); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertFalse("Target is suspended. It should have been running", (Boolean)wait.getReturnInfo()); + + wait.waitReset(); + } + + + + + @Test + public void resumeContainerContext() throws InterruptedException{ + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + final DataRequestMonitor rm = + new DataRequestMonitor(fRunCtrl.getExecutor(), null) { + @Override + protected void handleCompleted() { + wait.waitFinished(getStatus()); + } + }; + + final ServiceEventWaitor eventWaitor = + new ServiceEventWaitor( + getGDBLaunch().getSession(), + IResumedDMEvent.class); + + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + fRunCtrl.resume(fContainerDmc, rm); + } + }); + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + try { + eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER); + //TestsPlugin.debug("DsfMIRunningEvent received"); + } catch (Exception e) { + Assert.fail("Exception raised:: " + e.getMessage()); + e.printStackTrace(); + return; + } + + Assert.assertTrue(wait.getMessage(), wait.isOK()); + + wait.waitReset(); + fRunCtrl.getExecutor().submit(new Runnable() { + public void run() { + String pid = MIProcesses.UNIQUE_GROUP_ID; + IProcessDMContext procDmc = fProcService.createProcessContext(fGDBCtrl.getContext(), pid); + IContainerDMContext containerDmc = fProcService.createContainerContext(procDmc, pid); + + wait.setReturnInfo(fRunCtrl.isSuspended(containerDmc)); + wait.waitFinished(); + } + }); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + Assert.assertFalse("Target is suspended. It should have been running", (Boolean)wait.getReturnInfo()); + + wait.waitReset(); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/AsyncCompletionWaitor.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/AsyncCompletionWaitor.java new file mode 100644 index 00000000000..073232f7df1 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/AsyncCompletionWaitor.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.framework; + +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +public class AsyncCompletionWaitor { + + /* + * Indicates we will wait forever. Otherwise the time specified + * is in milliseconds. + */ + public final static int WAIT_FOREVER = 0; + + /* + * Private control space. + */ + private IStatus fStatus; + private Object fReturnInfo; + private boolean fWaitFinished; + private int fNumWaiting; + + /* + * Main constructor. + */ + public AsyncCompletionWaitor() { + waitReset(); + } + + /* + * A timeout of WAIT_FOREVER indicates we wait until the operation is + * completed by a call to waitFinished. Or if we are interrupted + * with an exception. + */ + public synchronized void waitUntilDone(int timeout) throws InterruptedException { + if (fWaitFinished) return; + + wait(timeout); + } + + + /* + * Indicates that we are done with the operation and the code + * waiting ( waitUntilDone ) will be allowed to continue. + */ + public void waitFinished() { + waitFinished(new Status(IStatus.OK, TestsPlugin.PLUGIN_ID, "")); + } + + public synchronized void waitFinished(IStatus status) { + + if (fWaitFinished) { + ((MultiStatus)fStatus).merge( + new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, + "waitFinished called too many times!", null)); + } + + ((MultiStatus)fStatus).merge(status); + + if (fNumWaiting == 0 || --fNumWaiting == 0) { + fWaitFinished = true; + notifyAll(); + } + } + + /* + * Resets the state so we allow ourselves to be reused instead + * of having to create a new wait object each time. + */ + public synchronized void waitReset() { + fWaitFinished = false; + fStatus = new MultiStatus(TestsPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$ + fReturnInfo = null; + fNumWaiting = 0; + } + + public boolean isOK() { + if ( fStatus == null ) { + // We timed out + return false; + } + + return fStatus.isOK(); + } + + public String getMessage() { + if ( fStatus == null ) { + return "Timed out"; //$NON-NLS-1$ + } + + // Build a concatenation of all messages + String fullMessage = ""; + IStatus[] children = fStatus.getChildren(); + for (int i=0; i 0) { + fullMessage += "\"" + children[i].getMessage() + "\", ";//$NON-NLS-1$//$NON-NLS-2$ + } + } + // Remove the trailing comma and space before returning (as long as they are there) + return fullMessage.length() <= 2 ? fullMessage : fullMessage.substring(0, fullMessage.length() - 2); + } + + public void setReturnInfo(Object info) { + fReturnInfo = info ; + } + + public Object getReturnInfo() { + return fReturnInfo; + } + + public void increment() { + fNumWaiting++; + } +} \ No newline at end of file diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BackgroundRunner.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BackgroundRunner.java new file mode 100644 index 00000000000..15767dea4d3 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BackgroundRunner.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.framework; + +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.swt.widgets.Display; +import org.junit.internal.runners.InitializationError; +import org.junit.runner.notification.RunNotifier; + +/** + * This runner starts an eclipse job ro run the tests, so as + * to release the UI thread. + */ +@SuppressWarnings("restriction") +public class BackgroundRunner extends TestClassRunner { + + public BackgroundRunner(Class klass) throws InitializationError { + super(klass); + } + + final static QualifiedName BACKGROUND_TEST_EXECUTION_FINISHED = new QualifiedName(TestsPlugin.getDefault().getBundle().getSymbolicName(), "background_test_execution_finished"); //$NON-NLS-1$ + + void invokeSuperRunImpl(RunNotifier notifier) { + super.run(notifier); + } + + /* + * This method overrides the one from TestClassRunner. + * What we do here is start a background job which will call + * TestClassRunner.run; this enables us to release + * the main UI thread. + * + * This has been adapted from the JUnits tests of TargetManagement + * (RSECoreTestCase and RSEWaitAndDispatchUtil) + */ + @Override + public void run(final RunNotifier notifier) { + + // Start the test in a background thread + Job job = new Job("GDB/MI JUnit Test Case Execution Job") { + @Override + protected IStatus run(IProgressMonitor monitor) { + invokeSuperRunImpl(notifier); + monitor.done(); + setProperty(BACKGROUND_TEST_EXECUTION_FINISHED, Boolean.TRUE); + + // The job never fails. The test result is the real result. + return Status.OK_STATUS; + } + }; + + // The job is not complete yet + job.setProperty(BACKGROUND_TEST_EXECUTION_FINISHED, Boolean.FALSE); + // schedule the job to run immediatelly + job.schedule(); + + // wait till the job finishes executing + waitAndDispatch(0, new BackgroundTestExecutionJobWaiter(job)); + } + + public interface IInterruptCondition { + public boolean isTrue(); + public void dispose(); + } + + private final static class BackgroundTestExecutionJobWaiter implements IInterruptCondition { + private final Job job; + + public BackgroundTestExecutionJobWaiter(Job job) { + assert job != null; + this.job = job; + } + + public boolean isTrue() { + // Interrupt the wait method if the job signaled that it has finished. + return ((Boolean)job.getProperty(BACKGROUND_TEST_EXECUTION_FINISHED)).booleanValue(); + } + + public void dispose() { + // nothing to do + } + } + + public static boolean waitAndDispatch(long timeout, IInterruptCondition condition) { + assert timeout >= 0 && condition != null; + + boolean isTimedOut= false; + if (timeout >= 0 && condition != null) { + long start = System.currentTimeMillis(); + Display display = Display.findDisplay(Thread.currentThread()); + if (display != null) { + // ok, we are running within a display thread --> keep the + // display event dispatching running. + long current = System.currentTimeMillis(); + while (timeout == 0 || (current - start) < timeout) { + if (condition.isTrue()) break; + if (!display.readAndDispatch()) display.sleep(); + current = System.currentTimeMillis(); + } + isTimedOut = (current - start) >= timeout && timeout > 0; + } else { + // ok, we are not running within a display thread --> we can + // just block the thread here + long current = System.currentTimeMillis(); + while (timeout == 0 || (current - start) < timeout) { + if (condition.isTrue()) break; + try { Thread.sleep(50); } catch (InterruptedException e) { /* ignored on purpose */ } + current = System.currentTimeMillis(); + } + isTimedOut = (current - start) >= timeout && timeout > 0; + } + } + + // Signal the interrupt condition that we are done here + // and it can cleanup whatever necessary. + condition.dispose(); + + return isTimedOut; + } + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java new file mode 100644 index 00000000000..b9a880bff8e --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/BaseTestCase.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.framework; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * This is the base class for the GDB/MI Unit tests. + * It provides the @Before and @After methods which setup + * and teardown the launch, for each test. + * If these methods are overwridden by a subclass, the new method + * must call super.baseSetup or super.baseTeardown itself, if this + * code is to be run. + */ +public class BaseTestCase { + + private static final String DEFAULT_TEST_APP = "data/launch/bin/GDBMIGenericTestApp"; + + private static GdbLaunch fLaunch; + private static Map attrs = new HashMap(); + + private MIStoppedEvent fInitialStoppedEvent = null; + + public GdbLaunch getGDBLaunch() { return fLaunch; } + + public static void setLaunchAttribute(String key, Object value) { + attrs.put(key, value); + } + + public MIStoppedEvent getInitialStoppedEvent() { return fInitialStoppedEvent; } + + @BeforeClass + public static void baseBeforeClassMethod() { + // Setup information for the launcher + attrs.put(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, DEFAULT_TEST_APP); + + attrs.put(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, true); + attrs.put(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT); + attrs.put(IGDBLaunchConfigurationConstants.ATTR_DEBUG_NAME, "gdb"); + attrs.put(IGDBLaunchConfigurationConstants.ATTR_GDB_INIT, ".gdbinit"); + attrs.put(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ); + + } + + @Before + public void baseBeforeMethod() throws Exception { + System.out.println("===================================================================="); + System.out.println("Launching test application: " + attrs.get(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME)); + System.out.println("===================================================================="); + + ILaunchManager launchMgr = DebugPlugin.getDefault().getLaunchManager(); + ILaunchConfigurationType lcType = launchMgr.getLaunchConfigurationType("org.eclipse.cdt.tests.dsf.gdb.TestLaunch"); + assert lcType != null; + + ILaunchConfigurationWorkingCopy lcWorkingCopy = lcType.newInstance( + null, + launchMgr.generateUniqueLaunchConfigurationNameFrom("Test Launch")); //$NON-NLS-1$ + assert lcWorkingCopy != null; + lcWorkingCopy.setAttributes(attrs); + + final ILaunchConfiguration lc = lcWorkingCopy.doSave(); + assert lc != null; + + fLaunch = (GdbLaunch)lc.launch(ILaunchManager.DEBUG_MODE, new NullProgressMonitor()); + assert fLaunch != null; + + // Now initialize our SyncUtility, since we have the launcher + SyncUtil.initialize(fLaunch.getSession()); + + try { + // Also wait for the program to stop before allowing tests to start + final ServiceEventWaitor eventWaitor = + new ServiceEventWaitor( + fLaunch.getSession(), + MIStoppedEvent.class); + fInitialStoppedEvent = eventWaitor.waitForEvent(10000); + } catch (Exception e) {} + + } + + @After + public void baseAfterMethod() throws Exception { + System.out.println("===================================================================="); + System.out.println("Tearing down test application: " + attrs.get(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME)); + System.out.println("===================================================================="); + if (fLaunch != null) { + fLaunch.terminate(); + fLaunch = null; + } + } + + @AfterClass + public static void baseAfterClassMehod() throws Exception { + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java new file mode 100644 index 00000000000..50266210273 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/ServiceEventWaitor.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.framework; + +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; + +/* + * This class provides a way to wait for an asynchronous ServerEvent + * to occur. The user of this class specifies which event is of + * interest using the proper constructor or the registerForEvent() method. + * waitForEvent() can then be called to block until the event occurs or + * the timeout elapses. + * + * Note that if the event occurs after regsiterForEvent() is called but + * before waitForEvent() is called, waitForEvent() will return immediatly + * since it will know the event has already occured. + */ + +public class ServiceEventWaitor { + /* + * Indicates we will wait forever. Otherwise the time specified + * is in milliseconds. + */ + public final static int WAIT_FOREVER = 0 ; + + /* The type of event to wait for */ + private Class fEventTypeClass; + private DsfSession fSession; + private V fEvent; + + + /* Empty contructor. registerForEvent() should be called when + * this constructor is used. + */ + public ServiceEventWaitor(DsfSession session) { + fSession = session; + } + + /* Contructor that takes the eventClass as parameter. This is a shortcut + * that avoids calling registerForEvent() + */ + public ServiceEventWaitor(DsfSession session, Class eventClass) { + this(session); + registerForEvent(eventClass); + } + + /* Specify which event to wait for, and add ourselves as + * a listener with the session + */ + public void registerForEvent(Class eventClass) { + fEventTypeClass = eventClass; + fEvent = null; + fSession.addServiceEventListener(this, null); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (fEventTypeClass != null) fSession.removeServiceEventListener(this); + } + + /* Block until 'timeout' or the previously specified event has been + * received. The reason we don's specify the event as a parameter + * is that we must be ready for the event to occur event before + * this method is called. + */ + public synchronized V waitForEvent(int timeout) throws Exception { + if (fEventTypeClass == null) { + throw new Exception("Event to wait for has not been specified!"); + } + // The event might have already been received + if (fEvent != null) return fEvent; + + wait(timeout); + + if (fEvent == null) { + throw new Exception("Timed out waiting for ServiceEvent: " + fEventTypeClass.getName()); + } + return fEvent; + } + + /* + * Listen to all possible events by having the base class be the parameter. + * and then igure out if that event is the one we were waiting for. + */ + @DsfServiceEventHandler + public void eventDispatched(V event) { + if (fEventTypeClass.isAssignableFrom(event.getClass())) { + synchronized(this) { + fEvent = event; + notifyAll(); + } + } + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java new file mode 100644 index 00000000000..5a946b65e23 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/framework/SyncUtil.java @@ -0,0 +1,342 @@ +/******************************************************************************* + * Copyright (c) 2007 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: + * Ericsson AB - Initial implementation of Test cases + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.framework; + +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.Callable; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext; +import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.StepType; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; +import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext; +import org.eclipse.cdt.dsf.mi.service.IMIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIProcesses; +import org.eclipse.cdt.dsf.mi.service.MIRunControl; +import org.eclipse.cdt.dsf.mi.service.MIStack; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakDelete; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakInsert; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIBreakList; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecContinue; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecFinish; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecNext; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecStep; +import org.eclipse.cdt.dsf.mi.service.command.commands.MIExecUntil; +import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakInsertInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; +import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; +import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.gdb.launching.TestsPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class SyncUtil { + + private static ICommandControlService fCommandControl; + private static MIRunControl fRunControl; + private static MIStack fStack; + private static IExpressions fExpressions; + private static DsfSession fSession; + + private static IContainerDMContext fGdbContainerDmc; + private static IBreakpointsTargetDMContext fBreakpointsDmc; + + // Initialize some common things, once the session has been established + public static void initialize(DsfSession session) { + fSession = session; + + DsfServicesTracker tracker = + new DsfServicesTracker(TestsPlugin.getBundleContext(), + fSession.getId()); + + fCommandControl = tracker.getService(ICommandControlService.class); + IMIProcesses procService = tracker.getService(IMIProcesses.class); + IProcessDMContext procDmc = procService.createProcessContext(fCommandControl.getContext(), MIProcesses.UNIQUE_GROUP_ID); + fGdbContainerDmc = procService.createContainerContext(procDmc, MIProcesses.UNIQUE_GROUP_ID); + + fBreakpointsDmc = (IBreakpointsTargetDMContext)fCommandControl.getContext(); + + fRunControl = tracker.getService(MIRunControl.class); + fStack = tracker.getService(MIStack.class); + fExpressions = tracker.getService(IExpressions.class); + + tracker.dispose(); + } + + public static MIStoppedEvent SyncStep(final StepType stepType, int numSteps) throws Throwable { + MIStoppedEvent retVal = null; + for (int i=0; i eventWaitor = + new ServiceEventWaitor( + fSession, + MIStoppedEvent.class); + + fRunControl.getExecutor().submit(new Runnable() { + public void run() { + // No need for a RequestMonitor since we will wait for the + // ServiceEvent telling us the program has been suspended again + switch(stepType) { + case STEP_INTO: + fCommandControl.queueCommand(new MIExecStep(dmc), null); + break; + case STEP_OVER: + fCommandControl.queueCommand(new MIExecNext(dmc), null); + break; + case STEP_RETURN: + fCommandControl.queueCommand(new MIExecFinish(fStack.createFrameDMContext(dmc, 0)), null); + break; + default: + Assert.assertTrue("Unsupported step type; " + stepType.toString(), false); + } + } + }); + + // Wait for the execution to suspend after the step + return eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER); + } + + public static MIStoppedEvent SyncRunToLine(final IExecutionDMContext dmc, final String fileName, final String lineNo, + final boolean skipBreakpoints) throws Throwable { + + final ServiceEventWaitor eventWaitor = + new ServiceEventWaitor( + fSession, + MIStoppedEvent.class); + + fRunControl.getExecutor().submit(new Runnable() { + public void run() { + // No need for a RequestMonitor since we will wait for the + // ServiceEvent telling us the program has been suspended again + + fCommandControl.queueCommand( + new MIExecUntil(dmc, fileName + ":" + lineNo), //$NON-NLS-1$ + null); + } + }); + + // Wait for the execution to suspend after the step + return eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER); + } + + public static MIStoppedEvent SyncRunToLine(final String fileName, final String lineNo, + final boolean skipBreakpoints) throws Throwable { + return SyncRunToLine(fGdbContainerDmc, fileName, lineNo, skipBreakpoints); + } + + public static MIStoppedEvent SyncRunToLine(final String fileName, final String lineNo) throws Throwable { + return SyncRunToLine(fGdbContainerDmc, fileName, lineNo, false); + } + + + public static int SyncAddBreakpoint(final String location) throws Throwable { + return SyncAddBreakpoint(location, true); + } + + public static int SyncAddBreakpoint(final String location, boolean temporary) + throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + DataRequestMonitor addBreakDone = + new DataRequestMonitor(fRunControl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }; + + fCommandControl.queueCommand( + new MIBreakInsert(fBreakpointsDmc, temporary, false, null, 0, location, 0), + addBreakDone); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + MIBreakInsertInfo info = (MIBreakInsertInfo) wait.getReturnInfo(); + return info.getMIBreakpoints()[0].getNumber(); + } + + + public static int[] SyncGetBreakpointList() throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + DataRequestMonitor listDRM = + new DataRequestMonitor(fRunControl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + wait.waitFinished(getStatus()); + } + }; + + fCommandControl.queueCommand(new MIBreakList(fBreakpointsDmc), listDRM); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + + MIBreakpoint[] breakpoints = listDRM.getData().getMIBreakpoints(); + int[] result = new int[breakpoints.length]; + for (int i = 0; i < breakpoints.length; i++) { + result[i] = breakpoints[i].getNumber(); + } + return result; + } + + public static void SyncDeleteBreakpoint(int breakpointIndex) throws Throwable { + SyncDeleteBreakpoint(new int[] {breakpointIndex}); + } + + public static void SyncDeleteBreakpoint(int[] breakpointIndices) throws Throwable { + + final AsyncCompletionWaitor wait = new AsyncCompletionWaitor(); + + DataRequestMonitor deleteBreakDone = + new DataRequestMonitor(fRunControl.getExecutor(), null) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + wait.setReturnInfo(getData()); + } + + wait.waitFinished(getStatus()); + } + }; + + fCommandControl.queueCommand( + new MIBreakDelete(fBreakpointsDmc, breakpointIndices), //$NON-NLS-1$ + deleteBreakDone); + + wait.waitUntilDone(AsyncCompletionWaitor.WAIT_FOREVER); + assertTrue(wait.getMessage(), wait.isOK()); + } + + + public static MIStoppedEvent SyncResumeUntilStopped(final IExecutionDMContext dmc) throws Throwable { + final ServiceEventWaitor eventWaitor = + new ServiceEventWaitor( + fSession, + MIStoppedEvent.class); + + fRunControl.getExecutor().submit(new Runnable() { + public void run() { + // No need for a RequestMonitor since we will wait for the + // ServiceEvent telling us the program has been suspended again + fCommandControl.queueCommand( + new MIExecContinue(dmc), + null); + } + }); + + // Wait for the execution to suspend after the step + return eventWaitor.waitForEvent(ServiceEventWaitor.WAIT_FOREVER); + } + + public static MIStoppedEvent SyncResumeUntilStopped() throws Throwable { + return SyncResumeUntilStopped(fGdbContainerDmc); + } + + public static MIStoppedEvent SyncRunToLocation(final String location) throws Throwable { + // Set a temporary breakpoint and run to it. + // Note that if there were other breakpoints set ahead of this one, + // they will stop execution earlier than planned + SyncAddBreakpoint(location, true); + return SyncResumeUntilStopped(); + } + + public static IFrameDMContext SyncGetStackFrame(final IExecutionDMContext execCtx, final int level) throws Throwable { + class StackFrameQuery extends Query { + @Override + protected void execute(final DataRequestMonitor rm) { + fStack.getFrames(execCtx, new DataRequestMonitor(fSession.getExecutor(), rm) { + @Override + protected void handleSuccess() { + if (getData().length > level) { + rm.setData(getData()[level]); + } else { + rm.setStatus(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, "Frame not available")); + } + rm.done(); + } + }); + } + } + + StackFrameQuery sfQuery = new StackFrameQuery(); + fSession.getExecutor().execute(sfQuery); + return sfQuery.get(); + } + + public static IExpressionDMContext SyncCreateExpression(final IDMContext parentCtx, final String expression) + throws Throwable { + Callable callable = new Callable() { + public IExpressionDMContext call() throws Exception { + return fExpressions.createExpression(parentCtx, expression); + } + }; + return fSession.getExecutor().submit(callable).get(); + } + + public static FormattedValueDMContext SyncGetFormattedValue( + final IFormattedValues service, final IFormattedDataDMContext dmc, final String formatId) throws Throwable + { + Callable callable = new Callable() { + public FormattedValueDMContext call() throws Exception { + return service.getFormattedValueContext(dmc, formatId); + } + }; + return fSession.getExecutor().submit(callable).get(); + } + + public static IMIExecutionDMContext SyncCreateExecutionContext(final IContainerDMContext parentCtx, final int threadId) + throws Throwable { + Callable callable = new Callable() { + public IMIExecutionDMContext call() throws Exception { + return fRunControl.createMIExecutionContext(parentCtx, threadId); + } + }; + return fSession.getExecutor().submit(callable).get(); +} + +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestLaunchDelegate.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestLaunchDelegate.java new file mode 100644 index 00000000000..46cfdb28047 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestLaunchDelegate.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 QNX Software Systems 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: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.launching; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.gdb.launching.FinalLaunchSequence; +import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; +import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils; +import org.eclipse.cdt.dsf.gdb.launching.ServicesLaunchSequence; +import org.eclipse.cdt.dsf.gdb.service.GdbDebugServicesFactory; +import org.eclipse.cdt.dsf.gdb.service.SessionType; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.model.ILaunchConfigurationDelegate2; +import org.eclipse.debug.core.model.LaunchConfigurationDelegate; + +/** + * The launch configuration delegate for the DSF GDB JUnit tests. + */ +@ThreadSafe +public class TestLaunchDelegate extends LaunchConfigurationDelegate + implements ILaunchConfigurationDelegate2 +{ + public final static String GDB_DEBUG_MODEL_ID = "org.eclipse.cdt.tests.dsf.gdb"; //$NON-NLS-1$ + + public void launch( ILaunchConfiguration config, String mode, ILaunch launch, IProgressMonitor monitor ) throws CoreException { + if ( monitor == null ) { + monitor = new NullProgressMonitor(); + } + if ( mode.equals( ILaunchManager.DEBUG_MODE ) ) { + launchDebugger( config, launch, monitor ); + } + } + + private void launchDebugger( ILaunchConfiguration config, ILaunch launch, IProgressMonitor monitor ) throws CoreException { + monitor.beginTask("Launching debugger session", 10); //$NON-NLS-1$ + if ( monitor.isCanceled() ) { + return; + } + try { + String debugMode = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE, ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ); + if ( debugMode.equals( ICDTLaunchConfigurationConstants.DEBUGGER_MODE_RUN ) ) { + launchLocalDebugSession( config, launch, monitor ); + } + } + finally { + monitor.done(); + } + } + + private void launchLocalDebugSession( final ILaunchConfiguration config, ILaunch l, IProgressMonitor monitor ) throws CoreException { + if ( monitor.isCanceled() ) { + return; + } + final GdbLaunch launch = (GdbLaunch)l; + + monitor.subTask("DSF GDB/MI reference JUnit tests"); //$NON-NLS-1$ + IPath exePath = new Path(getProgramName(config)); + verifyBinary(exePath); + + monitor.worked( 1 ); + + launch.setServiceFactory(new GdbDebugServicesFactory(LaunchUtils.getGDBVersion(config))); + + IProgressMonitor subMon1 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + final ServicesLaunchSequence servicesLaunchSequence = + new ServicesLaunchSequence(launch.getSession(), launch, subMon1); + launch.getSession().getExecutor().execute(servicesLaunchSequence); + try { + servicesLaunchSequence.get(); + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in services launch sequence", e1.getCause())); //$NON-NLS-1$ + } + + launch.initializeControl(); + + // Add the CLI and "inferior" process objects to the launch. + launch.addCLIProcess("gdb"); //$NON-NLS-1$ + launch.addInferiorProcess(exePath.lastSegment()); + + // Create and invoke the final launch sequence to setup GDB + IProgressMonitor subMon2 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); + final FinalLaunchSequence finalLaunchSequence = + new FinalLaunchSequence(launch.getSession().getExecutor(), launch, SessionType.LOCAL, false, subMon2); + launch.getSession().getExecutor().execute(finalLaunchSequence); + try { + finalLaunchSequence.get(); + } catch (InterruptedException e1) { + throw new DebugException(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, DebugException.INTERNAL_ERROR, "Interrupted Exception in dispatch thread", e1)); //$NON-NLS-1$ + } catch (ExecutionException e1) { + throw new DebugException(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, DebugException.REQUEST_FAILED, "Error in final launch sequence", e1.getCause())); //$NON-NLS-1$ + } + } + + /* + * Verify that the specified file exists on the file system. + */ + private void verifyBinary(IPath exePath) throws CoreException { + try { + new FileReader(exePath.toFile()); + } catch (Exception e) { + Throwable exception = new FileNotFoundException(exePath.toOSString() + " does not exist"); //$NON-NLS-1$ + int code = ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_BINARY; + throw new CoreException(new Status(IStatus.ERROR, TestsPlugin.PLUGIN_ID, code, exception == null ? "" : exception.getLocalizedMessage(), //$NON-NLS-1$ + exception)); + } + } + + @Override + public boolean preLaunchCheck( ILaunchConfiguration config, String mode, IProgressMonitor monitor ) throws CoreException { + return super.preLaunchCheck( config, mode, monitor ); + } + + @Override + public boolean buildForLaunch(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { + return false; + } + + @Override + public boolean finalLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { + return true; + } + + @Override + public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { + GdbLaunch launch = new GdbLaunch(configuration, mode, null); + launch.initialize(); + return launch; + } + + private static String getProgramName(ILaunchConfiguration configuration) throws CoreException { + return configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, (String)null); + } +} diff --git a/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java new file mode 100644 index 00000000000..7c3aa379ca0 --- /dev/null +++ b/dsf-gdb/org.eclipse.cdt.tests.dsf.gdb/src/org/eclipse/cdt/tests/dsf/gdb/launching/TestsPlugin.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.gdb.launching; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.eclipse.core.runtime.Plugin; +import org.osgi.framework.BundleContext; + +/** + * The main plugin class to be used in the desktop. + */ +public class TestsPlugin extends Plugin { + //The shared instance. + private static TestsPlugin plugin; + //Resource bundle. + private ResourceBundle resourceBundle; + private static BundleContext bundleContext; + + public static final String PLUGIN_ID = "org.eclipse.cdt.tests.dsf.gdb"; //$NON-NLS-1$ + + /** + * The constructor. + */ + public TestsPlugin() { + super(); + plugin = this; + try { + resourceBundle = ResourceBundle.getBundle("org.eclipse.cdt.tests.dsf.gdb.TestsPluginResources"); //$NON-NLS-1$ + } + catch (MissingResourceException x) { + resourceBundle = null; + } + } + /** + * This method is called upon plug-in activation + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + bundleContext = context; + } + /** + * This method is called when the plug-in is stopped + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + } + /** + * Returns the shared instance. + */ + public static TestsPlugin getDefault() { + return plugin; + } + + /** + * Returns the string from the plugin's resource bundle, + * or 'key' if not found. + */ + public static String getResourceString(String key) { + ResourceBundle bundle = TestsPlugin.getDefault().getResourceBundle(); + try { + return (bundle != null) ? bundle.getString(key) : key; + } + catch (MissingResourceException e) { + return key; + } + } + /** + * Returns the plugin's resource bundle, + */ + public ResourceBundle getResourceBundle() { + return resourceBundle; + } + /** + * Returns the plugin's bundle context, + */ + public static BundleContext getBundleContext() { + return bundleContext; + } + /** + * Convenience method which returns the unique identifier of this plugin. + */ + public static String getUniqueIdentifier() { + return getDefault().getBundle().getSymbolicName(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/.classpath b/dsf/org.eclipse.cdt.dsf.ui/.classpath new file mode 100644 index 00000000000..304e86186aa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dsf/org.eclipse.cdt.dsf.ui/.options b/dsf/org.eclipse.cdt.dsf.ui/.options new file mode 100644 index 00000000000..cda9510d97c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.options @@ -0,0 +1,10 @@ +org.eclipse.cdt.dsf.ui/debug = false + +org.eclipse.cdt.dsf.ui/debug/vm/contentProvider = false +org.eclipse.cdt.dsf.ui/debug/vm/delta = false +org.eclipse.cdt.dsf.ui/debug/vm/cache = false +org.eclipse.cdt.dsf.ui/debug/vm/presentationId = +org.eclipse.cdt.dsf.ui/debug/vm/atomicUpdate = false + +org.eclipse.cdt.dsf.ui/debug/stepping = false +org.eclipse.cdt.dsf.ui/debug/disassembly = false diff --git a/dsf/org.eclipse.cdt.dsf.ui/.project b/dsf/org.eclipse.cdt.dsf.ui/.project new file mode 100644 index 00000000000..984545aa7a1 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.project @@ -0,0 +1,34 @@ + + + org.eclipse.cdt.dsf.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters b/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters new file mode 100644 index 00000000000..cc3e1bff31e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.settings/.api_filters @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs b/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..5d20faf6545 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,70 @@ +#Mon Jun 23 15:02:26 CEST 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.5 diff --git a/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..78d85557a07 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/META-INF/MANIFEST.MF @@ -0,0 +1,47 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-Vendor: %providerName +Bundle-SymbolicName: org.eclipse.cdt.dsf.ui;singleton:=true +Bundle-Version: 2.0.0.qualifier +Bundle-Activator: org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.core.variables, + org.eclipse.debug.ui, + org.eclipse.ui.ide, + org.eclipse.jface.text, + org.eclipse.ui.workbench.texteditor, + org.eclipse.cdt.dsf, + org.eclipse.cdt.dsf.ui, + org.eclipse.cdt.core, + org.eclipse.cdt.debug.core, + org.eclipse.cdt.debug.ui, + org.eclipse.jface.text;bundle-version="3.4.0", + org.eclipse.ui.editors;bundle-version="3.4.0", + org.eclipse.ui.workbench.texteditor;bundle-version="3.4.0", + org.eclipse.ui.ide;bundle-version="3.4.0", + org.eclipse.cdt.ui;bundle-version="5.0.0", + org.eclipse.core.expressions;bundle-version="3.4.0", + org.eclipse.core.filesystem;bundle-version="1.2.0" +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.cdt.dsf.debug.ui, + org.eclipse.cdt.dsf.debug.ui.actions, + org.eclipse.cdt.dsf.debug.ui.contexts, + org.eclipse.cdt.dsf.debug.ui.sourcelookup, + org.eclipse.cdt.dsf.debug.ui.viewmodel, + org.eclipse.cdt.dsf.debug.ui.viewmodel.actions, + org.eclipse.cdt.dsf.debug.ui.viewmodel.expression, + org.eclipse.cdt.dsf.debug.ui.viewmodel.launch, + org.eclipse.cdt.dsf.debug.ui.viewmodel.modules, + org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat, + org.eclipse.cdt.dsf.debug.ui.viewmodel.register, + org.eclipse.cdt.dsf.debug.ui.viewmodel.update, + org.eclipse.cdt.dsf.debug.ui.viewmodel.variable, + org.eclipse.cdt.dsf.ui.concurrent, + org.eclipse.cdt.dsf.ui.viewmodel, + org.eclipse.cdt.dsf.ui.viewmodel.datamodel, + org.eclipse.cdt.dsf.ui.viewmodel.properties, + org.eclipse.cdt.dsf.ui.viewmodel.update +Bundle-RequiredExecutionEnvironment: J2SE-1.5 diff --git a/dsf/org.eclipse.cdt.dsf.ui/about.html b/dsf/org.eclipse.cdt.dsf.ui/about.html new file mode 100644 index 00000000000..cb740ae8bc8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/about.html @@ -0,0 +1,24 @@ + + + + +About +

About This Content

+ +

June 5, 2007

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + \ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/build.properties b/dsf/org.eclipse.cdt.dsf.ui/build.properties new file mode 100644 index 00000000000..e061b4e53d0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/build.properties @@ -0,0 +1,17 @@ +############################################################################### +# Copyright (c) 2006, 2008 Wind River Systems 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: +# Wind River Systems - initial API and implementation +############################################################################### +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + about.html diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif new file mode 100644 index 00000000000..34f5290474b Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/disassembly.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif new file mode 100644 index 00000000000..dd994fdbe69 Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/library_obj.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif new file mode 100644 index 00000000000..7da17d4194e Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/library_syms_obj.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif new file mode 100644 index 00000000000..b6b8dc6836d Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/refresh.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif new file mode 100644 index 00000000000..74d1cc35b7c Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshall.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif new file mode 100644 index 00000000000..2c508633881 Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshalways.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif new file mode 100644 index 00000000000..b4b031e58d9 Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshmanual.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif new file mode 100644 index 00000000000..f16fbb072f8 Binary files /dev/null and b/dsf/org.eclipse.cdt.dsf.ui/icons/refreshonbreak.gif differ diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.properties b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties new file mode 100644 index 00000000000..7bed11abaf6 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2006 Wind River Systems 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: +# Wind River Systems - initial API and implementation +############################################################################### +pluginName=Debugger Services Framework UI +providerName=Eclipse.org + diff --git a/dsf/org.eclipse.cdt.dsf.ui/plugin.xml b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml new file mode 100644 index 00000000000..8bafbeb2921 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/plugin.xml @@ -0,0 +1,599 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java new file mode 100644 index 00000000000..8150df5b347 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AbstractImageRegistry.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.graphics.Image; +import org.osgi.framework.Bundle; + + +/** + * Abstract image registry that allows for defining fallback paths for images. + */ +public abstract class AbstractImageRegistry extends ImageRegistry { + private HashMap fPlugins = new HashMap(); + private HashMap fLocations = new HashMap(); + private URL fBaseUrl; + + protected AbstractImageRegistry(Plugin plugin) { + fBaseUrl = plugin.getBundle().getEntry("/"); //$NON-NLS-1$ + } + + /** + * Defines the key for a local image, that must be found below the icons directory + * in the plugin. + * @param key Key by which the image can be referred by. + * @param dir Directory relative to icons/ + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void localImage(String key, String dir, String name) { + if (dir== null || dir.equals(""))//$NON-NLS-1$ + fLocations.put(key, new String[] {"icons/" + name}); //$NON-NLS-1$ + else + fLocations.put(key, new String[] {"icons/" + dir + "/" + name}); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Defines the key for a non-local image, that must be found below the icons directory + * of some plugin. + * @param key Key by which the image can be referred by. + * @param plugin The plugin id, where the icon is searched. + * @param dirs A couple of directories below icons/ in the plugin. If loading fails, + * the next dir will be taken as fallback. + * @param name The name of the file defining the icon. The name will be used as + * key. + */ + protected void externalImage(String key, String plugin, String[] dirs, String name) { + if (plugin != null) { + fPlugins.put(key, plugin); + } + String[] locations = new String[dirs.length]; + for (int i = 0; i < dirs.length; i++) { + String dir = dirs[i]; + if (dir== null || dir.equals(""))//$NON-NLS-1$ + locations[i] = "icons/" + name; //$NON-NLS-1$ + else + locations[i] = "icons/" + dir + "/" + name; //$NON-NLS-1$ //$NON-NLS-2$ + } + fLocations.put(key, locations); + } + + // overrider + @Override + final public Image get(String key) { + Image i = super.get(key); + if (i != null) { + return i; + } + + ImageDescriptor d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return super.get(key); + } + return null; + } + + // overrider + @Override + final public ImageDescriptor getDescriptor(String key) { + ImageDescriptor d = super.getDescriptor(key); + if (d != null) { + return d; + } + + d = createFileImageDescriptor(key); + if (d != null) { + put(key, d); + return d; + } + return null; + } + + private ImageDescriptor createFileImageDescriptor(String key) { + URL url = fBaseUrl; + String pluginId = fPlugins.get(key); + if (pluginId != null) { + Bundle bundle= Platform.getBundle(pluginId); + if (bundle != null) { + url = bundle.getEntry("/"); //$NON-NLS-1$ + } + } + String[] locations= fLocations.get(key); + if (locations != null) { + for (int i = 0; i < locations.length; i++) { + String loc = locations[i]; + URL full; + try { + full = new URL(url, loc); + ImageDescriptor candidate = ImageDescriptor.createFromURL(full); + if (candidate != null && candidate.getImageData() != null) { + return candidate; + } + } catch (MalformedURLException e) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Malformed Icon URL", e)); //$NON-NLS-1$ + } catch (SWTException e) { + // try the next one. + } + } + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java new file mode 100644 index 00000000000..54a4c31bc87 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/AddressRulerColumn.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.source.IAnnotationHover; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; +import org.eclipse.jface.text.source.IVerticalRulerListener; +import org.eclipse.swt.SWT; + +/** + * A vertical ruler column to display the instruction address. + */ +public class AddressRulerColumn extends DisassemblyRulerColumn implements IVerticalRulerInfo, IVerticalRulerInfoExtension, IAnnotationHover { + + private int fRadix; + private boolean fShowRadixPrefix; + private String fRadixPrefix; + private int fNumberOfDigits; + private int fAddressSize; + + /** + * Default constructor. + */ + public AddressRulerColumn() { + super(SWT.LEFT); + setShowRadixPrefix(true); + setAddressSize(32); + setRadix(16); + } + + @Override + protected String createDisplayString(int line) { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + int offset; + try { + offset = doc.getLineOffset(line); + AddressRangePosition pos = doc.getDisassemblyPosition(offset); + if (pos != null && pos.length > 0 && pos.offset == offset) { + if (pos.fValid) { + return getAddressText(pos.fAddressOffset); + } else { + return DOTS.substring(0, computeNumberOfCharacters()); + } + } + SourcePosition srcPos = doc.getSourcePosition(offset); + if (srcPos != null && srcPos.fValid && srcPos.length > 0) { + int srcLine; + int nLines; + if (srcPos.fFileInfo.fSource == null) { + srcLine = srcPos.fLine; + nLines = srcLine+1; + } else { + int delta = offset-srcPos.offset; + int baseOffset = srcPos.fFileInfo.fSource.getLineOffset(srcPos.fLine); + srcLine = srcPos.fFileInfo.fSource.getLineOfOffset(baseOffset+delta); + nLines = srcPos.fFileInfo.fSource.getNumberOfLines(); + } + String digitStr = Integer.toString(srcLine+1); + int maxDigits = (int)(Math.log(nLines)/Math.log(10))+1; + return SPACES.substring(0, maxDigits-digitStr.length())+digitStr; + } + } catch (BadLocationException e) { + // silently ignored + } + return ""; //$NON-NLS-1$ + } + + @Override + protected int computeNumberOfCharacters() { + return fNumberOfDigits + (fRadixPrefix != null ? fRadixPrefix.length() : 0) + 1; + } + + public void setAddressSize(int bits) { + fAddressSize= bits; + calculateNumberOfDigits(); + } + + public void setRadix(int radix) { + fRadix= radix; + calculateNumberOfDigits(); + setShowRadixPrefix(fShowRadixPrefix); + } + + private void calculateNumberOfDigits() { + fNumberOfDigits= BigInteger.ONE.shiftLeft(fAddressSize).subtract(BigInteger.ONE).toString(fRadix).length(); + } + + public void setShowRadixPrefix(boolean showRadixPrefix) { + fShowRadixPrefix = showRadixPrefix; + if (!fShowRadixPrefix) { + fRadixPrefix = null; + } else if (fRadix == 16) { + fRadixPrefix = "0x"; //$NON-NLS-1$ + } else if (fRadix == 8) { + fRadixPrefix = "0"; //$NON-NLS-1$ + } else { + fRadixPrefix = null; + } + } + + private String getAddressText(BigInteger address) { + StringBuffer buf = new StringBuffer(fNumberOfDigits + 3); + if (fRadixPrefix != null) { + buf.append(fRadixPrefix); + } + String str = address.toString(fRadix); + for (int i=str.length(); i fStore = new ArrayList(); + + private static String add(String plugin, String[] dirs, String name) { + String key = plugin+'/'+dirs[0]+'/'+name; + fStore.add(new Object[] {key, plugin, dirs, name}); + return key; + } + + private static final String ORG_ECLIPSE_DEBUG_UI_PLUGIN_ID = "org.eclipse.debug.ui"; //$NON-NLS-1$ + private static final String ORG_ECLIPSE_UI_PLUGIN_ID = "org.eclipse.ui"; //$NON-NLS-1$ + + public static final String ICON_ToggleBreakpoint = add(ORG_ECLIPSE_DEBUG_UI_PLUGIN_ID, new String[] { "full/obj16"}, "brkp_obj.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Refresh_enabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/elcl16"}, "refresh_nav.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Refresh_disabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/dlcl16"}, "refresh_nav.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Copy_enabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/etool16"}, "copy_edit.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + public static final String ICON_Copy_disabled = add(ORG_ECLIPSE_UI_PLUGIN_ID, new String[] {"full/dtool16"}, "copy_edit.gif"); //$NON-NLS-1$ //$NON-NLS-2$ + + private static DisassemblyImageRegistry INSTANCE= new DisassemblyImageRegistry(DsfUIPlugin.getDefault()); + + DisassemblyImageRegistry(Plugin plugin) { + super(plugin); + initialize(); + } + + void initialize() { + for (Iterator iter = fStore.iterator(); iter.hasNext();) { + Object[] element = iter.next(); + if (element.length == 2) { + String dir= (String) element[0]; + String name= (String) element[1]; + localImage(name, dir, name); + } else { + String key = (String) element[0]; + String plugin= (String) element[1]; + String[] dirs= (String[]) element[2]; + String name= (String) element[3]; + externalImage(key, plugin, dirs, name); + } + } + } + + public static ImageDescriptor getImageDescriptor(String key) { + return INSTANCE.getDescriptor(key); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java new file mode 100644 index 00000000000..3091a363909 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.osgi.util.NLS; + +public final class DisassemblyMessages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages";//$NON-NLS-1$ + + private DisassemblyMessages() { + // Do not instantiate + } + + public static String Disassembly_action_ShowAddresses_label; + public static String Disassembly_action_ShowFunctionOffsets_label; + public static String Disassembly_action_ShowDisassembly_label; + public static String Disassembly_action_ShowSource_label; + public static String Disassembly_action_ShowSymbols_label; + public static String Disassembly_action_ShowSimplified_label; + public static String Disassembly_action_SourceSteppingMode_error; + public static String Disassembly_action_SourceSteppingMode_label; + public static String Disassembly_action_AssemblySteppingMode_label; + public static String Disassembly_action_RunToHere_label; + public static String Disassembly_action_SetPCToHere_label; + public static String Disassembly_action_GotoPC_label; + public static String Disassembly_action_GotoPC_tooltip; + public static String Disassembly_action_GotoAddress_label; + public static String Disassembly_action_GotoSymbol_label; + public static String Disassembly_action_Copy_label; + public static String Disassembly_action_SelectAll_label; + public static String Disassembly_action_BreakpointProperties_label; + public static String Disassembly_action_RemoveBreakpoint_label; + public static String Disassembly_action_AddBreakpoint_label; + public static String Disassembly_action_AddHWBreakpoint_label; + public static String Disassembly_action_AddTracepoint_label; + public static String Disassembly_action_DisableBreakpoint_label; + public static String Disassembly_action_EnableBreakpoint_label; + public static String Disassembly_action_WatchExpression_label; + public static String Disassembly_action_ShowInMemory_label; + public static String Disassembly_action_RefreshView_label; + public static String Disassembly_action_OpenPreferences_label; + public static String Disassembly_GotoAddressDialog_title; + public static String Disassembly_GotoAddressDialog_label; + public static String Disassembly_GotoAddressDialog_error_invalid_address; + public static String Disassembly_GotoAddressDialog_error_not_a_number; + public static String Disassembly_GotoSymbolDialog_title; + public static String Disassembly_GotoSymbolDialog_label; + public static String Disassembly_message_notConnected; + public static String Disassembly_log_error_retrieveFrameAddress; + public static String Disassembly_log_error_locateFile; + public static String Disassembly_log_error_accessLineInfo; + public static String Disassembly_log_error_noFileInfo; + public static String Disassembly_log_error_fileTooLarge; + public static String Disassembly_log_error_readFile; + public static String Disassembly_log_error_createVersion; + public static String Disassembly_log_error_retrieveDisassembly; + public static String Disassembly_log_error_showDisassembly; + public static String Disassembly_log_error_invalidSymbol; + public static String DisassemblyPreferencePage_startAddress; + public static String DisassemblyPreferencePage_endAddress; + public static String DisassemblyPreferencePage_addressRadix; + public static String DisassemblyPreferencePage_instructionRadix; + public static String DisassemblyPreferencePage_showAddressRadix; + public static String DisassemblyPreferencePage_showSource; + public static String DisassemblyPreferencePage_showSymbols; + public static String DisassemblyPreferencePage_simplifiedMnemonics; + public static String DisassemblyPreferencePage_error_not_a_number; + public static String DisassemblyPreferencePage_error_negative_number; + public static String DisassemblyPreferencePage_radix_octal; + public static String DisassemblyPreferencePage_radix_decimal; + public static String DisassemblyPreferencePage_radix_hexadecimal; + public static String DisassemblyPreferencePage_showFunctionOffsets; + public static String DisassemblyPreferencePage_showAddress; + public static String DisassemblyPreferencePage_useSourceOnlyMode; + public static String DisassemblyPreferencePage_useSourceOnlyMode_noteTtitle; + public static String DisassemblyPreferencePage_useSourceOnlyMode_noteMessage; + public static String DisassemblyPreferencePage_avoidReadBeforePC; + public static String DisassemblyIPAnnotation_primary; + public static String DisassemblyIPAnnotation_secondary; + public static String SourceReadingJob_name; + public static String SourceColorerJob_name; + public static String EditionFinderJob_name; + public static String EditionFinderJob_task_get_timestamp; + public static String EditionFinderJob_task_search_history; + + static { + NLS.initializeMessages(BUNDLE_NAME, DisassemblyMessages.class); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties new file mode 100644 index 00000000000..479808236de --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyMessages.properties @@ -0,0 +1,89 @@ +########################################################################## +# Copyright (c) 2007, 2008 Wind River Systems 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: +# Wind River Systems - initial API and implementation +########################################################################## + +Disassembly_action_ShowAddresses_label=Show Addresses +Disassembly_action_ShowFunctionOffsets_label=Show Function Offsets +Disassembly_action_ShowDisassembly_label=Show Disassembly of Source +Disassembly_action_ShowSource_label=Show Source +Disassembly_action_ShowSymbols_label=Show Symbols +Disassembly_action_ShowSimplified_label=Show Simplified Instructions +Disassembly_action_SourceSteppingMode_error=Cannot open editor for file +Disassembly_action_SourceSteppingMode_label=Source Stepping Mode +Disassembly_action_AssemblySteppingMode_label=Assembly Stepping Mode +Disassembly_action_RunToHere_label=Run to Here +Disassembly_action_SetPCToHere_label=Set PC to Here +Disassembly_action_GotoPC_label=Go to Program Counter +Disassembly_action_GotoPC_tooltip=Go to Current Program Counter +Disassembly_action_GotoAddress_label=Go to Address... +Disassembly_action_GotoSymbol_label=Go to Symbol... +Disassembly_action_Copy_label=&Copy +Disassembly_action_SelectAll_label=Select &All +Disassembly_action_BreakpointProperties_label=Breakpoint Properties... +Disassembly_action_RemoveBreakpoint_label=Remove Breakpoint +Disassembly_action_AddBreakpoint_label=Add Breakpoint +Disassembly_action_AddHWBreakpoint_label=Add Hardware Breakpoint +Disassembly_action_AddTracepoint_label=Add Tracepoint... +Disassembly_action_DisableBreakpoint_label=Disable Breakpoint +Disassembly_action_EnableBreakpoint_label=Enable Breakpoint +Disassembly_action_WatchExpression_label=Watch Expression +Disassembly_action_ShowInMemory_label=Show In Memory View +Disassembly_action_RefreshView_label=Re&fresh View +Disassembly_action_OpenPreferences_label=&Preferences... + +Disassembly_GotoAddressDialog_title=Go to Address +Disassembly_GotoAddressDialog_label=Address: +Disassembly_GotoAddressDialog_error_invalid_address=Invalid address +Disassembly_GotoAddressDialog_error_not_a_number=Not a valid number format + +Disassembly_GotoSymbolDialog_title=Go to Symbol +Disassembly_GotoSymbolDialog_label=Symbol: + +Disassembly_message_notConnected=No debug context + +Disassembly_log_error_retrieveFrameAddress=Error retrieving frame address +Disassembly_log_error_locateFile=Unable to locate file:\ +Disassembly_log_error_accessLineInfo=Error accessing line information for:\ +Disassembly_log_error_noFileInfo=Debug information not found for:\ +Disassembly_log_error_fileTooLarge=Source file is too large to retrieve line information:\ +Disassembly_log_error_readFile=Cannot read source file:\ +Disassembly_log_error_createVersion=Cannot create version +Disassembly_log_error_retrieveDisassembly=Cannot retrieve disassembly for:\ +Disassembly_log_error_showDisassembly=Error opening Disassembly +Disassembly_log_error_invalidSymbol=Cannot evaluate symbolic address ''{0}'' + +DisassemblyPreferencePage_startAddress=Start address +DisassemblyPreferencePage_endAddress=End address +DisassemblyPreferencePage_addressRadix=Address display format +DisassemblyPreferencePage_instructionRadix=Instruction display format +DisassemblyPreferencePage_showAddressRadix=Force radix prefixes +DisassemblyPreferencePage_showSource=Show source +DisassemblyPreferencePage_showSymbols=Show symbols +DisassemblyPreferencePage_simplifiedMnemonics=Use simplified instruction mnemonics +DisassemblyPreferencePage_error_not_a_number=Not a valid number format +DisassemblyPreferencePage_error_negative_number=Address cannot be negative +DisassemblyPreferencePage_radix_octal=Octal +DisassemblyPreferencePage_radix_decimal=Decimal +DisassemblyPreferencePage_radix_hexadecimal=Hexadecimal +DisassemblyPreferencePage_showFunctionOffsets=Show function offsets +DisassemblyPreferencePage_showAddress=Show instruction address +DisassemblyPreferencePage_useSourceOnlyMode=Use Disassembly Viewer when debugging in source stepping mode +DisassemblyPreferencePage_useSourceOnlyMode_noteTtitle=Note: +DisassemblyPreferencePage_useSourceOnlyMode_noteMessage=When this option is enabled, the Disassembly Viewer will be used to display source locations in a special "source-only" mode, instead of using the normal Source Editor. +DisassemblyPreferencePage_avoidReadBeforePC=Do not disassemble code before current program counter + +DisassemblyIPAnnotation_primary=Debug Current Instruction Pointer +DisassemblyIPAnnotation_secondary=Debug Call Stack + +SourceReadingJob_name=Reading source file +SourceColorerJob_name=Coloring source file +EditionFinderJob_name=Finding best match for source file +EditionFinderJob_task_get_timestamp=Retrieving module timestamp +EditionFinderJob_task_search_history=Searching local history diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java new file mode 100644 index 00000000000..a8076def62c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyPart.java @@ -0,0 +1,3441 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.core.IAddress; +import org.eclipse.cdt.debug.core.CDIDebugModel; +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyAction; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoAddress; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoProgramCounter; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionGotoSymbol; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.ActionOpenPreferences; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.TextOperationAction; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.Addr2Line; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.BreakpointsAnnotationModel; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.ErrorPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.LabelPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences.DisassemblyPreferenceConstants; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.DisassemblyIPAnnotation; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util.HSL; +import org.eclipse.cdt.dsf.debug.service.IDisassembly; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IInstruction; +import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; +import org.eclipse.cdt.dsf.debug.service.IRunControl; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup; +import org.eclipse.cdt.dsf.debug.service.IStack; +import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; +import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; +import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMData; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.internal.ui.dnd.TextViewerDragAdapter; +import org.eclipse.cdt.utils.Addr64; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ISuspendResume; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.actions.IRunToLineTarget; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.GroupMarker; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.commands.ActionHandler; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IFindReplaceTarget; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextPresentationListener; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.IViewportListener; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.jface.text.source.AnnotationRulerColumn; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IAnnotationAccess; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IAnnotationModelExtension; +import org.eclipse.jface.text.source.IOverviewRuler; +import org.eclipse.jface.text.source.ISharedTextColors; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.text.source.IVerticalRulerExtension; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.text.source.OverviewRuler; +import org.eclipse.jface.text.source.SourceViewerConfiguration; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DragSource; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.FileTransfer; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.ide.IGotoMarker; +import org.eclipse.ui.part.WorkbenchPart; +import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; +import org.eclipse.ui.texteditor.AnnotationPreference; +import org.eclipse.ui.texteditor.ChainedPreferenceStore; +import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess; +import org.eclipse.ui.texteditor.ITextEditorActionConstants; +import org.eclipse.ui.texteditor.IUpdate; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; +import org.eclipse.ui.texteditor.MarkerAnnotationPreferences; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; +import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; + +/** + * DisassemblyPart + */ +@SuppressWarnings("restriction") +public abstract class DisassemblyPart extends WorkbenchPart implements IDisassemblyPart, IViewportListener, ITextPresentationListener, SessionEndedListener { + + private final static boolean DEBUG = "true".equals(Platform.getDebugOption("org.eclipse.cdt.dsf.ui/debug/disassembly")); //$NON-NLS-1$//$NON-NLS-2$ + + /** + * Annotation model attachment key for breakpoint annotations. + */ + private final static String BREAKPOINT_ANNOTATIONS= "breakpoints"; //$NON-NLS-1$ + + private final static BigInteger PC_UNKNOWN = BigInteger.valueOf(-1); + private final static BigInteger PC_RUNNING = BigInteger.valueOf(-2); + + /** Preference key for highlighting current line. */ + private final static String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE; + /** Preference key for highlight color of current line. */ + private final static String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR; + + /** The width of the vertical ruler. */ + protected final static int VERTICAL_RULER_WIDTH = 12; + + /** High water mark for cache */ + private final static int fgHighWaterMark = 500; + /** Low water mark for cache */ + private final static int fgLowWaterMark = 100; + + private static final String COMMAND_ID_GOTO_ADDRESS = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoAddress"; //$NON-NLS-1$ + private static final String COMMAND_ID_GOTO_PC = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoPC"; //$NON-NLS-1$ + private static final String COMMAND_ID_GOTO_SYMBOL = "org.eclipse.cdt.dsf.debug.ui.disassembly.commands.gotoSymbol"; //$NON-NLS-1$ +// private static final String COMMAND_ID_TOGGLE_BREAKPOINT = "org.eclipse.debug.ui.commands.ToggleBreakpoint"; //$NON-NLS-1$ +// private static final String COMMAND_ID_RUN_TO_LINE = "org.eclipse.debug.ui.commands.RunToLine"; //$NON-NLS-1$ +// private static final String COMMAND_ID_TOGGLE_STEPPING_MODE = "org.eclipse.cdt.dsf.debug.ui.debug.ui.menu.showDisassemblyAction"; //$NON-NLS-1$ + + private static final String KEY_BINDING_CONTEXT_DISASSEMBLY = "org.eclipse.cdt.dsf.debug.ui.disassembly.context"; //$NON-NLS-1$ + + protected DisassemblyViewer fViewer; + + protected AbstractDisassemblyAction fActionGotoPC; + protected AbstractDisassemblyAction fActionGotoAddress; + private AbstractDisassemblyAction fActionGotoSymbol; + private AbstractDisassemblyAction fActionToggleBreakpoint; + protected AbstractDisassemblyAction fActionToggleSource; + private AbstractDisassemblyAction fActionToggleFunctionColumn; + private AbstractDisassemblyAction fActionToggleSymbols; + private AbstractDisassemblyAction fActionRefreshView; + private Action fActionOpenPreferences; + private AbstractDisassemblyAction fActionToggleAddressColumn; + private AbstractDisassemblyAction fActionToggleBreakpointEnablement; + + protected DisassemblyDocument fDocument; + private IAnnotationAccess fAnnotationAccess; + private AnnotationRulerColumn fAnnotationRulerColumn; + private MarkerAnnotationPreferences fAnnotationPreferences; + private IPreferenceStore fPreferenceStore; + private IOverviewRuler fOverviewRuler; + private final ListenerList fRulerContextMenuListeners= new ListenerList(ListenerList.IDENTITY); + private SourceViewerDecorationSupport fDecorationSupport; + private Font fFont; + private IVerticalRuler fVerticalRuler; + private IFindReplaceTarget fFindReplaceTarget; + private IPropertyChangeListener fPropertyChangeListener= new PropertyChangeListener(); + private Color fInstructionColor; + private Color fErrorColor; + private Color fSourceColor; + private Color fLabelColor; + private Control fRedrawControl; + private RGB fPCAnnotationRGB; + private Composite fComposite; + + private DropTarget fDropTarget; + private DragSource fDragSource; + private TextViewerDragAdapter fDragSourceAdapter; + private DisassemblyDropAdapter fDropTargetAdapter; + + private FunctionOffsetRulerColumn fOpcodeRulerColumn; + private AddressRulerColumn fAddressRulerColumn; + + private BigInteger fStartAddress; + private BigInteger fEndAddress; + private int fAddressSize= 32; + + private volatile boolean fUpdatePending; + private BigInteger fPCAddress; + private BigInteger fGotoAddressPending= PC_UNKNOWN; + private BigInteger fFocusAddress= PC_UNKNOWN; + private int fBufferZone; + private IExecutionDMContext fTargetContext; + private String fDebugSessionId; + private int fTargetFrame; + private DisassemblyIPAnnotation fPCAnnotation; + private DisassemblyIPAnnotation fSecondaryPCAnnotation; + private boolean fPCAnnotationUpdatePending; + private ArrayList fPendingPCUpdates = new ArrayList(5); + private Position fScrollPos; + private int fScrollLine; + private Position fFocusPos; + private BigInteger fFrameAddress= PC_UNKNOWN; + protected Map fGlobalActions = new HashMap(); + private List fSelectionActions = new ArrayList(); + private List fStateDependentActions = new ArrayList(); + private boolean fSourceOnlyMode; + private boolean fShowSource; + private boolean fShowOpcodes; + private boolean fShowSymbols; + private Map fFile2Storage = new HashMap(); + private boolean fShowDisassembly; + private LinkedList fPCHistory = new LinkedList(); + private int fPCHistorySizeMax = 4; + private boolean fGotoFramePending; + + private String fPCAnnotationColorKey; + + private ArrayList fRunnableQueue = new ArrayList(); + + protected IPartListener2 fPartListener = + new IPartListener2() { + public void partActivated(IWorkbenchPartReference partRef) { + } + public void partBroughtToTop(IWorkbenchPartReference partRef) { + } + public void partClosed(IWorkbenchPartReference partRef) { + } + public void partDeactivated(IWorkbenchPartReference partRef) { + } + public void partOpened(IWorkbenchPartReference partRef) { + } + public void partHidden(IWorkbenchPartReference partRef) { + if (partRef.getPart(false) == DisassemblyPart.this) { + setActive(false); + } + } + public void partVisible(IWorkbenchPartReference partRef) { + if (partRef.getPart(false) == DisassemblyPart.this) { + setActive(true); + } + } + public void partInputChanged(IWorkbenchPartReference partRef) { + } + }; + + private boolean fActive = true; + private boolean fDoPendingPosted; + private boolean fUpdateBeforeFocus; + + private boolean fRefreshAll; + private IMarker fGotoMarkerPending; + private boolean fUpdateTitlePending; + private boolean fRefreshViewPending; + private boolean fUpdateSourcePending; + + private ArrayList fHandlerActivations; + private IContextActivation fContextActivation; + + private DsfServicesTracker fServicesTracker; + private IFrameDMContext fTargetFrameContext; + protected IFrameDMData fTargetFrameData; + + + private final class ActionRefreshView extends AbstractDisassemblyAction { + public ActionRefreshView() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_RefreshView_label); + setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_enabled)); + setDisabledImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Refresh_disabled)); + } + @Override + public void run() { + refreshView(10); + } + } + + private final class ActionToggleAddressColumn extends AbstractDisassemblyAction { + ActionToggleAddressColumn () { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowAddresses_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER, !isAddressRulerVisible()); + } + @Override + public void update() { + setChecked(isAddressRulerVisible()); + } + } + + private final class ActionToggleFunctionColumn extends AbstractDisassemblyAction { + ActionToggleFunctionColumn() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowFunctionOffsets_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS, !isOpcodeRulerVisible()); + } + @Override + public void update() { + setChecked(isOpcodeRulerVisible()); + } + } + + private final class ActionToggleBreakpoint extends AbstractDisassemblyAction { + private IBreakpoint fBreakpoint; + private int fLine; + public ActionToggleBreakpoint() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_AddBreakpoint_label); + setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_ToggleBreakpoint)); + } + @Override + public void run() { + try { + if (fBreakpoint != null) { + fBreakpoint.delete(); + } else { + insertBreakpoint(fLine, false); + } + } catch (CoreException e) { + DsfUIPlugin.getDefault().getLog().log(e.getStatus()); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + fLine = fVerticalRuler.getLineOfLastMouseButtonActivity(); + IBreakpoint[] bps = getBreakpointsAtLine(fLine); + if (bps == null) { + fBreakpoint = null; + setText(DisassemblyMessages.Disassembly_action_AddBreakpoint_label); + } else { + fBreakpoint = bps[0]; + setText(DisassemblyMessages.Disassembly_action_RemoveBreakpoint_label); + } + } + } + } + + private final class ActionToggleBreakpointEnablement extends AbstractDisassemblyAction { + private IBreakpoint fBreakpoint; + public ActionToggleBreakpointEnablement() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label); + } + @Override + public void run() { + try { + fBreakpoint.setEnabled(!fBreakpoint.isEnabled()); + } catch (CoreException e) { + internalError(e); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + int line = fVerticalRuler.getLineOfLastMouseButtonActivity(); + IBreakpoint[] bps = getBreakpointsAtLine(line); + if (bps == null || bps.length == 0) { + setEnabled(false); + } else { + fBreakpoint = bps[0]; + try { + if (fBreakpoint.isEnabled()) { + setText(DisassemblyMessages.Disassembly_action_DisableBreakpoint_label); + } else { + setText(DisassemblyMessages.Disassembly_action_EnableBreakpoint_label); + } + } catch (CoreException e) { + setEnabled(false); + } + } + } + } + } + + private final class ActionToggleSource extends AbstractDisassemblyAction { + public ActionToggleSource() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowSource_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + boolean showSourceEnabled = store.getBoolean(DisassemblyPreferenceConstants.SHOW_SOURCE); + if (showSourceEnabled == fShowSource) { + store.setValue(DisassemblyPreferenceConstants.SHOW_SOURCE, !fShowSource); + } else { + sourceModeChanged(!fShowSource); + } + } + @Override + public void update() { + super.update(); + if (isEnabled()) { + setEnabled(fShowDisassembly); + } + setChecked(fShowSource); + } + } + + private final class ActionToggleSymbols extends AbstractDisassemblyAction { + public ActionToggleSymbols() { + super(DisassemblyPart.this); + setText(DisassemblyMessages.Disassembly_action_ShowSymbols_label); + } + @Override + public void run() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + store.setValue(DisassemblyPreferenceConstants.SHOW_SYMBOLS, !fShowSymbols); + } + @Override + public void update() { + super.update(); + setChecked(fShowSymbols); + } + } + + /** + * Internal property change listener for handling changes in the + * preferences. + */ + class PropertyChangeListener implements IPropertyChangeListener { + /* + * @see IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + handlePreferenceStoreChanged(event); + } + } + + + /** + * The constructor. + */ + public DisassemblyPart() { + fAnnotationPreferences = new MarkerAnnotationPreferences(); + setPreferenceStore(new ChainedPreferenceStore(new IPreferenceStore[] { + DsfUIPlugin.getDefault().getPreferenceStore(), EditorsUI.getPreferenceStore() })); + fPCAddress = fFrameAddress = PC_UNKNOWN; + fTargetFrame = -1; + fBufferZone = 32; + fPCAnnotation = new DisassemblyIPAnnotation(true, 0); + fSecondaryPCAnnotation = new DisassemblyIPAnnotation(false, 0); + IPreferenceStore prefs = getPreferenceStore(); + fStartAddress = new BigInteger(prefs.getString(DisassemblyPreferenceConstants.START_ADDRESS)); + String endAddressString = prefs.getString(DisassemblyPreferenceConstants.END_ADDRESS); + if(endAddressString.startsWith("0x")) //$NON-NLS-1$ + fEndAddress = new BigInteger(endAddressString.substring(2), 16); + else + fEndAddress = new BigInteger(endAddressString, 16); + // TLETODO [disassembly[ source only mode + fSourceOnlyMode = false; //prefs.getBoolean(DisassemblyPreferenceConstants.USE_SOURCE_ONLY_MODE); + fShowSource = fSourceOnlyMode || prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SOURCE); + fShowDisassembly = !fSourceOnlyMode || !fShowSource; + fShowOpcodes = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS); + fShowSymbols = prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_SYMBOLS); + fUpdateBeforeFocus = !prefs.getBoolean(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC); + fPCHistorySizeMax = prefs.getInt(DisassemblyPreferenceConstants.PC_HISTORY_SIZE); + } + + public void logWarning(String message, Throwable error) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, message, error)); + } + + /* + * @see IAdaptable#getAdapter(java.lang.Class) + */ + @Override + @SuppressWarnings("unchecked") + public Object getAdapter(Class required) { + if (IVerticalRulerInfo.class.equals(required)) { + if (fVerticalRuler != null) { + return fVerticalRuler; + } + } else if (IDisassemblyPart.class.equals(required)) { + return this; + } else if (IFindReplaceTarget.class.equals(required)) { + if (fFindReplaceTarget == null) { + fFindReplaceTarget = (fViewer == null ? null : fViewer.getFindReplaceTarget()); + } + return fFindReplaceTarget; + } else if (ITextOperationTarget.class.equals(required)) { + return (fViewer == null ? null : fViewer.getTextOperationTarget()); + } else if (Control.class.equals(required)) { + return fViewer != null ? fViewer.getTextWidget() : null; + } else if (IGotoMarker.class.equals(required)) { + return new IGotoMarker() { + public void gotoMarker(IMarker marker) { + DisassemblyPart.this.gotoMarker(marker); + }}; + } else if (IToggleBreakpointsTarget.class.equals(required)) { + return new IToggleBreakpointsTarget() { + public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + ITextSelection textSelection = (ITextSelection)selection; + int line = textSelection.getStartLine(); + IBreakpoint[] bp = getBreakpointsAtLine(line); + if (bp == null || bp.length == 0) { + insertBreakpoint(line, false); + } else { + for (int i = 0; i < bp.length; i++) { + bp[i].delete(); + } + } + } + public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { + return fDebugSessionId != null; + } + public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) { + return false; + } + public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) { + return false; + }}; + } else if (IRunToLineTarget.class.equals(required)) { + return new IRunToLineTarget() { + public void runToLine(IWorkbenchPart part, ISelection selection, ISuspendResume target) throws CoreException { +// ITextSelection textSelection = (ITextSelection)selection; +// int line = textSelection.getStartLine(); +// BigInteger address = getAddressOfLine(line); + // TLETODO [disassembly] run to line +// getRunControl().runUntil(...); + } + public boolean canRunToLine(IWorkbenchPart part, ISelection selection, ISuspendResume target) { + return fTargetContext != null && isSuspended(fTargetContext) ; + }}; + } + return super.getAdapter(required); + } + + private void setPreferenceStore(IPreferenceStore store) { + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + } + + fPreferenceStore = store; + + if (fPreferenceStore != null) { + fPreferenceStore.addPropertyChangeListener(fPropertyChangeListener); + } + } + + /** + * Handles a property change event describing a change of the editor's + * preference store and updates the preference related editor properties. + *

+ * Subclasses may extend. + *

+ * + * @param event + * the property change event + */ + protected void handlePreferenceStoreChanged(PropertyChangeEvent event) { + + if (fViewer == null) + return; + + String property = event.getProperty(); + IPreferenceStore store = getPreferenceStore(); + + if (getFontPropertyPreferenceKey().equals(property)) { + initializeViewerFont(fViewer); + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER)) { + fActionToggleAddressColumn.update(); + if (isAddressRulerVisible()) { + showAddressRuler(); + } else { + hideAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.ADDRESS_RADIX)) { + if (fAddressRulerColumn != null) { + hideAddressRuler(); + showAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_ADDRESS_RADIX)) { + if (fAddressRulerColumn != null) { + hideAddressRuler(); + showAddressRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_SOURCE)) { + sourceModeChanged(store.getBoolean(property)); + } else if (property.equals(DisassemblyPreferenceConstants.INSTRUCTION_RADIX)) { + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateAddressRange(fStartAddress, fEndAddress, true); + if (!fShowDisassembly) { + fDocument.invalidateDisassemblyWithSource(true); + } + fDocument.setMaxOpcodeLength(0); + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_SYMBOLS)) { + boolean showSymbols = store.getBoolean(property); + if (fShowSymbols == showSymbols) { + return; + } + fShowSymbols = showSymbols; + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateAddressRange(fStartAddress, fEndAddress, true); + if (!fShowDisassembly) { + fDocument.invalidateDisassemblyWithSource(true); + } + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } else if (property.equals(DisassemblyPreferenceConstants.USE_SOURCE_ONLY_MODE)) { + fSourceOnlyMode = store.getBoolean(property); + if (fDebugSessionId != null) { + disassemblyModeChanged(isDissemblyMixedModeOn()); + } + } else if (property.equals(DisassemblyPreferenceConstants.SHOW_FUNCTION_OFFSETS)) { + fShowOpcodes = store.getBoolean(property); + fActionToggleFunctionColumn.update(); + if (isOpcodeRulerVisible()) { + showOpcodeRuler(); + } else { + hideOpcodeRuler(); + } + } else if (property.equals(DisassemblyPreferenceConstants.AVOID_READ_BEFORE_PC)) { + fUpdateBeforeFocus = !store.getBoolean(property); + updateVisibleArea(); + } else if (property.equals(fPCAnnotationColorKey)) { + fPCAnnotationRGB = PreferenceConverter.getColor(store, fPCAnnotationColorKey); + // redraw + for (Iterator it=fPCHistory.iterator(); it.hasNext();) { + AddressRangePosition pos = it.next(); + fViewer.invalidateTextPresentation(pos.offset, pos.length); + } + } else if (property.equals(DisassemblyPreferenceConstants.PC_HISTORY_SIZE)) { + fPCHistorySizeMax = store.getInt(property); + } + } + + /** + * This is a callback that will allow us to create the viewer and initialize + * it. + */ + @Override + public void createPartControl(Composite parent) { + fComposite = parent; + FillLayout layout = new FillLayout(); + layout.marginHeight = 2; + parent.setLayout(layout); + fVerticalRuler = createVerticalRuler(); + int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION; + fViewer = new DisassemblyViewer(parent, fVerticalRuler, getOverviewRuler(), true, styles); + SourceViewerConfiguration sourceViewerConfig = new DisassemblyViewerConfiguration(this); + fViewer.addTextPresentationListener(this); + fViewer.configure(sourceViewerConfig); + fDecorationSupport = new SourceViewerDecorationSupport(fViewer, getOverviewRuler(), getAnnotationAccess(), + getSharedColors()); + configureSourceViewerDecorationSupport(fDecorationSupport); + fDecorationSupport.install(getPreferenceStore()); + if (fPCAnnotationColorKey != null) { + fPCAnnotationRGB = PreferenceConverter.getColor(getPreferenceStore(), fPCAnnotationColorKey); + } else { + fPCAnnotationRGB = parent.getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION).getRGB(); + } + + initializeViewerFont(fViewer); + createActions(); + hookRulerContextMenu(); + hookContextMenu(); + contributeToActionBars(); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + updateSelectionDependentActions(); + } + }); + + fDocument = createDocument(); + fViewer.setDocument(fDocument, new AnnotationModel()); + JFaceResources.getFontRegistry().addListener(fPropertyChangeListener); + + fErrorColor = getSharedColors().getColor(new RGB(96, 0, 0)); + fInstructionColor = getSharedColors().getColor(new RGB(0, 0, 96)); + fSourceColor = getSharedColors().getColor(new RGB(64, 0, 80)); + fLabelColor = getSharedColors().getColor(new RGB(0, 0, 96)); + + if (isAddressRulerVisible()) { + showAddressRuler(); + } + if (isOpcodeRulerVisible()) { + showOpcodeRuler(); + } + initDragAndDrop(); + PlatformUI.getWorkbench().getHelpSystem().setHelp(fViewer.getControl(), IDisassemblyHelpContextIds.DISASSEMBLY_VIEW); + updateTitle(); + updateStateDependentActions(); + + if (fDebugSessionId != null) { + debugContextChanged(); + } else { + updateDebugContext(); + } + DsfSession.addSessionEndedListener(this); + } + + /* + * @see org.eclipse.ui.part.WorkbenchPart#setSite(org.eclipse.ui.IWorkbenchPartSite) + */ + @Override + protected void setSite(IWorkbenchPartSite site) { + super.setSite(site); + site.getPage().addPartListener(fPartListener); + } + + private DisassemblyDocument createDocument() { + DisassemblyDocument doc = new DisassemblyDocument(); + return doc; + } + + /* + * @see org.eclipse.ui.IWorkbenchPart#dispose() + */ + @Override + public void dispose() { + IWorkbenchPartSite site = getSite(); + site.setSelectionProvider(null); + site.getPage().removePartListener(fPartListener); + if (fHandlerActivations != null) { + IHandlerService handlerService = (IHandlerService)site.getService(IHandlerService.class); + handlerService.deactivateHandlers(fHandlerActivations); + fHandlerActivations = null; + } + if (fContextActivation != null) { + IContextService ctxService = (IContextService)site.getService(IContextService.class); + ctxService.deactivateContext(fContextActivation); + } + fViewer = null; + setDebugContext(null); + DsfSession.removeSessionEndedListener(this); + + fAnnotationAccess = null; + fAnnotationPreferences = null; + fAnnotationRulerColumn = null; + fComposite = null; + if (fDecorationSupport != null) { + fDecorationSupport.uninstall(); + fDecorationSupport = null; + } + if (fFont != null) { + fFont.dispose(); + fFont = null; + } + if (fDropTarget != null) { + fDropTarget.dispose(); + fDropTarget = null; + fDragSource.dispose(); + fDragSource = null; + } + if (fPropertyChangeListener != null) { + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(fPropertyChangeListener); + fPreferenceStore = null; + } + fPropertyChangeListener = null; + } + + fDocument.dispose(); + fDocument = null; + super.dispose(); + } + + private void initDragAndDrop() { + if (fDropTarget == null) { + Transfer[] dropTypes = new Transfer[] { FileTransfer.getInstance(), TextTransfer.getInstance() }; + Transfer[] dragTypes = new Transfer[] { TextTransfer.getInstance() }; + Control dropControl = getSourceViewer().getTextWidget(); + Control dragControl = dropControl; + int dropOps = DND.DROP_COPY | DND.DROP_DEFAULT; + int dragOps = DND.DROP_COPY | DND.DROP_DEFAULT; + + fDropTarget = new DropTarget(dropControl, dropOps); + fDropTarget.setTransfer(dropTypes); + fDropTargetAdapter = new DisassemblyDropAdapter(this); + fDropTarget.addDropListener(fDropTargetAdapter); + + fDragSource = new DragSource(dragControl, dragOps); + fDragSource.setTransfer(dragTypes); + fDragSourceAdapter = new TextViewerDragAdapter(getSourceViewer()); + fDragSource.addDragListener(fDragSourceAdapter); + } + } + + private ISourceViewer getSourceViewer() { + return fViewer; + } + + protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) { + Iterator e = fAnnotationPreferences.getAnnotationPreferences().iterator(); + while (e.hasNext()) { + AnnotationPreference pref = (AnnotationPreference)e.next(); + support.setAnnotationPreference(pref); + if (pref.getAnnotationType().equals(fPCAnnotation.getType())) { + fPCAnnotationColorKey = pref.getColorPreferenceKey(); + } + } + support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR); + support.setSymbolicFontName(getFontPropertyPreferenceKey()); + } + + /** + * Returns the symbolic font name for this view as defined in XML. + * + * @return a String with the symbolic font name or null if + * none is defined + */ + private String getSymbolicFontName() { + if (getConfigurationElement() != null) + return getConfigurationElement().getAttribute("symbolicFontName"); //$NON-NLS-1$ + else + return null; + } + + protected final String getFontPropertyPreferenceKey() { + String symbolicFontName = getSymbolicFontName(); + + if (symbolicFontName != null) + return symbolicFontName; + else + return JFaceResources.TEXT_FONT; + } + + /** + * Initializes the given viewer's font. + * + * @param viewer + * the viewer + */ + private void initializeViewerFont(ISourceViewer viewer) { + + boolean isSharedFont = true; + Font font = null; + String symbolicFontName = getSymbolicFontName(); + + if (symbolicFontName != null) + font = JFaceResources.getFont(symbolicFontName); + else if (fPreferenceStore != null) { + // Backward compatibility + if (fPreferenceStore.contains(JFaceResources.TEXT_FONT) + && !fPreferenceStore.isDefault(JFaceResources.TEXT_FONT)) { + FontData data = PreferenceConverter.getFontData(fPreferenceStore, JFaceResources.TEXT_FONT); + + if (data != null) { + isSharedFont = false; + font = new Font(viewer.getTextWidget().getDisplay(), data); + } + } + } + if (font == null) + font = JFaceResources.getTextFont(); + + setFont(viewer, font); + + if (fFont != null) { + fFont.dispose(); + fFont = null; + } + + if (!isSharedFont) + fFont = font; + } + + /** + * Sets the font for the given viewer sustaining selection and scroll + * position. + * + * @param sourceViewer + * the source viewer + * @param font + * the font + */ + private void setFont(ISourceViewer sourceViewer, Font font) { + if (sourceViewer.getDocument() != null) { + + Point selection = sourceViewer.getSelectedRange(); + int topIndex = sourceViewer.getTopIndex(); + + StyledText styledText = sourceViewer.getTextWidget(); + Control parent = styledText; + if (sourceViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension = (ITextViewerExtension) sourceViewer; + parent = extension.getControl(); + } + + parent.setRedraw(false); + + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + + sourceViewer.setSelectedRange(selection.x, selection.y); + sourceViewer.setTopIndex(topIndex); + + if (parent instanceof Composite) { + Composite composite = (Composite) parent; + composite.layout(true); + } + + parent.setRedraw(true); + + } else { + + StyledText styledText = sourceViewer.getTextWidget(); + styledText.setFont(font); + + if (fVerticalRuler instanceof IVerticalRulerExtension) { + IVerticalRulerExtension e = (IVerticalRulerExtension) fVerticalRuler; + e.setFont(font); + } + } + } + + protected IVerticalRuler createVerticalRuler() { + CompositeRuler ruler = createCompositeRuler(); + IPreferenceStore store = getPreferenceStore(); + if (ruler != null && store != null) { + for (Iterator iter = ruler.getDecoratorIterator(); iter.hasNext();) { + IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next(); + if (column instanceof AnnotationRulerColumn) { + fAnnotationRulerColumn = (AnnotationRulerColumn) column; + for (Iterator iter2 = fAnnotationPreferences.getAnnotationPreferences().iterator(); iter2.hasNext();) { + AnnotationPreference preference = (AnnotationPreference) iter2.next(); + String key = preference.getVerticalRulerPreferenceKey(); + boolean showAnnotation = true; + if (key != null && store.contains(key)) + showAnnotation = store.getBoolean(key); + if (showAnnotation) + fAnnotationRulerColumn.addAnnotationType(preference.getAnnotationType()); + } + fAnnotationRulerColumn.addAnnotationType(Annotation.TYPE_UNKNOWN); + break; + } + } + } + return ruler; + } + + /** + * Returns the vertical ruler. + * + * @return the vertical ruler + */ + protected IVerticalRuler getVerticalRuler() { + return fVerticalRuler; + } + + /** + * Returns the overview ruler. + * + * @return the overview ruler + */ + protected IOverviewRuler getOverviewRuler() { + if (fOverviewRuler == null) + fOverviewRuler = createOverviewRuler(getSharedColors()); + return fOverviewRuler; + } + + protected ISharedTextColors getSharedColors() { + return EditorsUI.getSharedTextColors(); + } + + protected IOverviewRuler createOverviewRuler(ISharedTextColors sharedColors) { + IOverviewRuler ruler = new OverviewRuler(getAnnotationAccess(), VERTICAL_RULER_WIDTH, sharedColors); + Iterator e = fAnnotationPreferences.getAnnotationPreferences().iterator(); + while (e.hasNext()) { + AnnotationPreference preference = (AnnotationPreference) e.next(); + if (preference.contributesToHeader()) + ruler.addHeaderAnnotationType(preference.getAnnotationType()); + } + return ruler; + } + + /** + * Creates a new address ruler column that is appropriately initialized. + * + * @return the created line number column + */ + protected IVerticalRulerColumn createAddressRulerColumn() { + fAddressRulerColumn= new AddressRulerColumn(); + initializeRulerColumn(fAddressRulerColumn, DisassemblyPreferenceConstants.ADDRESS_COLOR); + IPreferenceStore prefs = getPreferenceStore(); + fAddressRulerColumn.setRadix(prefs.getInt(DisassemblyPreferenceConstants.ADDRESS_RADIX)); + fAddressRulerColumn.setShowRadixPrefix(prefs.getBoolean(DisassemblyPreferenceConstants.SHOW_ADDRESS_RADIX)); + return fAddressRulerColumn; + } + + /** + * Creates a new ruler column that is appropriately initialized. + * + * @return the created line number column + */ + protected IVerticalRulerColumn createOpcodeRulerColumn() { + fOpcodeRulerColumn= new FunctionOffsetRulerColumn(); + initializeRulerColumn(fOpcodeRulerColumn, DisassemblyPreferenceConstants.OPCODE_COLOR); + return fOpcodeRulerColumn; + } + + /** + * Initializes the given address ruler column from the preference store. + * + * @param rulerColumn the ruler column to be initialized + */ + protected void initializeRulerColumn(DisassemblyRulerColumn rulerColumn, String colorPrefKey) { + ISharedTextColors sharedColors= getSharedColors(); + IPreferenceStore store= getPreferenceStore(); + if (store != null) { + + RGB rgb= null; + // foreground color + if (store.contains(colorPrefKey)) { + if (store.isDefault(colorPrefKey)) + rgb= PreferenceConverter.getDefaultColor(store, colorPrefKey); + else + rgb= PreferenceConverter.getColor(store, colorPrefKey); + } + if (rgb == null) + rgb= new RGB(0, 0, 0); + rulerColumn.setForeground(sharedColors.getColor(rgb)); + + rgb= null; + + rulerColumn.redraw(); + } + } + + + /** + * @return the preference store + */ + private IPreferenceStore getPreferenceStore() { + return fPreferenceStore; + } + + /** + * Creates a composite ruler to be used as the vertical ruler by this + * editor. Subclasses may re-implement this method. + * + * @return the vertical ruler + */ + protected CompositeRuler createCompositeRuler() { + CompositeRuler ruler = new CompositeRuler(); + ruler.addDecorator(0, new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess())); + return ruler; + } + + private boolean isAddressRulerVisible() { + return getPreferenceStore().getBoolean(DisassemblyPreferenceConstants.SHOW_ADDRESS_RULER); + } + + /** + * Shows the address ruler column. + */ + private void showAddressRuler() { + if (fAddressRulerColumn == null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.addDecorator(1, createAddressRulerColumn()); + } + } + } + + /** + * Hides the address ruler column. + */ + private void hideAddressRuler() { + if (fAddressRulerColumn != null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.removeDecorator(fAddressRulerColumn); + } + fAddressRulerColumn = null; + } + } + + private boolean isOpcodeRulerVisible() { + return fShowOpcodes; + } + + /** + * Shows the opcode ruler column. + */ + private void showOpcodeRuler() { + if (fOpcodeRulerColumn == null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.addDecorator(2, createOpcodeRulerColumn()); + } + } + } + + /** + * Hides the opcode ruler column. + */ + private void hideOpcodeRuler() { + if (fOpcodeRulerColumn != null) { + IVerticalRuler v= getVerticalRuler(); + if (v instanceof CompositeRuler) { + CompositeRuler c= (CompositeRuler) v; + c.removeDecorator(fOpcodeRulerColumn); + } + fOpcodeRulerColumn = null; + } + } + + /** + * Returns the annotation access. + * + * @return the annotation access + */ + protected IAnnotationAccess getAnnotationAccess() { + if (fAnnotationAccess == null) + fAnnotationAccess = createAnnotationAccess(); + return fAnnotationAccess; + } + + /** + * Creates the annotation access for this editor. + * + * @return the created annotation access + */ + protected IAnnotationAccess createAnnotationAccess() { + return new DefaultMarkerAnnotationAccess(); + } + + private void hookContextMenu() { + String id = "#DisassemblyPartContext"; //$NON-NLS-1$ + MenuManager menuMgr = new MenuManager(id, id); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + DisassemblyPart.this.fillContextMenu(manager); + } + }); + Menu menu = menuMgr.createContextMenu(fViewer.getTextWidget()); + fViewer.getTextWidget().setMenu(menu); + getSite().registerContextMenu(id, menuMgr, fViewer); + } + + private void hookRulerContextMenu() { + String id = "#DisassemblyPartRulerContext"; //$NON-NLS-1$ + MenuManager menuMgr = new MenuManager(id, id); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(new IMenuListener() { + public void menuAboutToShow(IMenuManager manager) { + DisassemblyPart.this.fillRulerContextMenu(manager); + } + }); + Menu menu = menuMgr.createContextMenu(fVerticalRuler.getControl()); + fVerticalRuler.getControl().setMenu(menu); + getSite().registerContextMenu(id, menuMgr, fViewer); + } + + private void contributeToActionBars() { + IWorkbenchPartSite site = getSite(); + site.setSelectionProvider(fViewer); + IContextService ctxService = (IContextService)site.getService(IContextService.class); + fContextActivation = ctxService.activateContext(KEY_BINDING_CONTEXT_DISASSEMBLY); + contributeToActionBars(getActionBars()); + } + + protected abstract IActionBars getActionBars(); + + protected void contributeToActionBars(IActionBars bars) { + for (Iterator iter = fGlobalActions.keySet().iterator(); iter.hasNext();) { + String key = iter.next(); + IAction action = fGlobalActions.get(key); + bars.setGlobalActionHandler(key, action); + } + IMenuManager menu = bars.getMenuManager(); + IMenuManager navigateMenu= menu.findMenuUsingPath(IWorkbenchActionConstants.M_NAVIGATE); + if (navigateMenu != null) { + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoPC); + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoAddress); + navigateMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fActionGotoSymbol); + } + bars.updateActionBars(); + } + + protected void fillContextMenu(IMenuManager manager) { + Point cursorLoc = getSite().getShell().getDisplay().getCursorLocation(); + fViewer.getTextWidget().toControl(cursorLoc); + fActionToggleSource.update(); + fActionToggleSymbols.update(); + manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$ + manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$ + manager.add(new Separator(IWorkbenchActionConstants.GO_TO)); + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + manager.add(fActionGotoSymbol); + manager.add(new Separator("group.debug")); //$NON-NLS-1$ + manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.COPY)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.SELECT_ALL)); + manager.add(new Separator(ITextEditorActionConstants.GROUP_SETTINGS)); + manager.add(fActionToggleSource); + manager.add(fActionToggleSymbols); + manager.add(fActionOpenPreferences); + manager.add(new Separator()); + manager.add(fActionRefreshView); + // Other plug-ins can contribute their actions here + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + } + + protected void fillRulerContextMenu(IMenuManager manager) { + fActionToggleBreakpoint.update(); + fActionToggleBreakpointEnablement.update(); + fActionToggleAddressColumn.update(); + fActionToggleFunctionColumn.update(); + + manager.add(new GroupMarker("group.top")); // ICommonMenuConstants.GROUP_TOP //$NON-NLS-1$ + manager.add(new Separator("group.breakpoints")); //$NON-NLS-1$ + manager.add(fActionToggleBreakpoint); + manager.add(fActionToggleBreakpointEnablement); + manager.add(new GroupMarker("debug")); //$NON-NLS-1$ + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + manager.add(new GroupMarker(ITextEditorActionConstants.GROUP_RESTORE)); + manager.add(new Separator("add")); //$NON-NLS-1$ + manager.add(new Separator(ITextEditorActionConstants.GROUP_RULERS)); + manager.add(fActionToggleAddressColumn); + manager.add(fActionToggleFunctionColumn); + manager.add(new Separator(ITextEditorActionConstants.GROUP_REST)); + + for (Object listener : fRulerContextMenuListeners.getListeners()) + ((IMenuListener) listener).menuAboutToShow(manager); + + manager.add(new Separator(ITextEditorActionConstants.GROUP_EDIT)); + manager.appendToGroup(ITextEditorActionConstants.GROUP_EDIT, fGlobalActions.get(ITextEditorActionConstants.COPY)); + } + + protected void fillLocalToolBar(IToolBarManager manager) { + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + } + + protected void updateSelectionDependentActions() { + Iterator iterator= fSelectionActions.iterator(); + while (iterator.hasNext()) { + IUpdate action = (IUpdate)iterator.next(); + action.update(); + } + } + + protected void updateStateDependentActions() { + Iterator iterator= fStateDependentActions.iterator(); + while (iterator.hasNext()) { + IUpdate action = iterator.next(); + action.update(); + } + } + + protected void createActions() { + Action action; + action= new TextOperationAction(fViewer, ITextOperationTarget.COPY); + action.setText(DisassemblyMessages.Disassembly_action_Copy_label); + action.setImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_enabled)); + action.setDisabledImageDescriptor(DisassemblyImageRegistry.getImageDescriptor(DisassemblyImageRegistry.ICON_Copy_disabled)); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY); + fGlobalActions.put(ITextEditorActionConstants.COPY, action); + fSelectionActions.add(action); + + action= new TextOperationAction(fViewer, ITextOperationTarget.SELECT_ALL); + action.setText(DisassemblyMessages.Disassembly_action_SelectAll_label); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.SELECT_ALL); + fGlobalActions.put(ITextEditorActionConstants.SELECT_ALL, action); + + action= new TextOperationAction(fViewer, ITextOperationTarget.PRINT); + action.setActionDefinitionId(IWorkbenchActionDefinitionIds.PRINT); + fGlobalActions.put(ITextEditorActionConstants.PRINT, action); + + fActionGotoPC = new ActionGotoProgramCounter(this); + fActionGotoPC.setActionDefinitionId(COMMAND_ID_GOTO_PC); + fStateDependentActions.add(fActionGotoPC); + registerWithHandlerService(fActionGotoPC); + + fActionGotoAddress = new ActionGotoAddress(this); + fActionGotoAddress.setActionDefinitionId(COMMAND_ID_GOTO_ADDRESS); + fStateDependentActions.add(fActionGotoAddress); + registerWithHandlerService(fActionGotoAddress); + + fActionGotoSymbol = new ActionGotoSymbol(this); + fActionGotoSymbol.setActionDefinitionId(COMMAND_ID_GOTO_SYMBOL); + fStateDependentActions.add(fActionGotoSymbol); + registerWithHandlerService(fActionGotoSymbol); + + fActionToggleSource = new ActionToggleSource(); + fStateDependentActions.add(fActionToggleSource); + fActionToggleBreakpoint = new ActionToggleBreakpoint(); +// fActionToggleBreakpoint.setActionDefinitionId(COMMAND_ID_TOGGLE_BREAKPOINT); +// registerWithHandlerService(fActionToggleBreakpoint); + fVerticalRuler.getControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseDoubleClick(MouseEvent e) { + fActionToggleBreakpoint.update(); + if (fActionToggleBreakpoint.isEnabled()) { + fActionToggleBreakpoint.run(); + } + } + }); + fActionToggleBreakpointEnablement = new ActionToggleBreakpointEnablement(); + fActionToggleAddressColumn = new ActionToggleAddressColumn(); + fActionToggleFunctionColumn = new ActionToggleFunctionColumn(); + fActionToggleSymbols = new ActionToggleSymbols(); +// fActionSourceSteppingMode.setActionDefinitionId(COMMAND_ID_TOGGLE_STEPPING_MODE); +// registerWithHandlerService(fActionSourceSteppingMode); + fActionRefreshView = new ActionRefreshView(); + fStateDependentActions.add(fActionRefreshView); + fGlobalActions.put(ActionFactory.REFRESH.getId(), fActionRefreshView); + fActionOpenPreferences = new ActionOpenPreferences(getSite().getShell()); + } + + /** + * Register given action with the handler service for key bindings. + * + * @param action + */ + private void registerWithHandlerService(IAction action) { + if (fHandlerActivations == null) { + fHandlerActivations = new ArrayList(5); + } + IHandlerService handlerService = (IHandlerService)getSite().getService(IHandlerService.class); + fHandlerActivations.add(handlerService.activateHandler(action.getActionDefinitionId(), new ActionHandler(action))); + } + + private void gotoFrame(IFrameDMContext frame) { + if (fActive) { + gotoFrame(frame.getLevel(), PC_UNKNOWN); + } + } + + private void gotoFrame(int frame) { + if (fActive) { + gotoFrame(frame, PC_UNKNOWN); + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoProgramCounter() + */ + public final void gotoProgramCounter() { + if (fPCAddress != PC_RUNNING) { + updatePC(fPCAddress); + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoAddress(java.math.BigInteger) + */ + public final void gotoAddress(BigInteger address) { + fFocusAddress = address; + if (fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("gotoAddress " + getAddressText(address)); //$NON-NLS-1$ + if (fGotoAddressPending == PC_UNKNOWN) { + fGotoAddressPending = address; + } + if (fUpdatePending) { + return; + } + AddressRangePosition pos = getPositionOfAddress(address); + if (pos != null) { + if (pos.fValid) { + AddressRangePosition previousPos = /* fUpdateBeforeFocus ? getPositionOfAddress(pos.fAddressOffset-1): */ null; + if (previousPos == null || previousPos.fValid) { + if (fGotoAddressPending.equals(address)) { + fGotoAddressPending = PC_UNKNOWN; + } + gotoPosition(pos, false); + } else { + int lines = fBufferZone+3; + BigInteger endAddress = pos.fAddressOffset; + BigInteger startAddress = previousPos.fAddressOffset.max( + endAddress.subtract(BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()))); + retrieveDisassembly(startAddress, endAddress, lines); + } + } else { + int lines = fBufferZone+3; + BigInteger endAddress = pos.fAddressOffset.add(pos.fAddressLength).min( + address.add(BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()))); + retrieveDisassembly(address, endAddress, lines); + } + } + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#gotoSymbol(java.lang.String) + */ + public final void gotoSymbol(final String symbol) { + if (!fActive || !isSuspended() || fTargetFrameContext == null) { + return; + } + final IExpressions expressions= getService(IExpressions.class); + if (expressions == null) { + return; + } + IExpressionDMContext exprDmc= expressions.createExpression(fTargetContext, '&'+symbol); + final FormattedValueDMContext valueDmc= expressions.getFormattedValueContext(exprDmc, IFormattedValues.HEX_FORMAT); + final DsfExecutor executor= getSession().getExecutor(); + executor.submit(new Runnable() { + public void run() { + expressions.getFormattedExpressionValue(valueDmc, new DataRequestMonitor(executor, null) { + @Override + protected void handleSuccess() { + FormattedValueDMData data= getData(); + final String value= data.getFormattedValue(); + final BigInteger address= decodeAddress(value); + if (address != null) { + asyncExec(new Runnable() { + public void run() { + gotoAddress(address); + }}); + } + } + @Override + protected void handleError() { + asyncExec(new Runnable() { + public void run() { + ErrorDialog.openError(getSite().getShell(), "Error", null, getStatus()); //$NON-NLS-1$ + }}); + } + }); + }}); + } + + private void gotoPosition(Position pos, boolean select) { + if (fViewer == null) { + return; + } + setFocusPosition(pos); + fViewer.setSelectedRange(pos.offset, select ? Math.max(pos.length-1, 0) : 0); + int revealOffset = pos.offset; + boolean onTop = false; + if (/* !fUpdateBeforeFocus && */ pos.offset > 0) { + try { + AddressRangePosition previousPos = fDocument.getModelPosition(pos.offset - 1); + if (previousPos instanceof LabelPosition) { + revealOffset = previousPos.offset; + onTop = true; + } else if (!previousPos.fValid) { + onTop = true; + } + } catch (BadLocationException e) { + // cannot happen + } + } + fViewer.revealOffset(revealOffset, onTop); + } + + private void gotoMarker(final IMarker marker) { + if (marker == null) { + return; + } + if (fDebugSessionId == null || fUpdatePending) { + fGotoMarkerPending = marker; + return; + } + fGotoMarkerPending = null; + + //TLETODO [disassembly] goto (breakpoint) marker + } + + /* + * @see org.eclipse.jface.text.IViewportListener#viewportChanged(int) + */ + public void viewportChanged(int verticalOffset) { + if (fDebugSessionId != null && fGotoAddressPending == PC_UNKNOWN && fScrollPos == null && !fUpdatePending && !fRefreshViewPending) { + fUpdatePending = true; + invokeLater(new Runnable() { + public void run() { + assert fUpdatePending; + if (fUpdatePending) { + fUpdatePending = false; + updateVisibleArea(); + } + } + }); + } + } + + /** + * Update lines of currently visible area + one page buffer zone below. + */ + private void updateVisibleArea() { + if (!fActive || fUpdatePending || fViewer == null || fDebugSessionId == null) { + return; + } + if (fTargetContext == null || !isSuspended(fTargetContext) || fFrameAddress == PC_UNKNOWN) { + return; + } + StyledText styledText = fViewer.getTextWidget(); + Rectangle clientArea = styledText.getClientArea(); + fBufferZone = Math.max(8, clientArea.height / styledText.getLineHeight()); + int topIndex = fViewer.getTopIndex(); + int bottomIndex = fViewer.getBottomIndex(); + int focusIndex = -1; + boolean focusVisible = false; + boolean isScrollingUp = fViewer.isUserTriggeredScrolling() && fViewer.getLastTopPixel() >= styledText.getTopPixel(); + if (fFocusPos != null) { + try { + int focusOffset = fFocusPos.offset; + focusIndex = fDocument.getLineOfOffset(focusOffset); + focusVisible = focusIndex >= topIndex && focusIndex <= bottomIndex; + // workaround for: Clicking the IP annotation in the right ruler has no effect. + // we deselect the IP location if it is scrolled outside the visible area + if (!focusVisible) { + Point selection = fViewer.getSelectedRange(); + if (selection.x == focusOffset && selection.y > 0) { + fViewer.setSelectedRange(selection.x, 0); + } + } + } catch (BadLocationException e) { + setFocusPosition(null); + } + } + if (!focusVisible) { + focusIndex = topIndex + fScrollLine; + } + BigInteger focusAddress = getAddressOfLine(focusIndex); + bottomIndex += 2; + AddressRangePosition bestPosition = null; + int bestLine = -1; + BigInteger bestDistance = null; + Iterator it = fDocument.getInvalidAddressRanges().iterator(); + while (it.hasNext()) { + AddressRangePosition p = it.next(); + try { + int line = fDocument.getLineOfOffset(p.offset); + if (line >= topIndex && line <= bottomIndex) { + if (p instanceof DisassemblyPosition || p.fAddressLength.compareTo( + BigInteger.valueOf(fBufferZone * 2)) <= 0) { + // small areas and known areas are OK to update + } else if (!isScrollingUp && !fUpdateBeforeFocus + && p.fAddressOffset.compareTo(focusAddress) < 0) { + continue; + } + BigInteger distance = p.fAddressOffset.subtract(focusAddress).abs(); + if (bestDistance == null || distance.compareTo(bestDistance) < 0) { + bestPosition = p; + bestLine = line; + bestDistance = distance; + if (bestDistance.compareTo(BigInteger.valueOf(fBufferZone * 2)) <= 0) { + break; + } + } + } + } catch (BadLocationException e) { + continue; + } + } + if (bestPosition != null) { + int lines = fBufferZone+3; + BigInteger startAddress = bestPosition.fAddressOffset; + BigInteger endAddress = bestPosition.fAddressOffset.add(bestPosition.fAddressLength); + BigInteger addressRange = BigInteger.valueOf(lines * fDocument.getMeanSizeOfInstructions()); + if (bestLine > focusIndex || bestLine == focusIndex && startAddress.compareTo(focusAddress) >= 0) { + // insert at start of range + if (endAddress.subtract(startAddress).compareTo(addressRange) < 0) { + // try to increase range to reduce number of requests + Iterator iter = fDocument.getModelPositionIterator(endAddress); + while (iter.hasNext()) { + AddressRangePosition p = (AddressRangePosition)iter.next(); + if (p.fValid) { + endAddress = endAddress.add(p.fAddressLength); + if (endAddress.subtract(startAddress).compareTo(addressRange) >= 0) { + break; + } + } else { + break; + } + } + } + } else { + // insert at end of range + startAddress = startAddress.max(endAddress.subtract(addressRange)); + // make sure we get all disassembly lines until endAddress + lines = endAddress.subtract(startAddress).intValue(); + } + retrieveDisassembly(startAddress, endAddress, lines); + } + scheduleDoPending(); + } + + private void asyncExec(Runnable runnable) { + if (fViewer != null) { + fViewer.getControl().getDisplay().asyncExec(runnable); + } + } + private void invokeLater(Runnable runnable) { + invokeLater(10, runnable); + } + private void invokeLater(int delay, Runnable runnable) { + if (fViewer != null) { + fViewer.getControl().getDisplay().timerExec(delay, runnable); + } + } + + /** + * Insert sourcelines if available. + */ + /*default*/ void updateInvalidSource() { + if (fViewer == null) { + return; + } + boolean unlock = false; + try { + if (fScrollPos == null) { + if (fUpdatePending) { + fUpdateSourcePending= true; + return; + } + fUpdateSourcePending= false; + unlock = true; + fUpdatePending = true; + lockScroller(); + } + ArrayList copy = new ArrayList(fDocument.getInvalidSource()); + Iterator it = copy.iterator(); + while (it.hasNext()) { + SourcePosition p = it.next(); + if (!p.fValid) { + insertSource(p); + } else if (DEBUG && fDocument.getInvalidSource().remove(p)) { + System.err.println("!!! valid source position in invalid source list at "+getAddressText(p.fAddressOffset)); //$NON-NLS-1$ + } + } + } finally { + if (unlock) { + fUpdatePending = false; + unlockScroller(); + doPending(); + } + } + } + + /** + * Show disassembly for given (source) file. + * + * @param file + * @param lines + */ + void retrieveDisassembly(final String file, final int lines, final boolean mixed) { + if (fDebugSessionId == null) { + return; + } + if (fUpdatePending) { + invokeLater(new Runnable() { + public void run() { + retrieveDisassembly(file, lines, mixed); + }}); + return; + } + if (DEBUG) System.out.println("retrieveDisassembly "+file); //$NON-NLS-1$ + String debuggerPath= file; + + // try reverse lookup + final ISourceLookup lookup= getService(ISourceLookup.class); + final ISourceLookupDMContext ctx= DMContexts.getAncestorOfType(fTargetContext, ISourceLookupDMContext.class); + final DsfExecutor executor= getSession().getExecutor(); + Query query= new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + final DataRequestMonitor request= new DataRequestMonitor(executor, rm) { + @Override + protected void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }; + lookup.getDebuggerPath(ctx, file, request); + } + }; + try { + getSession().getExecutor().execute(query); + debuggerPath= query.get(); + } catch (InterruptedException exc) { + internalError(exc); + } catch (ExecutionException exc) { + internalError(exc); + } + + final IDisassembly disassembly= fServicesTracker.getService(IDisassembly.class); + final IDisassemblyDMContext context= DMContexts.getAncestorOfType(fTargetContext, IDisassemblyDMContext.class); + + final String finalFile= debuggerPath; + final DataRequestMonitor disassemblyRequest= new DataRequestMonitor(executor, null) { + @Override + public void handleCompleted() { + final IMixedInstruction[] data= getData(); + if (!isCanceled() && data != null) { + asyncExec(new Runnable() { + public void run() { + if (!insertDisassembly(null, data)) { + // retry in non-mixed mode + retrieveDisassembly(file, lines, false); + } + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + ErrorDialog.openError(getSite().getShell(), "Error", null, getStatus()); //$NON-NLS-1$ + } + }); + } + fUpdatePending= false; + } + } + }; + assert !fUpdatePending; + fUpdatePending = true; + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, finalFile, 1, lines, disassemblyRequest); + }}); + } + + private void retrieveDisassembly(BigInteger startAddress, BigInteger endAddress, int lines) { + if (fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("retrieveDisassembly "+getAddressText(startAddress)+" "+lines+" lines"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + retrieveDisassembly(startAddress, endAddress, lines, true); + } + + private void retrieveDisassembly(final BigInteger startAddress, BigInteger endAddress, final int linesHint, boolean mixed) { + assert !fUpdatePending; + fUpdatePending = true; + final int lines= linesHint + 2; + final BigInteger addressLength= BigInteger.valueOf(lines * 4); + if (endAddress.subtract(startAddress).compareTo(addressLength) > 0) { + endAddress= startAddress.add(addressLength); + } + boolean insideActiveFrame= startAddress.equals(fFrameAddress); + String file= null; + int lineNumber= -1; + if (insideActiveFrame && fTargetFrameData != null) { + file= fTargetFrameData.getFile(); + if (file != null && file.trim().length() == 0) { + file= null; + } + lineNumber= fTargetFrameData.getLine(); + } + final String finalFile= file; + final int finalLineNumber= lineNumber; + final BigInteger finalEndAddress= endAddress; + + final DsfExecutor executor= getSession().getExecutor(); + final IDisassembly disassembly= fServicesTracker.getService(IDisassembly.class); + final IDisassemblyDMContext context= DMContexts.getAncestorOfType(fTargetContext, IDisassemblyDMContext.class); + + if (mixed) { + final DataRequestMonitor disassemblyRequest= new DataRequestMonitor(executor, null) { + @Override + public void handleCompleted() { + final IMixedInstruction[] data= getData(); + if (!isCanceled() && data != null) { + asyncExec(new Runnable() { + public void run() { + if (!insertDisassembly(startAddress, data)) { + // retry in non-mixed mode + retrieveDisassembly(startAddress, finalEndAddress, linesHint, false); + } + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + doScrollLocked(new Runnable() { + public void run() { + insertError(startAddress, status.getMessage()); + } + }); + }}); + } + fUpdatePending= false; + } + } + }; + if (file != null) { + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, finalFile, finalLineNumber, lines*2, disassemblyRequest); + }}); + } else { + executor.submit(new Runnable() { + public void run() { + disassembly.getMixedInstructions(context, startAddress, finalEndAddress, disassemblyRequest); + }}); + } + } else { + final DataRequestMonitor disassemblyRequest= new DataRequestMonitor(executor, null) { + @Override + public void handleCompleted() { + if (!isCanceled() && getData() != null) { + asyncExec(new Runnable() { + public void run() { + insertDisassembly(startAddress, getData()); + }}); + } else { + final IStatus status= getStatus(); + if (status != null && !status.isOK()) { + asyncExec(new Runnable() { + public void run() { + doScrollLocked(new Runnable() { + public void run() { + insertError(startAddress, status.getMessage()); + } + }); + }}); + } + fUpdatePending= false; + } + } + }; + if (file != null) { + executor.submit(new Runnable() { + public void run() { + disassembly.getInstructions(context, finalFile, finalLineNumber, lines, disassemblyRequest); + }}); + } else { + executor.submit(new Runnable() { + public void run() { + disassembly.getInstructions(context, startAddress, finalEndAddress, disassemblyRequest); + }}); + } + } + } + + private void insertError(BigInteger address, String message) { + AddressRangePosition p = null; + p = getPositionOfAddress(address); + if (p.fValid) { + return; + } + try { + fDocument.insertErrorLine(p, address, BigInteger.ONE, message); + } catch (BadLocationException exc) { + internalError(exc); + } + } + + private void insertDisassembly(BigInteger startAddress, IInstruction[] instructions) { + if (fViewer == null || fDebugSessionId == null) { + return; + } + if (DEBUG) System.out.println("insertDisassembly "+getAddressText(startAddress)); //$NON-NLS-1$ + assert fUpdatePending; + if (!fUpdatePending) { + // safe-guard in case something weird is going on + return; + } + try { + lockScroller(); + + AddressRangePosition p= null; + for (int j = 0; j < instructions.length; j++) { + IInstruction instruction = instructions[j]; + BigInteger address= instruction.getAdress(); + if (startAddress == null || startAddress.compareTo(BigInteger.ZERO) < 0) { + fGotoAddressPending = startAddress = address; + } + if (p == null || !p.containsAddress(address)) { + p = getPositionOfAddress(address); + } + if (p instanceof ErrorPosition && p.fValid) { + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else if (p == null || p.fValid) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + return; + } + boolean hasSource= false; + String compilationPath= null; + // insert symbol label + final String functionName= instruction.getFuntionName(); + if (functionName != null && functionName.length() > 0 && instruction.getOffset() == 0) { + p = fDocument.insertLabel(p, address, functionName, fShowSymbols && (!hasSource || fShowDisassembly)); + } + // determine instruction byte length + BigInteger instrLength= null; + if (j < instructions.length - 1) { + instrLength= instructions[j+1].getAdress().subtract(instruction.getAdress()).abs(); + } else if (instructions.length == 1) { + if (p.fAddressLength.compareTo(BigInteger.valueOf(8)) <= 0) { + instrLength= p.fAddressLength; + } + } + if (instrLength == null) { + // cannot determine length of last instruction + break; + } + final String opCode; + // insert function name+offset instead of opcode bytes + if (functionName != null && functionName.length() > 0) { + opCode= functionName + '+' + instruction.getOffset(); + } else { + opCode= ""; //$NON-NLS-1$ + } + p = fDocument.insertDisassemblyLine(p, address, instrLength.intValue(), opCode, instruction.getInstruction(), compilationPath, -1); + if (p == null) { + break; + } + } + + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + fUpdatePending = false; + updateInvalidSource(); + unlockScroller(); + doPending(); + updateVisibleArea(); + } + } + + private boolean insertDisassembly(BigInteger startAddress, IMixedInstruction[] mixedInstructions) { + if (fViewer == null || fDebugSessionId == null) { + return true; + } + if (DEBUG) System.out.println("insertDisassembly "+getAddressText(startAddress)); //$NON-NLS-1$ + assert fUpdatePending; + if (!fUpdatePending) { + // safe-guard in case something weird is going on + return true; + } + // indicates whether disassembly for the start address was inserted + boolean success= false; + try { + lockScroller(); + + AddressRangePosition p= null; + for (int i = 0; i < mixedInstructions.length; ++i) { + IMixedInstruction mixedInstruction= mixedInstructions[i]; + final String file= mixedInstruction.getFileName(); + final int lineNumber= mixedInstruction.getLineNumber() - 1; + IInstruction[] instructions= mixedInstruction.getInstructions(); + for (int j = 0; j < instructions.length; ++j) { + IInstruction instruction = instructions[j]; + BigInteger address= instruction.getAdress(); + if (startAddress == null || startAddress.compareTo(BigInteger.ZERO) < 0) { + fGotoAddressPending = startAddress = address; + } + if (p == null || !p.containsAddress(address)) { + p = getPositionOfAddress(address); + } + if (p instanceof ErrorPosition && p.fValid) { + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else if (p == null) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + return success; + } else if (p.fValid) { + if (DEBUG) System.out.println("Excess disassembly lines at " + getAddressText(address)); //$NON-NLS-1$ + if (file != null && lineNumber >= 0 || p.fAddressLength == BigInteger.ONE) { + // override probably unaligned disassembly + p.fValid = false; + fDocument.getInvalidAddressRanges().add(p); + } else { + return success; + } + } + boolean hasSource= false; + if (file != null && lineNumber >= 0) { + p = insertSource(p, address, file, lineNumber); + hasSource = fFile2Storage.get(file) != null; + } + // insert symbol label + final String functionName= instruction.getFuntionName(); + if (functionName != null && functionName.length() > 0 && instruction.getOffset() == 0) { + p = fDocument.insertLabel(p, address, functionName, fShowSymbols && (!hasSource || fShowDisassembly)); + } + // determine instruction byte length + BigInteger instrLength= null; + if (j < instructions.length - 1) { + instrLength= instructions[j+1].getAdress().subtract(instruction.getAdress()).abs(); + } else if (i < mixedInstructions.length - 1) { + int nextSrcLineIdx= i+1; + while (nextSrcLineIdx < mixedInstructions.length) { + IInstruction[] nextInstrs= mixedInstructions[nextSrcLineIdx].getInstructions(); + if (nextInstrs.length > 0) { + instrLength= nextInstrs[0].getAdress().subtract(instruction.getAdress()).abs(); + break; + } + ++nextSrcLineIdx; + } + if (nextSrcLineIdx >= mixedInstructions.length) { + break; + } + } else if (instructions.length == 1) { + if (p.fAddressLength.compareTo(BigInteger.valueOf(8)) <= 0) { + instrLength= p.fAddressLength; + } + } + if (instrLength == null) { + // cannot determine length of last instruction + break; + } + final String opCode; + // insert function name+offset instead of opcode bytes + if (functionName != null && functionName.length() > 0) { + opCode= functionName + '+' + instruction.getOffset(); + } else { + opCode= ""; //$NON-NLS-1$ + } + success= success || address.compareTo(startAddress) == 0; + p = fDocument.insertDisassemblyLine(p, address, instrLength.intValue(), opCode, instruction.getInstruction(), file, lineNumber); + if (p == null && success) { + break; + } + } + } + + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + fUpdatePending = false; + if (success) { + updateInvalidSource(); + unlockScroller(); + doPending(); + updateVisibleArea(); + } else { + unlockScroller(); + } + } + return success; + } + + private void retrieveFrameAddress(final IExecutionDMContext targetContext, final int frame) { + if (targetContext != null && isSuspended(targetContext)) { + if (fUpdatePending) { + gotoFrame(frame); + return; + } + if (DEBUG) System.out.println("retrieveFrameAddress "+frame); //$NON-NLS-1$ + fUpdatePending = true; + final IStack stack= fServicesTracker.getService(IStack.class); + final DsfExecutor executor= getSession().getExecutor(); + if (fTargetFrameContext == null) { + if (frame == 0) { + final DataRequestMonitor request= new DataRequestMonitor(executor, null) { + @Override + protected void handleCompleted() { + fUpdatePending= false; + fTargetFrameContext= getData(); + if (fTargetFrameContext != null) { + retrieveFrameAddress(targetContext, frame); + } + } + }; + executor.submit(new Runnable() { + public void run() { + stack.getTopFrame(targetContext, request); + }}); + } else { + // TODO retrieve other stack frame + } + return; + } + final DataRequestMonitor request= new DataRequestMonitor(executor, null) { + @Override + protected void handleCompleted() { + if (!isCanceled()) { + fUpdatePending= false; + final IFrameDMData frameData= getData(); + fTargetFrameData= frameData; + final IAddress address= frameData.getAddress(); + final BigInteger addressValue= address.getValue(); + if (DEBUG) System.out.println("retrieveFrameAddress done "+getAddressText(addressValue)); //$NON-NLS-1$ + asyncExec(new Runnable() { + public void run() { + if (address.getSize() * 4 > fAddressSize) { + addressSizeChanged(address.getSize() * 4); + } + if (frame == 0) { + updatePC(addressValue); + } else { + gotoFrame(frame, addressValue); + } + } + + }); + } + } + }; + executor.submit(new Runnable() { + public void run() { + stack.getFrameData(fTargetFrameContext, request); + }}); + } + } + + private void addressSizeChanged(int addressSize) { + BigInteger oldEndAddress= fEndAddress; + fEndAddress= BigInteger.ONE.shiftLeft(addressSize); + int oldAddressSize= fAddressSize; + fAddressSize= addressSize; + if (addressSize < oldAddressSize) { + fDocument.deleteDisassemblyRange(fEndAddress, oldEndAddress, true, true); + List positions= fDocument.getInvalidAddressRanges(); + List toRemove= new ArrayList(); + for (AddressRangePosition position : positions) { + if (position.fAddressOffset.compareTo(fEndAddress) >= 0) { + try { + fDocument.replace(position, position.length, ""); //$NON-NLS-1$ + fDocument.removeModelPosition(position); + toRemove.add(position); + } catch (BadLocationException exc) { + internalError(exc); + } + } else if (position.containsAddress(fEndAddress)){ + position.fAddressLength= fEndAddress.subtract(position.fAddressOffset); + } + } + positions.removeAll(toRemove); + } else if (addressSize > oldAddressSize) { + fDocument.insertInvalidAddressRange(fDocument.getLength(), 0, oldEndAddress, fEndAddress); + } else { + return; + } + if (fAddressRulerColumn != null) { + fAddressRulerColumn.setAddressSize(addressSize); + if (fComposite != null) { + fComposite.layout(true); + } + } + } + + private AddressRangePosition getPositionOfAddress(BigInteger address) { + if (address == null || address.compareTo(BigInteger.ZERO) < 0) { + return null; + } + AddressRangePosition pos = fDocument.getPositionOfAddress(address); + assert !(pos instanceof SourcePosition); + assert pos != null || address.compareTo(fStartAddress) < 0|| address.compareTo(fEndAddress) >= 0; + return pos; + } + + private BigInteger getAddressOfLine(int line) { + return fDocument.getAddressOfLine(line); + } + + /** + * Passing the focus request to the viewer's control. + */ + @Override + public void setFocus() { + fViewer.getControl().setFocus(); + } + + protected void setActive(boolean active) { + if (DEBUG) System.out.println("setActive("+ active +")"); //$NON-NLS-1$ //$NON-NLS-2$ + fActive = active; + if (fActive) { + if (fRefreshAll) { + fRefreshAll = false; + refreshView(0); + } else { + doPendingPCUpdates(); + if (fTargetContext != null) { + int frame = getActiveStackFrame(); + if (frame < 0 && isSuspended(fTargetContext)) { + frame= 0; + } + if (frame != fTargetFrame) { + gotoFrame(frame); + } + } + } + } else { + fGotoAddressPending= fFocusAddress= PC_UNKNOWN; + } + firePropertyChange(PROP_ACTIVE); + } + + private int getActiveStackFrame() { + if (fTargetFrameContext != null) { + return fTargetFrameContext.getLevel(); + } + return -1; + } + + /** + * + */ + protected void updateDebugContext() { + IAdaptable debugContext= DebugUITools.getDebugContext(); + if (debugContext instanceof IDMVMContext) { + setDebugContext((IDMVMContext)debugContext); + } + } + + protected void setDebugContext(IDMVMContext vmContext) { + if (vmContext != null) { + IDMContext dmContext= vmContext.getDMContext(); + String sessionId= dmContext.getSessionId(); + if (!sessionId.equals(fDebugSessionId)) { + // switch to different session or initiate session + if (DEBUG) System.out.println("DisassemblyPart.setDebugContext() " + sessionId); //$NON-NLS-1$ + fTargetContext= null; + if (dmContext instanceof IFrameDMContext) { + IFrameDMContext frame= (IFrameDMContext) dmContext; + IExecutionDMContext executionContext= DMContexts.getAncestorOfType(frame, IExecutionDMContext.class); + if (executionContext != null) { + fTargetContext= executionContext; + fTargetFrameContext= frame; + fTargetFrame= frame.getLevel(); + } + } + if (fTargetContext != null) { + if (fDebugSessionId != null) { + if (getSession() != null) { + getSession().removeServiceEventListener(this); + } + } + fDebugSessionId= sessionId; + if (fServicesTracker != null) { + fServicesTracker.dispose(); + } + fServicesTracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), sessionId); + if (fViewer != null) { + debugContextChanged(); + } + } + } else if (dmContext instanceof IFrameDMContext) { + // switch to different frame + IFrameDMContext frame= (IFrameDMContext) dmContext; + final IDMContext[] parents= frame.getParents(); + for (IDMContext context : parents) { + if (context instanceof IExecutionDMContext) { + fTargetContext= (IExecutionDMContext) context; + fTargetFrameContext= frame; + gotoFrame(frame); + break; + } + } + } + } else if (fDebugSessionId != null) { + if (getSession() != null) { + getSession().removeServiceEventListener(this); + } + fDebugSessionId= null; + fTargetContext= null; + if (fServicesTracker != null) { + fServicesTracker.dispose(); + fServicesTracker= null; + } + if (fViewer != null) { + debugContextChanged(); + } + } + } + + private void debugContextChanged() { + if (DEBUG) System.out.println("DisassemblyPart.debugContextChanged()"); //$NON-NLS-1$ + fRunnableQueue.clear(); + fUpdatePending = false; + resetViewer(); + if (fDebugSessionId != null) { + final DsfSession session= getSession(); + session.addServiceEventListener(this, null); + updatePC(PC_UNKNOWN); + + if (fGotoAddressPending != PC_UNKNOWN) { + gotoAddress(fGotoAddressPending); + } + if (fGotoMarkerPending != null) { + gotoMarker(fGotoMarkerPending); + } + fViewer.addViewportListener(this); + } else { + fViewer.removeViewportListener(this); + fGotoMarkerPending = null; +// invokeLater(new Runnable() { +// public void run() { +// closePart(); +// }}); + } + updateTitle(); + updateStateDependentActions(); + firePropertyChange(PROP_CONNECTED); + firePropertyChange(PROP_SUSPENDED); + } + + /* + * @see org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener#sessionEnded(org.eclipse.cdt.dsf.service.DsfSession) + */ + public void sessionEnded(DsfSession endedSsession) { + if (endedSsession.getId().equals(fDebugSessionId)) { + asyncExec(new Runnable() { + public void run() { + setDebugContext(null); + }}); + } + } + + @DsfServiceEventHandler + public void handleEvent(IExitedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + setDebugContext(null); + }}); + } + } + + @DsfServiceEventHandler + public void handleEvent(ISuspendedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + updatePC(PC_UNKNOWN); + firePropertyChange(PROP_SUSPENDED); + } + }); + } + } + + @DsfServiceEventHandler + public void handleEvent(IResumedDMEvent event) { + final IExecutionDMContext context= event.getDMContext(); + if (context.equals(fTargetContext) + || DMContexts.isAncestorOf(fTargetContext, context)) { + asyncExec(new Runnable() { + public void run() { + updatePC(PC_RUNNING); + firePropertyChange(PROP_SUSPENDED); + } + }); + } + } + + private void attachBreakpointsAnnotationModel() { + IAnnotationModel annotationModel = fViewer.getAnnotationModel(); + if (annotationModel instanceof IAnnotationModelExtension) { + IAnnotationModelExtension ame= (IAnnotationModelExtension) annotationModel; + ame.addAnnotationModel(BREAKPOINT_ANNOTATIONS, new BreakpointsAnnotationModel()); + } + } + + private void refreshView(int delay) { + if (fViewer == null || fRefreshViewPending || fRefreshAll) { + return; + } + fRunnableQueue.clear(); + fRefreshViewPending = true; + final long refreshViewScheduled = System.currentTimeMillis() + delay; + final Runnable refresh = new Runnable() { + public void run() { + fRefreshViewPending = false; + long now = System.currentTimeMillis(); + if (now >= refreshViewScheduled) { + if (DEBUG) System.err.println("*** refreshing view ***"); //$NON-NLS-1$ + fFocusAddress = PC_UNKNOWN; + int targetFrame= fTargetFrame; + resetViewer(); + if (fScrollPos != null) { + fScrollPos.isDeleted = true; + } + gotoFrame(targetFrame); + } else { + refreshView((int)(refreshViewScheduled - now)); + } + }}; + if (delay > 0) { + invokeLater(delay, new Runnable() { + public void run() { + doScrollLocked(refresh); + }}); + } else { + doScrollLocked(refresh); + } + } + + private void resetViewer() { + // clear all state and cache + fPCAnnotationUpdatePending = false; + fGotoFramePending = false; + fPCAddress = fFrameAddress = PC_RUNNING; + fTargetFrame = -1; + fGotoAddressPending = fFocusAddress; + fFocusAddress = PC_UNKNOWN; + setFocusPosition(null); + fPCHistory.clear(); + fPendingPCUpdates.clear(); + fFile2Storage.clear(); + DisassemblyDocument doc= fDocument; + fDocument = createDocument(); + fViewer.setDocument(fDocument, new AnnotationModel()); + doc.dispose(); + if (fDebugSessionId != null) { + attachBreakpointsAnnotationModel(); + fDocument.insertInvalidAddressRange(0, 0, fStartAddress, fEndAddress); + } + } + + private AddressRangePosition getPCPosition(BigInteger address) { + if (address.compareTo(BigInteger.ZERO) < 0) { + // invalid address + return null; + } + AddressRangePosition pos = getPositionOfAddress(address); + if (pos == null || !pos.fValid) { + // invalid disassembly line + return null; + } + if (pos.length > 0) { + // valid disassembly line + return pos; + } + // hidden disassembly + if (!(pos instanceof DisassemblyPosition)) { + return pos; + } + String srcFile = ((DisassemblyPosition)pos).getFile(); + if (srcFile == null) { + return pos; + } + SourceFileInfo fi = fDocument.getSourceInfo(srcFile); + if (fi == null) { + return pos; + } + if (fi.fSource == null) { + if (fi.fError != null) { + // could not read source + return pos; + } + return null; + } +// if (!fi.fValid) { +// // need line info first +// return null; +// } + // determine stmt line of source range + try { + int stmtLine = ((DisassemblyPosition)pos).getLine(); + if (stmtLine < 0) { + return pos; + } + BigInteger stmtAddress = fi.fLine2Addr[stmtLine]; + if (stmtAddress.compareTo(BigInteger.ZERO) < 0) { + return pos; + } + SourcePosition srcPos = fDocument.getSourcePosition(stmtAddress); + if (srcPos == null) { + return pos; + } else if (!srcPos.fValid) { + return null; + } + assert stmtLine >= srcPos.fLine; + int baseOffset = fi.fSource.getLineOffset(srcPos.fLine); + IRegion stmtLineRegion = fi.fSource.getLineInformation(stmtLine); + int lineOffset = stmtLineRegion.getOffset(); + int offset = srcPos.offset + lineOffset - baseOffset; + int length = stmtLineRegion.getLength() + 1; + if (offset >= srcPos.offset && offset < srcPos.offset + srcPos.length) { + return new AddressRangePosition(offset, length, address, BigInteger.ZERO); + } + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * Update the annotation indicating the given address. + * @return a position which denotes the documents position + */ + private AddressRangePosition updateAddressAnnotation(Annotation annotation, BigInteger address) { + IAnnotationModel annotationModel = fViewer.getAnnotationModel(); + annotationModel.removeAnnotation(annotation); + AddressRangePosition pos = getPCPosition(address); + if (pos != null) { + annotationModel.addAnnotation(annotation, new Position(pos.offset, Math.max(0, pos.length-1))); + } + return pos; + } + + public IBreakpoint[] getBreakpointsAtLine(int line) { + BreakpointsAnnotationModel bpModel= null; + IAnnotationModel am= fViewer.getAnnotationModel(); + if (am instanceof IAnnotationModelExtension) { + IAnnotationModelExtension ame= (IAnnotationModelExtension) am; + bpModel= (BreakpointsAnnotationModel) ame.getAnnotationModel(BREAKPOINT_ANNOTATIONS); + if (bpModel != null) { + IRegion lineRegion; + try { + lineRegion= fDocument.getLineInformation(line); + } catch (BadLocationException exc) { + return null; + } + int offset= lineRegion.getOffset(); + int length= lineRegion.getLength(); + @SuppressWarnings("unchecked") + Iterator it= bpModel.getAnnotationIterator(offset, length, true, true); + List bpList= new ArrayList(5); + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + while (it.hasNext()) { + final SimpleMarkerAnnotation annotation= it.next(); + IBreakpoint bp= bpMgr.getBreakpoint(annotation.getMarker()); + if (bp != null) { + bpList.add(bp); + } + } + if (bpList.size() > 0) { + return bpList.toArray(new IBreakpoint[bpList.size()]); + } + } + } + return null; + } + + private void gotoFrame(int frame, BigInteger address) { + if (DEBUG) System.out.println("gotoFrame " + frame + " " + getAddressText(address)); //$NON-NLS-1$ //$NON-NLS-2$ + fTargetFrame = frame; + fFrameAddress = address; + if (fTargetFrame == -1) { + fTargetFrame = getActiveStackFrame(); + if (fTargetFrame < 0 && isSuspended(fTargetContext)) { + fTargetFrame= 0; + } + if (fTargetFrame == -1) { + fGotoFramePending = false; + return; + } + } + fGotoFramePending = true; + if (frame == 0) { + fPCAddress = fFrameAddress; + } + if (fFrameAddress.compareTo(PC_UNKNOWN) == 0) { + if (!fUpdatePending) { + fGotoFramePending = false; + retrieveFrameAddress(fTargetContext, fTargetFrame); + } + return; + } + AddressRangePosition pcPos = updatePCAnnotation(); + if (pcPos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0) { + pcPos = getPCPosition(fFrameAddress); + if (pcPos == null) { + gotoAddress(fFrameAddress); + return; + } + } + if (pcPos != null) { + if (frame == 0) { + addToPCHistory(pcPos); + } + fGotoFramePending = false; + if (fGotoAddressPending == fFrameAddress) { + fGotoAddressPending = PC_UNKNOWN; + } +// if (DEBUG) System.out.println("pc updated "+getAddressText(address)); //$NON-NLS-1$ + gotoPosition(pcPos, false); + updateVisibleArea(); + } else { + // give up + fGotoFramePending = false; + fGotoAddressPending = PC_UNKNOWN; + } + doPendingPCUpdates(); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isActive() + */ + public final boolean isActive() { + return fActive; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isConnected() + */ + public final boolean isConnected() { + return fDebugSessionId != null && fTargetContext != null; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#isSuspended() + */ + public final boolean isSuspended() { + return isConnected() && isSuspended(fTargetContext); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#getTextViewer() + */ + public final ISourceViewer getTextViewer() { + return fViewer; + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#addRulerContextMenuListener(org.eclipse.jface.action.IMenuListener) + */ + public final void addRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.add(listener); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart#removeRulerContextMenuListener(org.eclipse.jface.action.IMenuListener) + */ + public final void removeRulerContextMenuListener(IMenuListener listener) { + fRulerContextMenuListeners.remove(listener); + } + + private boolean isSuspended(IExecutionDMContext targetContext) { + return getRunControl().isSuspended(targetContext); + } + + private IRunControl getRunControl() { + return getService(IRunControl.class); + } + + /*default*/ DsfSession getSession() { + return DsfSession.getSession(fDebugSessionId); + } + + /*default*/ V getService(Class serviceClass) { + if (fServicesTracker != null) { + return fServicesTracker.getService(serviceClass); + } + return null; + } + + /*default*/ IFrameDMContext getTargetFrameContext() { + return fTargetFrameContext; + } + + /** + * Schedule the retrieval of a module time stamp for the given address. + * Should return a Long object in case the value was computed, + * another object to be waited on if the retrieval is in progress, null + * if no time stamp could be retrieved. + * + * @param address + * @return Long, Object or null + */ + synchronized Object retrieveModuleTimestamp(BigInteger address) { + // TLETODO [disassembly] retrieve and cache module time stamp + return null; + } + + private void setFocusPosition(Position pcPos) { + if (fFocusPos != null) { + fDocument.removePosition(fFocusPos); + fFocusPos = null; + } + if (pcPos != null) { + fFocusPos = new Position(pcPos.offset, pcPos.length); + try { + fDocument.addPosition(fFocusPos); + } catch (BadLocationException e) { + internalError(e); + } + } else { + fFocusAddress = PC_UNKNOWN; + } + } + + private void doPendingPCUpdates() { + if (fPendingPCUpdates.isEmpty()) { + return; + } + BigInteger pc; + do { + pc = fPendingPCUpdates.remove(0); + if (pc.compareTo(BigInteger.ZERO) >= 0) { + break; + } + } while (!fPendingPCUpdates.isEmpty()); + gotoFrame(0, pc); + } + + private void addToPCHistory(AddressRangePosition pcPos) { + if (DEBUG) System.out.println("addToPCHistory "+getAddressText(pcPos.fAddressOffset)); //$NON-NLS-1$ + if (fPCHistorySizeMax <= 1) { + return; + } + AddressRangePosition first = null; + if (fPCHistory.size() > 0) { + first = fPCHistory.getFirst(); + if (first.fAddressOffset == pcPos.fAddressOffset) { + if (first.offset != pcPos.offset || first.length != pcPos.length) { + fPCHistory.removeFirst(); + fViewer.invalidateTextPresentation(first.offset, first.length); + } else { + return; + } + } + } + // clone and add + pcPos = new AddressRangePosition(pcPos.offset, pcPos.length, pcPos.fAddressOffset, BigInteger.ZERO); + fPCHistory.addFirst(pcPos); + try { + fDocument.addPosition(pcPos); + } catch (BadLocationException e) { + internalError(e); + } + // limit to max size + if (fPCHistory.size() > fPCHistorySizeMax) { + AddressRangePosition last = fPCHistory.removeLast(); + fDocument.removePosition(last); + fViewer.invalidateTextPresentation(last.offset, last.length); + } + // redraw + for (Iterator it=fPCHistory.iterator(); it.hasNext();) { + AddressRangePosition pos = it.next(); + fViewer.invalidateTextPresentation(pos.offset, pos.length); + } + } + + /** + * Update current pc. If a pc update is currently under way, adds this + * address to a list of pending pc updates. + * + * @param pc Current pc address. -1 means retrieve pc from top frame, -2 + * means target resumed + */ + private void updatePC(BigInteger pc) { + if (!fPendingPCUpdates.isEmpty()) { + BigInteger last = fPendingPCUpdates.get(fPendingPCUpdates.size()-1); + if (last.compareTo(BigInteger.ZERO) < 0) { + fPendingPCUpdates.remove(fPendingPCUpdates.size()-1); + } + } + fPendingPCUpdates.add(pc); + if (fPendingPCUpdates.size() > fPCHistorySizeMax) { + if (!fActive) { + // if not active, we can savely remove + // the pc updates before the history range + fPendingPCUpdates.remove(0); + } + // we ignore the current goto frame request + // and continue with the pending updates + fGotoFramePending = false; + } + if (fActive) { + if (fGotoFramePending) { + if (!fUpdatePending) { + gotoFrame(0, fFrameAddress); + } + } else { + doPendingPCUpdates(); + } + } + } + + private AddressRangePosition updatePCAnnotation() { + if (fUpdatePending) { + fPCAnnotationUpdatePending = true; + return null; + } + AddressRangePosition pos; + if (fTargetFrame == 0) { + // clear secondary + updateAddressAnnotation(fSecondaryPCAnnotation, PC_UNKNOWN); + // set primary + pos = updateAddressAnnotation(fPCAnnotation, fPCAddress); + } else { + // clear primary + updateAddressAnnotation(fPCAnnotation, PC_UNKNOWN); + // set secondary + pos = updateAddressAnnotation(fSecondaryPCAnnotation, fFrameAddress); + } + fPCAnnotationUpdatePending = pos == null && fFrameAddress.compareTo(BigInteger.ZERO) >= 0; + return pos; + } + + private void scheduleDoPending() { + if (!fUpdatePending && !fDoPendingPosted) { + fDoPendingPosted = true; + invokeLater(new Runnable() { + public void run() { + doPending(); + fDoPendingPosted = false; + } + }); + } + } + + private void doPending() { + if (fViewer == null || fDocument == null) { + return; + } + if (fUpdateSourcePending) { + updateInvalidSource(); + } + boolean sourceValid= fDocument.getInvalidSource().isEmpty(); + if (sourceValid || fShowDisassembly) { + if (fGotoFramePending) { + gotoFrame(fTargetFrame, fFrameAddress); + } + } + if (sourceValid) { + if (fGotoAddressPending != PC_UNKNOWN) { + gotoAddress(fGotoAddressPending); + } else if (fGotoMarkerPending != null) { + gotoMarker(fGotoMarkerPending); + } + if (fPCAnnotationUpdatePending && !fGotoFramePending) { + updatePCAnnotation(); + } + if (fUpdateTitlePending) { + updateTitle(); + } + } + } + + /** + * Safely run given runnable in a state when no update is pending. + * Delays execution by 10 ms if update is currently pending. + * @param doit + */ + private void doScrollLocked(final Runnable doit) { + if (fViewer == null || fDebugSessionId == null) { + // disposed + return; + } + if (!fActive) { + // refresh all when becoming active again + fRefreshViewPending= false; + fRefreshAll = true; + return; + } + if (doit != null) { + fRunnableQueue.add(doit); + } + if (fUpdatePending) { + if (fRunnableQueue.size() == 1) { + Runnable doitlater = new Runnable() { + public void run() { + doScrollLocked(null); + }}; + invokeLater(doitlater); + } + } else { + fUpdatePending = true; + lockScroller(); + try { + ArrayList copy = new ArrayList(fRunnableQueue); + fRunnableQueue.clear(); + for (Iterator iter = copy.iterator(); iter.hasNext();) { + Runnable doitnow = iter.next(); + try { + doitnow.run(); + } catch(Exception e) { + internalError(e); + } + } + } finally { + fUpdatePending = false; + unlockScroller(); + doPending(); + updateVisibleArea(); + } + } + } + + private void lockScroller() { + assert fScrollPos == null; + if (isOpcodeRulerVisible()) { + fRedrawControl = fViewer.getControl(); + } else { + fRedrawControl = fViewer.getTextWidget(); + } + fRedrawControl.setRedraw(false); + try { + int topOffset = fViewer.getTopIndexStartOffset(); + int topIndex = fViewer.getTopIndex(); + int bottomIndex = fViewer.getBottomIndex(); + int bottomOffset = fViewer.getBottomIndexEndOffset(); + int focusLine; + int focusOffset; + if (fFocusPos != null && fFocusPos.isDeleted) { + fFocusPos = null; + } + if (fFocusPos != null && fFocusPos.offset >= topOffset && fFocusPos.offset <= bottomOffset) { + focusOffset = fFocusPos.offset; + focusLine = fDocument.getLineOfOffset(focusOffset); + } else { + focusLine = Math.max(0, (topIndex + bottomIndex) / 2); + focusOffset = fDocument.getLineOffset(focusLine); + AddressRangePosition pos = fDocument.getDisassemblyPosition(focusOffset); + if (pos != null && !pos.fValid) { + // don't lock position of invalid range + focusOffset = pos.offset+pos.length; + focusLine = fDocument.getLineOfOffset(focusOffset); + } + } + fScrollPos = new Position(focusOffset); + fScrollLine = focusLine - topIndex; + fDocument.addPosition(fScrollPos); + } catch (BadLocationException e) { + // should not happen + internalError(e); + } + } + + private void unlockScroller() { + try { + if (fScrollPos == null) { + return; + } + if (fScrollPos.isDeleted) { + fScrollPos.isDeleted = false; + if (fScrollPos.offset >= fDocument.getLength()) { + fScrollPos.offset = 0; + fScrollLine = 0; + } + } + if (fFocusPos != null && (fFocusPos.isDeleted || fFocusPos.length == 0)) { + if (fFocusAddress.compareTo(BigInteger.ZERO) >= 0) { + fGotoAddressPending = fFocusAddress; + setFocusPosition(getPositionOfAddress(fFocusAddress)); + } + } + int topLine = fDocument.getLineOfOffset(fScrollPos.offset) - fScrollLine; + // limit text size + int lineCount = fDocument.getNumberOfLines(); + if (lineCount > fgHighWaterMark*fBufferZone) { + int startLine = Math.max(0, topLine-fgLowWaterMark/2*fBufferZone); + int endLine = Math.min(lineCount-1, topLine+fgLowWaterMark/2*fBufferZone); + fDocument.deleteLineRange(endLine, lineCount-1); + fDocument.deleteLineRange(0, startLine); + } + int lineHeight = fViewer.getTextWidget().getLineHeight(); + int topPixel = topLine * lineHeight; + if (Math.abs(fViewer.getTextWidget().getTopPixel() - topPixel) >= lineHeight) { + fViewer.setTopIndex(topLine); + } + } catch (BadLocationException e) { + // should not happen + internalError(e); + } finally { + if (fScrollPos != null && fDocument != null) { + fDocument.removePosition(fScrollPos); + fScrollPos = null; + } + if (fViewer != null) { + fRedrawControl.setRedraw(true); + getVerticalRuler().update(); + getOverviewRuler().update(); + } + } + } + + private void insertSource(SourcePosition pos) { + if (!fShowSource) { + fDocument.insertSource(pos, "", pos.fLine, true); //$NON-NLS-1$ + return; + } + SourceFileInfo fi = pos.fFileInfo; + BigInteger address = pos.fAddressOffset; + int lineNr = pos.fLine; + if (fi.fError != null) { + // handled below + } else if (fi.fValid) { +// assert fi.fLinesNode.isValid(); + Addr2Line a2l = fi.fAddr2Line[Addr2Line.hash(address, fi.fAddr2Line.length)]; + while (a2l != null && !a2l.addr.equals(address)) + a2l = a2l.next; + if (a2l != null) { + int first = a2l.first; + int line; + for (line = first; line <= a2l.last; ++line) { + if (!fi.fLine2Addr[line].equals(address)) { + if (line > first) { + String source = fi.getLines(first, line-1); + pos = fDocument.insertSource(pos, source, first, false); + } + first = line+1; + } + } + if (line > first) { + String source = fi.getLines(first, line-1); + fDocument.insertSource(pos, source, first, true); + if (source.length() == 0) { + fDocument.removeSourcePosition(pos); + } + } else if (first > a2l.first) { + fDocument.insertSource(pos, "", first, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } else { + // no source at all + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } else if (fi.fLinesNode == null) { + // TLETODO [disassembly] asynchronous line info + if (fi.fSource != null) { + fi.fError= new Error(); + } + } + if (fi.fError != null && !pos.fValid) { + if (fi.fSource != null) { + if (fi.fSource != null && lineNr >= 0 && lineNr < fi.fSource.getNumberOfLines()) { + fi.fStartAddress = fi.fStartAddress.min(pos.fAddressOffset); + fi.fEndAddress = fi.fEndAddress.max(pos.fAddressOffset.add(pos.fAddressLength)); + if (fi.fLine2Addr[lineNr] == null || fi.fLine2Addr[lineNr].compareTo(BigInteger.ZERO) < 0) { + fi.fLine2Addr[lineNr] = pos.fAddressOffset; + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else if (fi.fLine2Addr[lineNr].compareTo(pos.fAddressOffset) > 0) { + SourcePosition oldPos = fDocument.getSourcePosition(fi.fLine2Addr[lineNr]); + if (oldPos != null) { + try { + fDocument.replace(oldPos, oldPos.length, null); + } catch (BadLocationException e) { + internalError(e); + } + fDocument.removeSourcePosition(oldPos); + } + fi.fLine2Addr[lineNr] = pos.fAddressOffset; + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else if (fi.fLine2Addr[lineNr].equals(pos.fAddressOffset)) { + String sourceLine = fi.getLine(lineNr); + fDocument.insertSource(pos, sourceLine, lineNr, true); + } else { + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } + } else { + // no source at all + fDocument.insertSource(pos, "", lineNr, true); //$NON-NLS-1$ + fDocument.removeSourcePosition(pos); + } + } + } + + private void updateTitle() { + if (fDebugSessionId == null) { + String descr = DisassemblyMessages.Disassembly_message_notConnected; + String title = getConfigurationElement().getAttribute("name"); //$NON-NLS-1$ + setPartName(title); + setContentDescription(descr); + setTitleToolTip(title); + } else { + // TLETODO Proper content description + setContentDescription(""); //$NON-NLS-1$ + } + } + + private boolean isDissemblyMixedModeOn() { + // TLETODO [disassembly] mixed mode on/off + return true; + } + + /** + * Close this part + */ + protected abstract void closePart(); + + /* + * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation) + */ + @SuppressWarnings("unchecked") + public void applyTextPresentation(TextPresentation textPresentation) { + IRegion coverage = textPresentation.getExtent(); + if (coverage == null) { + coverage= new Region(0, fDocument.getLength()); + } + int startOffset = coverage.getOffset(); + int length = coverage.getLength(); + int endOffset = startOffset + length; + Iterator it; + try { + // make sure we start with first overlapping position + AddressRangePosition pos = fDocument.getModelPosition(startOffset); + assert pos != null; + if (pos == null) { + return; + } + it = fDocument.getPositionIterator(DisassemblyDocument.CATEGORY_MODEL, pos.offset); + } catch (BadPositionCategoryException e) { + return; + } catch (BadLocationException e) { + return; + } + ArrayList styleRanges = new ArrayList(); + while(it.hasNext()) { + AddressRangePosition pos = (AddressRangePosition)it.next(); + if (pos.offset >= endOffset) { + break; + } + if (pos.offset+pos.length <= startOffset) { + continue; + } + if (pos.fValid && pos.length > 0) { + if (pos instanceof DisassemblyPosition) { + DisassemblyPosition disPos = (DisassemblyPosition)pos; + styleRanges.add(new StyleRange(pos.offset, disPos.length, fInstructionColor, null, SWT.NULL)); + } else if (pos instanceof ErrorPosition) { + styleRanges.add(new StyleRange(pos.offset, pos.length, fErrorColor, null, SWT.NULL)); + } else if (pos instanceof LabelPosition) { + styleRanges.add(new StyleRange(pos.offset, pos.length, fLabelColor, null, SWT.BOLD)); + } else if (pos instanceof SourcePosition) { + SourcePosition srcPos = (SourcePosition)pos; + TextPresentation presentation = null; + if (srcPos.fFileInfo.fSource != null) { + presentation = srcPos.fFileInfo.getPresentation(srcPos.fFileInfo.getRegion(srcPos.fLine, pos.length)); + } + if (presentation != null) { + // clip result window to coverage + int start = Math.max(startOffset, srcPos.offset); + int end = Math.min(endOffset, srcPos.offset + srcPos.length); + int srcOffset = srcPos.fFileInfo.getLineOffset(srcPos.fLine); + int clipOffset = start - srcPos.offset; + presentation.setResultWindow(new Region(srcOffset + clipOffset, end-start)); + for (Iterator iter = presentation.getNonDefaultStyleRangeIterator(); iter.hasNext();) { + StyleRange styleRange = iter.next(); + styleRange.start += srcPos.offset + clipOffset; + styleRanges.add(styleRange); + } + } else { + styleRanges.add(new StyleRange(pos.offset, pos.length, fSourceColor, null, SWT.NULL)); + } + } + } + } + if (styleRanges.size() > 0) { + for (Iterator iter = styleRanges.iterator(); iter.hasNext();) { + textPresentation.addStyleRange(iter.next()); + } + } + // update pc history trail + if (fPCHistory.size() > 1) { + HSL hsv = new HSL(fPCAnnotationRGB); + double luminanceStep = (1-hsv.luminance)/(fPCHistorySizeMax+1); + hsv.luminance = 1 - luminanceStep * (fPCHistorySizeMax - fPCHistory.size()); + for (ListIterator listIt = fPCHistory.listIterator(fPCHistory.size()); listIt.hasPrevious();) { + AddressRangePosition pcPos = listIt.previous(); + hsv.luminance -= luminanceStep; + if (pcPos.isDeleted) { + listIt.remove(); + continue; + } + if (!pcPos.fValid) { + continue; + } + if (pcPos.overlapsWith(startOffset, length)) { + RGB rgb = hsv.toRGB(); + Color pcColor = getSharedColors().getColor(rgb); + Color textColor = null; + // experimental: if color is dark, use white (background) as text color +// Color textColor = hsv.luminance < 0.7 ? fViewer.getTextWidget().getBackground() : null; + textPresentation.mergeStyleRange(new StyleRange(pcPos.offset, pcPos.length, textColor, pcColor)); + } + } + } + } + + + private IBreakpoint insertBreakpoint(int line, boolean edit) throws CoreException { + SourcePosition srcPos = null; + try { + int lineOffset = fDocument.getLineOffset(line); + srcPos = fDocument.getSourcePosition(lineOffset); + } catch (BadLocationException e) { + // should not happen, but its safe to ignore anyway + } + boolean lineBreakpoint = srcPos != null && srcPos.length > 0; + + IResource resource; + ICBreakpoint bp; + + if (lineBreakpoint) { + SourceFileInfo srcInfo = srcPos.fFileInfo; + String filePath = null; + resource = (IResource)srcInfo.fFile.getAdapter(IResource.class); + if (resource != null) { + final IPath location= resource.getLocation(); + if (location == null) { + return null; + } + filePath = location.toOSString(); + } else { + resource = ResourcesPlugin.getWorkspace().getRoot(); + filePath = srcInfo.fFile.getFullPath().toOSString(); + } + BigInteger address = srcPos.fAddressOffset; + AddressRangePosition pos = fDocument.getDisassemblyPosition(address); + int srcLine = -1; + if (pos instanceof DisassemblyPosition) { + srcLine = ((DisassemblyPosition)pos).getLine(); + } + bp= CDIDebugModel.createLineBreakpoint(filePath, resource, srcLine + 1, true, 0, "", true); //$NON-NLS-1$ + } else { + resource = ResourcesPlugin.getWorkspace().getRoot(); + BigInteger address = getAddressOfLine(line); + bp= CDIDebugModel.createAddressBreakpoint(null, null, resource, new Addr64(address), true, 0, "", true); //$NON-NLS-1$ + } + + return bp; + } + + private AddressRangePosition insertSource(AddressRangePosition pos, BigInteger address, final String file, int lineNr) { + Object sourceElement = null; + if (fFile2Storage.containsKey(file)) { + sourceElement = fFile2Storage.get(file); + } else { + final ISourceLookup lookup= getService(ISourceLookup.class); + final ISourceLookupDMContext ctx= DMContexts.getAncestorOfType(fTargetContext, ISourceLookupDMContext.class); + final DsfExecutor executor= getSession().getExecutor(); + Query query= new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + final DataRequestMonitor request= new DataRequestMonitor(executor, rm) { + @Override + protected void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }; + lookup.getSource(ctx, file, request); + } + }; + try { + getSession().getExecutor().execute(query); + sourceElement= query.get(); + } catch (InterruptedException exc) { + internalError(exc); + } catch (ExecutionException exc) { + internalError(exc); + } + if (sourceElement instanceof File) { + sourceElement = new LocalFileStorage((File)sourceElement); + } + if (sourceElement instanceof IStorage) { + fFile2Storage.put(file, sourceElement); + } else { + fFile2Storage.put(file, null); + logWarning(DisassemblyMessages.Disassembly_log_error_locateFile+file, null); + } + } + if (sourceElement instanceof IStorage) { + SourceFileInfo fi = fDocument.getSourceInfo((IStorage)sourceElement); + if (fi == null) { + IStorage storage = (IStorage)sourceElement; + Display display = getSite().getShell().getDisplay(); + Runnable done = new SourceColorerJob(display, storage, this); + fi = fDocument.createSourceInfo(file, storage, done); + EditionFinderJob editionJob = null; + if (storage instanceof IFile) { + editionJob = new EditionFinderJob(fi, address, this); + editionJob.schedule(); + } + fi.fReadingJob.schedule(); + } + pos = fDocument.insertInvalidSource(pos, address, fi, lineNr); + } + return pos; + } + + private void disassemblyModeChanged(boolean isDisassemblyOn) { + if (fShowDisassembly == isDisassemblyOn) { + return; + } + if (fShowDisassembly && !fSourceOnlyMode) { + // if not in source-only mode, do not update if disassembly mode is disabled + return; + } + fShowDisassembly = isDisassemblyOn; + if (!fShowDisassembly) { + sourceModeChanged(true); + } + fActionToggleSource.update(); + Runnable doit = new Runnable() { + public void run() { + fDocument.invalidateDisassemblyWithSource(!fShowDisassembly); + fGotoFramePending = true; + }}; + doScrollLocked(doit); + } + + /** + * Turn on/off source mode. + * @param isSourceModeOn + */ + private void sourceModeChanged(boolean isSourceModeOn) { + if (fShowSource == isSourceModeOn) { + return; + } + fShowSource = isSourceModeOn; + fActionToggleSource.update(); + fDocument.invalidateSource(); + if (!fShowSource && !fShowDisassembly) { + disassemblyModeChanged(true); + } else { + fPCAnnotationUpdatePending = true; + updateInvalidSource(); + } + } + + public static BigInteger decodeAddress(String string) { + if (string.startsWith("0x")) { //$NON-NLS-1$ + return new BigInteger(string.substring(2), 16); + } + return new BigInteger(string); + } + + private static String getAddressText(BigInteger address) { + if (address == null) { + return ""; //$NON-NLS-1$ + } + if (address.compareTo(BigInteger.ZERO) < 0) { + return address.toString(); + } + String hex = address.toString(16); + return "0x" + "0000000000000000".substring(hex.length() + (address.bitLength() <= 32 ? 8 : 0)) + hex; //$NON-NLS-1$ //$NON-NLS-2$ + } + + static void internalError(Throwable e) { + if (DEBUG) { + System.err.println("Disassembly: Internal error"); //$NON-NLS-1$ + e.printStackTrace(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java new file mode 100644 index 00000000000..83015f86bef --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyRulerColumn.java @@ -0,0 +1,985 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.util.Arrays; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.IViewportListener; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; + +/** + * Vertical ruler column for use with disassembly parts. + *

+ * Derived from {@link org.eclipse.jface.text.source.LineNumberRulerColumn}. + *

+ */ +public class DisassemblyRulerColumn implements IVerticalRulerColumn { + protected final static String DOTS = "......................................................................"; //$NON-NLS-1$ + protected final static String SPACES = " "; //$NON-NLS-1$ + + /** + * Internal listener class. + */ + class InternalListener implements IViewportListener, ITextListener, ISelectionChangedListener { + + /* + * @see IViewportListener#viewportChanged(int) + */ + public void viewportChanged(int verticalPosition) { + if (verticalPosition != fScrollPos) + redraw(); + } + + /* + * @see ITextListener#textChanged(TextEvent) + */ + public void textChanged(TextEvent event) { + + if (updateNumberOfDigits()) { + computeIndentations(); + layout(event.getViewerRedrawState()); + return; + } + + if (!event.getViewerRedrawState()) + return; + + if (fSensitiveToTextChanges || event.getDocumentEvent() == null) + postRedraw(); + + } + + /* + * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) + */ + public void selectionChanged(SelectionChangedEvent event) { + postRedraw(); + } + } + + /** + * Handles all the mouse interaction in this line number ruler column. + */ + class MouseHandler implements MouseListener, MouseMoveListener, MouseTrackListener { + + /** The cached view port size */ + private int fCachedViewportSize; + /** The area of the line at which line selection started */ + private IRegion fStartLine; + /** The number of the line at which line selection started */ + private int fStartLineNumber; + /** The auto scroll direction */ + private int fAutoScrollDirection; + + /* + * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) + */ + public void mouseUp(MouseEvent event) { + // see bug 45700 + if (event.button == 1) { + stopSelecting(); + stopAutoScroll(); + postRedraw(); + } + } + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDown(MouseEvent event) { + fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); + // see bug 45700 + if (event.button == 1) { + startSelecting(); + } + } + + /* + * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent event) { + fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); + stopSelecting(); + stopAutoScroll(); + } + + /* + * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) + */ + public void mouseMove(MouseEvent event) { + if (!autoScroll(event)) { + int newLine = fParentRuler.toDocumentLineNumber(event.y); + expandSelection(newLine); + } + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent) + */ + public void mouseEnter(MouseEvent event) { + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent) + */ + public void mouseExit(MouseEvent event) { + } + + /* + * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent) + */ + public void mouseHover(MouseEvent event) { + } + + /** + * Called when line drag selection started. Adds mouse move and track + * listeners to this column's control. + */ + private void startSelecting() { + try { + + // select line + IDocument document = fCachedTextViewer.getDocument(); + fStartLineNumber = fParentRuler.getLineOfLastMouseButtonActivity(); + fStartLine = document.getLineInformation(fStartLineNumber); + fCachedTextViewer.setSelectedRange(fStartLine.getOffset(), fStartLine.getLength()); + fCachedViewportSize = getVisibleLinesInViewport(); + + // prepare for drag selection + fCanvas.addMouseMoveListener(this); + fCanvas.addMouseTrackListener(this); + + } catch (BadLocationException x) { + } + } + + /** + * Called when line drag selection stopped. Removes all previously + * installed listeners from this column's control. + */ + private void stopSelecting() { + // drag selection stopped + fCanvas.removeMouseMoveListener(this); + fCanvas.removeMouseTrackListener(this); + } + + /** + * Expands the line selection from the remembered start line to the + * given line. + * + * @param lineNumber + * the line to which to expand the selection + */ + private void expandSelection(int lineNumber) { + try { + + IDocument document = fCachedTextViewer.getDocument(); + IRegion lineInfo = document.getLineInformation(lineNumber); + + int start = Math.min(fStartLine.getOffset(), lineInfo.getOffset()); + int end = Math.max(fStartLine.getOffset() + fStartLine.getLength(), lineInfo.getOffset() + + lineInfo.getLength()); + + if (lineNumber < fStartLineNumber) + fCachedTextViewer.setSelectedRange(end, start - end); + else + fCachedTextViewer.setSelectedRange(start, end - start); + + } catch (BadLocationException x) { + } + } + + /** + * Called when auto scrolling stopped. Clears the auto scroll direction. + */ + private void stopAutoScroll() { + fAutoScrollDirection = SWT.NULL; + } + + /** + * Called on drag selection. + * + * @param event + * the mouse event caught by the mouse move listener + * @return true if scrolling happened, false + * otherwise + */ + private boolean autoScroll(MouseEvent event) { + Rectangle area = fCanvas.getClientArea(); + + if (event.y > area.height) { + autoScroll(SWT.DOWN); + return true; + } + + if (event.y < 0) { + autoScroll(SWT.UP); + return true; + } + + stopAutoScroll(); + return false; + } + + /** + * Scrolls the viewer into the given direction. + * + * @param direction + * the scroll direction + */ + private void autoScroll(int direction) { + + if (fAutoScrollDirection == direction) + return; + + final int TIMER_INTERVAL = 5; + final Display display = fCanvas.getDisplay(); + Runnable timer = null; + switch (direction) { + case SWT.UP: + timer = new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.UP) { + int top = getInclusiveTopIndex(); + if (top > 0) { + fCachedTextViewer.setTopIndex(top - 1); + expandSelection(top - 1); + display.timerExec(TIMER_INTERVAL, this); + } + } + } + }; + break; + case SWT.DOWN: + timer = new Runnable() { + public void run() { + if (fAutoScrollDirection == SWT.DOWN) { + int top = getInclusiveTopIndex(); + fCachedTextViewer.setTopIndex(top + 1); + expandSelection(top + 1 + fCachedViewportSize); + display.timerExec(TIMER_INTERVAL, this); + } + } + }; + break; + } + + if (timer != null) { + fAutoScrollDirection = direction; + display.timerExec(TIMER_INTERVAL, timer); + } + } + + /** + * Returns the viewer's first visible line, even if only partially + * visible. + * + * @return the viewer's first visible line + */ + private int getInclusiveTopIndex() { + if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) { + int top = fCachedTextViewer.getTopIndex(); + if ((fCachedTextWidget.getTopPixel() % fCachedTextWidget.getLineHeight()) != 0) + --top; + return top; + } + return -1; + } + } + + /** This column's parent ruler */ + private CompositeRuler fParentRuler; + /** Cached text viewer */ + private ITextViewer fCachedTextViewer; + /** Cached text widget */ + private StyledText fCachedTextWidget; + /** The columns canvas */ + private Canvas fCanvas; + /** Cache for the actual scroll position in pixels */ + private int fScrollPos; + /** The drawable for double buffering */ + private Image fBuffer; + /** The internal listener */ + private InternalListener fInternalListener = new InternalListener(); + /** The font of this column */ + private Font fFont; + /** The indentation cache */ + private int[] fIndentation; + /** Indicates whether this column reacts on text change events */ + private boolean fSensitiveToTextChanges = false; + /** The foreground color */ + private Color fForeground; + /** The background color */ + private Color fBackground; + /** Cached number of displayed digits */ + private int fCachedNumberOfDigits = -1; + /** Flag indicating whether a relayout is required */ + private boolean fRelayoutRequired = false; + /** + * Redraw runnable lock + */ + private Object fRunnableLock = new Object(); + /** + * Redraw runnable state + */ + private boolean fIsRunnablePosted = false; + /** + * Redraw runnable + */ + private Runnable fRunnable = new Runnable() { + public void run() { + synchronized (fRunnableLock) { + fIsRunnablePosted = false; + } + redraw(); + } + }; + private boolean fAlignRight; + private boolean fPaintStyleBackground; + private boolean fPaintSelectionBackground; + + /** + * Constructs a new vertical ruler column. + * + */ + public DisassemblyRulerColumn() { + this(SWT.LEFT); + // default constructor + } + + public DisassemblyRulerColumn(int align) { + this(align, true, false); + } + + public DisassemblyRulerColumn(int align, boolean paintSelection, boolean paintStyle) { + fAlignRight = (align & SWT.RIGHT) != 0; + fPaintSelectionBackground = paintSelection; + fPaintStyleBackground = paintStyle; + } + + /** + * Sets the foreground color of this column. + * + * @param foreground + * the foreground color + */ + public void setForeground(Color foreground) { + fForeground = foreground; + } + + /** + * Returns the foreground color being used to print the line numbers. + * + * @return the configured foreground color + */ + protected Color getForeground() { + return fForeground; + } + + /** + * Sets the background color of this column. + * + * @param background + * the background color + */ + public void setBackground(Color background) { + fBackground = background; + if (fCanvas != null && !fCanvas.isDisposed()) + fCanvas.setBackground(getBackground(fCanvas.getDisplay())); + } + + /** + * Returns the System background color for list widgets. + * + * @param display + * the display + * @return the System background color for list widgets + */ + protected Color getBackground(Display display) { + if (fBackground == null) + return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); + return fBackground; + } + + /* + * @see IVerticalRulerColumn#getControl() + */ + public Control getControl() { + return fCanvas; + } + + /* + * @see IVerticalRuleColumnr#getWidth + */ + public int getWidth() { + return fIndentation[0]; + } + + /** + * Computes the number of digits to be displayed. Returns true + * if the number of digits changed compared to the previous call of this + * method. If the method is called for the first time, the return value is + * also true. + * + * @return whether the number of digits has been changed + */ + protected boolean updateNumberOfDigits() { + if (fCachedTextViewer == null) + return false; + + int digits = computeNumberOfCharacters(); + + if (fCachedNumberOfDigits != digits) { + fCachedNumberOfDigits = digits; + return true; + } + + return false; + } + + /** + * Does the real computation of the number of characters. The default + * implementation computes the number of digits for the line number. + * Subclasses may override this method if they need extra space on the ruler. + * + * @return the number of characters to be displayed on the ruler. + */ + protected int computeNumberOfCharacters() { + IDocument document = fCachedTextViewer.getDocument(); + int lines= document == null ? 0 : document.getNumberOfLines(); + + int digits= 2; + while (lines > Math.pow(10, digits) - 1) { + ++digits; + } + return digits; + } + + /** + * Layouts the enclosing viewer to adapt the layout to changes of the size + * of the individual components. + * + * @param redraw + * true if this column can be redrawn + */ + protected void layout(boolean redraw) { + if (!redraw) { + fRelayoutRequired= true; + return; + } + + fRelayoutRequired= false; + if (fCachedTextViewer instanceof ITextViewerExtension) { + ITextViewerExtension extension= (ITextViewerExtension) fCachedTextViewer; + Control control= extension.getControl(); + if (control instanceof Composite && !control.isDisposed()) { + Composite composite= (Composite) control; + composite.layout(true); + } + } + } + + /** + * Computes the indentations for the given font and stores them in + * fIndentation. + */ + protected void computeIndentations() { + if (fCanvas == null) + return; + + GC gc= new GC(fCanvas); + try { + + gc.setFont(fCanvas.getFont()); + + fIndentation= new int[fCachedNumberOfDigits + 1]; + char[] digitStr= new char[fCachedNumberOfDigits + 1]; + Arrays.fill(digitStr, '9'); + Point p= gc.stringExtent(new String(digitStr, 0, fCachedNumberOfDigits + 1)); + fIndentation[0]= p.x; + + for (int i= 1; i <= fCachedNumberOfDigits; i++) { + p= gc.stringExtent(new String(digitStr, 0, i)); + fIndentation[i]= fIndentation[0] - p.x; + } + + } finally { + gc.dispose(); + } + } + + /* + * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite) + */ + public Control createControl(CompositeRuler parentRuler, Composite parentControl) { + + fParentRuler= parentRuler; + fCachedTextViewer= parentRuler.getTextViewer(); + fCachedTextWidget= fCachedTextViewer.getTextWidget(); + + fCanvas= new Canvas(parentControl, SWT.NONE); + fCanvas.setBackground(getBackground(fCanvas.getDisplay())); + fCanvas.setForeground(fForeground); + + fCanvas.addPaintListener(new PaintListener() { + public void paintControl(PaintEvent event) { + if (fCachedTextViewer != null) + doubleBufferPaint(event.gc); + } + }); + + fCanvas.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + handleDispose(); + fCachedTextViewer= null; + fCachedTextWidget= null; + } + }); + + fCanvas.addMouseListener(new MouseHandler()); + + if (fCachedTextViewer != null) { + + fCachedTextViewer.addViewportListener(fInternalListener); + fCachedTextViewer.addTextListener(fInternalListener); + fCachedTextViewer.getSelectionProvider().addSelectionChangedListener(fInternalListener); + + if (fFont == null) { + if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) + fFont= fCachedTextWidget.getFont(); + } + } + + if (fFont != null) + fCanvas.setFont(fFont); + + updateNumberOfDigits(); + computeIndentations(); + return fCanvas; + } + + /** + * Disposes the column's resources. + */ + protected void handleDispose() { + + if (fCachedTextViewer != null) { + fCachedTextViewer.removeViewportListener(fInternalListener); + fCachedTextViewer.removeTextListener(fInternalListener); + fCachedTextViewer.getSelectionProvider().removeSelectionChangedListener(fInternalListener); + } + + if (fBuffer != null) { + fBuffer.dispose(); + fBuffer= null; + } + } + + /** + * Double buffer drawing. + * + * @param dest + * the gc to draw into + */ + private void doubleBufferPaint(GC dest) { + + Point size= fCanvas.getSize(); + + if (size.x <= 0 || size.y <= 0) + return; + + if (fBuffer != null) { + Rectangle r= fBuffer.getBounds(); + if (r.width != size.x || r.height != size.y) { + fBuffer.dispose(); + fBuffer= null; + } + } + if (fBuffer == null) + fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); + + GC gc= new GC(fBuffer); + gc.setFont(fCanvas.getFont()); + if (fForeground != null) + gc.setForeground(fForeground); + + try { + gc.setBackground(getBackground(fCanvas.getDisplay())); + gc.fillRectangle(0, 0, size.x, size.y); + + if (fCachedTextViewer instanceof ITextViewerExtension5) + doPaint1(gc); + else + doPaint(gc); + + } finally { + gc.dispose(); + } + + dest.drawImage(fBuffer, 0, 0); + } + + /** + * Returns the viewport height in lines. + * + * @return the viewport height in lines + */ + protected int getVisibleLinesInViewport() { + Rectangle clArea= fCachedTextWidget.getClientArea(); + return clArea.height / fCachedTextWidget.getLineHeight(); + } + + /** + * Draws the ruler column. + * + * @param gc + * the gc to draw into + */ + private void doPaint(GC gc) { + + if (fCachedTextViewer == null) + return; + + if (fCachedTextWidget == null) + return; + + int firstLine= 0; + + int topLine= fCachedTextWidget.getTopIndex(); + fScrollPos= fCachedTextWidget.getTopPixel(); + int lineheight= fCachedTextWidget.getLineHeight(); + int partialLineHidden= fScrollPos % lineheight; + + if (partialLineHidden > 0 && topLine > 0) // widgetTopLine shows the + // first fully visible line + --topLine; + + int bottomLine; + + try { + + IRegion region= fCachedTextViewer.getVisibleRegion(); + IDocument doc= fCachedTextViewer.getDocument(); + + if (doc == null) + return; + + firstLine= doc.getLineOfOffset(region.getOffset()); + if (firstLine > topLine) + topLine= firstLine; + + bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); + + } catch (BadLocationException x) { + return; + } + + fSensitiveToTextChanges= bottomLine - topLine < getVisibleLinesInViewport(); + + int baselineBias= getBaselineBias(gc); + + int topInset= fCachedTextViewer.getTopInset(); + int y= topInset - partialLineHidden; + Point canvasSize= fCanvas.getSize(); + Point selection= fCachedTextWidget.getSelection(); + boolean selectedLine= false; + Color defaultForeground= gc.getForeground(); + Color defaultBackground= gc.getBackground(); + + for (int line= topLine; y < canvasSize.y && line <= bottomLine; line++, y += lineheight) { + int widgetOffset= fCachedTextWidget.getOffsetAtLine(line); + if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) { + if (!selectedLine) { + selectedLine= true; + gc.setForeground(fCachedTextWidget.getSelectionForeground()); + gc.setBackground(fCachedTextWidget.getSelectionBackground()); + } + } else if (selectedLine) { + selectedLine= false; + gc.setForeground(defaultForeground); + gc.setBackground(defaultBackground); + } + if (selectedLine) { + gc.fillRectangle(0, y, canvasSize.x, lineheight); + } else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) { + StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset); + if (style != null && style.background != null) { + gc.setBackground(style.background); + gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias); + gc.setBackground(defaultBackground); + } + } + paintLine(line, y, lineheight, gc, fCachedTextWidget.getDisplay()); + String s= createDisplayString(line); + int indentation= fAlignRight ? fIndentation[s.length()] : 0; + gc.drawString(s, indentation, y + baselineBias, true); + } + } + + /** + * Computes the string to be printed for line. The default + * implementation returns Integer.toString(line + 1). + * + * @param line + * the line number for which the string is generated + * @return the string to be printed on the ruler column for line + */ + String createDisplayString(int line) { + return Integer.toString(line + 1); + } + + /** + * Draws the ruler column. Uses ITextViewerExtension5 for the + * implementation. Will replace doPinat(GC). + * + * @param gc + * the gc to draw into + */ + private void doPaint1(GC gc) { + + if (fCachedTextViewer == null) + return; + + ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer; + + int widgetTopLine= fCachedTextWidget.getTopIndex(); + fScrollPos= fCachedTextWidget.getTopPixel(); + int lineheight= fCachedTextWidget.getLineHeight(); + int partialLineHidden= fScrollPos % lineheight; + + if (partialLineHidden > 0 && widgetTopLine > 0) // widgetTopLine shows + // the first fully + // visible line + --widgetTopLine; + + int modelTopLine= extension.widgetLine2ModelLine(widgetTopLine); + int modelBottomLine= fCachedTextViewer.getBottomIndex(); + if (modelBottomLine >= 0) + ++modelBottomLine; + + try { + + IRegion region= extension.getModelCoverage(); + IDocument doc= fCachedTextViewer.getDocument(); + + if (doc == null) + return; + + int coverageTopLine= doc.getLineOfOffset(region.getOffset()); + if (coverageTopLine > modelTopLine || modelTopLine == -1) + modelTopLine= coverageTopLine; + + int coverageBottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); + if (coverageBottomLine < modelBottomLine || modelBottomLine == -1) + modelBottomLine= coverageBottomLine; + + } catch (BadLocationException x) { + return; + } + + fSensitiveToTextChanges= modelBottomLine - modelTopLine < getVisibleLinesInViewport(); + + int baselineBias= getBaselineBias(gc); + + int topInset= fCachedTextViewer.getTopInset(); + int y= topInset - partialLineHidden; + Point canvasSize= fCanvas.getSize(); + Point selection= fCachedTextWidget.getSelection(); + boolean selectedLine= false; + Color defaultForeground= gc.getForeground(); + Color defaultBackground= gc.getBackground(); + + for (int modelLine= modelTopLine; y < canvasSize.y && modelLine <= modelBottomLine; modelLine++) { + + // don't draw hidden (e.g. folded) lines + int widgetLine= extension.modelLine2WidgetLine(modelLine); + if (widgetLine == -1) + continue; + int widgetOffset= fCachedTextWidget.getOffsetAtLine(widgetLine); + if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) { + if (!selectedLine) { + selectedLine= true; + gc.setForeground(fCachedTextWidget.getSelectionForeground()); + gc.setBackground(fCachedTextWidget.getSelectionBackground()); + } + } else if (selectedLine) { + selectedLine= false; + gc.setForeground(defaultForeground); + gc.setBackground(defaultBackground); + } + if (selectedLine) { + gc.fillRectangle(0, y, canvasSize.x, lineheight); + } else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) { + StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset); + if (style != null && style.background != null) { + gc.setBackground(style.background); + gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias); + gc.setBackground(defaultBackground); + } + } + + paintLine(modelLine, y, lineheight, gc, fCachedTextWidget.getDisplay()); + + String s= createDisplayString(modelLine); + int indentation= fAlignRight ? fIndentation[s.length()] : 0; + gc.drawString(s, indentation, y + baselineBias, true); + y += lineheight; + } + } + + /** + * Returns the difference between the baseline of the widget and the + * baseline as specified by the font for gc. When drawing + * text, the returned bias should be added to obtain text line up on + * the correct base line of the text widget. + * + * @param gc + * the GC to get the font metrics from + * @return the baseline bias to use when drawing text that is line up with + * fCachedTextWidget + */ + private int getBaselineBias(GC gc) { + /* + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951 widget line + * height may be more than the font height used for the text, + * since font styles (bold, italics...) can have larger font metrics + * than the simple font used for the numbers. + */ + int widgetBaseline= fCachedTextWidget.getBaseline(); + FontMetrics fm= gc.getFontMetrics(); + int fontBaseline= fm.getAscent() + fm.getLeading(); + Assert.isTrue(widgetBaseline >= fontBaseline); + int baselineBias= widgetBaseline - fontBaseline; + return baselineBias; + } + + /** + * Paints the line. After this method is called the text is painted + * on top of the result of this method. + *

+ * This default implementation does nothing. + *

+ * + * @param line + * the line of the document which the ruler is painted for + * @param y + * the y-coordinate of the box being painted for + * line, relative to gc + * @param lineheight + * the height of one line (and therefore of the box being + * painted) + * @param gc + * the drawing context the client may choose to draw on. + * @param display + * the display the drawing occurs on + */ + protected void paintLine(int line, int y, int lineheight, GC gc, Display display) { + } + + /** + * Triggers a redraw in the display thread. + */ + protected final void postRedraw() { + if (fCanvas != null && !fCanvas.isDisposed()) { + Display d= fCanvas.getDisplay(); + if (d != null) { + synchronized (fRunnableLock) { + if (fIsRunnablePosted) + return; + fIsRunnablePosted= true; + } + d.asyncExec(fRunnable); + } + } + } + + /* + * @see IVerticalRulerColumn#redraw() + */ + public void redraw() { + + if (fRelayoutRequired) { + layout(true); + return; + } + + if (fCanvas != null && !fCanvas.isDisposed()) { + GC gc= new GC(fCanvas); + doubleBufferPaint(gc); + gc.dispose(); + } + } + + /* + * @see IVerticalRulerColumn#setModel(IAnnotationModel) + */ + public void setModel(IAnnotationModel model) { + } + + /* + * @see IVerticalRulerColumn#setFont(Font) + */ + public void setFont(Font font) { + fFont= font; + if (fCanvas != null && !fCanvas.isDisposed()) { + fCanvas.setFont(fFont); + updateNumberOfDigits(); + computeIndentations(); + } + } + + /** + * Returns the parent (composite) ruler of this ruler column. + * + * @return the parent ruler + */ + protected CompositeRuler getParentRuler() { + return fParentRuler; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java new file mode 100644 index 00000000000..4d6fc9eac78 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyTextHover.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.LabelPosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourcePosition; +import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextViewer; + +/** + * A text hover to evaluate registers and variables under the cursor. + */ +@SuppressWarnings("restriction") +public class DisassemblyTextHover implements ITextHover { + + private final DisassemblyPart fDisassemblyPart; + + /** + * Create a new disassembly text hover. + */ + public DisassemblyTextHover(DisassemblyPart part) { + fDisassemblyPart= part; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverRegion(org.eclipse.jface.text.ITextViewer, int) + */ + public IRegion getHoverRegion(ITextViewer textViewer, int offset) { + IDocument doc = textViewer.getDocument(); + return CWordFinder.findWord(doc, offset); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) + */ + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + DisassemblyDocument doc = (DisassemblyDocument)textViewer.getDocument(); + int offset = hoverRegion.getOffset(); + AddressRangePosition pos; + try { + String ident = doc.get(offset, hoverRegion.getLength()); + String value = null; + pos = doc.getModelPosition(offset); + if (pos instanceof SourcePosition) { + value = evaluateExpression(ident); + } else if (pos instanceof LabelPosition) { + value = evaluateExpression(ident); + } else if (pos instanceof DisassemblyPosition) { + // first, try to evaluate as register + value = evaluateRegister(ident); + if (value == null) { + // if this fails, try expression + value = evaluateExpression(ident); + } + } + if (value != null) { + return ident + " = " + value; //$NON-NLS-1$ + } + } catch (BadLocationException e) { + if (DsfUIPlugin.getDefault().isDebugging()) { + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, "Internal Error", e)); //$NON-NLS-1$ + } + } + return null; + } + + /** + * Evaluate the given register. + * @param register + * @return register value or null + */ + private String evaluateRegister(String register) { + // TLETODO [disassembly] evaluate register + return null; + } + + /** + * Evaluate the given expression. + * @param expr + * @return expression value or null + */ + private String evaluateExpression(String expr) { + final IExpressions expressions= fDisassemblyPart.getService(IExpressions.class); + if (expressions == null) { + return null; + } + final IFrameDMContext frameDmc= fDisassemblyPart.getTargetFrameContext(); + if (frameDmc == null || !fDisassemblyPart.isSuspended()) { + return null; + } + IExpressionDMContext exprDmc= expressions.createExpression(frameDmc, expr); + final FormattedValueDMContext valueDmc= expressions.getFormattedValueContext(exprDmc, IFormattedValues.NATURAL_FORMAT); + final DsfExecutor executor= fDisassemblyPart.getSession().getExecutor(); + Query query= new Query() { + @Override + protected void execute(final DataRequestMonitor rm) { + expressions.getFormattedExpressionValue(valueDmc, new DataRequestMonitor(executor, rm) { + @Override + protected void handleSuccess() { + FormattedValueDMData data= getData(); + rm.setData(data); + rm.done(); + } + }); + }}; + + executor.execute(query); + FormattedValueDMData data= null; + try { + data= query.get(); + } catch (InterruptedException exc) { + } catch (ExecutionException exc) { + } + if (data != null) { + return data.getFormattedValue(); + } + return null; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java new file mode 100644 index 00000000000..5ab83c6c1d3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyView.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; + +/** + * DisassemblyView + */ +public class DisassemblyView extends DisassemblyPart implements IViewPart { + + private ISelectionListener fDebugViewListener; + + /** + * + */ + public DisassemblyView() { + super(); + } + + @Override + protected IActionBars getActionBars() { + return getViewSite().getActionBars(); + } + + /* + * @see org.eclipse.ui.IViewPart#getViewSite() + */ + public IViewSite getViewSite() { + return (IViewSite)getSite(); + } + + /* + * @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite) + */ + public void init(IViewSite site) throws PartInitException { + setSite(site); + } + + /* + * @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento) + */ + public void init(IViewSite site, IMemento memento) throws PartInitException { + setSite(site); + site.getPage().addSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, fDebugViewListener= new ISelectionListener() { + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + updateDebugContext(); + }}); + } + + /* + * @see org.eclipse.ui.IViewPart#saveState(org.eclipse.ui.IMemento) + */ + public void saveState(IMemento memento) { + } + + @Override + protected void contributeToActionBars(IActionBars bars) { + super.contributeToActionBars(bars); + fillLocalPullDown(bars.getMenuManager()); + } + + protected void fillLocalPullDown(IMenuManager manager) { + manager.add(fActionGotoPC); + manager.add(fActionGotoAddress); + manager.add(fActionToggleSource); + manager.add(new Separator()); + } + + @Override + protected void closePart() { + getViewSite().getPage().hideView(this); + } + + @Override + public void dispose() { + getSite().getPage().removeSelectionListener(fDebugViewListener); + super.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java new file mode 100644 index 00000000000..b57cd30f185 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewer.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.source.CompositeRuler; +import org.eclipse.jface.text.source.IOverviewRuler; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.IVerticalRulerColumn; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; + +/** + * DisassemblyViewer + */ +public class DisassemblyViewer extends SourceViewer { + + class ResizeListener implements ControlListener { + /* + * @see ControlListener#controlResized(ControlEvent) + */ + public void controlResized(ControlEvent e) { + updateViewportListeners(RESIZE); + } + /* + * @see ControlListener#controlMoved(ControlEvent) + */ + public void controlMoved(ControlEvent e) { + } + } + + private boolean fUserTriggeredScrolling; + private int fCachedLastTopPixel; + + // extra resize listener to workaround bug 171018 + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=171018 + private ResizeListener fResizeListener; + + /** + * Create a new DisassemblyViewer. + * @param parent + * @param ruler + * @param overviewRuler + * @param showsAnnotationOverview + * @param styles + */ + public DisassemblyViewer(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, boolean showsAnnotationOverview, int styles) { + super(parent, ruler, overviewRuler, showsAnnotationOverview, styles); + // always readonly + setEditable(false); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#createControl(org.eclipse.swt.widgets.Composite, int) + */ + @Override + protected void createControl(Composite parent, int styles) { + super.createControl(parent, styles); + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=171018 + getTextWidget().addControlListener(fResizeListener= new ResizeListener()); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#handleDispose() + */ + @Override + protected void handleDispose() { + if (fResizeListener != null) { + getTextWidget().removeControlListener(fResizeListener); + } + super.handleDispose(); + } + + /* + * @see org.eclipse.jface.text.source.SourceViewer#doOperation(int) + */ + @Override + public void doOperation(int operation) { + switch (operation) { + case COPY: + StyledText textWidget = getTextWidget(); + if (textWidget == null || !redraws()) { + return; + } + if (textWidget.getSelectionCount() == 0) { + return; + } + String selectedText; + try { + selectedText = getSelectedText(); + } catch (BadLocationException e) { + // should not happend + DsfUIPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, e.getLocalizedMessage(), e)); + return; + } + Clipboard clipboard = new Clipboard(textWidget.getDisplay()); + clipboard.setContents(new Object[] { selectedText }, new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + break; + default: + super.doOperation(operation); + } + } + + /** + * Get the selected text together with text displayed in visible + * ruler columns. + * @return the selected text + * @throws BadLocationException + */ + public String getSelectedText() throws BadLocationException { + StringBuffer text = new StringBuffer(200); + String lineSeparator = System.getProperty("line.separator"); //$NON-NLS-1$ + DisassemblyDocument doc = (DisassemblyDocument)getDocument(); + Point selection = getSelectedRange(); + int startOffset = selection.x; + int length = selection.y; + int endOffset = startOffset + length; + int startLine = doc.getLineOfOffset(startOffset); + int endLine = doc.getLineOfOffset(endOffset); + int firstLineOffset = startOffset - doc.getLineOffset(startLine); + if (firstLineOffset > 0) { + // partial first line + int lineLength = doc.getLineInformation(startLine).getLength(); + text.append(doc.get(startOffset, Math.min(lineLength - firstLineOffset, length))); + ++startLine; + if (startLine <= endLine) { + text.append(lineSeparator); + } + } + for (int line = startLine; line < endLine; ++line) { + String lineText = getLineText(line); + text.append(lineText); + text.append(lineSeparator); + } + if (doc.getLineOffset(endLine) < endOffset) { + // partial last line + if (startLine <= endLine) { + int lineStart = doc.getLineOffset(endLine); + text.append(getLinePrefix(endLine)); + text.append(doc.get(lineStart, endOffset - lineStart)); + } + } + return text.toString(); + } + + /** + * Return the content of the given line, excluding line separator. + * @param line the line number + * @return the line content + * @throws BadLocationException + */ + public String getLineText(int line) throws BadLocationException { + IDocument doc = getDocument(); + IRegion lineRegion = doc.getLineInformation(line); + return getLinePrefix(line) + doc.get(lineRegion.getOffset(), lineRegion.getLength()); + } + + /** + * Get the line prefix by concatenating the text displayed by + * the visible ruler columns. + * @param line the line number + * @return the prefix string with trailing blank or the empty string + */ + public String getLinePrefix(int line) { + StringBuffer prefix = new StringBuffer(10); + IVerticalRuler ruler = getVerticalRuler(); + if (ruler instanceof CompositeRuler) { + for (Iterator iter = ((CompositeRuler)ruler).getDecoratorIterator(); iter.hasNext();) { + IVerticalRulerColumn column = (IVerticalRulerColumn) iter.next(); + if (column instanceof DisassemblyRulerColumn) { + DisassemblyRulerColumn disassColumn = (DisassemblyRulerColumn)column; + String columnText = disassColumn.createDisplayString(line); + prefix.append(columnText); + int columnWidth = disassColumn.computeNumberOfCharacters(); + columnWidth -= columnText.length(); + while(columnWidth-- > 0) + prefix.append(' '); + prefix.append(' '); + } + } + } + return prefix.toString(); + } + + /** + * Scroll the given position into the visible area if it is not yet visible. + * @param offset + * @see org.eclipse.jface.text.TextViewer#revealRange(int, int) + */ + public void revealOffset(int offset, boolean onTop) { + try { + IDocument doc = getVisibleDocument(); + + int focusLine = doc.getLineOfOffset(offset); + + StyledText textWidget = getTextWidget(); + int top = textWidget.getTopIndex(); + if (top > -1) { + + // scroll vertically + int lines = getEstimatedVisibleLinesInViewport(); + int bottom = top + lines; + + int bottomBuffer = Math.max(1, lines / 3); + + if (!onTop && focusLine >= top && focusLine <= bottom - bottomBuffer) { + // do not scroll at all as it is already visible + } else { + if (focusLine > bottom - bottomBuffer && focusLine <= bottom) { + // focusLine is already in bottom bufferZone + // scroll to top of bottom bufferzone - for smooth down-scrolling + int scrollDelta = focusLine - (bottom - bottomBuffer); + textWidget.setTopIndex(top + scrollDelta); + } else { + // scroll to top of visible area minus buffer zone + int topBuffer = onTop ? 0 : lines / 3; + textWidget.setTopIndex(Math.max(0, focusLine - topBuffer)); + } + updateViewportListeners(INTERNAL); + } + } + } catch (BadLocationException ble) { + throw new IllegalArgumentException(ble.getLocalizedMessage()); + } + } + + /** + * @return the number of visible lines in the viewport assuming a constant + * line height. + */ + private int getEstimatedVisibleLinesInViewport() { + StyledText textWidget = getTextWidget(); + if (textWidget != null) { + Rectangle clArea= textWidget.getClientArea(); + if (!clArea.isEmpty()) + return clArea.height / textWidget.getLineHeight(); + } + return -1; + } + + int getLastTopPixel() { + return fCachedLastTopPixel; + } + boolean isUserTriggeredScrolling() { + return fUserTriggeredScrolling; + } + + /* + * @see org.eclipse.jface.text.TextViewer#updateViewportListeners(int) + */ + @Override + protected void updateViewportListeners(int origin) { + fCachedLastTopPixel = fLastTopPixel; + fUserTriggeredScrolling = origin != INTERNAL && origin != RESIZE; + super.updateViewportListeners(origin); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java new file mode 100644 index 00000000000..c11fbe38d03 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/DisassemblyViewerConfiguration.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.IUndoManager; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; +import org.eclipse.jface.text.presentation.IPresentationDamager; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.IPresentationRepairer; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.reconciler.IReconciler; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; + +/** + * DisassemblyViewerConfiguration + */ +public class DisassemblyViewerConfiguration extends TextSourceViewerConfiguration { + + private DisassemblyPart fPart; + + /** + * SimpleDamagerRepairer + */ + public class SimpleDamagerRepairer implements IPresentationDamager, IPresentationRepairer { + + /* + * @see org.eclipse.jface.text.presentation.IPresentationDamager#setDocument(org.eclipse.jface.text.IDocument) + */ + public void setDocument(IDocument document) { + } + + /* + * @see org.eclipse.jface.text.presentation.IPresentationDamager#getDamageRegion(org.eclipse.jface.text.ITypedRegion, org.eclipse.jface.text.DocumentEvent, boolean) + */ + public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent e, boolean documentPartitioningChanged) { + int start= e.fOffset; + int end= e.getOffset() + (e.getText() == null ? 0 : e.getText().length()); + return new Region(start, end - start); + } + + /* + * @see org.eclipse.jface.text.presentation.IPresentationRepairer#createPresentation(org.eclipse.jface.text.TextPresentation, org.eclipse.jface.text.ITypedRegion) + */ + public void createPresentation(TextPresentation presentation, ITypedRegion damage) { + // do nothing + } + + } + + /** + * + */ + public DisassemblyViewerConfiguration(DisassemblyPart part) { + super(EditorsUI.getPreferenceStore()); + fPart = part; + } + + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getPresentationReconciler(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconciler reconciler = new PresentationReconciler(); + SimpleDamagerRepairer dr = new SimpleDamagerRepairer(); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + return reconciler; + } + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getUndoManager(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IUndoManager getUndoManager(ISourceViewer sourceViewer) { + // no undo/redo + return null; + } + + /* + * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getTextHover(org.eclipse.jface.text.source.ISourceViewer, java.lang.String) + */ + @Override + public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) { + return new DisassemblyTextHover(fPart); + } + + /* + * @see org.eclipse.ui.editors.text.TextSourceViewerConfiguration#getHyperlinkDetectors(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { + IHyperlinkDetector[] inheritedDetectors= super.getHyperlinkDetectors(sourceViewer); + + if (fPart == null) + return inheritedDetectors; + + int inheritedDetectorsLength= inheritedDetectors != null ? inheritedDetectors.length : 0; + IHyperlinkDetector[] detectors= new IHyperlinkDetector[inheritedDetectorsLength + 1]; + detectors[0]= new DisassemblyHyperlinkDetector(fPart); + for (int i= 0; i < inheritedDetectorsLength; i++) { + detectors[i+1]= inheritedDetectors[i]; + } + + return detectors; + } + + /* + * @see org.eclipse.ui.editors.text.TextSourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer) + */ + @Override + public IReconciler getReconciler(ISourceViewer sourceViewer) { + // disable spell checking + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java new file mode 100644 index 00000000000..40b32498824 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/EditionFinderJob.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; + +/** + * A job to find a suitable edition from the local history + * based on a file and the timestamp of the code module. + */ +class EditionFinderJob extends Job { + + private final IFile fFile; + private final BigInteger fAddress; + private final DisassemblyPart fDisassemblyPart; + private final SourceFileInfo fSourceInfo; + + /** + * Create a new edition finder for a file resource and address. + * + * @param sourceInfo the file info containing the file resource for which to find an edition + * @param address address inside the module + * @param disassemblyPart the disassembly part where this job originated from + */ + public EditionFinderJob(SourceFileInfo sourceInfo, BigInteger address, DisassemblyPart disassemblyPart) { + super(DisassemblyMessages.EditionFinderJob_name); + Assert.isNotNull(sourceInfo); + Assert.isLegal(sourceInfo.fFile instanceof IFile); + fSourceInfo= sourceInfo; + fFile = (IFile)sourceInfo.fFile; + fAddress = address; + fDisassemblyPart= disassemblyPart; + setRule(fFile); + setSystem(true); + sourceInfo.fEditionJob= this; + } + + /* + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected IStatus run(IProgressMonitor monitor) { + monitor.beginTask(DisassemblyMessages.EditionFinderJob_name, 2); + monitor.subTask(DisassemblyMessages.EditionFinderJob_task_get_timestamp); + long moduleTime; + Object token = fDisassemblyPart.retrieveModuleTimestamp(fAddress); + if (token != null && !(token instanceof Long) && !monitor.isCanceled()) { + try { + synchronized (token) { + token.wait(1000); + } + } catch (InterruptedException e) { + DisassemblyPart.internalError(e); + } + token = fDisassemblyPart.retrieveModuleTimestamp(fAddress); + } + monitor.worked(1); + if (token instanceof Long && !monitor.isCanceled()) { + moduleTime = ((Long)token).longValue(); + long buildTime = moduleTime * 1000; + if (fFile.getLocalTimeStamp() > buildTime) { + monitor.subTask(DisassemblyMessages.EditionFinderJob_task_search_history); + // get history - recent states first + IFileState[] states; + try { + states = fFile.getHistory(new SubProgressMonitor(monitor, 1)); + } catch (CoreException e) { + states = new IFileState[0]; + } + for (int i = 0; i < states.length; i++) { + IFileState state = states[i]; + long saveTime = state.getModificationTime(); + if (saveTime <= buildTime) { + fSourceInfo.fEdition = state; + break; + } + } + } + } + fSourceInfo.fEditionJob = null; + monitor.worked(1); + monitor.done(); + return Status.OK_STATUS; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java new file mode 100644 index 00000000000..3c8809d3e10 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/FunctionOffsetRulerColumn.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.AddressRangePosition; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyPosition; +import org.eclipse.jface.text.BadLocationException; + +/** + * A vertical ruler column to display the function + offset of instructions. + */ +public class FunctionOffsetRulerColumn extends DisassemblyRulerColumn { + + /** Maximum width of column (in characters) */ + private static final int MAXWIDTH= 40; + + /** + * Default constructor. + */ + public FunctionOffsetRulerColumn() { + super(); + } + + /* + * @see org.eclipse.jface.text.source.LineNumberRulerColumn#createDisplayString(int) + */ + @Override + protected String createDisplayString(int line) { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + int offset; + try { + offset = doc.getLineOffset(line); + AddressRangePosition pos = doc.getDisassemblyPosition(offset); + if (pos instanceof DisassemblyPosition && pos.length > 0 && pos.offset == offset && pos.fValid) { + DisassemblyPosition disassPos = (DisassemblyPosition)pos; + int length = disassPos.fFunction.length; + if (length > MAXWIDTH) { + return "..." + new String(disassPos.fFunction, length - MAXWIDTH + 3, MAXWIDTH - 3); //$NON-NLS-1$ + } + return new String(disassPos.fFunction); + } else if (pos != null && !pos.fValid) { + return DOTS.substring(0, Math.min(MAXWIDTH, doc.getMaxFunctionLength())); + } + } catch (BadLocationException e) { + // silently ignored + } + return ""; //$NON-NLS-1$ + } + + @Override + protected int computeNumberOfCharacters() { + DisassemblyDocument doc = (DisassemblyDocument)getParentRuler().getTextViewer().getDocument(); + return Math.min(MAXWIDTH, doc.getMaxFunctionLength()); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java new file mode 100644 index 00000000000..4e4eb8838aa --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyHelpContextIds.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; + +/** + * IDisassemblyHelpContextIds + */ +public interface IDisassemblyHelpContextIds { + + public final static String PREFIX = DsfUIPlugin.PLUGIN_ID + '.'; + public final static String DISASSEMBLY_PREFERENCE_PAGE = PREFIX + "disassembly_preference_page"; //$NON-NLS-1$ + public final static String DISASSEMBLY_VIEW = PREFIX + "disassembly_view"; //$NON-NLS-1$ + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java new file mode 100644 index 00000000000..5b6b2ebe380 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/IDisassemblyPart.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import java.math.BigInteger; + +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Interface which the disassembly view and editor implement. + */ +public interface IDisassemblyPart extends IWorkbenchPart { + + /** + * Property id for the active state of the part. + */ + public final int PROP_ACTIVE= 0x505; + + /** + * Property id for the connected state of the part. + */ + public final int PROP_CONNECTED= 0x506; + + /** + * Property id for the suspended state of the underlying execution context. + */ + public final int PROP_SUSPENDED= 0x507; + + /** + * Test whether this part is connected to a debug session and execution context. + * + * @return true if the part is connected to a debug session and execution context + */ + boolean isConnected(); + + /** + * Test whether this part is active. A part is active if it is visible and connected. + * + * @return true if the part is active + */ + boolean isActive(); + + /** + * Test whether the underlying execution context is currently suspended. + * Implies connected state. + * + * @return true if the execution context is currently suspended + */ + boolean isSuspended(); + + /** + * Get access to the text viewer. + * + * @return the text viewer + */ + ISourceViewer getTextViewer(); + + /** + * Navigate to the given address. + * + * @param address + */ + void gotoAddress(BigInteger address); + + /** + * Navigate to current program counter. + */ + void gotoProgramCounter(); + + /** + * Navigate to the address the given expression evaluates to. + * + * @param expression a symbolic address expression + */ + void gotoSymbol(String expression); + + /** + * Adds a ruler context menu listener to the disassembly part. + * + * @param listener the listener + */ + void addRulerContextMenuListener(IMenuListener listener); + + /** + * Removes a ruler context menu listener from the disassembly part. + * + * @param listener the listener + */ + void removeRulerContextMenuListener(IMenuListener listener); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java new file mode 100644 index 00000000000..508aed24244 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/SourceColorerJob.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.DisassemblyDocument; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model.SourceFileInfo; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.progress.UIJob; + +/** + * UI job to color source code. + */ +class SourceColorerJob extends UIJob implements Runnable { + + private final DisassemblyPart fDisassemblyPart; + private final ISourceViewer fViewer; + private final DisassemblyDocument fDocument; + private final IStorage fStorage; + + public SourceColorerJob(Display jobDisplay, IStorage storage, DisassemblyPart disassemblyPart) { + super(DisassemblyMessages.SourceColorerJob_name); + fDisassemblyPart= disassemblyPart; + fViewer= disassemblyPart.getTextViewer(); + fDocument= (DisassemblyDocument) fViewer.getDocument(); + fStorage = storage; + setDisplay(fDisassemblyPart.getSite().getShell().getDisplay()); + setSystem(true); + setPriority(INTERACTIVE); + } + + /* + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (fViewer != null && !monitor.isCanceled()) { + monitor.beginTask(DisassemblyMessages.SourceColorerJob_name, IProgressMonitor.UNKNOWN); + SourceFileInfo fi = fDocument.getSourceInfo(fStorage); + if (fi != null) { + fi.initPresentationCreator(fViewer); + if (fi.fError != null) { + String message= DisassemblyMessages.Disassembly_log_error_readFile + fi.fFileKey; + fDisassemblyPart.logWarning(message, fi.fError); + } + } + fDisassemblyPart.updateInvalidSource(); + monitor.done(); + } + return Status.OK_STATUS; + } + + /* + * @see java.lang.Runnable#run() + */ + public void run() { + IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService)fDisassemblyPart.getSite().getAdapter(IWorkbenchSiteProgressService.class); + if(progressService != null) { + progressService.schedule(this, 0, true); + } else { + schedule(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java new file mode 100644 index 00000000000..81b935133c0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyAction.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.action.Action; +import org.eclipse.ui.IPropertyListener; +import org.eclipse.ui.texteditor.IUpdate; + +public abstract class AbstractDisassemblyAction extends Action implements IUpdate, IPropertyListener { + + protected IDisassemblyPart fDisassemblyPart; + + AbstractDisassemblyAction() { + } + + /** + * Create a disassembly action. + * + * @param disassemblyPart + */ + public AbstractDisassemblyAction(IDisassemblyPart disassemblyPart) { + Assert.isLegal(disassemblyPart != null); + fDisassemblyPart= disassemblyPart; + fDisassemblyPart.addPropertyListener(this); + } + + /** + * @return the disassembly part + */ + public final IDisassemblyPart getDisassemblyPart() { + return fDisassemblyPart; + } + + /* + * @see org.eclipse.jface.action.Action#run() + */ + @Override + public abstract void run(); + + public void update() { + boolean enabled= fDisassemblyPart == null || fDisassemblyPart.isConnected(); + setEnabled(enabled); + } + + /* + * @see org.eclipse.ui.IPropertyListener#propertyChanged(java.lang.Object, int) + */ + public void propertyChanged(Object source, int propId) { + if (source == fDisassemblyPart && (propId & 0x500) != 0) { + update(); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java new file mode 100644 index 00000000000..1a3ad778b39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyBreakpointRulerAction.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; + +/** + * Abstract implementation of a breakpoint ruler action. + */ +public abstract class AbstractDisassemblyBreakpointRulerAction extends AbstractDisassemblyRulerAction { + + /** + * Create breakpoint ruler action. + * + * @param disassemblyPart + * @param rulerInfo + */ + protected AbstractDisassemblyBreakpointRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + super(disassemblyPart, rulerInfo); + } + + /** + * Returns the breakpoint at the last line of mouse activity in the ruler + * or null if none. + * + * @return breakpoint associated with activity in the ruler or null + */ + protected IBreakpoint getBreakpoint() { + IAnnotationModel annotationModel = getAnnotationModel(); + IDocument document = getDocument(); + if (annotationModel != null) { + Iterator iterator = annotationModel.getAnnotationIterator(); + while (iterator.hasNext()) { + Object object = iterator.next(); + if (object instanceof SimpleMarkerAnnotation) { + SimpleMarkerAnnotation markerAnnotation = (SimpleMarkerAnnotation) object; + IMarker marker = markerAnnotation.getMarker(); + try { + if (marker.isSubtypeOf(IBreakpoint.BREAKPOINT_MARKER)) { + Position position = annotationModel.getPosition(markerAnnotation); + int line = document.getLineOfOffset(position.getOffset()); + if (line == getRulerInfo().getLineOfLastMouseButtonActivity()) { + IBreakpoint breakpoint = DebugPlugin.getDefault().getBreakpointManager().getBreakpoint(marker); + if (breakpoint != null) { + return breakpoint; + } + } + } + } catch (CoreException e) { + } catch (BadLocationException e) { + } + } + } + } + return null; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java new file mode 100644 index 00000000000..d5af5e28b04 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerAction.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + +/** + * Abstract implementation for disassembly vertical ruler actions. + */ +public abstract class AbstractDisassemblyRulerAction extends AbstractDisassemblyAction { + + private final IVerticalRulerInfo fRulerInfo; + + protected AbstractDisassemblyRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + fDisassemblyPart= disassemblyPart; + fRulerInfo= rulerInfo; + } + + public final IVerticalRulerInfo getRulerInfo() { + return fRulerInfo; + } + + public final IDocument getDocument() { + return getDisassemblyPart().getTextViewer().getDocument(); + } + + public final IAnnotationModel getAnnotationModel() { + return getDisassemblyPart().getTextViewer().getAnnotationModel(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java new file mode 100644 index 00000000000..91aafa31b32 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/AbstractDisassemblyRulerActionDelegate.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.ui.IEditorActionDelegate; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IViewActionDelegate; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * This class serves as an adapter for actions contributed to the vertical ruler's + * context menu. This adapter provides the contributed actions access to their disassembly part + * and the disassembly part's vertical ruler. These actions gain only limited access to the vertical + * ruler as defined by IVerticalRulerInfo. The adapter updates the + * adapter (inner) action on menu and mouse action on the vertical ruler.

+ * Extending classes must implement the factory method + * createAction(IDisassemblyPart, IVerticalRulerInfo). + * + * @see org.eclipse.ui.texteditor.AbstractRulerActionDelegate + */ +public abstract class AbstractDisassemblyRulerActionDelegate extends ActionDelegate implements IEditorActionDelegate, IViewActionDelegate, MouseListener, IMenuListener { + + /** The disassembly part. */ + private IDisassemblyPart fDisassemblyPart; + /** The action calling the action delegate. */ + private IAction fCallerAction; + /** The underlying action. */ + private IAction fAction; + + /** + * The factory method creating the underlying action. + * + * @param disassemblyPart the disassembly part the action to be created will work on + * @param rulerInfo the vertical ruler the action to be created will work on + * @return the created action + */ + protected abstract IAction createAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo); + + /* + * @see IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart) + */ + public void setActiveEditor(IAction callerAction, IEditorPart targetEditor) { + setTargetPart(callerAction, targetEditor); + } + + /* + * @see org.eclipse.ui.IViewActionDelegate#init(org.eclipse.ui.IViewPart) + */ + public void init(IViewPart view) { + setTargetPart(fCallerAction, view); + } + + @Override + public void init(IAction action) { + fCallerAction= action; + } + + private void setTargetPart(IAction callerAction, IWorkbenchPart targetPart) { + if (fDisassemblyPart != null) { + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fDisassemblyPart.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.removeMouseListener(this); + } + + fDisassemblyPart.removeRulerContextMenuListener(this); + } + + fDisassemblyPart= (IDisassemblyPart)(targetPart == null ? null : targetPart.getAdapter(IDisassemblyPart.class)); + fCallerAction= callerAction; + fAction= null; + + if (fDisassemblyPart != null) { + fDisassemblyPart.addRulerContextMenuListener(this); + + IVerticalRulerInfo rulerInfo= (IVerticalRulerInfo) fDisassemblyPart.getAdapter(IVerticalRulerInfo.class); + if (rulerInfo != null) { + fAction= createAction(fDisassemblyPart, rulerInfo); + update(); + + Control control= rulerInfo.getControl(); + if (control != null && !control.isDisposed()) + control.addMouseListener(this); + } + } + } + + @Override + public void run(IAction callerAction) { + if (fAction != null) + fAction.run(); + } + + @Override + public void runWithEvent(IAction action, Event event) { + if (fAction != null) + fAction.runWithEvent(event); + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + /* + * This is a ruler action - don't update on selection. + */ + } + + /** + * Updates to the current state. + */ + private void update() { + if (fAction instanceof IUpdate) { + ((IUpdate) fAction).update(); + if (fCallerAction != null) { + fCallerAction.setText(fAction.getText()); + fCallerAction.setEnabled(fAction.isEnabled()); + } + } + } + + /* + * @see IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager) + */ + public void menuAboutToShow(IMenuManager manager) { + update(); + } + + /* + * @see MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDoubleClick(MouseEvent e) { + } + + /* + * @see MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) + */ + public void mouseDown(MouseEvent e) { + update(); + } + + /* + * @see MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) + */ + public void mouseUp(MouseEvent e) { + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java new file mode 100644 index 00000000000..2b9a597ef15 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoAddress.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyPart; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.swt.widgets.Shell; + +public final class ActionGotoAddress extends AbstractDisassemblyAction { + public ActionGotoAddress(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoAddress_label); + } + @Override + public void run() { + IInputValidator validator = new IInputValidator() { + public String isValid(String input) { + if (input == null || input.length() == 0) + return " "; //$NON-NLS-1$ + try { + BigInteger address= DisassemblyPart.decodeAddress(input); + if (address.compareTo(BigInteger.ZERO) < 0) { + return DisassemblyMessages.Disassembly_GotoAddressDialog_error_invalid_address; + } + } catch (NumberFormatException x) { + return DisassemblyMessages.Disassembly_GotoAddressDialog_error_not_a_number; //; + } + return null; + } + }; + String defaultValue = ((ITextSelection)getDisassemblyPart().getSite().getSelectionProvider().getSelection()).getText(); + if (validator.isValid(defaultValue) != null) { + defaultValue = DsfUIPlugin.getDefault().getDialogSettings().get("gotoAddress"); //$NON-NLS-1$ + if (validator.isValid(defaultValue) != null) { + defaultValue = ""; //$NON-NLS-1$ + } + } + String dlgTitle = DisassemblyMessages.Disassembly_GotoAddressDialog_title; + String dlgLabel = DisassemblyMessages.Disassembly_GotoAddressDialog_label; + final Shell shell= getDisassemblyPart().getSite().getShell(); + InputDialog dlg = new InputDialog(shell, dlgTitle, dlgLabel, defaultValue, validator); + if (dlg.open() == IDialogConstants.OK_ID) { + String value = dlg.getValue(); + BigInteger address= DisassemblyPart.decodeAddress(value); + DsfUIPlugin.getDefault().getDialogSettings().put("gotoAddress", value); //$NON-NLS-1$ + getDisassemblyPart().gotoAddress(address); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java new file mode 100644 index 00000000000..5dfddfb633e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoProgramCounter.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; + +public final class ActionGotoProgramCounter extends AbstractDisassemblyAction { + public ActionGotoProgramCounter(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoPC_label); + setToolTipText(DisassemblyMessages.Disassembly_action_GotoPC_tooltip); + } + @Override + public void run() { + getDisassemblyPart().gotoProgramCounter(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java new file mode 100644 index 00000000000..893970968b7 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionGotoSymbol.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.internal.ui.text.CWordFinder; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.swt.widgets.Shell; + +@SuppressWarnings("restriction") +public final class ActionGotoSymbol extends AbstractDisassemblyAction { + public ActionGotoSymbol(IDisassemblyPart disassemblyPart) { + super(disassemblyPart); + setText(DisassemblyMessages.Disassembly_action_GotoSymbol_label); + } + @Override + public void run() { + ITextViewer viewer = getDisassemblyPart().getTextViewer(); + IDocument document= viewer.getDocument(); + IRegion wordRegion = CWordFinder.findWord(document, viewer.getSelectedRange().x); + String defaultValue = null; + if (wordRegion != null) { + try { + defaultValue = document.get(wordRegion.getOffset(), wordRegion.getLength()); + } catch (BadLocationException e) { + // safely ignored + } + } + if (defaultValue == null) { + defaultValue = DsfUIPlugin.getDefault().getDialogSettings().get("gotoSymbol"); //$NON-NLS-1$ + if (defaultValue == null) { + defaultValue = ""; //$NON-NLS-1$ + } + } + String dlgTitle = DisassemblyMessages.Disassembly_GotoSymbolDialog_title; + String dlgLabel = DisassemblyMessages.Disassembly_GotoSymbolDialog_label; + final Shell shell= getDisassemblyPart().getSite().getShell(); + InputDialog dlg = new InputDialog(shell, dlgTitle, dlgLabel, defaultValue, null); + if (dlg.open() == IDialogConstants.OK_ID) { + String value = dlg.getValue(); + DsfUIPlugin.getDefault().getDialogSettings().put("gotoSymbol", value); //$NON-NLS-1$ + getDisassemblyPart().gotoSymbol(value); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java new file mode 100644 index 00000000000..dd5186fa8d9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/ActionOpenPreferences.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.jface.action.Action; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.PreferencesUtil; + +public final class ActionOpenPreferences extends Action { + private final static String PREF_PAGE_ID = "org.eclipse.cdt.dsf.debug.ui.disassembly.preferencePage"; //$NON-NLS-1$ + private final Shell fShell; + public ActionOpenPreferences(Shell shell) { + fShell= shell; + setText(DisassemblyMessages.Disassembly_action_OpenPreferences_label); + } + @Override + public void run() { + PreferencesUtil.createPreferenceDialogOn(fShell, PREF_PAGE_ID, new String[] { PREF_PAGE_ID }, null).open(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java new file mode 100644 index 00000000000..4aab7e44ea2 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerAction.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.debug.core.model.ICBreakpoint; +import org.eclipse.cdt.debug.internal.ui.CBreakpointContext; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jface.text.source.IVerticalRulerInfo; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.dialogs.PropertyDialogAction; + +/** + * Ruler action to display breakpoint properties. + */ +public class BreakpointPropertiesRulerAction extends AbstractDisassemblyBreakpointRulerAction { + + private Object fContext; + + protected BreakpointPropertiesRulerAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + super(disassemblyPart, rulerInfo); + setText(DisassemblyMessages.Disassembly_action_BreakpointProperties_label); + } + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyAction#run() + */ + @Override + public void run() { + if ( fContext != null ) { + PropertyDialogAction action = new PropertyDialogAction( getDisassemblyPart().getSite(), new ISelectionProvider() { + + public void addSelectionChangedListener( ISelectionChangedListener listener ) { + } + + public ISelection getSelection() { + return new StructuredSelection( fContext ); + } + + public void removeSelectionChangedListener( ISelectionChangedListener listener ) { + } + + public void setSelection( ISelection selection ) { + } + } ); + action.run(); + action.dispose(); + } + } + + /* + * @see IUpdate#update() + */ + @Override + public void update() { + IBreakpoint breakpoint= getBreakpoint(); + if (breakpoint instanceof ICBreakpoint) { + fContext = new CBreakpointContext((ICBreakpoint)breakpoint, getDebugContext()); + } else { + fContext = breakpoint; + } + setEnabled( fContext != null ); + } + + private ISelection getDebugContext() { + return DebugUITools.getDebugContextManager().getContextService(getDisassemblyPart().getSite().getWorkbenchWindow()).getActiveContext(); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java new file mode 100644 index 00000000000..b5589d0d4f3 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/BreakpointPropertiesRulerActionDelegate.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.source.IVerticalRulerInfo; + +/** + * Ruler action delegate for the breakpoint properties action. + */ +public class BreakpointPropertiesRulerActionDelegate extends AbstractDisassemblyRulerActionDelegate { + + /* + * @see org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions.AbstractDisassemblyRulerActionDelegate#createAction(org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyPart, org.eclipse.jface.text.source.IVerticalRulerInfo) + */ + @Override + protected IAction createAction(IDisassemblyPart disassemblyPart, IVerticalRulerInfo rulerInfo) { + return new BreakpointPropertiesRulerAction(disassemblyPart, rulerInfo); + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java new file mode 100644 index 00000000000..87ac2124c45 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/actions/TextOperationAction.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.actions; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.texteditor.IUpdate; + +/** + * TextOperationAction + */ +public class TextOperationAction extends Action implements IUpdate { + + private int fOperationCode= -1; + private ITextOperationTarget fOperationTarget; + + public TextOperationAction(ITextViewer viewer, int operationCode) { + fOperationCode= operationCode; + fOperationTarget= viewer.getTextOperationTarget(); + update(); + } + + /** + * Updates the enabled state of the action. + * Fires a property change if the enabled state changes. + * + * @see Action#firePropertyChange(String, Object, Object) + */ + public void update() { + + boolean wasEnabled= isEnabled(); + boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode)); + setEnabled(isEnabled); + + if (wasEnabled != isEnabled) { + firePropertyChange(ENABLED, wasEnabled ? Boolean.TRUE : Boolean.FALSE, isEnabled ? Boolean.TRUE : Boolean.FALSE); + } + } + + /** + * @see Action#run() + */ + @Override + public void run() { + if (fOperationCode != -1 && fOperationTarget != null) { + fOperationTarget.doOperation(fOperationCode); + } + } + } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java new file mode 100644 index 00000000000..7f72b603188 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/Addr2Line.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + + +public class Addr2Line { + public BigInteger addr; + public Addr2Line next; + public int first; + public int last; + + public static int hash(BigInteger addr, int size) { + return (int)((addr.shiftRight(2).longValue()) % size); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java new file mode 100644 index 00000000000..7f4b865fdf0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/AddressRangePosition.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +import org.eclipse.jface.text.Position; + +/** + * AddressRangePosition + */ +public class AddressRangePosition extends Position { + + public BigInteger fAddressOffset; + public BigInteger fAddressLength; + public boolean fValid; + + /** + * @param offset + * @param length + */ + public AddressRangePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength) { + super(offset, length); + fAddressOffset = addressOffset; + fAddressLength = addressLength; + fValid = true; + } + + /** + * @param offset + * @param length + * @param valid + */ + public AddressRangePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, boolean valid) { + super(offset, length); + fAddressOffset = addressOffset; + fAddressLength = addressLength; + fValid = valid; + } + + /** + * @param address + * @return + */ + public boolean containsAddress(BigInteger address) { + return address.compareTo(fAddressOffset) >= 0 + && address.compareTo(fAddressOffset.add(fAddressLength)) < 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object other) { + // identity comparison + return this == other; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java new file mode 100644 index 00000000000..c95b04a817a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/BreakpointsAnnotationModel.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems, Inc. 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: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; +import java.util.Iterator; + +import org.eclipse.cdt.debug.core.model.ICAddressBreakpoint; +import org.eclipse.cdt.debug.core.model.ICLineBreakpoint; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IMarkerDelta; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointListener; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ILineBreakpoint; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationModel; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.texteditor.MarkerAnnotation; +import org.eclipse.ui.texteditor.SimpleMarkerAnnotation; + +/** + * Annotation model for breakpoints in the disassembly. + * Works only with {@link DisassemblyDocument}. + */ +public class BreakpointsAnnotationModel extends AnnotationModel implements IBreakpointListener, IDocumentListener { + + private Runnable fCatchup; + + @Override + public void connect(IDocument document) { + super.connect(document); + if (document instanceof DisassemblyDocument) { + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + addBreakpoints(bpMgr.getBreakpoints()); + bpMgr.addBreakpointListener(this); + document.addDocumentListener(this); + } + } + + @Override + public void disconnect(IDocument document) { + if (document instanceof DisassemblyDocument) { + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + bpMgr.removeBreakpointListener(this); + document.removeDocumentListener(this); + fCatchup= null; + } + super.disconnect(document); + } + + private void catchupWithBreakpoints() { + removeAllAnnotations(false); + final IBreakpointManager bpMgr= DebugPlugin.getDefault().getBreakpointManager(); + addBreakpoints(bpMgr.getBreakpoints()); + } + + private void addBreakpoints(IBreakpoint[] breakpoints) { + for (IBreakpoint breakpoint : breakpoints) { + addBreakpointAnnotation(breakpoint, false); + } + fireModelChanged(); + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint) + */ + public void breakpointAdded(IBreakpoint breakpoint) { + addBreakpointAnnotation(breakpoint, true); + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) { + Annotation a= findAnnotation(breakpoint.getMarker()); + if (a != null) { + if (a instanceof SimpleMarkerAnnotation) { + ((SimpleMarkerAnnotation)a).update(); + } + synchronized (getLockObject()) { + getAnnotationModelEvent().annotationChanged(a); + } + fireModelChanged(); + } + } + + /* + * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta) + */ + public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) { + Annotation a= findAnnotation(breakpoint.getMarker()); + if (a != null) { + removeAnnotation(a, true); + } + } + + @SuppressWarnings("unchecked") + private Annotation findAnnotation(IMarker marker) { + for (Iterator it= getAnnotationIterator(false); it.hasNext();) { + SimpleMarkerAnnotation a= it.next(); + if (a.getMarker().equals(marker)) { + return a; + } + } + return null; + } + + private void addBreakpointAnnotation(IBreakpoint breakpoint, boolean fireEvent) { + final IMarker marker= breakpoint.getMarker(); + if (marker == null) { + return; + } + try { + Position position= createPositionFromBreakpoint(breakpoint); + if (position != null) { + addAnnotation(new MarkerAnnotation(marker), position, fireEvent); + } + } catch (CoreException exc) { + // ignore problems accessing attributes + } catch (BadLocationException exc) { + // ignore wrong positions + } + } + + private Position createPositionFromBreakpoint(IBreakpoint breakpoint) throws CoreException { + if (breakpoint instanceof ICAddressBreakpoint) { + ICAddressBreakpoint addressBreakpoint= (ICAddressBreakpoint) breakpoint; + return createPositionFromAddress(decodeAddress(addressBreakpoint.getAddress())); + } else if (breakpoint instanceof ILineBreakpoint) { + ILineBreakpoint lineBreakpoint= (ILineBreakpoint) breakpoint; + Position position= null; + final int lineNumber= lineBreakpoint.getLineNumber() - 1; + final IMarker marker= breakpoint.getMarker(); + if (marker.getResource().getType() == IResource.FILE) { + position= createPositionFromSourceLine((IFile) marker.getResource(), lineNumber); + } else if (breakpoint instanceof ICLineBreakpoint) { + ICLineBreakpoint cBreakpoint= (ICLineBreakpoint) breakpoint; + position= createPositionFromSourceLine(cBreakpoint.getFileName(), lineNumber); + if (position == null) { + position= createPositionFromAddress(decodeAddress(cBreakpoint.getAddress())); + } + } else { + String fileName= marker.getAttribute(ICLineBreakpoint.SOURCE_HANDLE, null); + if (fileName != null) { + position= createPositionFromSourceLine(fileName, lineNumber); + } + } + return position; + } + return null; + } + + private Position createPositionFromSourceLine(String fileName, int lineNumber) { + return getDisassemblyDocument().getSourcePosition(fileName, lineNumber); + } + + private Position createPositionFromSourceLine(IFile file, int lineNumber) { + return getDisassemblyDocument().getSourcePosition(file, lineNumber); + } + + private Position createPositionFromAddress(BigInteger address) { + AddressRangePosition p= getDisassemblyDocument().getDisassemblyPosition(address); + if (p != null && p.fValid) { + return new Position(p.offset, p.length); + } + return null; + } + + private DisassemblyDocument getDisassemblyDocument() { + return (DisassemblyDocument) fDocument; + } + + /** + * Decode given string representation of a non-negative integer. A + * hexadecimal encoded integer is expected to start with 0x. + * + * @param string + * decimal or hexadecimal representation of an non-negative integer + * @return address value as BigInteger + */ + private static BigInteger decodeAddress(String string) { + if (string.startsWith("0x")) { //$NON-NLS-1$ + return new BigInteger(string.substring(2), 16); + } + return new BigInteger(string); + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + if (fCatchup == null && event.fText != null && event.fText.length() > 0) { + fCatchup= new Runnable() { + public void run() { + if (fCatchup == this) { + catchupWithBreakpoints(); + fCatchup= null; + } + }}; + Display.getCurrent().timerExec(50, fCatchup); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java new file mode 100644 index 00000000000..84666b0351c --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyDocument.java @@ -0,0 +1,1423 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text.REDDocument; +import org.eclipse.core.resources.IStorage; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; + +/** + * DisassemblyDocument + */ +public class DisassemblyDocument extends REDDocument { + + public final static String CATEGORY_MODEL = "category_model"; //$NON-NLS-1$ + public final static String CATEGORY_DISASSEMBLY = "category_disassembly"; //$NON-NLS-1$ + public final static String CATEGORY_SOURCE = "category_source"; //$NON-NLS-1$ + public final static String CATEGORY_LABELS = "category_labels"; //$NON-NLS-1$ + + private final static boolean DEBUG = false; + + public ArrayList fInvalidAddressRanges = new ArrayList(); + public ArrayList fInvalidSource = new ArrayList(); + private Map fFileInfoMap = new HashMap(); + + private int fMaxFunctionLength = 0; + + private boolean fShowAddresses = false; + private int fRadix = 16; + private boolean fShowRadixPrefix = false; + private String fRadixPrefix; + private int fNumberOfDigits; + private boolean fShowCodeBytes = false; + + private int fNumberOfInstructions; + private double fMeanSizeOfInstructions = 4; + + public DisassemblyDocument() { + super(); + } + + /* + * @see org.eclipse.jface.text.AbstractDocument#completeInitialization() + */ + @Override + protected void completeInitialization() { + super.completeInitialization(); + addPositionCategory(CATEGORY_MODEL); + addPositionCategory(CATEGORY_DISASSEMBLY); + addPositionCategory(CATEGORY_SOURCE); + addPositionCategory(CATEGORY_LABELS); + setRadix(16); + setShowRadixPrefix(false); + } + + /** + * Cleanup. + */ + @Override + public void dispose() { + super.dispose(); + if (fFileInfoMap != null) { + // cleanup source info + for (Iterator iter = fFileInfoMap.values().iterator(); iter.hasNext();) { + SourceFileInfo fi = iter.next(); + fi.dispose(); + } + fFileInfoMap = null; + } + } + + public List getInvalidAddressRanges() { + return fInvalidAddressRanges; + } + + public List getInvalidSource() { + return fInvalidSource; + } + + public void setMaxOpcodeLength(int opcodeLength) { + fMaxFunctionLength = opcodeLength; + } + + public int getMaxFunctionLength() { + return fMaxFunctionLength; + } + + public int getAddressLength() { + return fNumberOfDigits+2; + } + + public int getMeanSizeOfInstructions() { + return (int)(fMeanSizeOfInstructions+.9); + } + + public Iterator getModelPositionIterator(BigInteger address) { + try { + return getPositionIterator(CATEGORY_MODEL, address); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return null; + } + + public Iterator getPositionIterator(String category, int offset) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List positions = (List) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + int idx = computeIndexInPositionList(positions, offset, true); + return positions.listIterator(idx); + } + + public Iterator getPositionIterator(String category, BigInteger address) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List positions = (List) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + int idx = computeIndexInPositionListFirst(positions, address); + return positions.listIterator(idx); + } + + public int computeIndexInCategory(String category, BigInteger address) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List c = (List) getDocumentManagedPositions().get(category); + if (c == null) { + throw new BadPositionCategoryException(); + } + return computeIndexInPositionListFirst(c, address); + } + + /** + * Computes the index in the list of positions at which a position with the + * given address would be inserted. The position is supposed to become the + * first in this list of all positions with the same offset. + * + * @param positions + * the list in which the index is computed + * @param address + * the address for which the index is computed + * @return the computed index + * + */ + protected int computeIndexInPositionListFirst(List positions, BigInteger address) { + int size = positions.size(); + if (size == 0) { + return 0; + } + int left = 0; + int right = size - 1; + int mid = 0; + while (left <= right) { + mid = (left + right) / 2; + AddressRangePosition range = positions.get(mid); + if (address.compareTo(range.fAddressOffset) < 0) { + right = mid - 1; + } else if (address.compareTo(range.fAddressOffset) == 0) { + break; + } else if (address.compareTo(range.fAddressOffset.add(range.fAddressLength)) >= 0) { + left = mid + 1; + } else { + break; + } + } + int idx = mid; + AddressRangePosition p = positions.get(idx); + if (address.compareTo(p.fAddressOffset.add(p.fAddressLength)) > 0) { + ++idx; + } else if (address.compareTo(p.fAddressOffset) == 0) { + do { + --idx; + if (idx < 0) { + break; + } + p = positions.get(idx); + } while (address.compareTo(p.fAddressOffset) == 0); + ++idx; + } + return idx; + } + + /** + * Computes the index in the list of positions at which a position with the + * given address would be inserted. The position is supposed to become the + * last but one in this list of all positions with the same address. + * + * @param positions + * the list in which the index is computed + * @param address + * the address for which the index is computed + * @return the computed index + * + */ + protected int computeIndexInPositionListLast(List positions, BigInteger address) { + int size = positions.size(); + if (size == 0) { + return 0; + } + int left = 0; + int right = size - 1; + int mid = 0; + while (left <= right) { + mid = (left + right) / 2; + AddressRangePosition range = positions.get(mid); + if (address.compareTo(range.fAddressOffset) < 0) { + right = mid - 1; + } else if (address.compareTo(range.fAddressOffset) == 0) { + break; + } else if (address.compareTo(range.fAddressOffset.add(range.fAddressLength)) >= 0) { + left = mid + 1; + } else { + break; + } + } + int idx = mid; + AddressRangePosition p = positions.get(idx); + if (address.compareTo(p.fAddressOffset) > 0) { + ++idx; + } else if (address.compareTo(p.fAddressOffset) == 0 && p.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + do { + ++idx; + if (idx == size) { + break; + } + p = positions.get(idx); + } while (address.compareTo(p.fAddressOffset) == 0 && p.fAddressLength.compareTo(BigInteger.ZERO) == 0); + // --idx; + } + return idx; + } + + /** + * Computes the index in the list of positions at which a position with the + * given offset would be inserted. The position is supposed to become the + * last in this list of all positions with the same offset. + * + * @param positions + * the list in which the index is computed + * @param offset + * the offset for which the index is computed + * @return the computed index + * + * @see IDocument#computeIndexInCategory(String, int) + */ + protected int computeIndexInPositionListLast(List positions, int offset) { + + if (positions.size() == 0) + return 0; + + int left = 0; + int right = positions.size() - 1; + int mid = 0; + Position p = null; + + while (left < right) { + + mid = (left + right) / 2; + + p = positions.get(mid); + if (offset < p.getOffset()) { + if (left == mid) + right = left; + else + right = mid - 1; + } else if (offset > p.getOffset()) { + if (right == mid) + left = right; + else + left = mid + 1; + } else if (offset == p.getOffset()) { + left = right = mid; + } + + } + + int pos = left; + p = positions.get(pos); + while (offset >= p.getOffset()) { + // entry will become the last of all entries with the same offset + ++pos; + if (pos == positions.size()) { + break; + } + p = positions.get(pos); + } + + assert 0 <= pos && pos <= positions.size(); + + return pos; + } + + /** + * Get the position for the supplied category and index. + * + * @param category + * @param index + * @return a Position matching the category and index, or null. + */ + public Position getPositionOfIndex(String category, int index) throws BadPositionCategoryException { + if (index >= 0) { + @SuppressWarnings("unchecked") + List positions = (List) getDocumentManagedPositions().get(category); + if (positions == null) { + throw new BadPositionCategoryException(); + } + if (index < positions.size()) { + return positions.get(index); + } + } + return null; + } + + /** + * @param address + * @return + */ + public AddressRangePosition getPositionOfAddress(BigInteger address) { + AddressRangePosition pos = getPositionOfAddress(CATEGORY_DISASSEMBLY, address); + return pos; + } + + /** + * @param category + * @param address + * @return + */ + public AddressRangePosition getPositionOfAddress(String category, BigInteger address) { + @SuppressWarnings("unchecked") + List positions = (List) getDocumentManagedPositions().get(category); + if (positions == null) { + return null; + } + int index = computeIndexInPositionListFirst(positions, address); + if (index < positions.size()) { + AddressRangePosition p = positions.get(index); + if (address.compareTo(p.fAddressOffset) == 0 || p.containsAddress(address)) { + return p; + } + } + return null; + } + + /** + * @param category + * @param range + * @return + */ + public AddressRangePosition getPositionInAddressRange(String category, AddressRangePosition range) { + @SuppressWarnings("unchecked") + List positions = (List) getDocumentManagedPositions().get(category); + if (positions == null) { + return null; + } + BigInteger endAddress = range.fAddressOffset.add(range.fAddressLength); + int index = computeIndexInPositionListFirst(positions, range.fAddressOffset); + if (index < positions.size()) { + do { + AddressRangePosition p = positions.get(index); + if (p.fAddressOffset.compareTo(endAddress) >= 0) { + --index; + } else { + return p; + } + } while (index >= 0); + } + return null; + } + + /** + * Compute the address of the given document line number. + * + * @param line + * @return the address of the given document line number, -1 if no valid + * address can be computed + */ + public BigInteger getAddressOfLine(int line) { + try { + int offset = getLineOffset(line); + return getAddressOfOffset(offset); + } catch (BadLocationException e) { + // intentionally ignored + } + return BigInteger.valueOf(-1); + } + + /** + * Compute the address off the given document offset. + * + * @param offset + * @return the address of the given document offset, -1 if no valid address + * can be computed + */ + public BigInteger getAddressOfOffset(int offset) { + AddressRangePosition pos; + try { + pos = getModelPosition(offset); + } catch (BadLocationException e) { + internalError(e); + return BigInteger.valueOf(-1); + } + if (pos == null) { + return BigInteger.valueOf(-1); + } + return pos.fAddressOffset; + } + + /** + * @param offset + * @return + */ + public AddressRangePosition getDisassemblyPosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_DISASSEMBLY, offset, false); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (AddressRangePosition) p; + } + + /** + * @param address + * @return + */ + public AddressRangePosition getDisassemblyPosition(BigInteger address) { + return getPositionOfAddress(CATEGORY_DISASSEMBLY, address); + } + + + /** + * @param offset + * @return + * @throws BadLocationException + */ + public AddressRangePosition getModelPosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_MODEL, offset, false); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (AddressRangePosition) p; + } + + /** + * @param offset + * @return + * @throws BadLocationException + */ + public SourcePosition getSourcePosition(int offset) throws BadLocationException { + Position p = null; + try { + p = getPosition(CATEGORY_SOURCE, offset, true); + } catch (BadPositionCategoryException e) { + // cannot happen + } + return (SourcePosition) p; + } + + /** + * @param address + * @return + */ + public SourcePosition getSourcePosition(BigInteger address) { + return (SourcePosition) getPositionOfAddress(CATEGORY_SOURCE, address); + } + + /** + * @param address + * @return + */ + public LabelPosition getLabelPosition(BigInteger address) { + return (LabelPosition) getPositionOfAddress(CATEGORY_LABELS, address); + } + + /** + * @param range + * @return + */ + public SourcePosition getSourcePositionInAddressRange(AddressRangePosition range) { + return (SourcePosition) getPositionInAddressRange(CATEGORY_SOURCE, range); + } + + /** + * Compute document position of the given source line. + * + * @param file the file as an IStorage + * @param lineNumber the 0-based line number + * @return the document position or null + */ + public Position getSourcePosition(IStorage file, int lineNumber) { + SourceFileInfo info= getSourceInfo(file); + return getSourcePosition(info, lineNumber); + } + + /** + * Compute document position of the given source line. + * + * @param fileName the file name, may be a raw debugger path or the path to an external file + * @param lineNumber the 0-based line number + * @return the document position or null + */ + public Position getSourcePosition(String fileName, int lineNumber) { + SourceFileInfo info= getSourceInfo(fileName); + if (info == null) { + info= getSourceInfo(new LocalFileStorage(new File(fileName))); + } + return getSourcePosition(info, lineNumber); + } + + /** + * Compute document position of the given source line. + * + * @param info + * @param lineNumber the 0-based line number + * @return the document position or null + */ + protected Position getSourcePosition(SourceFileInfo info, int lineNumber) { + if (info == null || info.fSource == null) { + return null; + } + try { + SourcePosition srcPos= null; + IRegion stmtLineRegion= info.fSource.getLineInformation(lineNumber); + final int lineOffset = stmtLineRegion.getOffset(); + final int lineLength = stmtLineRegion.getLength() + 1; + BigInteger stmtAddress = info.fLine2Addr[lineNumber]; + if (stmtAddress != null && stmtAddress.compareTo(BigInteger.ZERO) > 0) { + srcPos = getSourcePosition(stmtAddress); + } + if (srcPos == null) { + for (Iterator iterator = getPositionIterator(CATEGORY_SOURCE, 0); iterator.hasNext(); ) { + SourcePosition pos= (SourcePosition) iterator.next(); + if (pos.fFileInfo == info && pos.fValid && lineNumber >= pos.fLine) { + int baseOffset= info.fSource.getLineOffset(pos.fLine); + if (lineOffset + lineLength - baseOffset <= pos.length) { + srcPos= pos; + break; + } + } + } + if (srcPos == null) { + return null; + } + } else if (!srcPos.fValid) { + return null; + } + assert lineNumber >= srcPos.fLine; + int baseOffset = info.fSource.getLineOffset(srcPos.fLine); + int offset = srcPos.offset + lineOffset - baseOffset; + if (offset >= srcPos.offset && offset < srcPos.offset + srcPos.length) { + return new Position(offset, lineLength); + } + } catch (BadLocationException exc) { + // TLETODO Auto-generated catch block + exc.printStackTrace(); + } catch (BadPositionCategoryException exc) { + // TLETODO Auto-generated catch block + exc.printStackTrace(); + } + return null; + } + + + /** + * @param category + * @param offset + * @return + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public Position getPosition(String category, int offset, boolean allowZeroLength) throws BadLocationException, BadPositionCategoryException { + @SuppressWarnings("unchecked") + List list = (List) getDocumentManagedPositions().get(category); + int idx; + idx = computeIndexInPositionList(list, offset, true); + if (idx > 0) { + --idx; + } + while (idx < list.size()) { + Position pos = list.get(idx); + if (pos.offset > offset) { + break; + } + if (pos.includes(offset)) { + return pos; + } + if (allowZeroLength && pos.offset == offset) { + return pos; + } + ++idx; + } + return null; + } + + /** + * @param pos + */ + public void addModelPosition(AddressRangePosition pos) { + try { + addPositionLast(CATEGORY_MODEL, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void addModelPositionFirst(AddressRangePosition pos) { + @SuppressWarnings("unchecked") + List list = (List) getDocumentManagedPositions().get(CATEGORY_MODEL); + int idx; + idx = computeIndexInPositionListFirst(list, pos.fAddressOffset.add(pos.fAddressLength)); + if (idx < list.size()) { + AddressRangePosition nextPos = list.get(idx); + assert nextPos.fAddressOffset.compareTo(pos.fAddressOffset.add(pos.fAddressLength)) == 0; + } + list.add(idx, pos); + } + + /** + * @param pos + * @throws BadLocationException + */ + public void addDisassemblyPosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_DISASSEMBLY, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + if (pos instanceof DisassemblyPosition) { + int functionLength = ((DisassemblyPosition)pos).fFunction.length; + if (functionLength > fMaxFunctionLength) { + fMaxFunctionLength = functionLength; + } + if (fNumberOfInstructions < 100) { + fMeanSizeOfInstructions = (fMeanSizeOfInstructions * fNumberOfInstructions + pos.fAddressLength.floatValue()) / (++fNumberOfInstructions); + } + } + } + + /** + * @param pos + * @throws BadPositionCategoryException + */ + public void addPositionLast(String category, AddressRangePosition pos) throws BadPositionCategoryException { + @SuppressWarnings("unchecked") + List list = (List) getDocumentManagedPositions().get(category); + if (list == null) { + throw new BadPositionCategoryException(); + } + int idx; + idx = computeIndexInPositionListLast(list, pos.fAddressOffset); + list.add(idx, pos); + } + + /** + * @param pos + * @throws BadLocationException + */ + public void addLabelPosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_LABELS, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void addSourcePosition(AddressRangePosition pos) throws BadLocationException { + try { + addPositionLast(CATEGORY_SOURCE, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeDisassemblyPosition(AddressRangePosition pos) { + try { + removePosition(CATEGORY_DISASSEMBLY, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeSourcePosition(AddressRangePosition pos) { + try { + removePosition(CATEGORY_SOURCE, pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + */ + public void removeModelPosition(AddressRangePosition pos) { + try { + removePosition(getCategory(pos), pos); + } catch (BadPositionCategoryException e) { + // cannot happen + } + } + + /** + * @param pos + * @return + */ + private static String getCategory(AddressRangePosition pos) { + if (pos instanceof LabelPosition) { + return CATEGORY_LABELS; + } else if (pos instanceof SourcePosition) { + return CATEGORY_SOURCE; + } + return CATEGORY_DISASSEMBLY; + } + + /* + * @see org.eclipse.jface.text.IDocument#removePosition(java.lang.String, + * org.eclipse.jface.text.Position) + */ + @Override + public void removePosition(String category, Position position) throws BadPositionCategoryException { + super.removePosition(category, position); + if (category != CATEGORY_MODEL && position instanceof AddressRangePosition) { + super.removePosition(CATEGORY_MODEL, position); + } + } + + @SuppressWarnings("unchecked") + public void removePositions(String category, List toRemove) { + if (toRemove.isEmpty()) { + return; + } + List positions = (List) getDocumentManagedPositions().get(category); + if (positions != null) { + positions.removeAll(toRemove); + } + if (category != CATEGORY_MODEL) { + positions = (List) getDocumentManagedPositions().get(CATEGORY_MODEL); + if (positions != null) { + positions.removeAll(toRemove); + } + } + } + + public void addPositionLast(String category, Position position) throws BadLocationException, + BadPositionCategoryException { + + if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength())) + throw new BadLocationException(); + + if (category == null) + throw new BadPositionCategoryException(); + + @SuppressWarnings("unchecked") + List list = (List) getDocumentManagedPositions().get(category); + if (list == null) + throw new BadPositionCategoryException(); + + list.add(computeIndexInPositionListLast(list, position.offset), position); + } + + public void checkConsistency() { + AddressRangePosition last = null; + try { + for (Iterator it = getPositionIterator(CATEGORY_MODEL, 0); it.hasNext();) { + AddressRangePosition pos = (AddressRangePosition) it.next(); + if (last != null) { + assert last.fAddressOffset.compareTo(pos.fAddressOffset) <= 0; + assert last.fAddressOffset.add(last.fAddressLength).compareTo(pos.fAddressOffset) == 0; + assert last.offset <= pos.offset; + assert last.offset + last.length == pos.offset; + } + last = pos; + } + } catch (BadPositionCategoryException e) { + assert false; + } + } + + /** + * @param insertPos + * @param replaceLength + * @param text + * @throws BadLocationException + */ + public void replace(AddressRangePosition insertPos, int replaceLength, String text) throws BadLocationException { + int delta = (text != null ? text.length() : 0) - replaceLength; + if (delta != 0) { + BigInteger address = insertPos.fAddressOffset; + Iterator it = getModelPositionIterator(address); + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + assert pos.fAddressOffset.compareTo(address) >= 0; + if (pos.fAddressOffset.compareTo(address) > 0) { + break; + } + if (pos.offset > insertPos.offset) { + break; + } + if (pos == insertPos) { + break; + } + } + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + pos.offset += delta; + } + } + super.replace(insertPos.offset, replaceLength, text); + } + + /** + * @param pos + * @param insertPos + * @param line + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public AddressRangePosition insertAddressRange(AddressRangePosition pos, AddressRangePosition insertPos, String line, boolean addToModel) + throws BadLocationException { + final BigInteger address = insertPos.fAddressOffset; + BigInteger length = insertPos.fAddressLength; + if (pos == null) { + pos = getPositionOfAddress(address); + } + assert !pos.isDeleted && !pos.fValid && (length.compareTo(BigInteger.ZERO) == 0 || pos.containsAddress(address)); + int insertOffset; + int replaceLength = 0; + if (length.compareTo(BigInteger.ONE) > 0 && !pos.containsAddress(address.add(length.subtract(BigInteger.ONE)))) { + // merge with successor positions + Iterator it = getModelPositionIterator(pos.fAddressOffset.add(pos.fAddressLength)); + assert it.hasNext(); + do { + AddressRangePosition overlap = it.next(); + BigInteger posEndAddress= pos.fAddressOffset.add(pos.fAddressLength); + assert pos.offset <= overlap.offset && overlap.fAddressOffset.compareTo(posEndAddress) == 0; + if (overlap instanceof LabelPosition || overlap instanceof SourcePosition) { + // don't override label or source positions, instead fix + // length of disassembly line to insert + length = insertPos.fAddressLength = posEndAddress.subtract(address.max(pos.fAddressOffset)); + break; + } + pos.fAddressLength = pos.fAddressLength.add(overlap.fAddressLength); + replaceLength = overlap.offset + overlap.length - pos.offset - pos.length; + it.remove(); + removeModelPosition(overlap); + if (!overlap.fValid) { + fInvalidAddressRanges.remove(overlap); + } + } while(!pos.containsAddress(address.add(length.subtract(BigInteger.ONE)))); + } + BigInteger newEndAddress = pos.fAddressOffset.add(pos.fAddressLength); + BigInteger newStartAddress = address.add(length); + assert newEndAddress.compareTo(newStartAddress) >= 0; + if (address.compareTo(pos.fAddressOffset) == 0) { + // insert at start of range + insertOffset = pos.offset; + if (replaceLength == 0 && newEndAddress.compareTo(newStartAddress) > 0) { + // optimization: shrink position in place + pos.fAddressOffset = newStartAddress; + pos.fAddressLength = pos.fAddressLength.subtract(length); + // don't insert new pos + newEndAddress = newStartAddress; + } else { + replaceLength += pos.length; + fInvalidAddressRanges.remove(pos); + removeDisassemblyPosition(pos); + pos = null; + } + } else { + // insert in mid/end of range + insertOffset = pos.offset + pos.length; + pos.fAddressLength = address.subtract(pos.fAddressOffset); + assert pos.fAddressLength.compareTo(BigInteger.ZERO) > 0; + pos = null; + } + if (newEndAddress.compareTo(newStartAddress) > 0) { + pos = insertInvalidAddressRange(insertOffset+replaceLength, 0, newStartAddress, newEndAddress); + } + assert pos == null || pos.fAddressLength.compareTo(BigInteger.ZERO) > 0 && pos.containsAddress(address.add(length)); + assert insertOffset + replaceLength <= getLength(); + + insertPos.offset = insertOffset; + if (addToModel) { + addModelPosition(insertPos); + } + replace(insertPos, replaceLength, line); + if (DEBUG) checkConsistency(); + return pos; + } + + /** + * @param pos + * @param address + * @param length + * @param instruction + * @throws BadPositionCategoryException + * @throws BadLocationException + */ + public AddressRangePosition insertDisassemblyLine(AddressRangePosition pos, BigInteger address, int length, String opcode, String instruction, String file, int lineNr) + throws BadLocationException { + String disassLine = null; + if (instruction == null || instruction.length() == 0) { + disassLine = ""; //$NON-NLS-1$ + } else { + disassLine = buildDisassemblyLine(address, opcode, instruction); + } + AddressRangePosition disassPos; + if (lineNr < 0) { + disassPos = new DisassemblyPosition(0, disassLine.length(), address, BigInteger.valueOf(length), opcode); + } else { + disassPos = new DisassemblyWithSourcePosition(0, disassLine.length(), address, BigInteger.valueOf(length), + opcode, file, lineNr); + } + pos = insertAddressRange(pos, disassPos, disassLine, true); + addDisassemblyPosition(disassPos); + return pos; + } + /** + * @param address + * @param opcode + * @param instruction + */ + private String buildDisassemblyLine(BigInteger address, String opcode, String instruction) { + StringBuffer buf = new StringBuffer(40); + if (fShowAddresses) { + if (fRadixPrefix != null) { + buf.append(fRadixPrefix); + } + String str = address.toString(fRadix); + for (int i=str.length(); i 0) { + buf.append(opcode); + int tab = 16; + if (opcode.length() >= 16) { + tab = (opcode.length() + 8) & ~7; + } + int diff = tab - opcode.length(); + while (diff-- > 0) { + buf.append(' '); + } + } else if (!fShowAddresses) { + buf.append(' '); + buf.append(' '); + } + int n = instruction.length(); + int prefixLen = buf.length(); + for (int j = 0; j < n; j++) { + char ch = instruction.charAt(j); + if (ch == '\t') { + int tab = (buf.length()-prefixLen + 8) & ~0x7; + do + buf.append(' '); + while (buf.length()-prefixLen < tab); + } else { + buf.append(ch); + } + } + buf.append('\n'); + return buf.toString(); + } + + public void setRadix(int radix) { + fRadix = radix; + fNumberOfDigits = (int)(Math.log(1L<<32)/Math.log(radix)+0.9); + setShowRadixPrefix(fShowRadixPrefix); + } + + public void setShowRadixPrefix(boolean showRadixPrefix) { + fShowRadixPrefix = showRadixPrefix; + if (!fShowRadixPrefix) { + fRadixPrefix = null; + } else if (fRadix == 16) { + fRadixPrefix = "0x"; //$NON-NLS-1$ + } else if (fRadix == 8) { + fRadixPrefix = "0"; //$NON-NLS-1$ + } else { + fRadixPrefix = null; + } + } + + public AddressRangePosition insertErrorLine(AddressRangePosition pos, BigInteger address, BigInteger length, String line) + throws BadLocationException { + int hashCode = line.hashCode(); + final long alignment = 0x1L; + if (alignment > 1 && !(pos instanceof ErrorPosition)) { + AddressRangePosition before = getPositionOfAddress(address.subtract(BigInteger.ONE)); + if (before instanceof ErrorPosition && before.hashCode() == hashCode && before.offset + before.length == pos.offset) { + assert before.fAddressOffset.add(before.fAddressLength).compareTo(address) == 0; + assert pos.fAddressOffset.compareTo(address) == 0; + // merge with previous error position + BigInteger pageOffset = before.fAddressOffset.and(BigInteger.valueOf(~(alignment-1))); + BigInteger mergeLen = pageOffset.add(BigInteger.valueOf(alignment)) + .subtract((before.fAddressOffset.add(before.fAddressLength))).min(length); + if (mergeLen.compareTo(BigInteger.ZERO) > 0) { + pos.fAddressLength = pos.fAddressLength.subtract(mergeLen); + if (pos.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + replace(pos, pos.length, null); + removeModelPosition(pos); + fInvalidAddressRanges.remove(pos); + pos = null; + } else { + pos.fAddressOffset = pos.fAddressOffset.add(mergeLen); + } + before.fAddressLength = before.fAddressLength.add(mergeLen); + address = address.add(mergeLen); + length = length.subtract(mergeLen); + if (DEBUG) checkConsistency(); + if (length.compareTo(BigInteger.ZERO) == 0) { + return pos; + } + } + } + AddressRangePosition after = getPositionOfAddress(address.add(length)); + if (after instanceof ErrorPosition && after.hashCode() == hashCode && pos.offset + pos.length == after.offset) { + assert after.fAddressOffset == address.add(length); + assert pos.fAddressOffset.add(pos.fAddressLength).compareTo(after.fAddressOffset) == 0; + // merge with next error position + BigInteger pageOffset = after.fAddressOffset.add(BigInteger.valueOf(~(alignment-1))); + BigInteger mergeLen = after.fAddressOffset.subtract(pageOffset).min(length); + if (mergeLen.compareTo(BigInteger.ZERO) > 0) { + after.fAddressOffset = after.fAddressOffset.subtract(mergeLen); + after.fAddressLength = after.fAddressLength.add(mergeLen); + pos.fAddressLength = pos.fAddressLength.subtract(mergeLen); + if (pos.fAddressLength.compareTo(BigInteger.ZERO) == 0) { + replace(pos, pos.length, null); + removeModelPosition(pos); + fInvalidAddressRanges.remove(pos); + pos = null; + } + if (DEBUG) checkConsistency(); + length = length.subtract(mergeLen); + if (length.compareTo(BigInteger.ZERO) == 0) { + return pos; + } + } + } + } + BigInteger pageOffset = address.and(BigInteger.valueOf(~(alignment-1))); + BigInteger posLen = pageOffset.add(BigInteger.valueOf(alignment)).subtract(address).min(length); + while (length.compareTo(BigInteger.ZERO) > 0) { + AddressRangePosition errorPos = new ErrorPosition(0, 0, address, posLen, hashCode); + String errorLine = buildDisassemblyLine(address, null, line); + // TLEHACK: check for error messages, which occur only temporarily: + // "Target is busy. Try again later" + // "Cannot Perform requested Operation" + if (line.startsWith("Target is busy") || line.startsWith("Cannot perform")) { //$NON-NLS-1$ //$NON-NLS-2$ + // try again only once... + if (!(pos instanceof ErrorPosition)) { + errorLine = "...\n"; //$NON-NLS-1$ + errorPos.fValid = false; + } + } + errorPos.length = errorLine.length(); + pos = insertAddressRange(pos, errorPos, errorLine, true); + addDisassemblyPosition(errorPos); + if (!errorPos.fValid) { + fInvalidAddressRanges.add(errorPos); + } + length = length.subtract(posLen); + address = address.add(posLen); + posLen = BigInteger.valueOf(alignment).min(length); + } + return pos; + } + + /** + * @param pos + * @param address + * @param label + * @throws BadLocationException + * @throws BadPositionCategoryException + */ + public AddressRangePosition insertLabel(AddressRangePosition pos, BigInteger address, String label, boolean showLabels) + throws BadLocationException { + String labelLine = showLabels ? label + ":\n" : ""; //$NON-NLS-1$ //$NON-NLS-2$ + LabelPosition labelPos = getLabelPosition(address); + if (labelPos != null) { + assert labelPos.fAddressOffset.compareTo(address) == 0; + if (labelPos.length != labelLine.length()) { + int oldLength = labelPos.length; + labelPos.length = labelLine.length(); + replace(labelPos, oldLength, labelLine); + } + return pos; + } + labelPos = new LabelPosition(0, labelLine.length(), address, null); + pos = insertAddressRange(pos, labelPos, labelLine, true); + addLabelPosition(labelPos); + return pos; + } + + /** + * @param pos + * @param address + * @param source + * @param line + * @param endOfSource + * @throws BadLocationException + * @throws BadPositionCategoryException + */ + public SourcePosition insertSource(SourcePosition pos, String source, int line, boolean endOfSource) { +// System.out.println("insertSource at "+getAddressText(pos.fAddressOffset)); +// System.out.println(source); + String sourceLines = source; + if (source.length() > 0 && sourceLines.charAt(source.length() - 1) != '\n') { + sourceLines += "\n"; //$NON-NLS-1$ + } + try { + assert !pos.fValid; + int oldLength = pos.length; + pos.length = sourceLines.length(); + pos.fLine = line; + pos.fValid = true; + fInvalidSource.remove(pos); + replace(pos, oldLength, sourceLines); + if (!endOfSource) { + if (pos.length > 0) { + SourcePosition oldPos = getSourcePosition(pos.offset+pos.length); + if (oldPos == null || oldPos.fAddressOffset.compareTo(pos.fAddressOffset) != 0) { + pos = new SourcePosition(pos.offset+pos.length, 0, pos.fAddressOffset, pos.fFileInfo, line, false); + addSourcePosition(pos); + addModelPosition(pos); + fInvalidSource.add(pos); + } else { + //TLETODO need more checks for correct source pos + pos = oldPos; + } + } + } + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * @param pos + * @param address + * @param fi + * @param lineNr + * @return + */ + public AddressRangePosition insertInvalidSource(AddressRangePosition pos, BigInteger address, SourceFileInfo fi, int lineNr) { + SourcePosition sourcePos = getSourcePosition(address); + if (sourcePos != null) { + return pos; + } + String sourceLine = ""; //$NON-NLS-1$ + sourcePos = new SourcePosition(0, sourceLine.length(), address, fi, lineNr, false); + try { + pos = insertAddressRange(pos, sourcePos, sourceLine, true); + addSourcePosition(sourcePos); + assert !fInvalidSource.contains(sourcePos); + fInvalidSource.add(sourcePos); + } catch (BadLocationException e) { + internalError(e); + } + return pos; + } + + /** + * @param offset + * @param replaceLength + * @param startAddress + * @param endAddress + * @return + */ + public AddressRangePosition insertInvalidAddressRange(int offset, int replaceLength, BigInteger startAddress, BigInteger endAddress) { + String periods = "...\n"; //$NON-NLS-1$ + AddressRangePosition newPos = new AddressRangePosition(offset, periods.length(), startAddress, endAddress + .subtract(startAddress), false); + try { + addModelPositionFirst(newPos); + replace(newPos, replaceLength, periods); + addDisassemblyPosition(newPos); + fInvalidAddressRanges.add(newPos); + } catch (BadLocationException e) { + internalError(e); + } + return newPos; + } + + public void invalidateAddressRange(BigInteger startAddress, BigInteger endAddress, boolean collapse) { + deleteDisassemblyRange(startAddress, endAddress, true, collapse); + } + + public void deleteDisassemblyRange(BigInteger startAddress, BigInteger endAddress, boolean invalidate, boolean collapse) { + String replacement = invalidate ? "...\n" : null; //$NON-NLS-1$ + int replaceLen = replacement != null ? replacement.length() : 0; + AddressRangePosition lastPos = null; + ArrayList toRemove = new ArrayList(); + Iterator it = getModelPositionIterator(startAddress); + while (it.hasNext()) { + AddressRangePosition pos = it.next(); + BigInteger posEndAddress = pos.fAddressOffset.add(pos.fAddressLength); + if (pos instanceof LabelPosition) { + if (!invalidate && pos.length > 0 && posEndAddress.compareTo(endAddress) > 0) { + try { + int oldLength = pos.length; + pos.length = 0; + replace(pos, oldLength, null); + } catch (BadLocationException e) { + internalError(e); + } + } + pos = null; + } else if (pos instanceof SourcePosition) { + pos = null; + } else if (pos instanceof ErrorPosition) { + pos = null; + } else if (pos instanceof DisassemblyPosition) { + // optimization: join adjacent positions + if (collapse && lastPos != null + && (invalidate || lastPos.fValid == pos.fValid) + && lastPos.offset+lastPos.length == pos.offset) { + assert lastPos.fAddressOffset.add(lastPos.fAddressLength).compareTo(pos.fAddressOffset) == 0; + lastPos.length += pos.length; + lastPos.fAddressLength = lastPos.fAddressLength.add(pos.fAddressLength); + toRemove.add(pos); + if (!pos.fValid) { + fInvalidAddressRanges.remove(pos); + } + pos = null; + if (posEndAddress.compareTo(endAddress) < 0) { + continue; + } + } + } + if (lastPos != null) { + try { + if (lastPos.length > 0 || replaceLen > 0) { + int oldLength = lastPos.length; + lastPos.length = replaceLen; + replace(lastPos, oldLength, replacement); + } + } catch (BadLocationException e) { + internalError(e); + } + } + if (pos == null && posEndAddress.compareTo(endAddress) >= 0) { + break; + } + lastPos = null; + if (pos != null) { + if (pos.fValid && invalidate) { + pos.fValid = false; + fInvalidAddressRanges.add(pos); + } + lastPos = pos; + } + } + removePositions(CATEGORY_DISASSEMBLY, toRemove); + if (DEBUG) checkConsistency(); + } + + public void invalidateSource() { + Iterator it; + try { + it = getPositionIterator(CATEGORY_SOURCE, 0); + } catch (BadPositionCategoryException e) { + internalError(e); + return; + } + while (it.hasNext()) { + SourcePosition srcPos = (SourcePosition)it.next(); + if (srcPos != null && srcPos.fValid) { + srcPos.fValid = false; + assert !getInvalidSource().contains(srcPos); + getInvalidSource().add(srcPos); + } + } + } + + public void invalidateDisassemblyWithSource(boolean removeDisassembly) { + for (Iterator it = fFileInfoMap.values().iterator(); it.hasNext();) { + SourceFileInfo info = it.next(); + if (info.fLine2Addr != null) { + deleteDisassemblyRange(info.fStartAddress, info.fEndAddress.add(BigInteger.ONE), !removeDisassembly, !removeDisassembly); + } + } + } + + /** + * @param start + * @param end + * @throws BadLocationException + */ + public void deleteLineRange(int start, int end) throws BadLocationException { + if (start >= end) { + return; + } + int startOffset = getLineOffset(start); + int endOffset = getLineOffset(end); + int replaceLength = 0; + AddressRangePosition startPos = getDisassemblyPosition(startOffset); + if (startPos == null) { + return; + } + startOffset = startPos.offset; + AddressRangePosition endPos = getDisassemblyPosition(endOffset); + if (endPos == null) { + return; + } + BigInteger startAddress = BigInteger.ZERO; + BigInteger addressLength = BigInteger.ZERO; + ArrayList toRemove = new ArrayList(); + try { + Iterator it = getPositionIterator(DisassemblyDocument.CATEGORY_MODEL, startAddress); + while (it.hasNext()) { + AddressRangePosition p = it.next(); + addressLength = addressLength.add(p.fAddressLength); + replaceLength += p.length; + toRemove.add(p); + if (!p.fValid) { + if (p instanceof SourcePosition) { + getInvalidSource().remove(p); + } else { + getInvalidAddressRanges().remove(p); + } + } + if (addressLength.compareTo(BigInteger.ZERO) > 0 && p.fAddressOffset.compareTo(endPos.fAddressOffset) >= 0) { + break; + } + } + } catch (BadPositionCategoryException e) { + // cannot happen + } + for (Iterator iter = toRemove.iterator(); iter.hasNext();) { + AddressRangePosition pos = iter.next(); + removeModelPosition(pos); + } + if (addressLength.compareTo(BigInteger.ZERO) > 0) { + insertInvalidAddressRange(startOffset, replaceLength, startAddress, startAddress.add(addressLength)); + } + } + + public SourceFileInfo getSourceInfo(BigInteger address) { + AddressRangePosition pos = getDisassemblyPosition(address); + if (pos instanceof DisassemblyPosition) { + DisassemblyPosition disassPos = (DisassemblyPosition)pos; + return getSourceInfo(disassPos.getFile()); + } + return null; + } + + public SourceFileInfo getSourceInfo(String file) { + if (fFileInfoMap == null || file == null) { + return null; + } + for (Iterator iter = fFileInfoMap.values().iterator(); iter.hasNext();) { + SourceFileInfo info = iter.next(); + if (file.equals(info.fFileKey)) { + return info; + } + } + return null; + } + + public SourceFileInfo getSourceInfo(IStorage sourceElement) { + if (fFileInfoMap == null) { + return null; + } + SourceFileInfo fi = fFileInfoMap.get(sourceElement); + return fi; + } + + public SourceFileInfo createSourceInfo(String fileKey, IStorage sourceElement, Runnable done) { + SourceFileInfo fi = new SourceFileInfo(fileKey, sourceElement); + assert fFileInfoMap != null; + if (fFileInfoMap != null) { + fFileInfoMap.put(sourceElement, fi); + new SourceReadingJob(fi, done); + } + return fi; + } + + private void internalError(Throwable e) { + if (DEBUG) { + System.err.println("Disassembly: Internal error"); //$NON-NLS-1$ + e.printStackTrace(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java new file mode 100644 index 00000000000..d07f699353a --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyPosition.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * DisassemblyPosition + */ +public class DisassemblyPosition extends AddressRangePosition { + + public char[] fFunction; + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param addressLength + * @param opcodes + */ + public DisassemblyPosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, String opcodes) { + super(offset, length, addressOffset, addressLength); + fFunction = opcodes.toCharArray(); + } + + /** + * @return source file + */ + public String getFile() { + return null; + } + + /** + * @return source line number + */ + public int getLine() { + return -1; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java new file mode 100644 index 00000000000..3b466857f28 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/DisassemblyWithSourcePosition.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * DisassemblyWithSourcePosition + */ +public class DisassemblyWithSourcePosition extends DisassemblyPosition { + + private String fFile; + private int fLine; + + /** + * @param offset + * @param length + * @param addressOffset + * @param addressLength + * @param opcodes + */ + public DisassemblyWithSourcePosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, String opcodes, String file, int lineNr) { + super(offset, length, addressOffset, addressLength, opcodes); + fFile = file; + fLine = lineNr; + } + + @Override + public String getFile() { + return fFile; + } + + @Override + public int getLine() { + return fLine; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java new file mode 100644 index 00000000000..3e47f0783c9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/ErrorPosition.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * ErrorPosition + */ +public class ErrorPosition extends AddressRangePosition { + + public int fHashCode; + + /** + * @param offset + * @param length + * @param addressOffset + * @param addressLength + */ + public ErrorPosition(int offset, int length, BigInteger addressOffset, BigInteger addressLength, int hashCode) { + super(offset, length, addressOffset, addressLength); + fHashCode = hashCode; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return fHashCode; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java new file mode 100644 index 00000000000..5dbaea989c0 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/LabelPosition.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * LabelPosition + */ +public class LabelPosition extends AddressRangePosition { + + public String fLabel; + + /** + * @param offset + * @param length + * @param addressOffset + */ + public LabelPosition(int offset, int length, BigInteger addressOffset, String label) { + super(offset, length, addressOffset, BigInteger.ZERO); + fLabel = label; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java new file mode 100644 index 00000000000..152566bed25 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceDocumentProvider.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.util.Iterator; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.text.REDDocument; +import org.eclipse.cdt.internal.ui.editor.CDocumentSetupParticipant; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFileState; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.ui.IStorageEditorInput; +import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.editors.text.StorageDocumentProvider; + +/** + * SourceDocumentProvider + */ +@SuppressWarnings("restriction") +public class SourceDocumentProvider extends StorageDocumentProvider { + + public SourceDocumentProvider() { + super(); + } + + /** + * Dispose all connected documents. + */ + public void dispose() { + Iterator it = getConnectedElements(); + while(it.hasNext()) { + Object element = it.next(); + ElementInfo info = getElementInfo(element); + // force refcount to 1 + info.fCount = 1; + disconnect(element); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createDocument(java.lang.Object) + */ + @Override + protected IDocument createEmptyDocument() { + IDocument doc = new REDDocument(); + return doc; + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#createAnnotationModel(java.lang.Object) + */ + @Override + protected IAnnotationModel createAnnotationModel(Object element) throws CoreException { + return null; + } + + /* + * @see org.eclipse.ui.editors.text.StorageDocumentProvider#setupDocument(java.lang.Object, org.eclipse.jface.text.IDocument) + */ + @Override + protected void setupDocument(Object element, IDocument document) { + super.setupDocument(element, document); + if (element instanceof IStorageEditorInput) { + new CDocumentSetupParticipant().setup(document); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#disposeElementInfo(java.lang.Object, org.eclipse.ui.texteditor.AbstractDocumentProvider.ElementInfo) + */ + @Override + protected void disposeElementInfo(Object element, ElementInfo info) { + super.disposeElementInfo(element, info); + IDocument doc = info.fDocument; + if (doc instanceof REDDocument) { + ((REDDocument)doc).dispose(); + } + } + + /* + * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getModificationStamp(java.lang.Object) + */ + @Override + public long getModificationStamp(Object element) { + try { + if (element instanceof IStorageEditorInput) { + IStorage file= ((IStorageEditorInput)element).getStorage(); + if (file instanceof IFile) { + return ((IFile)file).getLocalTimeStamp(); + } else if (file instanceof IFileState) { + return ((IFileState)file).getModificationTime(); + } else if (file instanceof LocalFileStorage) { + return ((LocalFileStorage)file).getFile().lastModified(); + } + } else if (element instanceof IURIEditorInput) { + return EFS.getStore(((IURIEditorInput)element).getURI()).fetchInfo().getLastModified(); + } + } catch (CoreException e) { + // ignore + } + return 0; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java new file mode 100644 index 00000000000..8980e78c637 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceEditorInput.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.util.StorageEditorInput; +import org.eclipse.core.resources.IStorage; + +/** + * SourceEditorInput + */ +public class SourceEditorInput extends StorageEditorInput { + + /** + * @param storage + */ + public SourceEditorInput(IStorage storage) { + super(storage); + } + + /* + * @see org.eclipse.ui.IEditorInput#exists() + */ + public boolean exists() { + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java new file mode 100644 index 00000000000..f433228e9c4 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceFileInfo.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.LanguageManager; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.ISourcePresentationCreator; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.presentation.SourcePresentationCreatorFactory; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.content.IContentType; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.ui.IEditorInput; + +/** + * Holds information about a source file. + */ +public class SourceFileInfo { + public final String fFileKey; + public final IStorage fFile; + public IStorage fEdition; + public BigInteger[] fLine2Addr; + public Addr2Line[] fAddr2Line; + public volatile IDocument fSource; + public volatile boolean fValid; + public Object fLinesNode; + public Throwable fError; + public volatile SourceReadingJob fReadingJob; + public volatile Job fEditionJob; + public ISourcePresentationCreator fPresentationCreator; + public BigInteger fStartAddress = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + public BigInteger fEndAddress= BigInteger.ZERO; + + public SourceFileInfo(String fileKey, IStorage file) { + fFileKey = fileKey; + fFile = fEdition = file; + } + + /** + * Initialize source document. + * @throws CoreException + */ + public void initSource() throws CoreException { + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + IEditorInput input = new SourceEditorInput(fEdition); + synchronized (provider) { + provider.connect(input); + } + IStatus status = provider.getStatus(input); + if (status != null && !status.isOK()) { + throw new CoreException(status); + } + } + + /** + * Initialize presentation creator. + * @param viewer + */ + public void initPresentationCreator(ITextViewer viewer) { + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + IEditorInput input = new SourceEditorInput(fEdition); + IDocument doc = provider.getDocument(input); + if (doc != null) { + IContentType contentType= null; + if (fEdition instanceof IFile) { + IFile file= (IFile)fEdition; + contentType= CCorePlugin.getContentType(file.getProject(), file.getName()); + } else { + contentType= CCorePlugin.getContentType(fEdition.getName()); + } + ILanguage language= null; + if (contentType != null) { + language= LanguageManager.getInstance().getLanguage(contentType); + } + if (language != null) { + fPresentationCreator= SourcePresentationCreatorFactory.create(language, fEdition, viewer); + } + int lines = doc.getNumberOfLines(); + fLine2Addr = new BigInteger[lines]; + fAddr2Line = new Addr2Line[lines / 10 + 1]; + // assign fSource last, triggering source update + fSource = doc; + } + } + + /** + * Dispose this object. + */ + public void dispose() { + if (fReadingJob != null) { + if (!fReadingJob.cancel()) { + fReadingJob.dispose(); + } + fReadingJob = null; + } + if (fPresentationCreator != null) { + fPresentationCreator.dispose(); + fPresentationCreator = null; + } + SourceDocumentProvider provider = DsfUIPlugin.getSourceDocumentProvider(); + synchronized (provider) { + provider.disconnect(new SourceEditorInput(fEdition)); + } + fSource = null; + fValid = false; +// fLinesNode = null; + } + + public String getLine(int lineNr) { + return getLines(lineNr, lineNr); + } + + public String getLines(int first, int last) { + try { + int startOffset = fSource.getLineOffset(first); + int endOffset; + if (last < fSource.getNumberOfLines()-1) { + IRegion lastRegion = fSource.getLineInformation(last+1); + endOffset = lastRegion.getOffset(); + } else { + // last line + IRegion lastRegion = fSource.getLineInformation(last); + endOffset = lastRegion.getOffset() + lastRegion.getLength(); + } + return fSource.get(startOffset, endOffset - startOffset); + } catch (BadLocationException e) { + return null; + } + } + + public IRegion getRegion(int line, int length) { + try { + IRegion lineRegion = fSource.getLineInformation(line); + return new Region(lineRegion.getOffset(), length); + } catch (BadLocationException e) { + return null; + } + } + + /** + * Get or create text presentation for the given region. + * Must be called in display thread. + * @param region + * @return text presentation + */ + public TextPresentation getPresentation(IRegion region) { + if (fSource != null && fPresentationCreator != null) { + return fPresentationCreator.getPresentation(region, fSource); + } + return null; + } + + /** + * @return offset of given line + */ + public int getLineOffset(int line) { + if (fSource != null) { + try { + return fSource.getLineOffset(line); + } catch (BadLocationException e) { + // ignored + } + } + return -1; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java new file mode 100644 index 00000000000..f5138df554b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourcePosition.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import java.math.BigInteger; + +/** + * SourcePosition + */ +public class SourcePosition extends AddressRangePosition { + + public SourceFileInfo fFileInfo; + public int fLine; + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param fileInfo + * @param line + */ + public SourcePosition(int offset, int length, BigInteger addressOffset, SourceFileInfo fileInfo, int line) { + this(offset, length, addressOffset, fileInfo, line, true); + } + + /** + * + * @param offset + * @param length + * @param addressOffset + * @param fileInfo + * @param line + * @param valid + */ + public SourcePosition(int offset, int length, BigInteger addressOffset, SourceFileInfo fileInfo, int line, boolean valid) { + super(offset, length, addressOffset, BigInteger.ZERO, valid); + fFileInfo = fileInfo; + fLine = line; + } + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java new file mode 100644 index 00000000000..2fb06aa1d53 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/model/SourceReadingJob.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.model; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; + + +/** + * Low-level job to read source files in the background. + */ +public class SourceReadingJob extends Job { + + private final static String NAME = DisassemblyMessages.SourceReadingJob_name; + + private SourceFileInfo fFileInfo; + private Runnable fDone; + + public SourceReadingJob(SourceFileInfo fi, Runnable done) { + super(NAME); + fFileInfo = fi; + fFileInfo.fReadingJob = this; + fDone = done; + if (fi.fFile instanceof ISchedulingRule) { + setRule((ISchedulingRule)fi.fFile); + } + setSystem(true); + // usually short lived job + setPriority(SHORT); + if (fi.fFile.getFullPath() != null) { + String fileName = fi.fFile.getFullPath().lastSegment(); + setName(NAME + " (" + fileName + ')'); //$NON-NLS-1$ + } + } + + public synchronized void dispose() { + fDone = null; + Thread thread = getThread(); + if (thread != null && thread.isAlive()) { + thread.interrupt(); + } + } + + @Override + public IStatus run(IProgressMonitor monitor) { + if (fFileInfo.fEditionJob != null) { + try { + fFileInfo.fEditionJob.join(); + } catch (InterruptedException e) { + // ignore + } + } + try { + fFileInfo.initSource(); + } catch (Throwable e) { + fFileInfo.fError = e; + } finally { + fFileInfo.fReadingJob = null; + synchronized (this) { + if (fDone != null && !getThread().isInterrupted()) { + fDone.run(); + } + } + } + // errors are handled elsewhere + return Status.OK_STATUS; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java new file mode 100644 index 00000000000..e06a20dd61b --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferenceConstants.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences; + +import java.math.BigInteger; + +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.editors.text.EditorsUI; +import org.eclipse.ui.editors.text.TextEditorPreferenceConstants; + +/** + * DisassemblyPreferenceConstants + */ +public class DisassemblyPreferenceConstants { + + public static final String START_ADDRESS = "disassembly.startAddress"; //$NON-NLS-1$ + public static final String END_ADDRESS = "disassembly.endAddress"; //$NON-NLS-1$ + public static final String PC_HISTORY_SIZE = "disassembly.pcHistorySize"; //$NON-NLS-1$ + public static final String SHOW_SOURCE = "disassembly.showSource"; //$NON-NLS-1$ + public static final String SHOW_LABELS = "disassembly.showLabels"; //$NON-NLS-1$ + public static final String SHOW_SYMBOLS = "disassembly.showSymbols"; //$NON-NLS-1$ + public static final String SIMPLIFIED = "disassembly.simplified"; //$NON-NLS-1$ + public static final String INSTRUCTION_RADIX = "disassembly.instructionRadix"; //$NON-NLS-1$ + public static final String ADDRESS_RADIX = "disassembly.addressRadix"; //$NON-NLS-1$ + public static final String SHOW_ADDRESS_RADIX = "disassembly.showAddressRadix"; //$NON-NLS-1$ + public static final String SHOW_ADDRESS_RULER = "disassembly.showAddressRuler"; //$NON-NLS-1$ + public static final String ADDRESS_COLOR = "disassembly.addressColor"; //$NON-NLS-1$ + public static final String SHOW_FUNCTION_OFFSETS = "disassembly.showFunctionOffsetRuler"; //$NON-NLS-1$ + public static final String OPCODE_COLOR = "disassembly.opcodeColor"; //$NON-NLS-1$ + public static final String USE_SOURCE_ONLY_MODE = "disassembly.useSourceOnlyMode"; //$NON-NLS-1$ + public static final String AVOID_READ_BEFORE_PC = "disassembly.avoidReadBeforePC"; //$NON-NLS-1$ + + /** + * + */ + private DisassemblyPreferenceConstants() { + // not intended to be subclassed or instatiated + } + + /** + * Initialize preference default values. + * @param store + */ + public static void initializeDefaults(IPreferenceStore store) { + TextEditorPreferenceConstants.initializeDefaultValues(store); + store.setDefault(START_ADDRESS, 0x0L); + store.setDefault(END_ADDRESS, "0x" + BigInteger.ONE.shiftLeft(64).toString(16)); //$NON-NLS-1$ + store.setDefault(PC_HISTORY_SIZE, 4); + store.setDefault(SHOW_SOURCE, true); + store.setDefault(SHOW_FUNCTION_OFFSETS, false); + store.setDefault(SHOW_LABELS, true); + store.setDefault(SHOW_SYMBOLS, true); + store.setDefault(SIMPLIFIED, true); + store.setDefault(INSTRUCTION_RADIX, 16); + store.setDefault(ADDRESS_RADIX, 16); + store.setDefault(SHOW_ADDRESS_RADIX, false); + store.setDefault(SHOW_ADDRESS_RULER, true); + store.setDefault(AVOID_READ_BEFORE_PC, false); + store.setDefault(USE_SOURCE_ONLY_MODE, false); + PreferenceConverter.setDefault(store, ADDRESS_COLOR, new RGB(0, 96, 0)); + PreferenceConverter.setDefault(store, OPCODE_COLOR, new RGB(96, 0, 0)); + } + + public static class Initializer extends AbstractPreferenceInitializer { + @Override + public void initializeDefaultPreferences() { + IPreferenceStore store = DsfUIPlugin.getDefault().getPreferenceStore(); + initializeDefaults(store); + EditorsUI.useAnnotationsPreferencePage(store); + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java new file mode 100644 index 00000000000..b41dcd46e39 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/disassembly/preferences/DisassemblyPreferencePage.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 Wind River Systems 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.internal.ui.disassembly.preferences; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.DisassemblyMessages; +import org.eclipse.cdt.dsf.debug.internal.ui.disassembly.IDisassemblyHelpContextIds; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +/** + * DisassemblyPreferencePage + */ +public class DisassemblyPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private List